Line | Count | Source |
1 | | /** |
2 | | * @file diff.c |
3 | | * @author Michal Vasko <mvasko@cesnet.cz> |
4 | | * @brief diff functions |
5 | | * |
6 | | * Copyright (c) 2020 - 2025 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 | | |
16 | | #include "diff.h" |
17 | | |
18 | | #include <assert.h> |
19 | | #include <stddef.h> |
20 | | #include <stdint.h> |
21 | | #include <stdio.h> |
22 | | #include <stdlib.h> |
23 | | #include <string.h> |
24 | | |
25 | | #include "compat.h" |
26 | | #include "context.h" |
27 | | #include "dict.h" |
28 | | #include "log.h" |
29 | | #include "ly_common.h" |
30 | | #include "plugins_exts.h" |
31 | | #include "plugins_exts/metadata.h" |
32 | | #include "plugins_internal.h" |
33 | | #include "plugins_types.h" |
34 | | #include "set.h" |
35 | | #include "tree.h" |
36 | | #include "tree_data.h" |
37 | | #include "tree_data_internal.h" |
38 | | #include "tree_edit.h" |
39 | | #include "tree_schema.h" |
40 | | #include "tree_schema_internal.h" |
41 | | |
42 | | #define LOGERR_META(ctx, meta_name, node) \ |
43 | | { \ |
44 | | char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \ |
45 | | LOGERR(ctx, LY_EINVAL, "Failed to find metadata \"%s\" for node \"%s\".", meta_name, __path); \ |
46 | | free(__path); \ |
47 | | } |
48 | | |
49 | | #define LOGERR_NOINST(ctx, node) \ |
50 | | { \ |
51 | | char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \ |
52 | | LOGERR(ctx, LY_EINVAL, "Failed to find node \"%s\" instance in data.", __path); \ |
53 | | free(__path); \ |
54 | | } |
55 | | |
56 | | #define LOGERR_UNEXPVAL(ctx, node, data_source) \ |
57 | 0 | { \ |
58 | 0 | char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \ |
59 | 0 | LOGERR(ctx, LY_EINVAL, "Unexpected value of node \"%s\" in %s.", __path, data_source); \ |
60 | 0 | free(__path); \ |
61 | 0 | } |
62 | | |
63 | | #define LOGERR_MERGEOP(ctx, node, src_op, trg_op) \ |
64 | 0 | { \ |
65 | 0 | char *__path = lyd_path(node, LYD_PATH_STD, NULL, 0); \ |
66 | 0 | LOGERR(ctx, LY_EINVAL, "Unable to merge operation \"%s\" with \"%s\" for node \"%s\".", \ |
67 | 0 | lyd_diff_op2str(trg_op), lyd_diff_op2str(src_op), __path); \ |
68 | 0 | free(__path); \ |
69 | 0 | } |
70 | | |
71 | | static const char * |
72 | | lyd_diff_op2str(enum lyd_diff_op op) |
73 | 0 | { |
74 | 0 | switch (op) { |
75 | 0 | case LYD_DIFF_OP_CREATE: |
76 | 0 | return "create"; |
77 | 0 | case LYD_DIFF_OP_DELETE: |
78 | 0 | return "delete"; |
79 | 0 | case LYD_DIFF_OP_REPLACE: |
80 | 0 | return "replace"; |
81 | 0 | case LYD_DIFF_OP_NONE: |
82 | 0 | return "none"; |
83 | 0 | } |
84 | | |
85 | 0 | LOGINT(NULL); |
86 | 0 | return NULL; |
87 | 0 | } |
88 | | |
89 | | static enum lyd_diff_op |
90 | | lyd_diff_str2op(const char *str) |
91 | 0 | { |
92 | 0 | switch (str[0]) { |
93 | 0 | case 'c': |
94 | 0 | assert(!strcmp(str, "create")); |
95 | 0 | return LYD_DIFF_OP_CREATE; |
96 | 0 | case 'd': |
97 | 0 | assert(!strcmp(str, "delete")); |
98 | 0 | return LYD_DIFF_OP_DELETE; |
99 | 0 | case 'r': |
100 | 0 | assert(!strcmp(str, "replace")); |
101 | 0 | return LYD_DIFF_OP_REPLACE; |
102 | 0 | case 'n': |
103 | 0 | assert(!strcmp(str, "none")); |
104 | 0 | return LYD_DIFF_OP_NONE; |
105 | 0 | } |
106 | | |
107 | 0 | LOGINT(NULL); |
108 | 0 | return 0; |
109 | 0 | } |
110 | | |
111 | | /** |
112 | | * @brief Create diff metadata for a nested user-ordered node with the effective operation "create". |
113 | | * |
114 | | * @param[in] node User-rodered node to update. |
115 | | * @return LY_ERR value. |
116 | | */ |
117 | | static LY_ERR |
118 | | lyd_diff_add_create_nested_userord(struct lyd_node *node) |
119 | 0 | { |
120 | 0 | LY_ERR rc = LY_SUCCESS; |
121 | 0 | const char *meta_name, *meta_val; |
122 | 0 | size_t buflen = 0, bufused = 0; |
123 | 0 | uint32_t pos; |
124 | 0 | char *dyn = NULL; |
125 | |
|
126 | 0 | assert(lysc_is_userordered(node->schema)); |
127 | | |
128 | | /* get correct metadata name and value */ |
129 | 0 | if (lysc_is_dup_inst_list(node->schema)) { |
130 | 0 | meta_name = "yang:position"; |
131 | |
|
132 | 0 | pos = lyd_list_pos(node); |
133 | 0 | if (pos > 1) { |
134 | 0 | if (asprintf(&dyn, "%" PRIu32, pos - 1) == -1) { |
135 | 0 | LOGMEM(LYD_CTX(node)); |
136 | 0 | rc = LY_EMEM; |
137 | 0 | goto cleanup; |
138 | 0 | } |
139 | 0 | meta_val = dyn; |
140 | 0 | } else { |
141 | 0 | meta_val = ""; |
142 | 0 | } |
143 | 0 | } else if (node->schema->nodetype == LYS_LIST) { |
144 | 0 | meta_name = "yang:key"; |
145 | |
|
146 | 0 | if (node->prev->next && (node->prev->schema == node->schema)) { |
147 | 0 | LY_CHECK_GOTO(rc = lyd_path_list_predicate(node->prev, &dyn, &buflen, &bufused, 0), cleanup); |
148 | 0 | meta_val = dyn; |
149 | 0 | } else { |
150 | 0 | meta_val = ""; |
151 | 0 | } |
152 | 0 | } else { |
153 | 0 | meta_name = "yang:value"; |
154 | |
|
155 | 0 | if (node->prev->next && (node->prev->schema == node->schema)) { |
156 | 0 | meta_val = lyd_get_value(node->prev); |
157 | 0 | } else { |
158 | 0 | meta_val = ""; |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | | /* create the metadata */ |
163 | 0 | LY_CHECK_GOTO(rc = lyd_new_meta(NULL, node, NULL, meta_name, meta_val, LYD_NEW_VAL_STORE_ONLY, NULL), cleanup); |
164 | |
|
165 | 0 | cleanup: |
166 | 0 | free(dyn); |
167 | 0 | return rc; |
168 | 0 | } |
169 | | |
170 | | /** |
171 | | * @brief Find metadata/an attribute of a node. |
172 | | * |
173 | | * @param[in] node Node to search. |
174 | | * @param[in] name Metadata/attribute name. |
175 | | * @param[out] meta Metadata found, NULL if not found. |
176 | | * @param[out] attr Attribute found, NULL if not found. |
177 | | */ |
178 | | static void |
179 | | lyd_diff_find_meta(const struct lyd_node *node, const char *name, struct lyd_meta **meta, struct lyd_attr **attr) |
180 | 0 | { |
181 | 0 | struct lyd_meta *m; |
182 | 0 | struct lyd_attr *a; |
183 | |
|
184 | 0 | if (meta) { |
185 | 0 | *meta = NULL; |
186 | 0 | } |
187 | 0 | if (attr) { |
188 | 0 | *attr = NULL; |
189 | 0 | } |
190 | |
|
191 | 0 | if (node->schema) { |
192 | 0 | assert(meta); |
193 | | |
194 | 0 | LY_LIST_FOR(node->meta, m) { |
195 | 0 | if (!strcmp(m->name, name) && !strcmp(m->annotation->module->name, "yang")) { |
196 | 0 | *meta = m; |
197 | 0 | break; |
198 | 0 | } |
199 | 0 | } |
200 | 0 | } else { |
201 | 0 | assert(attr); |
202 | | |
203 | 0 | LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, a) { |
204 | | /* name */ |
205 | 0 | if (strcmp(a->name.name, name)) { |
206 | 0 | continue; |
207 | 0 | } |
208 | | |
209 | | /* module */ |
210 | 0 | switch (a->format) { |
211 | 0 | case LY_VALUE_JSON: |
212 | 0 | if (strcmp(a->name.module_name, "yang")) { |
213 | 0 | continue; |
214 | 0 | } |
215 | 0 | break; |
216 | 0 | case LY_VALUE_XML: |
217 | 0 | if (strcmp(a->name.module_ns, "urn:ietf:params:xml:ns:yang:1")) { |
218 | 0 | continue; |
219 | 0 | } |
220 | 0 | break; |
221 | 0 | default: |
222 | 0 | LOGINT(LYD_CTX(node)); |
223 | 0 | return; |
224 | 0 | } |
225 | | |
226 | 0 | *attr = a; |
227 | 0 | break; |
228 | 0 | } |
229 | 0 | } |
230 | 0 | } |
231 | | |
232 | | /** |
233 | | * @brief Learn operation of a diff node. |
234 | | * |
235 | | * @param[in] diff_node Diff node. |
236 | | * @param[out] op Operation. |
237 | | * @param[out] found Whether any @p op was found. If not set, no found operation is an error. |
238 | | * @return LY_ERR value. |
239 | | */ |
240 | | static LY_ERR |
241 | | lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op, ly_bool *found) |
242 | 0 | { |
243 | 0 | struct lyd_meta *meta = NULL; |
244 | 0 | struct lyd_attr *attr = NULL; |
245 | 0 | const struct lyd_node *diff_parent; |
246 | 0 | const char *str; |
247 | 0 | char *path; |
248 | |
|
249 | 0 | for (diff_parent = diff_node; diff_parent; diff_parent = diff_parent->parent) { |
250 | 0 | lyd_diff_find_meta(diff_parent, "operation", &meta, &attr); |
251 | 0 | if (!meta && !attr) { |
252 | 0 | continue; |
253 | 0 | } |
254 | | |
255 | 0 | str = meta ? lyd_get_meta_value(meta) : attr->value; |
256 | 0 | if ((str[0] == 'r') && (diff_parent != diff_node)) { |
257 | | /* we do not care about this operation if it's in our parent */ |
258 | 0 | continue; |
259 | 0 | } |
260 | 0 | *op = lyd_diff_str2op(str); |
261 | 0 | if (found) { |
262 | 0 | *found = 1; |
263 | 0 | } |
264 | 0 | return LY_SUCCESS; |
265 | 0 | } |
266 | | |
267 | | /* operation not found */ |
268 | 0 | if (found) { |
269 | 0 | *found = 0; |
270 | 0 | return LY_SUCCESS; |
271 | 0 | } else { |
272 | 0 | path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0); |
273 | 0 | LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path); |
274 | 0 | free(path); |
275 | 0 | return LY_EINT; |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | /** |
280 | | * @brief Remove metadata/an attribute from a node. |
281 | | * |
282 | | * @param[in] node Node to update. |
283 | | * @param[in] name Metadata/attribute name. |
284 | | */ |
285 | | static void |
286 | | lyd_diff_del_meta(struct lyd_node *node, const char *name) |
287 | 0 | { |
288 | 0 | struct lyd_meta *meta; |
289 | 0 | struct lyd_attr *attr; |
290 | |
|
291 | 0 | lyd_diff_find_meta(node, name, &meta, &attr); |
292 | |
|
293 | 0 | if (meta) { |
294 | 0 | lyd_free_meta_single(meta); |
295 | 0 | } else if (attr) { |
296 | 0 | lyd_free_attr_single(LYD_CTX(node), attr); |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | /** |
301 | | * @brief Insert a node into siblings. |
302 | | * |
303 | | * - if the node is part of some other tree, it is automatically unlinked. |
304 | | * - difference with the lyd_insert_sibling() is that the subsequent nodes are never inserted. |
305 | | * - insert ignores node ordering, which is fine since it's not desirable to sort diff nodes. |
306 | | * |
307 | | * @param[in] sibling Siblings to insert into, can even be NULL. |
308 | | * @param[in] node Node to insert. |
309 | | * @param[out] first Return the first sibling after insertion. Can be the address of @p sibling. |
310 | | */ |
311 | | static void |
312 | | lyd_diff_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_node **first_sibling) |
313 | 0 | { |
314 | 0 | assert(node && first_sibling); |
315 | | |
316 | 0 | lyd_unlink_ignore_lyds(NULL, node); |
317 | 0 | *first_sibling = lyd_first_sibling(sibling); |
318 | 0 | lyd_insert_node(NULL, first_sibling, node, LYD_INSERT_NODE_LAST_BY_SCHEMA); |
319 | 0 | } |
320 | | |
321 | | /** |
322 | | * @brief Duplicate a node with any parents and connect it to the parent, if any. |
323 | | * |
324 | | * @param[in] node Node to duplicate. |
325 | | * @param[in] op Diff operation. |
326 | | * @param[in] parent Parent to connect to, if any. |
327 | | * @param[in,out] first First top-level node. |
328 | | * @param[out] dup Duplicated @p node. |
329 | | * @return LY_ERR value. |
330 | | */ |
331 | | static LY_ERR |
332 | | lyd_diff_dup(const struct lyd_node *node, enum lyd_diff_op op, struct lyd_node *parent, struct lyd_node **first, |
333 | | struct lyd_node **dup) |
334 | 0 | { |
335 | 0 | struct lyd_node *dup_parent, *d; |
336 | 0 | const struct lyd_node *node_parent; |
337 | 0 | const struct lysc_node *sparent; |
338 | 0 | uint32_t diff_opts; |
339 | |
|
340 | 0 | diff_opts = LYD_DUP_NO_META | LYD_DUP_WITH_FLAGS; |
341 | 0 | if (lysc_is_userordered(node->schema)) { |
342 | 0 | diff_opts |= LYD_DUP_NO_LYDS; |
343 | 0 | } |
344 | 0 | if ((op != LYD_DIFF_OP_REPLACE) || !lysc_is_userordered(node->schema) || (node->schema->flags & LYS_CONFIG_R)) { |
345 | | /* move applies only to the user-ordered list, no descendants */ |
346 | 0 | diff_opts |= LYD_DUP_RECURSIVE; |
347 | 0 | } |
348 | | |
349 | | /* duplicate the node */ |
350 | 0 | LY_CHECK_RET(lyd_dup_single(node, NULL, diff_opts, dup)); |
351 | |
|
352 | 0 | dup_parent = *dup; |
353 | 0 | node_parent = node; |
354 | 0 | sparent = parent ? parent->schema : NULL; |
355 | 0 | while (lysc_data_parent(dup_parent->schema) && |
356 | 0 | !lyd_compare_schema_equal(lysc_data_parent(dup_parent->schema), sparent, 0)) { |
357 | 0 | node_parent = node_parent->parent; |
358 | | |
359 | | /* duplicate the next parent */ |
360 | 0 | LY_CHECK_RET(lyd_dup_single(node_parent, NULL, LYD_DUP_NO_META | LYD_DUP_WITH_FLAGS, &d)); |
361 | | |
362 | | /* connect the existing dup tree into the parent */ |
363 | 0 | lyd_insert_node(d, NULL, dup_parent, LYD_INSERT_NODE_DEFAULT); |
364 | 0 | dup_parent = d; |
365 | 0 | } |
366 | | |
367 | | /* connect to the parent/data tree */ |
368 | 0 | if (parent) { |
369 | 0 | lyd_insert_node(parent, NULL, dup_parent, LYD_INSERT_NODE_DEFAULT); |
370 | 0 | } else { |
371 | 0 | lyd_diff_insert_sibling(*first, dup_parent, first); |
372 | 0 | } |
373 | | |
374 | | /* add parent operation, if any */ |
375 | 0 | if (dup_parent != *dup) { |
376 | 0 | LY_CHECK_RET(lyd_new_meta(NULL, dup_parent, NULL, "yang:operation", "none", LYD_NEW_VAL_STORE_ONLY, NULL)); |
377 | 0 | } |
378 | | |
379 | 0 | return LY_SUCCESS; |
380 | 0 | } |
381 | | |
382 | | LY_ERR |
383 | | lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value, |
384 | | const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position, |
385 | | struct lyd_node **diff, struct lyd_node **diff_node) |
386 | 0 | { |
387 | 0 | struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL, *elem; |
388 | 0 | const struct lyd_node *parent = NULL; |
389 | 0 | enum lyd_diff_op cur_op; |
390 | 0 | struct lyd_meta *meta; |
391 | 0 | ly_bool found; |
392 | |
|
393 | 0 | assert(diff); |
394 | | |
395 | | /* replace leaf always needs orig-default and orig-value */ |
396 | 0 | assert((node->schema->nodetype != LYS_LEAF) || (op != LYD_DIFF_OP_REPLACE) || (orig_default && orig_value)); |
397 | | |
398 | | /* create on userord needs key/value */ |
399 | 0 | assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_CREATE) || |
400 | 0 | (lysc_is_dup_inst_list(node->schema) && position) || key); |
401 | 0 | assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) || |
402 | 0 | (op != LYD_DIFF_OP_CREATE) || (lysc_is_dup_inst_list(node->schema) && position) || value); |
403 | | |
404 | | /* move on userord needs both key and orig-key/value and orig-value */ |
405 | 0 | assert((node->schema->nodetype != LYS_LIST) || !(node->schema->flags & LYS_ORDBY_USER) || (op != LYD_DIFF_OP_REPLACE) || |
406 | 0 | (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (key && orig_key)); |
407 | 0 | assert((node->schema->nodetype != LYS_LEAFLIST) || !(node->schema->flags & LYS_ORDBY_USER) || |
408 | 0 | (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) || |
409 | 0 | (value && orig_value)); |
410 | | |
411 | 0 | if (diff_node) { |
412 | 0 | *diff_node = NULL; |
413 | 0 | } |
414 | | |
415 | | /* find the first existing parent */ |
416 | 0 | siblings = *diff; |
417 | 0 | do { |
418 | | /* find next node parent */ |
419 | 0 | parent = node; |
420 | 0 | while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) { |
421 | 0 | parent = parent->parent; |
422 | 0 | } |
423 | |
|
424 | 0 | if (lysc_is_dup_inst_list(parent->schema)) { |
425 | | /* assume it never exists, we are not able to distinguish whether it does or not */ |
426 | 0 | match = NULL; |
427 | 0 | break; |
428 | 0 | } |
429 | | |
430 | | /* check whether it exists in the diff */ |
431 | 0 | if (lyd_find_sibling_first(siblings, parent, &match)) { |
432 | 0 | break; |
433 | 0 | } |
434 | | |
435 | | /* another parent found */ |
436 | 0 | diff_parent = match; |
437 | | |
438 | | /* move down in the diff */ |
439 | 0 | siblings = lyd_child_no_keys(match); |
440 | 0 | } while (parent != node); |
441 | |
|
442 | 0 | if (match && (parent == node)) { |
443 | | /* special case when there is already an operation on our descendant */ |
444 | 0 | assert(!lyd_diff_get_op(diff_parent, &cur_op, NULL)); |
445 | | |
446 | | /* move it to the end where it is expected (matters for user-ordered lists) */ |
447 | 0 | if (lysc_is_userordered(diff_parent->schema)) { |
448 | 0 | for (elem = diff_parent; elem->next && (elem->next->schema == elem->schema); elem = elem->next) {} |
449 | 0 | if (elem != diff_parent) { |
450 | 0 | LY_CHECK_RET(lyd_insert_after(elem, diff_parent)); |
451 | 0 | } |
452 | 0 | } |
453 | | |
454 | | /* will be replaced by the new operation but keep the current op for descendants */ |
455 | 0 | lyd_diff_del_meta(diff_parent, "operation"); |
456 | 0 | LY_LIST_FOR(lyd_child_no_keys(diff_parent), elem) { |
457 | 0 | lyd_diff_find_meta(elem, "operation", &meta, NULL); |
458 | 0 | if (meta) { |
459 | | /* explicit operation, fine */ |
460 | 0 | continue; |
461 | 0 | } |
462 | | |
463 | | /* set the none operation */ |
464 | 0 | LY_CHECK_RET(lyd_new_meta(NULL, elem, NULL, "yang:operation", "none", LYD_NEW_VAL_STORE_ONLY, NULL)); |
465 | 0 | } |
466 | | |
467 | 0 | dup = diff_parent; |
468 | 0 | } else { |
469 | | /* duplicate the subtree (and connect to the diff if possible) */ |
470 | 0 | LY_CHECK_RET(lyd_diff_dup(node, op, diff_parent, diff, &dup)); |
471 | 0 | } |
472 | | |
473 | | /* add subtree operation if needed */ |
474 | 0 | LY_CHECK_RET(lyd_diff_get_op(dup, &cur_op, &found)); |
475 | 0 | if (!found || (cur_op != op)) { |
476 | 0 | LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:operation", lyd_diff_op2str(op), LYD_NEW_VAL_STORE_ONLY, NULL)); |
477 | 0 | } |
478 | | |
479 | 0 | if (op == LYD_DIFF_OP_CREATE) { |
480 | | /* all nested user-ordered (leaf-)lists need special metadata for create op */ |
481 | 0 | LYD_TREE_DFS_BEGIN(dup, elem) { |
482 | 0 | if ((elem != dup) && lysc_is_userordered(elem->schema)) { |
483 | 0 | LY_CHECK_RET(lyd_diff_add_create_nested_userord(elem)); |
484 | 0 | } |
485 | 0 | LYD_TREE_DFS_END(dup, elem); |
486 | 0 | } |
487 | 0 | } |
488 | | |
489 | | /* orig-default */ |
490 | 0 | if (orig_default) { |
491 | 0 | LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-default", orig_default, LYD_NEW_VAL_STORE_ONLY, NULL)); |
492 | 0 | } |
493 | | |
494 | | /* orig-value */ |
495 | 0 | if (orig_value) { |
496 | 0 | LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-value", orig_value, LYD_NEW_VAL_STORE_ONLY, NULL)); |
497 | 0 | } |
498 | | |
499 | | /* key */ |
500 | 0 | if (key) { |
501 | 0 | LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:key", key, LYD_NEW_VAL_STORE_ONLY, NULL)); |
502 | 0 | } |
503 | | |
504 | | /* value */ |
505 | 0 | if (value) { |
506 | 0 | LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:value", value, LYD_NEW_VAL_STORE_ONLY, NULL)); |
507 | 0 | } |
508 | | |
509 | | /* position */ |
510 | 0 | if (position) { |
511 | 0 | LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:position", position, LYD_NEW_VAL_STORE_ONLY, NULL)); |
512 | 0 | } |
513 | | |
514 | | /* orig-key */ |
515 | 0 | if (orig_key) { |
516 | 0 | LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-key", orig_key, LYD_NEW_VAL_STORE_ONLY, NULL)); |
517 | 0 | } |
518 | | |
519 | | /* orig-position */ |
520 | 0 | if (orig_position) { |
521 | 0 | LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-position", orig_position, LYD_NEW_VAL_STORE_ONLY, NULL)); |
522 | 0 | } |
523 | | |
524 | 0 | if (diff_node) { |
525 | 0 | *diff_node = dup; |
526 | 0 | } |
527 | 0 | return LY_SUCCESS; |
528 | 0 | } |
529 | | |
530 | | /** |
531 | | * @brief Get a userord entry for a specific user-ordered list/leaf-list. Create if does not exist yet. |
532 | | * |
533 | | * @param[in] first Node from the first tree, can be NULL (on create). |
534 | | * @param[in] schema Schema node of the list/leaf-list. |
535 | | * @param[in,out] userord Sized array of userord items. |
536 | | * @return Userord item for all the user-ordered list/leaf-list instances. |
537 | | */ |
538 | | static struct lyd_diff_userord * |
539 | | lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schema, struct lyd_diff_userord **userord) |
540 | 0 | { |
541 | 0 | struct lyd_diff_userord *item; |
542 | 0 | struct lyd_node *iter; |
543 | 0 | const struct lyd_node **node; |
544 | 0 | LY_ARRAY_COUNT_TYPE u; |
545 | |
|
546 | 0 | LY_ARRAY_FOR(*userord, u) { |
547 | 0 | if ((*userord)[u].schema == schema) { |
548 | 0 | return &(*userord)[u]; |
549 | 0 | } |
550 | 0 | } |
551 | | |
552 | | /* it was not added yet, add it now */ |
553 | 0 | LY_ARRAY_NEW_RET(schema->module->ctx, *userord, item, NULL); |
554 | |
|
555 | 0 | item->schema = schema; |
556 | 0 | item->pos = 0; |
557 | 0 | item->inst = NULL; |
558 | | |
559 | | /* store all the instance pointers in the current order */ |
560 | 0 | if (first) { |
561 | 0 | LYD_LIST_FOR_INST(lyd_first_sibling(first), first->schema, iter) { |
562 | 0 | LY_ARRAY_NEW_RET(schema->module->ctx, item->inst, node, NULL); |
563 | 0 | *node = iter; |
564 | 0 | } |
565 | 0 | } |
566 | | |
567 | 0 | return item; |
568 | 0 | } |
569 | | |
570 | | /** |
571 | | * @brief Check whether there are any metadata differences on 2 nodes. |
572 | | * |
573 | | * @param[in] first First node. |
574 | | * @param[in] second Second node. |
575 | | * @return 1 if there are some differences; |
576 | | * @return 0 otherwise. |
577 | | */ |
578 | | static ly_bool |
579 | | lyd_diff_node_metadata_check(const struct lyd_node *first, const struct lyd_node *second) |
580 | 0 | { |
581 | 0 | ly_bool rc = 0; |
582 | 0 | const struct lys_module *mod; |
583 | 0 | const struct lyd_meta *m, **meta_second = NULL; |
584 | 0 | uint32_t i, m_second_count = 0; |
585 | 0 | const struct lyd_node *first_ch, *second_ch; |
586 | |
|
587 | 0 | assert(first && second); |
588 | | |
589 | 0 | mod = ly_ctx_get_module_implemented(LYD_CTX(first), "yang"); |
590 | 0 | assert(mod); |
591 | | |
592 | | /* collect second node metadata that we can delete from */ |
593 | 0 | LY_LIST_FOR(second->meta, m) { |
594 | 0 | if (m->annotation->module == mod) { |
595 | 0 | continue; |
596 | 0 | } |
597 | | |
598 | 0 | meta_second = ly_realloc(meta_second, (m_second_count + 1) * sizeof *meta_second); |
599 | 0 | LY_CHECK_ERR_GOTO(!meta_second, LOGMEM(LYD_CTX(first)), cleanup); |
600 | 0 | meta_second[m_second_count] = m; |
601 | 0 | ++m_second_count; |
602 | 0 | } |
603 | | |
604 | | /* go through first metadata and search for a match in second */ |
605 | 0 | LY_LIST_FOR(first->meta, m) { |
606 | 0 | if (m->annotation->module == mod) { |
607 | 0 | continue; |
608 | 0 | } |
609 | | |
610 | 0 | for (i = 0; i < m_second_count; ++i) { |
611 | 0 | if (!lyd_compare_meta(m, meta_second[i])) { |
612 | 0 | break; |
613 | 0 | } |
614 | 0 | } |
615 | |
|
616 | 0 | if (i == m_second_count) { |
617 | | /* not found */ |
618 | 0 | rc = 1; |
619 | 0 | goto cleanup; |
620 | 0 | } |
621 | | |
622 | | /* found, remove from the second metadata to consider */ |
623 | 0 | --m_second_count; |
624 | 0 | if (i < m_second_count) { |
625 | 0 | memcpy(&meta_second[i], &meta_second[i + 1], (m_second_count - i) * sizeof *meta_second); |
626 | 0 | } |
627 | 0 | } |
628 | | |
629 | 0 | if (m_second_count) { |
630 | | /* not found */ |
631 | 0 | rc = 1; |
632 | 0 | goto cleanup; |
633 | 0 | } |
634 | | |
635 | | /* for lists, we also need to check their keys */ |
636 | 0 | if (first->schema->nodetype == LYS_LIST) { |
637 | 0 | first_ch = lyd_child(first); |
638 | 0 | second_ch = lyd_child(second); |
639 | 0 | while (first_ch && lysc_is_key(first_ch->schema)) { |
640 | | /* check every key */ |
641 | 0 | assert(second_ch && (first_ch->schema == second_ch->schema)); |
642 | 0 | rc = lyd_diff_node_metadata_check(first_ch, second_ch); |
643 | 0 | LY_CHECK_GOTO(rc, cleanup); |
644 | |
|
645 | 0 | first_ch = first_ch->next; |
646 | 0 | second_ch = second_ch->next; |
647 | 0 | } |
648 | 0 | } |
649 | | |
650 | 0 | cleanup: |
651 | 0 | free(meta_second); |
652 | 0 | return rc; |
653 | 0 | } |
654 | | |
655 | | /** |
656 | | * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered |
657 | | * lists/leaf-lists. |
658 | | * |
659 | | * @param[in] first Node from the first tree, can be NULL (on create). |
660 | | * @param[in] second Node from the second tree, can be NULL (on delete). |
661 | | * @param[in] options Diff options. |
662 | | * @param[in] userord_item Userord item of @p first and/or @p second node. |
663 | | * @param[out] op Operation. |
664 | | * @param[out] orig_default Original default metadata. |
665 | | * @param[out] value Value metadata. |
666 | | * @param[out] orig_value Original value metadata |
667 | | * @param[out] key Key metadata. |
668 | | * @param[out] orig_key Original key metadata. |
669 | | * @param[out] position Position metadata. |
670 | | * @param[out] orig_position Original position metadata. |
671 | | * @return LY_SUCCESS on success, |
672 | | * @return LY_ENOT if there is no change to be added into diff, |
673 | | * @return LY_ERR value on other errors. |
674 | | */ |
675 | | static LY_ERR |
676 | | lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, |
677 | | struct lyd_diff_userord *userord_item, enum lyd_diff_op *op, const char **orig_default, char **value, |
678 | | char **orig_value, char **key, char **orig_key, char **position, char **orig_position) |
679 | 0 | { |
680 | 0 | LY_ERR rc = LY_SUCCESS; |
681 | 0 | const struct lysc_node *schema; |
682 | 0 | size_t buflen, bufused; |
683 | 0 | uint32_t first_pos, second_pos, comp_opts; |
684 | |
|
685 | 0 | assert(first || second); |
686 | | |
687 | 0 | *orig_default = NULL; |
688 | 0 | *value = NULL; |
689 | 0 | *orig_value = NULL; |
690 | 0 | *key = NULL; |
691 | 0 | *orig_key = NULL; |
692 | 0 | *position = NULL; |
693 | 0 | *orig_position = NULL; |
694 | |
|
695 | 0 | schema = first ? first->schema : second->schema; |
696 | 0 | assert(lysc_is_userordered(schema)); |
697 | | |
698 | | /* find user-ordered first position */ |
699 | 0 | if (first) { |
700 | 0 | for (first_pos = 0; first_pos < LY_ARRAY_COUNT(userord_item->inst); ++first_pos) { |
701 | 0 | if (userord_item->inst[first_pos] == first) { |
702 | 0 | break; |
703 | 0 | } |
704 | 0 | } |
705 | 0 | assert(first_pos < LY_ARRAY_COUNT(userord_item->inst)); |
706 | 0 | } else { |
707 | 0 | first_pos = 0; |
708 | 0 | } |
709 | | |
710 | | /* prepare position of the next instance */ |
711 | 0 | second_pos = userord_item->pos++; |
712 | | |
713 | | /* learn operation first */ |
714 | 0 | if (!second) { |
715 | 0 | *op = LYD_DIFF_OP_DELETE; |
716 | 0 | } else if (!first) { |
717 | 0 | *op = LYD_DIFF_OP_CREATE; |
718 | 0 | } else { |
719 | 0 | comp_opts = lysc_is_dup_inst_list(second->schema) ? LYD_COMPARE_FULL_RECURSION : 0; |
720 | 0 | if (lyd_compare_single(second, userord_item->inst[second_pos], comp_opts)) { |
721 | | /* in first, there is a different instance on the second position, we are going to move 'first' node */ |
722 | 0 | *op = LYD_DIFF_OP_REPLACE; |
723 | 0 | } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) { |
724 | | /* default flag change */ |
725 | 0 | *op = LYD_DIFF_OP_NONE; |
726 | 0 | } else if ((options & LYD_DIFF_META) && lyd_diff_node_metadata_check(first, second)) { |
727 | | /* metadata changes */ |
728 | 0 | *op = LYD_DIFF_OP_NONE; |
729 | 0 | } else { |
730 | | /* no changes */ |
731 | 0 | return LY_ENOT; |
732 | 0 | } |
733 | 0 | } |
734 | | |
735 | | /* |
736 | | * set each attribute correctly based on the operation and node type |
737 | | */ |
738 | | |
739 | | /* orig-default */ |
740 | 0 | if ((schema->nodetype == LYS_LEAFLIST) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) { |
741 | 0 | if (first->flags & LYD_DEFAULT) { |
742 | 0 | *orig_default = "true"; |
743 | 0 | } else { |
744 | 0 | *orig_default = "false"; |
745 | 0 | } |
746 | 0 | } |
747 | | |
748 | | /* value */ |
749 | 0 | if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) && |
750 | 0 | ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) { |
751 | 0 | if (second_pos) { |
752 | 0 | *value = strdup(lyd_get_value(userord_item->inst[second_pos - 1])); |
753 | 0 | LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup); |
754 | 0 | } else { |
755 | 0 | *value = strdup(""); |
756 | 0 | LY_CHECK_ERR_GOTO(!*value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup); |
757 | 0 | } |
758 | 0 | } |
759 | | |
760 | | /* orig-value */ |
761 | 0 | if ((schema->nodetype == LYS_LEAFLIST) && !lysc_is_dup_inst_list(schema) && |
762 | 0 | ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) { |
763 | 0 | if (first_pos) { |
764 | 0 | *orig_value = strdup(lyd_get_value(userord_item->inst[first_pos - 1])); |
765 | 0 | LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup); |
766 | 0 | } else { |
767 | 0 | *orig_value = strdup(""); |
768 | 0 | LY_CHECK_ERR_GOTO(!*orig_value, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup); |
769 | 0 | } |
770 | 0 | } |
771 | | |
772 | | /* key */ |
773 | 0 | if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) && |
774 | 0 | ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) { |
775 | 0 | if (second_pos) { |
776 | 0 | buflen = bufused = 0; |
777 | 0 | LY_CHECK_GOTO(rc = lyd_path_list_predicate(userord_item->inst[second_pos - 1], key, &buflen, &bufused, 0), cleanup); |
778 | 0 | } else { |
779 | 0 | *key = strdup(""); |
780 | 0 | LY_CHECK_ERR_GOTO(!*key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup); |
781 | 0 | } |
782 | 0 | } |
783 | | |
784 | | /* orig-key */ |
785 | 0 | if ((schema->nodetype == LYS_LIST) && !lysc_is_dup_inst_list(schema) && |
786 | 0 | ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) { |
787 | 0 | if (first_pos) { |
788 | 0 | buflen = bufused = 0; |
789 | 0 | LY_CHECK_GOTO(rc = lyd_path_list_predicate(userord_item->inst[first_pos - 1], orig_key, &buflen, &bufused, 0), cleanup); |
790 | 0 | } else { |
791 | 0 | *orig_key = strdup(""); |
792 | 0 | LY_CHECK_ERR_GOTO(!*orig_key, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup); |
793 | 0 | } |
794 | 0 | } |
795 | | |
796 | | /* position */ |
797 | 0 | if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_CREATE))) { |
798 | 0 | if (second_pos) { |
799 | 0 | if (asprintf(position, "%" PRIu32, second_pos) == -1) { |
800 | 0 | LOGMEM(schema->module->ctx); |
801 | 0 | rc = LY_EMEM; |
802 | 0 | goto cleanup; |
803 | 0 | } |
804 | 0 | } else { |
805 | 0 | *position = strdup(""); |
806 | 0 | LY_CHECK_ERR_GOTO(!*position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup); |
807 | 0 | } |
808 | 0 | } |
809 | | |
810 | | /* orig-position */ |
811 | 0 | if (lysc_is_dup_inst_list(schema) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_DELETE))) { |
812 | 0 | if (first_pos) { |
813 | 0 | if (asprintf(orig_position, "%" PRIu32, first_pos) == -1) { |
814 | 0 | LOGMEM(schema->module->ctx); |
815 | 0 | rc = LY_EMEM; |
816 | 0 | goto cleanup; |
817 | 0 | } |
818 | 0 | } else { |
819 | 0 | *orig_position = strdup(""); |
820 | 0 | LY_CHECK_ERR_GOTO(!*orig_position, LOGMEM(schema->module->ctx); rc = LY_EMEM, cleanup); |
821 | 0 | } |
822 | 0 | } |
823 | | |
824 | | /* |
825 | | * update our instances - apply the change |
826 | | */ |
827 | 0 | if (*op == LYD_DIFF_OP_CREATE) { |
828 | | /* insert the instance */ |
829 | 0 | LY_ARRAY_CREATE_GOTO(schema->module->ctx, userord_item->inst, 1, rc, cleanup); |
830 | 0 | if (second_pos < LY_ARRAY_COUNT(userord_item->inst)) { |
831 | 0 | memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos, |
832 | 0 | (LY_ARRAY_COUNT(userord_item->inst) - second_pos) * sizeof *userord_item->inst); |
833 | 0 | } |
834 | 0 | LY_ARRAY_INCREMENT(userord_item->inst); |
835 | 0 | userord_item->inst[second_pos] = second; |
836 | |
|
837 | 0 | } else if (*op == LYD_DIFF_OP_DELETE) { |
838 | | /* remove the instance */ |
839 | 0 | if (first_pos + 1 < LY_ARRAY_COUNT(userord_item->inst)) { |
840 | 0 | memmove(userord_item->inst + first_pos, userord_item->inst + first_pos + 1, |
841 | 0 | (LY_ARRAY_COUNT(userord_item->inst) - first_pos - 1) * sizeof *userord_item->inst); |
842 | 0 | } |
843 | 0 | LY_ARRAY_DECREMENT(userord_item->inst); |
844 | |
|
845 | 0 | } else if (*op == LYD_DIFF_OP_REPLACE) { |
846 | | /* move the instances */ |
847 | 0 | memmove(userord_item->inst + second_pos + 1, userord_item->inst + second_pos, |
848 | 0 | (first_pos - second_pos) * sizeof *userord_item->inst); |
849 | 0 | userord_item->inst[second_pos] = first; |
850 | 0 | } |
851 | | |
852 | 0 | cleanup: |
853 | 0 | if (rc) { |
854 | 0 | free(*value); |
855 | 0 | *value = NULL; |
856 | 0 | free(*orig_value); |
857 | 0 | *orig_value = NULL; |
858 | 0 | free(*key); |
859 | 0 | *key = NULL; |
860 | 0 | free(*orig_key); |
861 | 0 | *orig_key = NULL; |
862 | 0 | free(*position); |
863 | 0 | *position = NULL; |
864 | 0 | free(*orig_position); |
865 | 0 | *orig_position = NULL; |
866 | 0 | } |
867 | 0 | return rc; |
868 | 0 | } |
869 | | |
870 | | /** |
871 | | * @brief Get all the metadata to be stored in a diff for the 2 nodes. Cannot be used for user-ordered |
872 | | * lists/leaf-lists. |
873 | | * |
874 | | * @param[in] first Node from the first tree, can be NULL (on create). |
875 | | * @param[in] second Node from the second tree, can be NULL (on delete). |
876 | | * @param[in] options Diff options. |
877 | | * @param[out] op Operation. |
878 | | * @param[out] orig_default Original default metadata. |
879 | | * @param[out] orig_value Original value metadata. |
880 | | * @return LY_SUCCESS on success, |
881 | | * @return LY_ENOT if there is no change to be added into diff, |
882 | | * @return LY_ERR value on other errors. |
883 | | */ |
884 | | static LY_ERR |
885 | | lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, enum lyd_diff_op *op, |
886 | | const char **orig_default, char **orig_value) |
887 | 0 | { |
888 | 0 | const struct lysc_node *schema; |
889 | 0 | const char *str_val; |
890 | |
|
891 | 0 | assert(first || second); |
892 | | |
893 | 0 | *orig_default = NULL; |
894 | 0 | *orig_value = NULL; |
895 | |
|
896 | 0 | schema = first ? first->schema : second->schema; |
897 | 0 | assert(!lysc_is_userordered(schema)); |
898 | | |
899 | | /* learn operation first */ |
900 | 0 | if (!second) { |
901 | 0 | *op = LYD_DIFF_OP_DELETE; |
902 | 0 | } else if (!first) { |
903 | 0 | *op = LYD_DIFF_OP_CREATE; |
904 | 0 | } else { |
905 | 0 | switch (schema->nodetype) { |
906 | 0 | case LYS_CONTAINER: |
907 | 0 | case LYS_RPC: |
908 | 0 | case LYS_ACTION: |
909 | 0 | case LYS_NOTIF: |
910 | 0 | if ((options & LYD_DIFF_META) && lyd_diff_node_metadata_check(first, second)) { |
911 | | /* metadata changes */ |
912 | 0 | *op = LYD_DIFF_OP_NONE; |
913 | 0 | } else { |
914 | | /* no changes */ |
915 | 0 | return LY_ENOT; |
916 | 0 | } |
917 | 0 | break; |
918 | 0 | case LYS_LIST: |
919 | 0 | case LYS_LEAFLIST: |
920 | 0 | if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) { |
921 | | /* default flag change */ |
922 | 0 | *op = LYD_DIFF_OP_NONE; |
923 | 0 | } else if ((options & LYD_DIFF_META) && lyd_diff_node_metadata_check(first, second)) { |
924 | | /* metadata changes */ |
925 | 0 | *op = LYD_DIFF_OP_NONE; |
926 | 0 | } else { |
927 | | /* no changes */ |
928 | 0 | return LY_ENOT; |
929 | 0 | } |
930 | 0 | break; |
931 | 0 | case LYS_LEAF: |
932 | 0 | case LYS_ANYXML: |
933 | 0 | case LYS_ANYDATA: |
934 | 0 | if (lyd_compare_single(first, second, 0)) { |
935 | | /* different values */ |
936 | 0 | *op = LYD_DIFF_OP_REPLACE; |
937 | 0 | } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) { |
938 | | /* default flag change */ |
939 | 0 | *op = LYD_DIFF_OP_NONE; |
940 | 0 | } else if ((options & LYD_DIFF_META) && lyd_diff_node_metadata_check(first, second)) { |
941 | | /* metadata changes */ |
942 | 0 | *op = LYD_DIFF_OP_NONE; |
943 | 0 | } else { |
944 | | /* no changes */ |
945 | 0 | return LY_ENOT; |
946 | 0 | } |
947 | 0 | break; |
948 | 0 | default: |
949 | 0 | LOGINT_RET(schema->module->ctx); |
950 | 0 | } |
951 | 0 | } |
952 | | |
953 | | /* |
954 | | * set each attribute correctly based on the operation and node type |
955 | | */ |
956 | | |
957 | | /* orig-default */ |
958 | 0 | if ((schema->nodetype & LYD_NODE_TERM) && ((*op == LYD_DIFF_OP_REPLACE) || (*op == LYD_DIFF_OP_NONE))) { |
959 | 0 | if (first->flags & LYD_DEFAULT) { |
960 | 0 | *orig_default = "true"; |
961 | 0 | } else { |
962 | 0 | *orig_default = "false"; |
963 | 0 | } |
964 | 0 | } |
965 | | |
966 | | /* orig-value */ |
967 | 0 | if ((schema->nodetype & (LYS_LEAF | LYS_ANYDATA)) && (*op == LYD_DIFF_OP_REPLACE)) { |
968 | 0 | if (schema->nodetype == LYS_LEAF) { |
969 | 0 | str_val = lyd_get_value(first); |
970 | 0 | *orig_value = strdup(str_val ? str_val : ""); |
971 | 0 | LY_CHECK_ERR_RET(!*orig_value, LOGMEM(schema->module->ctx), LY_EMEM); |
972 | 0 | } else { |
973 | 0 | LY_CHECK_RET(lyd_any_value_str(first, LYD_XML, orig_value)); |
974 | 0 | } |
975 | 0 | } |
976 | | |
977 | 0 | return LY_SUCCESS; |
978 | 0 | } |
979 | | |
980 | | /** |
981 | | * @brief Find a matching instance of a node in a data tree. |
982 | | * |
983 | | * @param[in] siblings Siblings to search in. |
984 | | * @param[in] target Target node to search for. |
985 | | * @param[in] defaults Whether to consider (or ignore) default values. |
986 | | * @param[in,out] dup_inst_ht Duplicate instance cache. |
987 | | * @param[out] match Found match, NULL if no matching node found. |
988 | | * @return LY_ERR value. |
989 | | */ |
990 | | static LY_ERR |
991 | | lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults, |
992 | | struct ly_ht **dup_inst_ht, struct lyd_node **match) |
993 | 0 | { |
994 | 0 | LY_ERR r; |
995 | |
|
996 | 0 | if (!target->schema) { |
997 | | /* try to find the same opaque node */ |
998 | 0 | r = lyd_find_sibling_opaq_next(siblings, LYD_NAME(target), match); |
999 | 0 | } else if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) { |
1000 | | /* try to find the exact instance */ |
1001 | 0 | r = lyd_find_sibling_first(siblings, target, match); |
1002 | 0 | } else { |
1003 | | /* try to simply find the node, there cannot be more instances */ |
1004 | 0 | r = lyd_find_sibling_val(siblings, target->schema, NULL, 0, match); |
1005 | 0 | } |
1006 | 0 | if (r && (r != LY_ENOTFOUND)) { |
1007 | 0 | return r; |
1008 | 0 | } |
1009 | | |
1010 | | /* update match as needed */ |
1011 | 0 | LY_CHECK_RET(lyd_dup_inst_next(match, dup_inst_ht)); |
1012 | |
|
1013 | 0 | if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) { |
1014 | | /* ignore default nodes */ |
1015 | 0 | *match = NULL; |
1016 | 0 | } |
1017 | 0 | return LY_SUCCESS; |
1018 | 0 | } |
1019 | | |
1020 | | /** |
1021 | | * @brief Create a diff metadata instance. |
1022 | | * |
1023 | | * @param[in,out] parent Parent node of the diff metadata. |
1024 | | * @param[in] diff_meta_name Diff metadata name with a mdoule name. |
1025 | | * @param[in] meta_module Changed metadata module name. |
1026 | | * @param[in] meta_name Changed metadata name. |
1027 | | * @param[in] meta_value Changed metadata value. |
1028 | | * @return LY_ERR value. |
1029 | | */ |
1030 | | static LY_ERR |
1031 | | lyd_diff_node_metadata_add(struct lyd_node *parent, const char *diff_meta_name, const char *meta_module, |
1032 | | const char *meta_name, const char *meta_value) |
1033 | 0 | { |
1034 | 0 | LY_ERR rc = LY_SUCCESS; |
1035 | 0 | char *val = NULL; |
1036 | | |
1037 | | /* prepare the value */ |
1038 | 0 | if (asprintf(&val, "%s:%s=%s", meta_module, meta_name, meta_value) == -1) { |
1039 | 0 | LOGMEM(LYD_CTX(parent)); |
1040 | 0 | rc = LY_EMEM; |
1041 | 0 | goto cleanup; |
1042 | 0 | } |
1043 | | |
1044 | | /* create the metadata */ |
1045 | 0 | LY_CHECK_GOTO(rc = lyd_new_meta(NULL, parent, NULL, diff_meta_name, val, 0, NULL), cleanup); |
1046 | |
|
1047 | 0 | cleanup: |
1048 | 0 | free(val); |
1049 | 0 | return rc; |
1050 | 0 | } |
1051 | | |
1052 | | /** |
1053 | | * @brief Add metadata differences of 2 nodes into a diff node. |
1054 | | * |
1055 | | * @param[in] first First node. |
1056 | | * @param[in] second Second node. |
1057 | | * @param[in,out] diff_node Diff node to add to. |
1058 | | * @return LY_ERR value. |
1059 | | */ |
1060 | | static LY_ERR |
1061 | | lyd_diff_node_metadata(const struct lyd_node *first, const struct lyd_node *second, struct lyd_node *diff_node) |
1062 | 0 | { |
1063 | 0 | LY_ERR rc = LY_SUCCESS; |
1064 | 0 | const struct lys_module *mod; |
1065 | 0 | const struct lyd_meta *m, **meta_second = NULL; |
1066 | 0 | uint32_t i, m_second_count = 0, match_ann_idx; |
1067 | |
|
1068 | 0 | mod = ly_ctx_get_module_implemented(LYD_CTX(diff_node), "yang"); |
1069 | 0 | assert(mod); |
1070 | | |
1071 | | /* collect second node metadata that we can delete from */ |
1072 | 0 | if (second) { |
1073 | 0 | LY_LIST_FOR(second->meta, m) { |
1074 | 0 | if (m->annotation->module == mod) { |
1075 | 0 | continue; |
1076 | 0 | } |
1077 | | |
1078 | 0 | meta_second = ly_realloc(meta_second, (m_second_count + 1) * sizeof *meta_second); |
1079 | 0 | LY_CHECK_ERR_GOTO(!meta_second, LOGMEM(LYD_CTX(diff_node)); rc = LY_EMEM, cleanup); |
1080 | 0 | meta_second[m_second_count] = m; |
1081 | 0 | ++m_second_count; |
1082 | 0 | } |
1083 | 0 | } |
1084 | | |
1085 | | /* go through first metadata and search for match in second */ |
1086 | 0 | if (first) { |
1087 | 0 | LY_LIST_FOR(first->meta, m) { |
1088 | 0 | if (m->annotation->module == mod) { |
1089 | 0 | continue; |
1090 | 0 | } |
1091 | | |
1092 | 0 | match_ann_idx = m_second_count; |
1093 | 0 | for (i = 0; i < m_second_count; ++i) { |
1094 | | /* annotation match */ |
1095 | 0 | if (m->annotation != meta_second[i]->annotation) { |
1096 | 0 | continue; |
1097 | 0 | } |
1098 | 0 | if (match_ann_idx == m_second_count) { |
1099 | 0 | match_ann_idx = i; |
1100 | 0 | } |
1101 | | |
1102 | | /* value match */ |
1103 | 0 | if (LYSC_GET_TYPE_PLG(m->value.realtype->plugin_ref)->compare(LYD_CTX(diff_node), |
1104 | 0 | &m->value, &meta_second[i]->value)) { |
1105 | 0 | continue; |
1106 | 0 | } |
1107 | 0 | break; |
1108 | 0 | } |
1109 | |
|
1110 | 0 | if (i < m_second_count) { |
1111 | | /* found, no change */ |
1112 | 0 | --m_second_count; |
1113 | 0 | if (i < m_second_count) { |
1114 | 0 | memcpy(&meta_second[i], &meta_second[i + 1], (m_second_count - i) * sizeof *meta_second); |
1115 | 0 | } |
1116 | 0 | } else if (match_ann_idx < m_second_count) { |
1117 | | /* found with a different value, replace */ |
1118 | 0 | i = match_ann_idx; |
1119 | 0 | rc = lyd_diff_node_metadata_add(diff_node, "yang:meta-replace", meta_second[i]->annotation->module->name, |
1120 | 0 | meta_second[i]->name, lyd_get_meta_value(meta_second[i])); |
1121 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1122 | 0 | rc = lyd_diff_node_metadata_add(diff_node, "yang:meta-orig", m->annotation->module->name, m->name, |
1123 | 0 | lyd_get_meta_value(m)); |
1124 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1125 | |
|
1126 | 0 | --m_second_count; |
1127 | 0 | if (i < m_second_count) { |
1128 | 0 | memcpy(&meta_second[i], &meta_second[i + 1], (m_second_count - i) * sizeof *meta_second); |
1129 | 0 | } |
1130 | 0 | } else { |
1131 | | /* not found, delete */ |
1132 | 0 | rc = lyd_diff_node_metadata_add(diff_node, "yang:meta-delete", m->annotation->module->name, m->name, |
1133 | 0 | lyd_get_meta_value(m)); |
1134 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1135 | 0 | } |
1136 | 0 | } |
1137 | 0 | } |
1138 | | |
1139 | 0 | for (i = 0; i < m_second_count; ++i) { |
1140 | | /* not processed, create */ |
1141 | 0 | rc = lyd_diff_node_metadata_add(diff_node, "yang:meta-create", meta_second[i]->annotation->module->name, |
1142 | 0 | meta_second[i]->name, lyd_get_meta_value(meta_second[i])); |
1143 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1144 | 0 | } |
1145 | | |
1146 | 0 | cleanup: |
1147 | 0 | free(meta_second); |
1148 | 0 | return rc; |
1149 | 0 | } |
1150 | | |
1151 | | /** |
1152 | | * @brief Add metadata differences of 2 subtrees into a diff subtree. |
1153 | | * |
1154 | | * @param[in] first_subtree First subtree. |
1155 | | * @param[in] second_subtree Second subtree. |
1156 | | * @param[in] keys_only Whether to check all the subtrees recursively or only the direct key children. |
1157 | | * @param[in,out] diff_subtree Diff subtree to add to. |
1158 | | * @return LY_ERR value. |
1159 | | */ |
1160 | | static LY_ERR |
1161 | | lyd_diff_node_metadata_r(const struct lyd_node *first_subtree, const struct lyd_node *second_subtree, ly_bool keys_only, |
1162 | | struct lyd_node *diff_subtree) |
1163 | 0 | { |
1164 | 0 | struct lyd_node *diff_node, *first, *second; |
1165 | | |
1166 | | /* metadata diff on the node itself */ |
1167 | 0 | LY_CHECK_RET(lyd_diff_node_metadata(first_subtree, second_subtree, diff_subtree)); |
1168 | | |
1169 | | /* metadata diff on all the children, recursively */ |
1170 | 0 | LY_LIST_FOR(lyd_child(diff_subtree), diff_node) { |
1171 | 0 | if (keys_only && !lysc_is_key(diff_node->schema)) { |
1172 | 0 | break; |
1173 | 0 | } |
1174 | | |
1175 | 0 | lyd_find_sibling_first(lyd_child(first_subtree), diff_node, &first); |
1176 | 0 | lyd_find_sibling_first(lyd_child(second_subtree), diff_node, &second); |
1177 | 0 | LY_CHECK_RET(lyd_diff_node_metadata_r(first, second, keys_only, diff_node)); |
1178 | 0 | } |
1179 | | |
1180 | 0 | return LY_SUCCESS; |
1181 | 0 | } |
1182 | | |
1183 | | /** |
1184 | | * @brief Perform diff for all siblings at certain depth, recursively. |
1185 | | * |
1186 | | * For user-ordered lists/leaf-lists a specific structure is used for storing |
1187 | | * the current order. The idea is to apply all the generated diff changes |
1188 | | * virtually on the first tree so that we can continue to generate correct |
1189 | | * changes after some were already generated. |
1190 | | * |
1191 | | * The algorithm then uses second tree position-based changes with a before |
1192 | | * (preceding) item anchor. |
1193 | | * |
1194 | | * Example: |
1195 | | * |
1196 | | * Virtual first tree leaf-list order: |
1197 | | * 1 2 [3] 4 5 |
1198 | | * |
1199 | | * Second tree leaf-list order: |
1200 | | * 1 2 [5] 3 4 |
1201 | | * |
1202 | | * We are at the 3rd node now. We look at whether the nodes on the 3rd position |
1203 | | * match - they do not - move nodes so that the 3rd position node is final -> |
1204 | | * -> move node 5 to the 3rd position -> move node 5 after node 2. |
1205 | | * |
1206 | | * Required properties: |
1207 | | * Stored operations (move) should not be affected by later operations - |
1208 | | * - would cause a redundantly long list of operations, possibly inifinite. |
1209 | | * |
1210 | | * Implemenation justification: |
1211 | | * First, all delete operations and only then move/create operations are stored. |
1212 | | * Also, preceding anchor is used and after each iteration another node is |
1213 | | * at its final position. That results in the invariant that all preceding |
1214 | | * nodes are final and will not be changed by the later operations, meaning |
1215 | | * they can safely be used as anchors for the later operations. |
1216 | | * |
1217 | | * @param[in] first First tree first sibling. |
1218 | | * @param[in] second Second tree first sibling. |
1219 | | * @param[in] options Diff options. |
1220 | | * @param[in] nosiblings Whether to skip following siblings. |
1221 | | * @param[in,out] diff Diff to append to. |
1222 | | * @return LY_ERR value. |
1223 | | */ |
1224 | | static LY_ERR |
1225 | | lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings, |
1226 | | struct lyd_node **diff) |
1227 | 0 | { |
1228 | 0 | LY_ERR rc = LY_SUCCESS, r; |
1229 | 0 | const struct lyd_node *iter_first, *iter_second; |
1230 | 0 | struct lyd_node *match_second, *match_first, *diff_node; |
1231 | 0 | struct lyd_diff_userord *userord = NULL, *userord_item; |
1232 | 0 | struct ly_ht *dup_inst_first = NULL, *dup_inst_second = NULL; |
1233 | 0 | LY_ARRAY_COUNT_TYPE u; |
1234 | 0 | enum lyd_diff_op op; |
1235 | 0 | const char *orig_default; |
1236 | 0 | char *orig_value, *key, *value, *position, *orig_key, *orig_position; |
1237 | | |
1238 | | /* compare first tree to the second tree - delete, replace, none */ |
1239 | 0 | LY_LIST_FOR(first, iter_first) { |
1240 | 0 | if (!iter_first->schema) { |
1241 | 0 | continue; |
1242 | 0 | } |
1243 | | |
1244 | 0 | assert(!(iter_first->schema->flags & LYS_KEY)); |
1245 | 0 | if ((iter_first->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) { |
1246 | | /* skip default nodes */ |
1247 | 0 | continue; |
1248 | 0 | } |
1249 | | |
1250 | 0 | diff_node = NULL; |
1251 | | |
1252 | | /* find a match in the second tree */ |
1253 | 0 | LY_CHECK_GOTO(rc = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second, |
1254 | 0 | &match_second), cleanup); |
1255 | |
|
1256 | 0 | if (lysc_is_userordered(iter_first->schema)) { |
1257 | | /* get (create) userord entry */ |
1258 | 0 | userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord); |
1259 | 0 | LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); rc = LY_EMEM, cleanup); |
1260 | | |
1261 | | /* we are handling only user-ordered node delete now */ |
1262 | 0 | if (!match_second) { |
1263 | | /* get all the attributes */ |
1264 | 0 | LY_CHECK_GOTO(rc = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op, |
1265 | 0 | &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup); |
1266 | | |
1267 | | /* there must be changes, it is deleted */ |
1268 | 0 | assert(op == LYD_DIFF_OP_DELETE); |
1269 | 0 | rc = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key, |
1270 | 0 | orig_position, diff, &diff_node); |
1271 | 0 | free(orig_value); |
1272 | 0 | free(key); |
1273 | 0 | free(value); |
1274 | 0 | free(position); |
1275 | 0 | free(orig_key); |
1276 | 0 | free(orig_position); |
1277 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1278 | 0 | } |
1279 | 0 | } else { |
1280 | | /* get all the attributes */ |
1281 | 0 | r = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value); |
1282 | 0 | if (r && (r != LY_ENOT)) { |
1283 | 0 | goto cleanup; |
1284 | 0 | } |
1285 | | |
1286 | | /* add into diff if there are any changes */ |
1287 | 0 | if (!r) { |
1288 | 0 | if (op == LYD_DIFF_OP_DELETE) { |
1289 | 0 | rc = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff, &diff_node); |
1290 | 0 | } else { |
1291 | 0 | assert(match_second); |
1292 | 0 | rc = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff, &diff_node); |
1293 | 0 | } |
1294 | 0 | free(orig_value); |
1295 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1296 | 0 | } |
1297 | 0 | } |
1298 | | |
1299 | 0 | if (match_second) { |
1300 | 0 | if ((options & LYD_DIFF_META) && diff_node) { |
1301 | | /* create metadata diff for the node (and list keys, if relevant) */ |
1302 | 0 | LY_CHECK_GOTO(rc = lyd_diff_node_metadata_r(iter_first, match_second, 1, diff_node), cleanup); |
1303 | 0 | } |
1304 | | |
1305 | | /* check descendants, if any, recursively */ |
1306 | 0 | LY_CHECK_GOTO(rc = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second), |
1307 | 0 | options, 0, diff), cleanup); |
1308 | 0 | } else { |
1309 | 0 | if ((options & LYD_DIFF_META) && diff_node) { |
1310 | | /* create metadata diff for the node and all its descendants */ |
1311 | 0 | LY_CHECK_GOTO(rc = lyd_diff_node_metadata_r(iter_first, NULL, 0, diff_node), cleanup); |
1312 | 0 | } |
1313 | 0 | } |
1314 | | |
1315 | 0 | if (nosiblings) { |
1316 | 0 | break; |
1317 | 0 | } |
1318 | 0 | } |
1319 | | |
1320 | | /* reset all cached positions */ |
1321 | 0 | LY_ARRAY_FOR(userord, u) { |
1322 | 0 | userord[u].pos = 0; |
1323 | 0 | } |
1324 | | |
1325 | | /* compare second tree to the first tree - create, user-ordered move */ |
1326 | 0 | LY_LIST_FOR(second, iter_second) { |
1327 | 0 | if (!iter_second->schema) { |
1328 | 0 | continue; |
1329 | 0 | } |
1330 | | |
1331 | 0 | assert(!(iter_second->schema->flags & LYS_KEY)); |
1332 | 0 | if ((iter_second->flags & LYD_DEFAULT) && !(options & LYD_DIFF_DEFAULTS)) { |
1333 | | /* skip default nodes */ |
1334 | 0 | continue; |
1335 | 0 | } |
1336 | | |
1337 | 0 | diff_node = NULL; |
1338 | | |
1339 | | /* find a match in the first tree */ |
1340 | 0 | LY_CHECK_GOTO(rc = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first, |
1341 | 0 | &match_first), cleanup); |
1342 | |
|
1343 | 0 | if (lysc_is_userordered(iter_second->schema)) { |
1344 | | /* get userord entry */ |
1345 | 0 | userord_item = lyd_diff_userord_get(match_first, iter_second->schema, &userord); |
1346 | 0 | LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); rc = LY_EMEM, cleanup); |
1347 | | |
1348 | | /* get all the attributes */ |
1349 | 0 | r = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default, |
1350 | 0 | &value, &orig_value, &key, &orig_key, &position, &orig_position); |
1351 | 0 | if (r && (r != LY_ENOT)) { |
1352 | 0 | goto cleanup; |
1353 | 0 | } |
1354 | | |
1355 | | /* add into diff if there are any changes */ |
1356 | 0 | if (!r) { |
1357 | 0 | rc = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key, |
1358 | 0 | orig_position, diff, &diff_node); |
1359 | 0 | free(orig_value); |
1360 | 0 | free(key); |
1361 | 0 | free(value); |
1362 | 0 | free(position); |
1363 | 0 | free(orig_key); |
1364 | 0 | free(orig_position); |
1365 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1366 | 0 | } |
1367 | 0 | } else if (!match_first) { |
1368 | | /* get all the attributes */ |
1369 | 0 | LY_CHECK_GOTO(rc = lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup); |
1370 | | |
1371 | | /* there must be changes, it is created */ |
1372 | 0 | assert(op == LYD_DIFF_OP_CREATE); |
1373 | 0 | rc = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff, &diff_node); |
1374 | 0 | free(orig_value); |
1375 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1376 | 0 | } /* else was handled */ |
1377 | | |
1378 | 0 | if ((options & LYD_DIFF_META) && diff_node) { |
1379 | | /* create metadata diff for the node and all its descendants */ |
1380 | 0 | LY_CHECK_GOTO(rc = lyd_diff_node_metadata_r(match_first, iter_second, 0, diff_node), cleanup); |
1381 | 0 | } |
1382 | | |
1383 | 0 | if (nosiblings) { |
1384 | 0 | break; |
1385 | 0 | } |
1386 | 0 | } |
1387 | | |
1388 | 0 | cleanup: |
1389 | 0 | lyd_dup_inst_free(dup_inst_first); |
1390 | 0 | lyd_dup_inst_free(dup_inst_second); |
1391 | 0 | LY_ARRAY_FOR(userord, u) { |
1392 | 0 | LY_ARRAY_FREE(userord[u].inst); |
1393 | 0 | } |
1394 | 0 | LY_ARRAY_FREE(userord); |
1395 | 0 | if (rc) { |
1396 | 0 | lyd_free_siblings(*diff); |
1397 | 0 | *diff = NULL; |
1398 | 0 | } |
1399 | 0 | return rc; |
1400 | 0 | } |
1401 | | |
1402 | | static LY_ERR |
1403 | | lyd_diff(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings, |
1404 | | struct lyd_node **diff) |
1405 | 0 | { |
1406 | 0 | const struct ly_ctx *ctx; |
1407 | |
|
1408 | 0 | LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL); |
1409 | |
|
1410 | 0 | if (first) { |
1411 | 0 | ctx = LYD_CTX(first); |
1412 | 0 | } else if (second) { |
1413 | 0 | ctx = LYD_CTX(second); |
1414 | 0 | } else { |
1415 | 0 | ctx = NULL; |
1416 | 0 | } |
1417 | |
|
1418 | 0 | if (first && second && (lysc_data_parent(first->schema) != lysc_data_parent(second->schema))) { |
1419 | 0 | LOGERR(ctx, LY_EINVAL, "Invalid arguments - cannot create diff for unrelated data (%s()).", __func__); |
1420 | 0 | return LY_EINVAL; |
1421 | 0 | } |
1422 | | |
1423 | 0 | *diff = NULL; |
1424 | |
|
1425 | 0 | return lyd_diff_siblings_r(first, second, options, nosiblings, diff); |
1426 | 0 | } |
1427 | | |
1428 | | LIBYANG_API_DEF LY_ERR |
1429 | | lyd_diff_tree(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, struct lyd_node **diff) |
1430 | 0 | { |
1431 | 0 | return lyd_diff(first, second, options, 1, diff); |
1432 | 0 | } |
1433 | | |
1434 | | LIBYANG_API_DEF LY_ERR |
1435 | | lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, struct lyd_node **diff) |
1436 | 0 | { |
1437 | 0 | return lyd_diff(first, second, options, 0, diff); |
1438 | 0 | } |
1439 | | |
1440 | | /** |
1441 | | * @brief Insert a diff node into a data tree. |
1442 | | * |
1443 | | * @param[in,out] first_node First sibling of the data tree. |
1444 | | * @param[in] parent_node Data tree sibling parent node. |
1445 | | * @param[in] new_node Node to insert. |
1446 | | * @param[in] userord_anchor Optional anchor (key, value, or position) of relative (leaf-)list instance. If not set, |
1447 | | * the user-ordered instance will be inserted at the first position. |
1448 | | * @return err_info, NULL on success. |
1449 | | */ |
1450 | | static LY_ERR |
1451 | | lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, struct lyd_node *new_node, |
1452 | | const char *userord_anchor) |
1453 | 0 | { |
1454 | 0 | LY_ERR ret; |
1455 | 0 | struct lyd_node *anchor; |
1456 | 0 | uint32_t pos, anchor_pos; |
1457 | 0 | int found; |
1458 | |
|
1459 | 0 | assert(new_node); |
1460 | | |
1461 | 0 | if (!*first_node) { |
1462 | 0 | if (!parent_node) { |
1463 | | /* no parent or siblings */ |
1464 | 0 | *first_node = new_node; |
1465 | 0 | return LY_SUCCESS; |
1466 | 0 | } |
1467 | | |
1468 | | /* simply insert into parent, no other children */ |
1469 | 0 | if (userord_anchor) { |
1470 | 0 | LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.", |
1471 | 0 | new_node->schema->name); |
1472 | 0 | return LY_EINVAL; |
1473 | 0 | } |
1474 | 0 | return lyd_insert_child(parent_node, new_node); |
1475 | 0 | } |
1476 | | |
1477 | 0 | assert(!(*first_node)->parent || ((*first_node)->parent == parent_node)); |
1478 | | |
1479 | 0 | if (!lysc_is_userordered(new_node->schema)) { |
1480 | | /* simple insert */ |
1481 | 0 | return lyd_insert_sibling(*first_node, new_node, first_node); |
1482 | 0 | } |
1483 | | |
1484 | 0 | if (userord_anchor) { |
1485 | | /* find the anchor sibling */ |
1486 | 0 | if (lysc_is_dup_inst_list(new_node->schema)) { |
1487 | 0 | anchor_pos = atoi(userord_anchor); |
1488 | 0 | if (!anchor_pos) { |
1489 | 0 | LOGERR(LYD_CTX(new_node), LY_EINVAL, "Invalid user-ordered anchor value \"%s\".", userord_anchor); |
1490 | 0 | return LY_EINVAL; |
1491 | 0 | } |
1492 | | |
1493 | 0 | found = 0; |
1494 | 0 | pos = 1; |
1495 | 0 | LYD_LIST_FOR_INST(*first_node, new_node->schema, anchor) { |
1496 | 0 | if (pos == anchor_pos) { |
1497 | 0 | found = 1; |
1498 | 0 | break; |
1499 | 0 | } |
1500 | 0 | ++pos; |
1501 | 0 | } |
1502 | 0 | if (!found) { |
1503 | 0 | LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.", |
1504 | 0 | new_node->schema->name); |
1505 | 0 | return LY_EINVAL; |
1506 | 0 | } |
1507 | 0 | } else { |
1508 | 0 | ret = lyd_find_sibling_val(*first_node, new_node->schema, userord_anchor, 0, &anchor); |
1509 | 0 | if (ret == LY_ENOTFOUND) { |
1510 | 0 | LOGERR(LYD_CTX(new_node), LY_EINVAL, "Node \"%s\" instance to insert next to not found.", |
1511 | 0 | new_node->schema->name); |
1512 | 0 | return LY_EINVAL; |
1513 | 0 | } else if (ret) { |
1514 | 0 | return ret; |
1515 | 0 | } |
1516 | 0 | } |
1517 | | |
1518 | | /* insert after */ |
1519 | 0 | LY_CHECK_RET(lyd_insert_after(anchor, new_node)); |
1520 | 0 | assert(new_node->prev == anchor); |
1521 | 0 | if (*first_node == new_node) { |
1522 | 0 | *first_node = anchor; |
1523 | 0 | } |
1524 | 0 | } else { |
1525 | | /* find the first instance */ |
1526 | 0 | ret = lyd_find_sibling_val(*first_node, new_node->schema, NULL, 0, &anchor); |
1527 | 0 | LY_CHECK_RET(ret && (ret != LY_ENOTFOUND), ret); |
1528 | |
|
1529 | 0 | if (anchor) { |
1530 | | /* insert before the first instance */ |
1531 | 0 | LY_CHECK_RET(lyd_insert_before(anchor, new_node)); |
1532 | 0 | if ((*first_node)->prev->next) { |
1533 | 0 | assert(!new_node->prev->next); |
1534 | 0 | *first_node = new_node; |
1535 | 0 | } |
1536 | 0 | } else { |
1537 | | /* insert anywhere */ |
1538 | 0 | LY_CHECK_RET(lyd_insert_sibling(*first_node, new_node, first_node)); |
1539 | 0 | } |
1540 | 0 | } |
1541 | | |
1542 | 0 | return LY_SUCCESS; |
1543 | 0 | } |
1544 | | |
1545 | | /** |
1546 | | * @brief Parse a diff metadata value into the changed metadata instance name (with module name) and value. |
1547 | | * |
1548 | | * @param[in] meta Diff metadata instance. |
1549 | | * @param[out] name Optional changed metadata name. |
1550 | | * @param[out] value Optional changed metadata value. |
1551 | | * @return LY_ERR value. |
1552 | | */ |
1553 | | static LY_ERR |
1554 | | lyd_diff_apply_metadata_parse(const struct lyd_meta *meta, char **name, const char **value) |
1555 | 0 | { |
1556 | 0 | LY_ERR rc = LY_SUCCESS; |
1557 | 0 | const char *v, *ptr; |
1558 | |
|
1559 | 0 | v = lyd_get_meta_value(meta); |
1560 | 0 | ptr = strchr(v, '='); |
1561 | 0 | LY_CHECK_ERR_GOTO(!ptr, LOGINT(meta->annotation->module->ctx); rc = LY_EINT, cleanup); |
1562 | |
|
1563 | 0 | if (name) { |
1564 | 0 | *name = strndup(v, ptr - v); |
1565 | 0 | LY_CHECK_ERR_GOTO(!*name, LOGMEM(meta->annotation->module->ctx); rc = LY_EMEM, cleanup); |
1566 | 0 | } |
1567 | | |
1568 | 0 | if (value) { |
1569 | 0 | *value = ptr + 1; |
1570 | 0 | } |
1571 | |
|
1572 | 0 | cleanup: |
1573 | 0 | return rc; |
1574 | 0 | } |
1575 | | |
1576 | | /** |
1577 | | * @brief Find a metadata instance with a specific value. |
1578 | | * |
1579 | | * @param[in] meta First metadata to consider. |
1580 | | * @param[in] name Metadata name with module name. |
1581 | | * @param[in] value Metadata value. |
1582 | | * @param[in] log Whether to log an error if not found. |
1583 | | * @param[out] match Found metadata. |
1584 | | * @return LY_ERR value. |
1585 | | */ |
1586 | | static LY_ERR |
1587 | | lyd_diff_metadata_find(const struct lyd_meta *meta, const char *name, const char *value, ly_bool log, |
1588 | | struct lyd_meta **match) |
1589 | 0 | { |
1590 | 0 | const struct lyd_meta *m; |
1591 | |
|
1592 | 0 | for (m = meta; (m = lyd_find_meta(m, NULL, name)); m = m->next) { |
1593 | 0 | if (!strcmp(lyd_get_meta_value(m), value)) { |
1594 | 0 | *match = (struct lyd_meta *)m; |
1595 | 0 | return LY_SUCCESS; |
1596 | 0 | } |
1597 | 0 | } |
1598 | | |
1599 | 0 | *match = NULL; |
1600 | 0 | if (log) { |
1601 | 0 | LOGINT(meta->annotation->module->ctx); |
1602 | 0 | return LY_EINT; |
1603 | 0 | } else { |
1604 | 0 | return LY_SUCCESS; |
1605 | 0 | } |
1606 | 0 | } |
1607 | | |
1608 | | /** |
1609 | | * @brief Add a metadata into an array. |
1610 | | * |
1611 | | * @param[in] meta Metadata to add. |
1612 | | * @param[in,out] meta_a Metadata array. |
1613 | | * @param[in,out] meta_a_count Count of @p meta_a items. |
1614 | | * @return LY_ERR value. |
1615 | | */ |
1616 | | static LY_ERR |
1617 | | lyd_diff_meta_store(struct lyd_meta *meta, struct lyd_meta ***meta_a, uint32_t *meta_a_count) |
1618 | 0 | { |
1619 | 0 | *meta_a = ly_realloc(*meta_a, (*meta_a_count + 1) * sizeof **meta_a); |
1620 | 0 | LY_CHECK_ERR_RET(!*meta_a, LOGMEM(meta->annotation->module->ctx), LY_EMEM); |
1621 | 0 | (*meta_a)[*meta_a_count] = meta; |
1622 | 0 | ++(*meta_a_count); |
1623 | |
|
1624 | 0 | return LY_SUCCESS; |
1625 | 0 | } |
1626 | | |
1627 | | /** |
1628 | | * @brief Align all meta-replace metadata with its matching meta-orig metadata. |
1629 | | * |
1630 | | * @param[in] meta_replace Meta-replace metadata. |
1631 | | * @param[in] mr_count Count of @p meta_replace. |
1632 | | * @param[in,out] meta_orig Meta-orig metadata to align and reorder. |
1633 | | * @param[in] mo_count Count of @p meta_orig. |
1634 | | * @return LY_ERR value. |
1635 | | */ |
1636 | | static LY_ERR |
1637 | | lyd_diff_metadata_replace_orig_align(struct lyd_meta **meta_replace, uint32_t mr_count, struct lyd_meta **meta_orig, |
1638 | | uint32_t mo_count) |
1639 | 0 | { |
1640 | 0 | const struct ly_ctx *ctx; |
1641 | 0 | struct lyd_meta *m; |
1642 | 0 | const char *val1, *val2, *ptr; |
1643 | 0 | uint32_t i, j; |
1644 | |
|
1645 | 0 | if (!mr_count) { |
1646 | 0 | return LY_SUCCESS; |
1647 | 0 | } |
1648 | | |
1649 | 0 | ctx = meta_replace[0]->annotation->module->ctx; |
1650 | |
|
1651 | 0 | LY_CHECK_ERR_RET(mr_count != mo_count, LOGINT(ctx), LY_EINT); |
1652 | |
|
1653 | 0 | for (i = 0; i < mr_count; ++i) { |
1654 | | /* meta-replace value */ |
1655 | 0 | val1 = lyd_get_meta_value(meta_replace[i]); |
1656 | 0 | ptr = strchr(val1, '='); |
1657 | 0 | LY_CHECK_ERR_RET(!ptr, LOGINT(ctx), LY_EINT); |
1658 | 0 | ++ptr; |
1659 | | |
1660 | | /* find matching meta-orig value */ |
1661 | 0 | j = i; |
1662 | 0 | while (j < mo_count) { |
1663 | 0 | val2 = lyd_get_meta_value(meta_orig[j]); |
1664 | 0 | if (!strncmp(val1, val2, ptr - val1)) { |
1665 | 0 | break; |
1666 | 0 | } |
1667 | | |
1668 | 0 | ++j; |
1669 | 0 | } |
1670 | 0 | LY_CHECK_ERR_RET(j == mo_count, LOGINT(ctx), LY_EINT); |
1671 | |
|
1672 | 0 | if (j != i) { |
1673 | | /* non-matching index, move it */ |
1674 | 0 | m = meta_orig[i]; |
1675 | 0 | meta_orig[i] = meta_orig[j]; |
1676 | 0 | meta_orig[j] = m; |
1677 | 0 | } |
1678 | 0 | } |
1679 | | |
1680 | 0 | return LY_SUCCESS; |
1681 | 0 | } |
1682 | | |
1683 | | /** |
1684 | | * @brief Apply any metadata changes in the diff. |
1685 | | * |
1686 | | * @param[in,out] node Node to change. |
1687 | | * @param[in] diff_node Diff node to read the metadata changes from. |
1688 | | * @return LY_ERR value. |
1689 | | */ |
1690 | | static LY_ERR |
1691 | | lyd_diff_apply_metadata(struct lyd_node *node, const struct lyd_node *diff_node) |
1692 | 0 | { |
1693 | 0 | LY_ERR rc = LY_SUCCESS; |
1694 | 0 | struct lyd_meta *m, *m2, **meta_replace = NULL, **meta_orig = NULL; |
1695 | 0 | uint32_t i, mr_count = 0, mo_count = 0; |
1696 | 0 | const struct lys_module *mod; |
1697 | 0 | const char *meta_value, *old_meta_value; |
1698 | 0 | char *meta_name = NULL; |
1699 | 0 | const struct lyd_node *diff_ch; |
1700 | 0 | struct lyd_node *node_ch; |
1701 | |
|
1702 | 0 | mod = ly_ctx_get_module_implemented(LYD_CTX(node), "yang"); |
1703 | 0 | assert(mod); |
1704 | | |
1705 | | /* go through all the metadata */ |
1706 | 0 | LY_LIST_FOR(diff_node->meta, m) { |
1707 | 0 | if (m->annotation->module != mod) { |
1708 | 0 | continue; |
1709 | 0 | } |
1710 | | |
1711 | 0 | if (!strcmp(m->name, "meta-create")) { |
1712 | | /* parse the value */ |
1713 | 0 | LY_CHECK_GOTO(rc = lyd_diff_apply_metadata_parse(m, &meta_name, &meta_value), cleanup); |
1714 | | |
1715 | | /* create the metadata instance */ |
1716 | 0 | LY_CHECK_GOTO(rc = lyd_new_meta(NULL, node, NULL, meta_name, meta_value, 0, NULL), cleanup); |
1717 | |
|
1718 | 0 | free(meta_name); |
1719 | 0 | meta_name = NULL; |
1720 | 0 | } else if (!strcmp(m->name, "meta-delete")) { |
1721 | | /* parse the value */ |
1722 | 0 | LY_CHECK_GOTO(rc = lyd_diff_apply_metadata_parse(m, &meta_name, &meta_value), cleanup); |
1723 | | |
1724 | | /* find the metadata instance and free it */ |
1725 | 0 | LY_CHECK_GOTO(rc = lyd_diff_metadata_find(node->meta, meta_name, meta_value, 1, &m2), cleanup); |
1726 | 0 | lyd_free_meta_single(m2); |
1727 | |
|
1728 | 0 | free(meta_name); |
1729 | 0 | meta_name = NULL; |
1730 | 0 | } else if (!strcmp(m->name, "meta-replace")) { |
1731 | | /* just store it, to be able to correctly match to 'meta-orig' */ |
1732 | 0 | LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_replace, &mr_count), cleanup); |
1733 | 0 | } else if (!strcmp(m->name, "meta-orig")) { |
1734 | | /* just store it */ |
1735 | 0 | LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_orig, &mo_count), cleanup); |
1736 | 0 | } |
1737 | 0 | } |
1738 | | |
1739 | | /* make sure meta_replace and meta_orig arrays are aligned */ |
1740 | 0 | LY_CHECK_GOTO(rc = lyd_diff_metadata_replace_orig_align(meta_replace, mr_count, meta_orig, mo_count), cleanup); |
1741 | | |
1742 | | /* process replaced metadata */ |
1743 | 0 | LY_CHECK_ERR_GOTO(mr_count != mo_count, LOGINT(LYD_CTX(node)); rc = LY_EINT, cleanup); |
1744 | 0 | for (i = 0; i < mr_count; ++i) { |
1745 | | /* get the changed meta name with '=' */ |
1746 | 0 | LY_CHECK_GOTO(rc = lyd_diff_apply_metadata_parse(meta_replace[i], &meta_name, &meta_value), cleanup); |
1747 | | |
1748 | | /* parse the orig value */ |
1749 | 0 | LY_CHECK_GOTO(rc = lyd_diff_apply_metadata_parse(meta_orig[i], NULL, &old_meta_value), cleanup); |
1750 | | |
1751 | | /* find the metadata instance */ |
1752 | 0 | LY_CHECK_GOTO(rc = lyd_diff_metadata_find(node->meta, meta_name, old_meta_value, 0, &m2), cleanup); |
1753 | 0 | LY_CHECK_ERR_GOTO(!m2, LOGINT(LYD_CTX(node)); rc = LY_EINT, cleanup); |
1754 | | |
1755 | | /* change its value */ |
1756 | 0 | LY_CHECK_GOTO(rc = lyd_change_meta(m2, meta_value), cleanup); |
1757 | | |
1758 | | /* meta-orig spent */ |
1759 | 0 | meta_orig[i] = NULL; |
1760 | |
|
1761 | 0 | free(meta_name); |
1762 | 0 | meta_name = NULL; |
1763 | 0 | } |
1764 | | |
1765 | | /* for lists, we also need to process their keys */ |
1766 | 0 | if (diff_node->schema->nodetype == LYS_LIST) { |
1767 | 0 | diff_ch = lyd_child(diff_node); |
1768 | 0 | node_ch = lyd_child(node); |
1769 | 0 | while (diff_ch && lysc_is_key(diff_ch->schema)) { |
1770 | | /* process every key */ |
1771 | 0 | assert(node_ch && (diff_ch->schema == node_ch->schema)); |
1772 | 0 | rc = lyd_diff_apply_metadata(node_ch, diff_ch); |
1773 | 0 | LY_CHECK_GOTO(rc, cleanup); |
1774 | |
|
1775 | 0 | diff_ch = diff_ch->next; |
1776 | 0 | node_ch = node_ch->next; |
1777 | 0 | } |
1778 | 0 | } |
1779 | | |
1780 | 0 | cleanup: |
1781 | 0 | free(meta_name); |
1782 | 0 | free(meta_replace); |
1783 | 0 | free(meta_orig); |
1784 | 0 | return rc; |
1785 | 0 | } |
1786 | | |
1787 | | /** |
1788 | | * @brief Apply diff subtree on data tree nodes, recursively. |
1789 | | * |
1790 | | * @param[in,out] first_node First sibling of the subtree. |
1791 | | * @param[in] parent_node Parent of the first sibling. |
1792 | | * @param[in] diff_node Current diff node. |
1793 | | * @param[in] diff_cb Optional diff callback. |
1794 | | * @param[in] cb_data User data for @p diff_cb. |
1795 | | * @param[in,out] dup_inst Duplicate instance cache for all @p diff_node siblings. |
1796 | | * @return LY_ERR value. |
1797 | | */ |
1798 | | static LY_ERR |
1799 | | lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node, |
1800 | | lyd_diff_cb diff_cb, void *cb_data, struct ly_ht **dup_inst) |
1801 | 0 | { |
1802 | 0 | LY_ERR rc = LY_SUCCESS, r; |
1803 | 0 | struct lyd_node *match, *diff_child; |
1804 | 0 | const char *str_val, *meta_str; |
1805 | 0 | enum lyd_diff_op op; |
1806 | 0 | struct lyd_meta *meta; |
1807 | 0 | struct ly_ht *child_dup_inst = NULL; |
1808 | 0 | const struct ly_ctx *ctx = LYD_CTX(diff_node); |
1809 | | |
1810 | | /* read all the valid attributes */ |
1811 | 0 | LY_CHECK_RET(lyd_diff_get_op(diff_node, &op, NULL)); |
1812 | | |
1813 | | /* handle specific user-ordered (leaf-)lists operations separately */ |
1814 | 0 | if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) { |
1815 | 0 | if (op == LYD_DIFF_OP_REPLACE) { |
1816 | | /* find the node (we must have some siblings because the node was only moved) */ |
1817 | 0 | LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); |
1818 | 0 | LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL); |
1819 | 0 | } else { |
1820 | | /* duplicate the node */ |
1821 | 0 | LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match)); |
1822 | 0 | } |
1823 | | |
1824 | | /* get "key", "value", or "position" metadata string value */ |
1825 | 0 | if (lysc_is_dup_inst_list(diff_node->schema)) { |
1826 | 0 | meta_str = "yang:position"; |
1827 | 0 | } else if (diff_node->schema->nodetype == LYS_LIST) { |
1828 | 0 | meta_str = "yang:key"; |
1829 | 0 | } else { |
1830 | 0 | meta_str = "yang:value"; |
1831 | 0 | } |
1832 | 0 | meta = lyd_find_meta(diff_node->meta, NULL, meta_str); |
1833 | 0 | LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_str, diff_node), LY_EINVAL); |
1834 | 0 | str_val = lyd_get_meta_value(meta); |
1835 | | |
1836 | | /* insert/move the node */ |
1837 | 0 | if (str_val[0]) { |
1838 | 0 | r = lyd_diff_insert(first_node, parent_node, match, str_val); |
1839 | 0 | } else { |
1840 | 0 | r = lyd_diff_insert(first_node, parent_node, match, NULL); |
1841 | 0 | } |
1842 | 0 | if (r) { |
1843 | 0 | if (op == LYD_DIFF_OP_CREATE) { |
1844 | 0 | lyd_free_tree(match); |
1845 | 0 | } |
1846 | 0 | return r; |
1847 | 0 | } |
1848 | 0 | } else { |
1849 | | /* apply operation */ |
1850 | 0 | switch (op) { |
1851 | 0 | case LYD_DIFF_OP_NONE: |
1852 | | /* find the node */ |
1853 | 0 | LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); |
1854 | 0 | LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL); |
1855 | |
|
1856 | 0 | if (match->schema->nodetype & LYD_NODE_TERM) { |
1857 | | /* special case of only dflt flag change */ |
1858 | 0 | if (diff_node->flags & LYD_DEFAULT) { |
1859 | 0 | match->flags |= LYD_DEFAULT; |
1860 | 0 | } else { |
1861 | 0 | match->flags &= ~LYD_DEFAULT; |
1862 | 0 | } |
1863 | 0 | } |
1864 | 0 | break; |
1865 | 0 | case LYD_DIFF_OP_CREATE: |
1866 | | /* duplicate the node */ |
1867 | 0 | LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match)); |
1868 | | |
1869 | | /* insert it at the end */ |
1870 | 0 | if (parent_node) { |
1871 | 0 | r = lyd_insert_child(parent_node, match); |
1872 | 0 | } else { |
1873 | 0 | r = lyd_insert_sibling(*first_node, match, first_node); |
1874 | 0 | } |
1875 | 0 | if (r) { |
1876 | 0 | lyd_free_tree(match); |
1877 | 0 | return r; |
1878 | 0 | } |
1879 | | |
1880 | 0 | break; |
1881 | 0 | case LYD_DIFF_OP_DELETE: |
1882 | | /* find the node */ |
1883 | 0 | LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); |
1884 | 0 | LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL); |
1885 | | |
1886 | | /* remove it */ |
1887 | 0 | if ((match == *first_node) && !match->parent) { |
1888 | 0 | assert(!parent_node); |
1889 | | /* we have removed the top-level node */ |
1890 | 0 | *first_node = (*first_node)->next; |
1891 | 0 | } |
1892 | 0 | lyd_free_tree(match); |
1893 | | |
1894 | | /* we are not going recursively in this case, the whole subtree was already deleted */ |
1895 | 0 | return LY_SUCCESS; |
1896 | 0 | case LYD_DIFF_OP_REPLACE: |
1897 | 0 | if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) { |
1898 | 0 | LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".", |
1899 | 0 | lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node)); |
1900 | 0 | return LY_EINVAL; |
1901 | 0 | } |
1902 | | |
1903 | | /* find the node */ |
1904 | 0 | LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); |
1905 | 0 | LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL); |
1906 | | |
1907 | | /* update the value */ |
1908 | 0 | if (diff_node->schema->nodetype == LYS_LEAF) { |
1909 | 0 | r = lyd_change_term(match, lyd_get_value(diff_node)); |
1910 | 0 | LY_CHECK_ERR_RET(r && (r != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL); |
1911 | 0 | } else { |
1912 | 0 | struct lyd_node_any *any = (struct lyd_node_any *)diff_node; |
1913 | |
|
1914 | 0 | LY_CHECK_RET(lyd_any_copy_value(match, any->child, any->value, any->hints)); |
1915 | 0 | } |
1916 | | |
1917 | | /* with flags */ |
1918 | 0 | match->flags = diff_node->flags; |
1919 | 0 | break; |
1920 | 0 | default: |
1921 | 0 | LOGINT_RET(ctx); |
1922 | 0 | } |
1923 | 0 | } |
1924 | | |
1925 | | /* apply any metadata changes */ |
1926 | 0 | LY_CHECK_RET(lyd_diff_apply_metadata(match, diff_node)); |
1927 | |
|
1928 | 0 | if (diff_cb) { |
1929 | | /* call callback */ |
1930 | 0 | LY_CHECK_RET(diff_cb(diff_node, match, cb_data)); |
1931 | 0 | } |
1932 | | |
1933 | | /* apply diff recursively */ |
1934 | 0 | LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) { |
1935 | 0 | rc = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst); |
1936 | 0 | if (rc) { |
1937 | 0 | break; |
1938 | 0 | } |
1939 | 0 | } |
1940 | |
|
1941 | 0 | lyd_dup_inst_free(child_dup_inst); |
1942 | 0 | return rc; |
1943 | 0 | } |
1944 | | |
1945 | | LIBYANG_API_DEF LY_ERR |
1946 | | lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const struct lys_module *mod, |
1947 | | lyd_diff_cb diff_cb, void *cb_data) |
1948 | 0 | { |
1949 | 0 | const struct lyd_node *root; |
1950 | 0 | struct ly_ht *dup_inst = NULL; |
1951 | 0 | LY_ERR ret = LY_SUCCESS; |
1952 | |
|
1953 | 0 | LY_LIST_FOR(diff, root) { |
1954 | 0 | if (mod && (lyd_owner_module(root) != mod)) { |
1955 | | /* skip data nodes from different modules */ |
1956 | 0 | continue; |
1957 | 0 | } |
1958 | | |
1959 | | /* apply relevant nodes from the diff datatree */ |
1960 | 0 | ret = lyd_diff_apply_r(data, NULL, root, diff_cb, cb_data, &dup_inst); |
1961 | 0 | if (ret) { |
1962 | 0 | break; |
1963 | 0 | } |
1964 | 0 | } |
1965 | |
|
1966 | 0 | lyd_dup_inst_free(dup_inst); |
1967 | 0 | return ret; |
1968 | 0 | } |
1969 | | |
1970 | | LIBYANG_API_DEF LY_ERR |
1971 | | lyd_diff_apply_all(struct lyd_node **data, const struct lyd_node *diff) |
1972 | 0 | { |
1973 | 0 | return lyd_diff_apply_module(data, diff, NULL, NULL, NULL); |
1974 | 0 | } |
1975 | | |
1976 | | /** |
1977 | | * @brief Update operations on a diff node when the new operation is NONE. |
1978 | | * |
1979 | | * @param[in] diff_match Node from the diff. |
1980 | | * @param[in] cur_op Current operation of @p diff_match. |
1981 | | * @param[in] src_diff Current source diff node. |
1982 | | * @return LY_ERR value. |
1983 | | */ |
1984 | | static LY_ERR |
1985 | | lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff) |
1986 | 0 | { |
1987 | 0 | switch (cur_op) { |
1988 | 0 | case LYD_DIFF_OP_NONE: |
1989 | 0 | case LYD_DIFF_OP_CREATE: |
1990 | 0 | case LYD_DIFF_OP_REPLACE: |
1991 | 0 | if (src_diff->schema->nodetype & LYD_NODE_TERM) { |
1992 | | /* NONE on a term means only its dflt flag was changed */ |
1993 | 0 | diff_match->flags &= ~LYD_DEFAULT; |
1994 | 0 | diff_match->flags |= src_diff->flags & LYD_DEFAULT; |
1995 | 0 | } |
1996 | 0 | break; |
1997 | 0 | default: |
1998 | | /* delete operation is not valid */ |
1999 | 0 | LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_NONE); |
2000 | 0 | return LY_EINVAL; |
2001 | 0 | } |
2002 | | |
2003 | 0 | return LY_SUCCESS; |
2004 | 0 | } |
2005 | | |
2006 | | /** |
2007 | | * @brief Set a specific operation of a node. Delete the previous operation, if any. |
2008 | | * Does not change the default flag. |
2009 | | * |
2010 | | * @param[in] node Node to change. |
2011 | | * @param[in] op Operation to set. |
2012 | | * @return LY_ERR value. |
2013 | | */ |
2014 | | static LY_ERR |
2015 | | lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op) |
2016 | 0 | { |
2017 | 0 | lyd_diff_del_meta(node, "operation"); |
2018 | |
|
2019 | 0 | if (node->schema) { |
2020 | 0 | return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), LYD_NEW_VAL_STORE_ONLY, NULL); |
2021 | 0 | } else { |
2022 | 0 | return lyd_new_attr(node, "yang", "operation", lyd_diff_op2str(op), NULL); |
2023 | 0 | } |
2024 | 0 | } |
2025 | | |
2026 | | /** |
2027 | | * @brief In user-ordered lists, certain operations on sibling nodes can result in logically identical changes. |
2028 | | * However, applying the first change may cause the second one to fail. |
2029 | | * Check whether this diff node is redundant. |
2030 | | * |
2031 | | * @param[in,out] diff The node whose metadata has been modified. |
2032 | | * @param[in] child The child of diff node. |
2033 | | * @return 0 if not, non-zero if it is. |
2034 | | */ |
2035 | | static ly_bool |
2036 | | lyd_diff_is_redundant_userord_move(struct lyd_node **diff, struct lyd_node *child) |
2037 | 0 | { |
2038 | 0 | LY_ERR ret = LY_SUCCESS; |
2039 | 0 | struct lyd_meta *meta1, *meta2; |
2040 | 0 | struct lyd_meta *orig_val_meta = NULL, *val_meta = NULL; |
2041 | 0 | struct lyd_node *diff_iter = *diff; |
2042 | 0 | char *buff1 = NULL, *buff2 = NULL; |
2043 | 0 | const char *llist_value1 = NULL, *llist_value2 = NULL; |
2044 | 0 | const char *name = NULL, *name_iter = NULL; |
2045 | 0 | size_t bufflen1 = 0, buffused1 = 0; |
2046 | 0 | size_t bufflen2 = 0, buffused2 = 0; |
2047 | 0 | const char *orig_meta_name, *meta_name; |
2048 | | |
2049 | | /* get metadata names */ |
2050 | 0 | if (lysc_is_dup_inst_list((*diff)->schema)) { |
2051 | 0 | meta_name = "yang:position"; |
2052 | 0 | orig_meta_name = "yang:orig-position"; |
2053 | 0 | } else if ((*diff)->schema->nodetype == LYS_LIST) { |
2054 | 0 | meta_name = "yang:key"; |
2055 | 0 | orig_meta_name = "yang:orig-key"; |
2056 | 0 | } else { |
2057 | 0 | meta_name = "yang:value"; |
2058 | 0 | orig_meta_name = "yang:orig-value"; |
2059 | 0 | } |
2060 | | |
2061 | | /* check for redundant move */ |
2062 | 0 | orig_val_meta = lyd_find_meta((*diff)->meta, NULL, orig_meta_name); |
2063 | 0 | val_meta = lyd_find_meta((*diff)->meta, NULL, meta_name); |
2064 | 0 | assert(orig_val_meta && val_meta); |
2065 | | |
2066 | 0 | if (!lyd_compare_meta(orig_val_meta, val_meta)) { |
2067 | | /* there is actually no move */ |
2068 | 0 | lyd_free_meta_single(orig_val_meta); |
2069 | 0 | lyd_free_meta_single(val_meta); |
2070 | 0 | if (child) { |
2071 | | /* change operation to NONE, we have siblings */ |
2072 | 0 | lyd_diff_change_op((*diff), LYD_DIFF_OP_NONE); |
2073 | 0 | goto cleanup; |
2074 | 0 | } |
2075 | | |
2076 | | /* redundant node, BUT !! |
2077 | | * In diff the move operation is always converted to be INSERT_AFTER, which is fine |
2078 | | * because the data that this is applied on should not change for the diff lifetime. |
2079 | | * However, when we are merging 2 diffs, this conversion is actually lossy because |
2080 | | * if the data change, the move operation can also change its meaning. In this specific |
2081 | | * case the move operation will be lost. But it can be considered a feature, it is not supported. |
2082 | | */ |
2083 | 0 | ret = 1; |
2084 | 0 | goto cleanup; |
2085 | 0 | } |
2086 | | |
2087 | | /* itereate throught previous nodes and look for logically identical changes */ |
2088 | 0 | diff_iter = (*diff)->prev; |
2089 | 0 | while (diff_iter != (*diff)) { |
2090 | |
|
2091 | 0 | meta1 = lyd_find_meta((*diff)->meta, NULL, meta_name); |
2092 | 0 | meta2 = lyd_find_meta(diff_iter->meta, NULL, orig_meta_name); |
2093 | |
|
2094 | 0 | name = lyd_get_meta_value(meta1); |
2095 | 0 | name_iter = lyd_get_meta_value(meta2); |
2096 | |
|
2097 | 0 | if (!name || !name_iter) { |
2098 | 0 | goto next_iter; |
2099 | 0 | } |
2100 | | |
2101 | | /* if keys don't match, skip - not a candidate for cyclic change */ |
2102 | 0 | if (strcmp(name, name_iter)) { |
2103 | 0 | goto next_iter; |
2104 | 0 | } |
2105 | | |
2106 | 0 | meta1 = lyd_find_meta((*diff)->meta, NULL, orig_meta_name); |
2107 | 0 | meta2 = lyd_find_meta(diff_iter->meta, NULL, meta_name); |
2108 | | |
2109 | | /* store string values of metadata to compare later */ |
2110 | 0 | name = lyd_get_meta_value(meta1); |
2111 | 0 | name_iter = lyd_get_meta_value(meta2); |
2112 | |
|
2113 | 0 | if (!name || !name_iter) { |
2114 | 0 | goto next_iter; |
2115 | 0 | } |
2116 | | |
2117 | 0 | if ((*diff)->schema->nodetype == LYS_LIST) { |
2118 | | |
2119 | | /* reuse buffers by resetting used size */ |
2120 | 0 | buffused1 = buffused2 = 0; |
2121 | 0 | LY_CHECK_GOTO(ret = lyd_path_list_predicate(*diff, &buff1, &bufflen1, &buffused1, 0), cleanup); |
2122 | 0 | LY_CHECK_GOTO(ret = lyd_path_list_predicate(diff_iter, &buff2, &bufflen2, &buffused2, 0), cleanup); |
2123 | | |
2124 | | /* compare path predicates with metadata - check if this is a reversed operation */ |
2125 | 0 | if (!strcmp(buff1, name_iter) && !strcmp(buff2, name)) { |
2126 | | |
2127 | | /* found a cyclic change - remove and free the node */ |
2128 | 0 | ret = 1; |
2129 | 0 | goto cleanup; |
2130 | 0 | } |
2131 | 0 | } else { |
2132 | 0 | llist_value1 = lyd_get_value(*diff); |
2133 | 0 | llist_value2 = lyd_get_value(diff_iter); |
2134 | | |
2135 | | /* compare vlaue of data node with metadata - check if this is a reversed operation */ |
2136 | 0 | if (!strcmp(llist_value1, name_iter) && !strcmp(llist_value2, name)) { |
2137 | | |
2138 | | /* found a cyclic change - remove and free the node */ |
2139 | 0 | ret = 1; |
2140 | 0 | goto cleanup; |
2141 | 0 | } |
2142 | 0 | } |
2143 | | |
2144 | 0 | next_iter: |
2145 | 0 | diff_iter = diff_iter->prev; |
2146 | 0 | } |
2147 | | |
2148 | 0 | cleanup: |
2149 | 0 | free(buff1); |
2150 | 0 | free(buff2); |
2151 | 0 | return ret; |
2152 | 0 | } |
2153 | | |
2154 | | /** |
2155 | | * @brief Propagate key/value metadata from a list or leaf-list node |
2156 | | * to its sibling nodes that reference it via key/value. |
2157 | | * |
2158 | | * This is used to ensure correct ordering in user-ordered lists |
2159 | | * or leaf-lists by updating the corresponding metadata in sibling |
2160 | | * nodes before the reference node changes. |
2161 | | * |
2162 | | * @param[in] diff Node from lyd_diff_merge_replace(). |
2163 | | * @param[in] meta_name Name of the metadata ("key" or "value"). |
2164 | | * @return LY_ERR value. |
2165 | | */ |
2166 | | static LY_ERR |
2167 | | lyd_diff_propagate_meta(struct lyd_node *diff, const char *meta_name) |
2168 | 0 | { |
2169 | 0 | LY_ERR ret = LY_SUCCESS; |
2170 | 0 | const struct lys_module *mod; |
2171 | 0 | struct lyd_meta *meta1, *meta2; |
2172 | 0 | struct lyd_node *diff_iter = NULL; |
2173 | 0 | char *buff = NULL; |
2174 | 0 | size_t bufflen = 0, buffused = 0; |
2175 | 0 | const char *meta_value; |
2176 | | |
2177 | | /* get "yang" module for the metadata */ |
2178 | 0 | mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang"); |
2179 | 0 | assert(mod); |
2180 | | |
2181 | | /* |
2182 | | * iterate through all siblings of the diff |
2183 | | * if a sibling references the diff node via metadata, update it |
2184 | | */ |
2185 | 0 | LY_LIST_FOR(diff, diff_iter) { |
2186 | | /* find the relevant metadata on the current sibling */ |
2187 | 0 | if ((meta1 = lyd_find_meta(diff_iter->meta, mod, meta_name))) { |
2188 | 0 | if (diff->schema->nodetype == LYS_LEAFLIST) { |
2189 | 0 | if (!strcmp(lyd_get_meta_value(meta1), lyd_get_value(diff))) { |
2190 | | /* replace the old metadata with the updated one from the changed node */ |
2191 | 0 | lyd_diff_del_meta(diff_iter, meta_name); |
2192 | 0 | meta2 = lyd_find_meta(diff->meta, mod, meta_name); |
2193 | 0 | LY_CHECK_GOTO((ret = lyd_dup_meta_single(meta2, diff_iter, NULL)), cleanup); |
2194 | 0 | } |
2195 | 0 | } else { |
2196 | 0 | buffused = 0; |
2197 | |
|
2198 | 0 | LY_CHECK_GOTO((ret = lyd_path_list_predicate(diff, &buff, &bufflen, &buffused, 0)), cleanup); |
2199 | 0 | meta_value = lyd_get_meta_value(meta1); |
2200 | | |
2201 | | /* if the path predicate matches, replace the metadata */ |
2202 | 0 | if (!strcmp(buff, meta_value)) { |
2203 | 0 | meta1 = lyd_find_meta(diff_iter->meta, mod, meta_name); |
2204 | 0 | meta2 = lyd_find_meta(diff->meta, mod, meta_name); |
2205 | 0 | LY_CHECK_GOTO((ret = lyd_change_meta(meta1, lyd_get_meta_value(meta2))), cleanup); |
2206 | 0 | } |
2207 | 0 | } |
2208 | 0 | } |
2209 | 0 | } |
2210 | | |
2211 | 0 | cleanup: |
2212 | 0 | free(buff); |
2213 | 0 | return ret; |
2214 | 0 | } |
2215 | | |
2216 | | /** |
2217 | | * @brief Update operations on a diff node when the new operation is REPLACE. |
2218 | | * |
2219 | | * @param[in] diff_match Node from the diff. |
2220 | | * @param[in] cur_op Current operation of @p diff_match. |
2221 | | * @param[in] src_diff Current source diff node. |
2222 | | * @return LY_ERR value. |
2223 | | */ |
2224 | | static LY_ERR |
2225 | | lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff) |
2226 | 0 | { |
2227 | 0 | LY_ERR ret; |
2228 | 0 | const char *str_val, *meta_name, *orig_meta_name; |
2229 | 0 | struct lyd_meta *meta; |
2230 | 0 | const struct lys_module *mod; |
2231 | 0 | const struct lyd_node_any *any; |
2232 | 0 | const struct ly_ctx *ctx = LYD_CTX(diff_match); |
2233 | | |
2234 | | /* get "yang" module for the metadata */ |
2235 | 0 | mod = ly_ctx_get_module_latest(LYD_CTX(diff_match), "yang"); |
2236 | 0 | assert(mod); |
2237 | | |
2238 | 0 | switch (cur_op) { |
2239 | 0 | case LYD_DIFF_OP_REPLACE: |
2240 | 0 | case LYD_DIFF_OP_CREATE: |
2241 | 0 | switch (diff_match->schema->nodetype) { |
2242 | 0 | case LYS_LIST: |
2243 | 0 | case LYS_LEAFLIST: |
2244 | | /* it was created/moved somewhere, but now it will be created/moved somewhere else, |
2245 | | * keep orig_key/orig_value (only replace oper) and replace key/value */ |
2246 | 0 | assert(lysc_is_userordered(diff_match->schema)); |
2247 | 0 | if (lysc_is_dup_inst_list(diff_match->schema)) { |
2248 | 0 | meta_name = "position"; |
2249 | 0 | } else if (diff_match->schema->nodetype == LYS_LIST) { |
2250 | 0 | meta_name = "key"; |
2251 | 0 | } else { |
2252 | 0 | meta_name = "value"; |
2253 | 0 | } |
2254 | | |
2255 | | /* update sibling nodes which reference the diff_match by key/value */ |
2256 | 0 | LY_CHECK_RET(lyd_diff_propagate_meta(diff_match, meta_name)); |
2257 | |
|
2258 | 0 | lyd_diff_del_meta(diff_match, meta_name); |
2259 | 0 | meta = lyd_find_meta(src_diff->meta, mod, meta_name); |
2260 | 0 | LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL); |
2261 | 0 | LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL)); |
2262 | |
|
2263 | 0 | break; |
2264 | 0 | case LYS_LEAF: |
2265 | | /* replaced with the exact same value, impossible */ |
2266 | 0 | if (!lyd_compare_single(diff_match, src_diff, 0)) { |
2267 | 0 | LOGERR_UNEXPVAL(ctx, diff_match, "target diff"); |
2268 | 0 | return LY_EINVAL; |
2269 | 0 | } |
2270 | | |
2271 | | /* modify the node value */ |
2272 | 0 | if (lyd_change_term(diff_match, lyd_get_value(src_diff))) { |
2273 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
2274 | 0 | } |
2275 | | |
2276 | 0 | if (cur_op == LYD_DIFF_OP_REPLACE) { |
2277 | | /* compare values whether there is any change at all */ |
2278 | 0 | meta = lyd_find_meta(diff_match->meta, mod, "orig-value"); |
2279 | 0 | LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "orig-value", diff_match), LY_EINVAL); |
2280 | 0 | str_val = lyd_get_meta_value(meta); |
2281 | 0 | ret = lyd_value_compare((struct lyd_node_term *)diff_match, str_val, strlen(str_val)); |
2282 | 0 | if (!ret) { |
2283 | | /* values are the same, remove orig-value meta and set oper to NONE */ |
2284 | 0 | lyd_free_meta_single(meta); |
2285 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE)); |
2286 | 0 | } |
2287 | 0 | } |
2288 | | |
2289 | | /* modify the default flag */ |
2290 | 0 | diff_match->flags &= ~LYD_DEFAULT; |
2291 | 0 | diff_match->flags |= src_diff->flags & LYD_DEFAULT; |
2292 | 0 | break; |
2293 | 0 | case LYS_ANYXML: |
2294 | 0 | case LYS_ANYDATA: |
2295 | 0 | if (!lyd_compare_single(diff_match, src_diff, 0)) { |
2296 | 0 | LOGERR_UNEXPVAL(ctx, diff_match, "target diff"); |
2297 | 0 | return LY_EINVAL; |
2298 | 0 | } |
2299 | | |
2300 | | /* modify the node value */ |
2301 | 0 | any = (struct lyd_node_any *)src_diff; |
2302 | 0 | LY_CHECK_RET(lyd_any_copy_value(diff_match, any->child, any->value, any->hints)); |
2303 | 0 | break; |
2304 | 0 | default: |
2305 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
2306 | 0 | } |
2307 | 0 | break; |
2308 | 0 | case LYD_DIFF_OP_NONE: |
2309 | 0 | switch (diff_match->schema->nodetype) { |
2310 | 0 | case LYS_LIST: |
2311 | | /* it is moved now */ |
2312 | 0 | assert(lysc_is_userordered(diff_match->schema)); |
2313 | | |
2314 | | /* change the operation */ |
2315 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE)); |
2316 | | |
2317 | | /* set orig-meta and meta */ |
2318 | 0 | if (lysc_is_dup_inst_list(diff_match->schema)) { |
2319 | 0 | meta_name = "position"; |
2320 | 0 | orig_meta_name = "orig-position"; |
2321 | 0 | } else { |
2322 | 0 | meta_name = "key"; |
2323 | 0 | orig_meta_name = "orig-key"; |
2324 | 0 | } |
2325 | |
|
2326 | 0 | meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name); |
2327 | 0 | LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL); |
2328 | 0 | LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL)); |
2329 | |
|
2330 | 0 | meta = lyd_find_meta(src_diff->meta, mod, meta_name); |
2331 | 0 | LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL); |
2332 | 0 | LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL)); |
2333 | 0 | break; |
2334 | 0 | case LYS_LEAF: |
2335 | | /* only dflt flag changed, now value changed as well, update the operation */ |
2336 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE)); |
2337 | | |
2338 | | /* modify the node value */ |
2339 | 0 | if (lyd_change_term(diff_match, lyd_get_value(src_diff))) { |
2340 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
2341 | 0 | } |
2342 | 0 | break; |
2343 | 0 | default: |
2344 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
2345 | 0 | } |
2346 | 0 | break; |
2347 | 0 | default: |
2348 | | /* delete operation is not valid */ |
2349 | 0 | LOGERR_MERGEOP(ctx, diff_match, cur_op, LYD_DIFF_OP_REPLACE); |
2350 | 0 | return LY_EINVAL; |
2351 | 0 | } |
2352 | | |
2353 | 0 | return LY_SUCCESS; |
2354 | 0 | } |
2355 | | |
2356 | | /** |
2357 | | * @brief Update operations in a diff node when the new operation is CREATE. |
2358 | | * |
2359 | | * @param[in,out] diff_match Node from the diff, may be replaced. |
2360 | | * @param[in,out] diff Diff root node, may be updated. |
2361 | | * @param[in] cur_op Current operation of @p diff_match. |
2362 | | * @param[in] src_diff Current source diff node. |
2363 | | * @param[in] options Diff merge options. |
2364 | | * @return LY_ERR value. |
2365 | | */ |
2366 | | static LY_ERR |
2367 | | lyd_diff_merge_create(struct lyd_node **diff_match, struct lyd_node **diff, enum lyd_diff_op cur_op, |
2368 | | const struct lyd_node *src_diff, uint16_t options) |
2369 | 0 | { |
2370 | 0 | struct lyd_node *child, *src_dup, *to_free = NULL; |
2371 | 0 | const struct lysc_node_leaf *sleaf = NULL; |
2372 | 0 | uint32_t trg_flags; |
2373 | 0 | const char *meta_name, *orig_meta_name; |
2374 | 0 | struct lyd_meta *meta, *orig_meta; |
2375 | 0 | const struct ly_ctx *ctx = LYD_CTX(*diff_match); |
2376 | 0 | LY_ERR r; |
2377 | | |
2378 | | /* create operation is valid only for data nodes */ |
2379 | 0 | LY_CHECK_ERR_RET(!src_diff->schema, LOGINT(ctx), LY_EINT); |
2380 | |
|
2381 | 0 | switch (cur_op) { |
2382 | 0 | case LYD_DIFF_OP_DELETE: |
2383 | | /* remember current flags */ |
2384 | 0 | trg_flags = (*diff_match)->flags; |
2385 | |
|
2386 | 0 | if (lysc_is_userordered(src_diff->schema)) { |
2387 | 0 | assert((*diff_match)->schema); |
2388 | | |
2389 | | /* get anchor metadata */ |
2390 | 0 | if (lysc_is_dup_inst_list((*diff_match)->schema)) { |
2391 | 0 | meta_name = "yang:position"; |
2392 | 0 | orig_meta_name = "yang:orig-position"; |
2393 | 0 | } else if ((*diff_match)->schema->nodetype == LYS_LIST) { |
2394 | 0 | meta_name = "yang:key"; |
2395 | 0 | orig_meta_name = "yang:orig-key"; |
2396 | 0 | } else { |
2397 | 0 | meta_name = "yang:value"; |
2398 | 0 | orig_meta_name = "yang:orig-value"; |
2399 | 0 | } |
2400 | 0 | meta = lyd_find_meta(src_diff->meta, NULL, meta_name); |
2401 | 0 | LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL); |
2402 | 0 | orig_meta = lyd_find_meta((*diff_match)->meta, NULL, orig_meta_name); |
2403 | 0 | LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, *diff_match), LY_EINVAL); |
2404 | |
|
2405 | 0 | if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) { |
2406 | | /* deleted + created at another position -> operation REPLACE */ |
2407 | 0 | LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE)); |
2408 | | |
2409 | | /* add anchor metadata */ |
2410 | 0 | LY_CHECK_RET(lyd_dup_meta_single(meta, *diff_match, NULL)); |
2411 | | |
2412 | | /* previous created nodes affect the metadata so move it at the end (of the instances) */ |
2413 | 0 | child = *diff_match; |
2414 | 0 | while (child->next && (child->next->schema == (*diff_match)->schema)) { |
2415 | 0 | child = child->next; |
2416 | 0 | } |
2417 | 0 | if (child != *diff_match) { |
2418 | 0 | LY_CHECK_RET(lyd_insert_after(child, *diff_match)); |
2419 | 0 | } |
2420 | 0 | } else { |
2421 | | /* deleted + created at the same position -> operation NONE */ |
2422 | 0 | LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE)); |
2423 | | |
2424 | | /* delete anchor metadata */ |
2425 | 0 | lyd_free_meta_single(orig_meta); |
2426 | 0 | } |
2427 | 0 | } else if (src_diff->schema->nodetype == LYS_LEAF) { |
2428 | 0 | if (options & LYD_DIFF_MERGE_DEFAULTS) { |
2429 | | /* we are dealing with a leaf and are handling default values specially (as explicit nodes) */ |
2430 | 0 | sleaf = (struct lysc_node_leaf *)src_diff->schema; |
2431 | 0 | } |
2432 | |
|
2433 | 0 | if (sleaf && sleaf->dflt.str && !lysc_value_cmp(src_diff->schema, NULL, &sleaf->dflt, lyd_get_value(src_diff))) { |
2434 | | /* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */ |
2435 | 0 | LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE)); |
2436 | 0 | } else if (!lyd_compare_single(*diff_match, src_diff, 0)) { |
2437 | | /* deleted + created -> operation NONE */ |
2438 | 0 | LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE)); |
2439 | 0 | } else if ((*diff_match)->schema) { |
2440 | | /* we deleted it, but it was created with a different value -> operation REPLACE */ |
2441 | 0 | LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE)); |
2442 | | |
2443 | | /* current value is the previous one (meta) */ |
2444 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-value", |
2445 | 0 | lyd_get_value(*diff_match), LYD_NEW_VAL_STORE_ONLY, NULL)); |
2446 | | |
2447 | | /* update the value itself */ |
2448 | 0 | LY_CHECK_RET(lyd_change_term(*diff_match, lyd_get_value(src_diff))); |
2449 | 0 | } else { |
2450 | | /* also operation REPLACE but we need to change an opaque node into a data node */ |
2451 | 0 | LY_CHECK_RET(lyd_dup_single(src_diff, (*diff_match)->parent, LYD_DUP_NO_META | LYD_DUP_WITH_FLAGS, &src_dup)); |
2452 | 0 | if (!(*diff_match)->parent) { |
2453 | | /* will always be inserted before diff_match, which is opaque */ |
2454 | 0 | LY_CHECK_RET(lyd_insert_sibling(*diff_match, src_dup, diff)); |
2455 | 0 | } |
2456 | 0 | to_free = *diff_match; |
2457 | 0 | *diff_match = src_dup; |
2458 | |
|
2459 | 0 | r = lyd_new_meta(ctx, src_dup, NULL, "yang:orig-value", lyd_get_value(to_free), LYD_NEW_VAL_STORE_ONLY, NULL); |
2460 | 0 | lyd_free_tree(to_free); |
2461 | 0 | LY_CHECK_RET(r); |
2462 | 0 | LY_CHECK_RET(lyd_new_meta(ctx, src_dup, NULL, "yang:operation", lyd_diff_op2str(LYD_DIFF_OP_REPLACE), LYD_NEW_VAL_STORE_ONLY, NULL)); |
2463 | 0 | } |
2464 | 0 | } else { |
2465 | | /* deleted + created -> operation NONE */ |
2466 | 0 | LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE)); |
2467 | 0 | } |
2468 | | |
2469 | 0 | assert((*diff_match)->schema); |
2470 | 0 | if ((*diff_match)->schema->nodetype & LYD_NODE_TERM) { |
2471 | | /* add orig-dflt metadata */ |
2472 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-default", |
2473 | 0 | trg_flags & LYD_DEFAULT ? "true" : "false", LYD_NEW_VAL_STORE_ONLY, NULL)); |
2474 | | |
2475 | | /* update dflt flag itself */ |
2476 | 0 | (*diff_match)->flags &= ~LYD_DEFAULT; |
2477 | 0 | (*diff_match)->flags |= src_diff->flags & LYD_DEFAULT; |
2478 | 0 | } |
2479 | | |
2480 | | /* but the operation of its children should remain DELETE */ |
2481 | 0 | LY_LIST_FOR(lyd_child_no_keys(*diff_match), child) { |
2482 | 0 | LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE)); |
2483 | 0 | } |
2484 | 0 | break; |
2485 | 0 | default: |
2486 | | /* create and replace operations are not valid */ |
2487 | 0 | LOGERR_MERGEOP(LYD_CTX(src_diff), *diff_match, cur_op, LYD_DIFF_OP_CREATE); |
2488 | 0 | return LY_EINVAL; |
2489 | 0 | } |
2490 | | |
2491 | 0 | return LY_SUCCESS; |
2492 | 0 | } |
2493 | | |
2494 | | /** |
2495 | | * @brief Update operations on a diff node when the new operation is DELETE. |
2496 | | * |
2497 | | * @param[in] diff_match Node from the diff. |
2498 | | * @param[in] cur_op Current operation of @p diff_match. |
2499 | | * @param[in] src_diff Current source diff node. |
2500 | | * @return LY_ERR value. |
2501 | | */ |
2502 | | static LY_ERR |
2503 | | lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff) |
2504 | 0 | { |
2505 | 0 | struct lyd_node *child; |
2506 | 0 | struct lyd_meta *meta; |
2507 | 0 | struct lyd_attr *attr; |
2508 | 0 | const char *meta_name; |
2509 | 0 | const struct ly_ctx *ctx = LYD_CTX(diff_match); |
2510 | 0 | LY_ERR r; |
2511 | | |
2512 | | /* we can delete only exact existing nodes */ |
2513 | 0 | LY_CHECK_ERR_RET(lyd_compare_single(diff_match, src_diff, 0), LOGINT(LYD_CTX(src_diff)), LY_EINT); |
2514 | |
|
2515 | 0 | switch (cur_op) { |
2516 | 0 | case LYD_DIFF_OP_CREATE: |
2517 | | /* it was created, but then deleted -> set NONE operation */ |
2518 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE)); |
2519 | |
|
2520 | 0 | if (diff_match->schema->nodetype & LYD_NODE_TERM) { |
2521 | | /* add orig-default meta because it is expected */ |
2522 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default", |
2523 | 0 | src_diff->flags & LYD_DEFAULT ? "true" : "false", LYD_NEW_VAL_STORE_ONLY, NULL)); |
2524 | 0 | } |
2525 | 0 | break; |
2526 | 0 | case LYD_DIFF_OP_REPLACE: |
2527 | | /* remove the redundant metadata */ |
2528 | 0 | if (lysc_is_userordered(diff_match->schema)) { |
2529 | 0 | if (lysc_is_dup_inst_list(diff_match->schema)) { |
2530 | 0 | meta_name = "position"; |
2531 | 0 | } else if (diff_match->schema->nodetype == LYS_LIST) { |
2532 | 0 | meta_name = "key"; |
2533 | 0 | } else { |
2534 | 0 | meta_name = "value"; |
2535 | 0 | } |
2536 | 0 | } else { |
2537 | 0 | assert(diff_match->schema->nodetype == LYS_LEAF); |
2538 | | |
2539 | | /* switch value for the original one */ |
2540 | 0 | meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-value"); |
2541 | 0 | LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-value", diff_match), LY_EINVAL); |
2542 | 0 | if (lyd_change_term(diff_match, lyd_get_meta_value(meta))) { |
2543 | 0 | LOGERR_UNEXPVAL(ctx, diff_match, "target diff"); |
2544 | 0 | return LY_EINVAL; |
2545 | 0 | } |
2546 | | |
2547 | | /* switch default for the original one, then remove the meta */ |
2548 | 0 | meta = lyd_find_meta(diff_match->meta, NULL, "yang:orig-default"); |
2549 | 0 | LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, "yang:orig-default", diff_match), LY_EINVAL); |
2550 | 0 | diff_match->flags &= ~LYD_DEFAULT; |
2551 | 0 | if (meta->value.boolean) { |
2552 | 0 | diff_match->flags |= LYD_DEFAULT; |
2553 | 0 | } |
2554 | 0 | lyd_free_meta_single(meta); |
2555 | |
|
2556 | 0 | meta_name = "orig-value"; |
2557 | 0 | } |
2558 | 0 | lyd_diff_del_meta(diff_match, meta_name); |
2559 | | |
2560 | | /* it was being changed, but should be deleted instead -> set DELETE operation */ |
2561 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE)); |
2562 | 0 | break; |
2563 | 0 | case LYD_DIFF_OP_NONE: |
2564 | | /* it was not modified, but should be deleted -> set DELETE operation */ |
2565 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_DELETE)); |
2566 | 0 | break; |
2567 | 0 | default: |
2568 | | /* delete operation is not valid */ |
2569 | 0 | LOGERR_MERGEOP(LYD_CTX(diff_match), diff_match, cur_op, LYD_DIFF_OP_DELETE); |
2570 | 0 | return LY_EINVAL; |
2571 | 0 | } |
2572 | | |
2573 | 0 | if (!lysc_is_dup_inst_list(diff_match->schema)) { |
2574 | | /* keep operation without one for descendants that are yet to be merged */ |
2575 | 0 | LY_LIST_FOR(lyd_child_no_keys(diff_match), child) { |
2576 | 0 | lyd_diff_find_meta(child, "operation", &meta, &attr); |
2577 | 0 | if (meta || attr) { |
2578 | 0 | continue; |
2579 | 0 | } |
2580 | | |
2581 | 0 | if (!child->schema) { |
2582 | 0 | r = lyd_find_sibling_opaq_next(lyd_child(src_diff), LYD_NAME(child), NULL); |
2583 | 0 | } else if (child->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) { |
2584 | 0 | r = lyd_find_sibling_first(lyd_child(src_diff), child, NULL); |
2585 | 0 | } else { |
2586 | 0 | r = lyd_find_sibling_val(lyd_child(src_diff), child->schema, NULL, 0, NULL); |
2587 | 0 | } |
2588 | 0 | if (!r) { |
2589 | 0 | LY_CHECK_RET(lyd_diff_change_op(child, cur_op)); |
2590 | 0 | } else if (r != LY_ENOTFOUND) { |
2591 | 0 | return r; |
2592 | 0 | } |
2593 | 0 | } |
2594 | 0 | } /* else key-less list, for which all the descendants act as keys */ |
2595 | | |
2596 | 0 | return LY_SUCCESS; |
2597 | 0 | } |
2598 | | |
2599 | | /** |
2600 | | * @brief Check a node is redundant based on having any diff metadata. |
2601 | | * |
2602 | | * @param[in] diff Diff node to check. |
2603 | | * @return 1 if the node is redundant; |
2604 | | * @return 0 otherwise. |
2605 | | */ |
2606 | | static ly_bool |
2607 | | lyd_diff_is_redundant_meta(const struct lyd_node *diff) |
2608 | 0 | { |
2609 | 0 | const struct lyd_meta *m; |
2610 | 0 | const struct lyd_node *child; |
2611 | | |
2612 | | /* diff metadata on the node */ |
2613 | 0 | LY_LIST_FOR(diff->meta, m) { |
2614 | 0 | if (!strncmp(m->name, "meta-", 5)) { |
2615 | 0 | return 0; |
2616 | 0 | } |
2617 | 0 | } |
2618 | | |
2619 | | /* diff metadata on keys */ |
2620 | 0 | LY_LIST_FOR(lyd_child(diff), child) { |
2621 | 0 | if (!lysc_is_key(child->schema)) { |
2622 | 0 | break; |
2623 | 0 | } |
2624 | | |
2625 | 0 | LY_LIST_FOR(child->meta, m) { |
2626 | 0 | if (!strncmp(m->name, "meta-", 5)) { |
2627 | 0 | return 0; |
2628 | 0 | } |
2629 | 0 | } |
2630 | 0 | } |
2631 | | |
2632 | 0 | return 1; |
2633 | 0 | } |
2634 | | |
2635 | | /** |
2636 | | * @brief Check whether this diff node is redundant (does not change data). |
2637 | | * |
2638 | | * @param[in] diff Diff node. |
2639 | | * @return 0 if not, non-zero if it is. |
2640 | | */ |
2641 | | static ly_bool |
2642 | | lyd_diff_is_redundant(struct lyd_node *diff) |
2643 | 0 | { |
2644 | 0 | enum lyd_diff_op op; |
2645 | 0 | struct lyd_meta *meta; |
2646 | 0 | struct lyd_node *child; |
2647 | 0 | const struct lys_module *mod; |
2648 | 0 | const char *str; |
2649 | |
|
2650 | 0 | assert(diff); |
2651 | | |
2652 | 0 | if (lysc_is_dup_inst_list(diff->schema)) { |
2653 | | /* all descendants are keys */ |
2654 | 0 | child = NULL; |
2655 | 0 | } else { |
2656 | 0 | child = lyd_child_no_keys(diff); |
2657 | 0 | } |
2658 | 0 | mod = ly_ctx_get_module_implemented(LYD_CTX(diff), "yang"); |
2659 | 0 | assert(mod); |
2660 | | |
2661 | | /* get node operation */ |
2662 | 0 | LY_CHECK_RET(lyd_diff_get_op(diff, &op, NULL), 0); |
2663 | |
|
2664 | 0 | if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) { |
2665 | | |
2666 | | /** userordered lists can have different nodes that lead to identical changes. |
2667 | | * if such a redundant node is detected, this function returns non-zero. |
2668 | | */ |
2669 | 0 | LY_CHECK_RET(lyd_diff_is_redundant_userord_move(&diff, child), 1); |
2670 | |
|
2671 | 0 | } else if (op == LYD_DIFF_OP_NONE) { |
2672 | 0 | if (!diff->schema) { |
2673 | | /* opaque node with none must be redundant */ |
2674 | 0 | return 1; |
2675 | 0 | } |
2676 | | |
2677 | | /* check for diff metadata */ |
2678 | 0 | if (!lyd_diff_is_redundant_meta(diff)) { |
2679 | 0 | return 0; |
2680 | 0 | } |
2681 | | |
2682 | 0 | if (diff->schema->nodetype & LYD_NODE_TERM) { |
2683 | | /* check whether at least the default flags are different */ |
2684 | 0 | meta = lyd_find_meta(diff->meta, mod, "orig-default"); |
2685 | 0 | assert(meta); |
2686 | 0 | str = lyd_get_meta_value(meta); |
2687 | | |
2688 | | /* if previous and current dflt flags are the same, this node is redundant */ |
2689 | 0 | if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) { |
2690 | 0 | return 1; |
2691 | 0 | } |
2692 | 0 | return 0; |
2693 | 0 | } |
2694 | 0 | } |
2695 | | |
2696 | 0 | if (!child && (op == LYD_DIFF_OP_NONE)) { |
2697 | 0 | return 1; |
2698 | 0 | } |
2699 | | |
2700 | 0 | return 0; |
2701 | 0 | } |
2702 | | |
2703 | | /** |
2704 | | * @brief Merge all diff metadata found on a source diff node. |
2705 | | * |
2706 | | * @param[in] src_diff Source node. |
2707 | | * @param[in,out] trg_diff Target node to update. |
2708 | | * @return LY_ERR value. |
2709 | | */ |
2710 | | static LY_ERR |
2711 | | lyd_diff_merge_metadata(const struct lyd_node *src_diff, struct lyd_node *trg_diff) |
2712 | 0 | { |
2713 | 0 | LY_ERR rc = LY_SUCCESS; |
2714 | 0 | const struct lys_module *mod; |
2715 | 0 | struct lyd_meta *m, **src_meta_replace = NULL, **src_meta_orig = NULL; |
2716 | 0 | struct lyd_meta **trg_meta_replace = NULL, **trg_meta_orig = NULL, *m1, *m2; |
2717 | 0 | uint32_t i, j, src_mr_count = 0, src_mo_count = 0, trg_mr_count = 0, trg_mo_count = 0; |
2718 | |
|
2719 | 0 | assert(src_diff->schema == trg_diff->schema); |
2720 | | |
2721 | 0 | mod = ly_ctx_get_module_implemented(LYD_CTX(src_diff), "yang"); |
2722 | 0 | assert(mod); |
2723 | | |
2724 | | /* collect all the metadata so we can safely modify them */ |
2725 | 0 | LY_LIST_FOR(trg_diff->meta, m) { |
2726 | 0 | if (m->annotation->module != mod) { |
2727 | 0 | continue; |
2728 | 0 | } |
2729 | | |
2730 | 0 | if (!strcmp(m->name, "meta-replace")) { |
2731 | 0 | LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &trg_meta_replace, &trg_mr_count), cleanup); |
2732 | 0 | } else if (!strcmp(m->name, "meta-orig")) { |
2733 | 0 | LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &trg_meta_orig, &trg_mo_count), cleanup); |
2734 | 0 | } |
2735 | 0 | } |
2736 | | |
2737 | | /* make sure meta_replace and meta_orig arrays are aligned */ |
2738 | 0 | rc = lyd_diff_metadata_replace_orig_align(trg_meta_replace, trg_mr_count, trg_meta_orig, trg_mo_count); |
2739 | 0 | LY_CHECK_GOTO(rc, cleanup); |
2740 | |
|
2741 | 0 | LY_LIST_FOR(src_diff->meta, m) { |
2742 | 0 | if (m->annotation->module != mod) { |
2743 | 0 | continue; |
2744 | 0 | } |
2745 | | |
2746 | 0 | if (!strcmp(m->name, "meta-create")) { |
2747 | | /* find relevant metadata in the target */ |
2748 | 0 | rc = lyd_diff_metadata_find(trg_diff->meta, "yang:meta-delete", lyd_get_meta_value(m), 0, &m1); |
2749 | 0 | LY_CHECK_GOTO(rc, cleanup); |
2750 | 0 | m2 = NULL; |
2751 | 0 | for (i = 0; i < trg_mo_count; ++i) { |
2752 | 0 | if (lyd_get_meta_value(m) == lyd_get_meta_value(trg_meta_orig[i])) { |
2753 | 0 | m2 = trg_meta_orig[i]; |
2754 | 0 | break; |
2755 | 0 | } |
2756 | 0 | } |
2757 | |
|
2758 | 0 | if (m1) { |
2759 | | /* create + delete -> no change */ |
2760 | 0 | lyd_free_meta_single(m1); |
2761 | 0 | } else if (m2) { |
2762 | | /* create + replace -> create with updated value */ |
2763 | 0 | rc = lyd_new_meta(NULL, trg_diff, mod, "meta-create", lyd_get_meta_value(trg_meta_replace[i]), 0, NULL); |
2764 | 0 | LY_CHECK_GOTO(rc, cleanup); |
2765 | | |
2766 | | /* remove meta-replace and meta-orig */ |
2767 | 0 | lyd_free_meta_single(trg_meta_replace[i]); |
2768 | 0 | --trg_mr_count; |
2769 | 0 | if (i < trg_mr_count) { |
2770 | 0 | memmove(&trg_meta_replace[i], &trg_meta_replace[i + 1], (trg_mr_count - i) * sizeof *trg_meta_replace); |
2771 | 0 | } |
2772 | |
|
2773 | 0 | lyd_free_meta_single(trg_meta_orig[i]); |
2774 | 0 | --trg_mo_count; |
2775 | 0 | if (i < trg_mo_count) { |
2776 | 0 | memmove(&trg_meta_orig[i], &trg_meta_orig[i + 1], (trg_mo_count - i) * sizeof *trg_meta_orig); |
2777 | 0 | } |
2778 | 0 | } else { |
2779 | | /* copy to the target */ |
2780 | 0 | LY_CHECK_GOTO(rc = lyd_dup_meta_single(m, trg_diff, NULL), cleanup); |
2781 | 0 | } |
2782 | 0 | } else if (!strcmp(m->name, "meta-delete")) { |
2783 | | /* find relevant metadata in the target */ |
2784 | 0 | rc = lyd_diff_metadata_find(trg_diff->meta, "yang:meta-create", lyd_get_meta_value(m), 0, &m1); |
2785 | 0 | LY_CHECK_GOTO(rc, cleanup); |
2786 | |
|
2787 | 0 | if (m1) { |
2788 | | /* delete + create -> no change */ |
2789 | 0 | lyd_free_meta_single(m1); |
2790 | 0 | } else { |
2791 | | /* copy to the target */ |
2792 | 0 | LY_CHECK_GOTO(rc = lyd_dup_meta_single(m, trg_diff, NULL), cleanup); |
2793 | 0 | } |
2794 | 0 | } else if (!strcmp(m->name, "meta-replace")) { |
2795 | | /* collect all meta-replace metadata */ |
2796 | 0 | LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &src_meta_replace, &src_mr_count), cleanup); |
2797 | 0 | } else if (!strcmp(m->name, "meta-orig")) { |
2798 | | /* collect all meta-orig metadata */ |
2799 | 0 | LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &src_meta_orig, &src_mo_count), cleanup); |
2800 | 0 | } |
2801 | 0 | } |
2802 | | |
2803 | | /* make sure meta_replace and meta_orig arrays are aligned */ |
2804 | 0 | rc = lyd_diff_metadata_replace_orig_align(src_meta_replace, src_mr_count, src_meta_orig, src_mo_count); |
2805 | 0 | LY_CHECK_GOTO(rc, cleanup); |
2806 | |
|
2807 | 0 | for (i = 0; i < src_mr_count; ++i) { |
2808 | | /* find relevant metadata in the target */ |
2809 | 0 | rc = lyd_diff_metadata_find(trg_diff->meta, "yang:meta-delete", lyd_get_meta_value(src_meta_replace[i]), 0, &m1); |
2810 | 0 | LY_CHECK_GOTO(rc, cleanup); |
2811 | 0 | m2 = NULL; |
2812 | 0 | for (j = 0; j < trg_mo_count; ++j) { |
2813 | 0 | if (lyd_get_meta_value(trg_meta_orig[j]) == lyd_get_meta_value(src_meta_replace[i])) { |
2814 | 0 | m2 = trg_meta_orig[j]; |
2815 | 0 | break; |
2816 | 0 | } |
2817 | 0 | } |
2818 | |
|
2819 | 0 | if (m1) { |
2820 | | /* replace + delete -> delete with updated value */ |
2821 | 0 | LY_CHECK_GOTO(rc = lyd_change_meta(m1, lyd_get_meta_value(src_meta_orig[i])), cleanup); |
2822 | 0 | } else if (m2) { |
2823 | | /* replace + replace -> replace (orig) with updated value */ |
2824 | 0 | LY_CHECK_GOTO(rc = lyd_change_meta(m2, lyd_get_meta_value(src_meta_orig[i])), cleanup); |
2825 | 0 | } else { |
2826 | | /* copy to the target */ |
2827 | 0 | LY_CHECK_GOTO(rc = lyd_dup_meta_single(src_meta_replace[i], trg_diff, NULL), cleanup); |
2828 | 0 | LY_CHECK_GOTO(rc = lyd_dup_meta_single(src_meta_orig[i], trg_diff, NULL), cleanup); |
2829 | 0 | } |
2830 | 0 | } |
2831 | | |
2832 | 0 | cleanup: |
2833 | 0 | free(src_meta_replace); |
2834 | 0 | free(src_meta_orig); |
2835 | 0 | free(trg_meta_replace); |
2836 | 0 | free(trg_meta_orig); |
2837 | 0 | return rc; |
2838 | 0 | } |
2839 | | |
2840 | | /** |
2841 | | * @brief Merge all diff metadata found on a source diff subtree, recursively. |
2842 | | * |
2843 | | * @param[in] src_diff Source subtree. |
2844 | | * @param[in,out] trg_diff Target subtree to update. |
2845 | | * @param[in] keys_only Whether to process the node with keys only or with all the descendants. |
2846 | | * @return LY_ERR value. |
2847 | | */ |
2848 | | static LY_ERR |
2849 | | lyd_diff_merge_metadata_r(const struct lyd_node *src_diff, struct lyd_node *trg_diff, ly_bool keys_only) |
2850 | 0 | { |
2851 | 0 | const struct lyd_node *src_child; |
2852 | 0 | struct lyd_node *trg_child; |
2853 | | |
2854 | | /* merge metadata on the node itself */ |
2855 | 0 | LY_CHECK_RET(lyd_diff_merge_metadata(src_diff, trg_diff)); |
2856 | | |
2857 | | /* merge descendants recursively */ |
2858 | 0 | trg_child = lyd_child(trg_diff); |
2859 | 0 | LY_LIST_FOR(lyd_child(src_diff), src_child) { |
2860 | 0 | if (keys_only && !lysc_is_key(src_child->schema)) { |
2861 | 0 | break; |
2862 | 0 | } |
2863 | | |
2864 | 0 | LY_CHECK_RET(lyd_diff_merge_metadata(src_child, trg_child)); |
2865 | |
|
2866 | 0 | trg_child = trg_child->next; |
2867 | 0 | } |
2868 | | |
2869 | 0 | return LY_SUCCESS; |
2870 | 0 | } |
2871 | | |
2872 | | /** |
2873 | | * @brief Merge sysrepo diff subtree with another diff, recursively. |
2874 | | * |
2875 | | * @param[in] src_diff Source diff node. |
2876 | | * @param[in] diff_parent Current sysrepo diff parent. |
2877 | | * @param[in] diff_cb Optional diff callback. |
2878 | | * @param[in] cb_data User data for @p diff_cb. |
2879 | | * @param[in,out] dup_inst Duplicate instance cache for all @p src_diff siblings. |
2880 | | * @param[in] options Diff merge options. |
2881 | | * @param[in,out] diff Diff root node. |
2882 | | * @return LY_ERR value. |
2883 | | */ |
2884 | | static LY_ERR |
2885 | | lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data, |
2886 | | struct ly_ht **dup_inst, uint16_t options, struct lyd_node **diff) |
2887 | 0 | { |
2888 | 0 | LY_ERR ret = LY_SUCCESS; |
2889 | 0 | struct lyd_node *child, *diff_node = NULL; |
2890 | 0 | enum lyd_diff_op src_op, cur_op; |
2891 | 0 | struct ly_ht *child_dup_inst = NULL; |
2892 | 0 | uint32_t diff_opts; |
2893 | | |
2894 | | /* get source node operation */ |
2895 | 0 | LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op, NULL)); |
2896 | | |
2897 | | /* find an equal node in the current diff */ |
2898 | 0 | LY_CHECK_RET(lyd_diff_find_match(diff_parent ? lyd_child_no_keys(diff_parent) : *diff, src_diff, 1, dup_inst, &diff_node)); |
2899 | |
|
2900 | 0 | if (diff_node) { |
2901 | | /* get target (current) operation */ |
2902 | 0 | LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op, NULL)); |
2903 | | |
2904 | | /* merge operations */ |
2905 | 0 | switch (src_op) { |
2906 | 0 | case LYD_DIFF_OP_REPLACE: |
2907 | 0 | ret = lyd_diff_merge_replace(diff_node, cur_op, src_diff); |
2908 | 0 | break; |
2909 | 0 | case LYD_DIFF_OP_CREATE: |
2910 | 0 | if ((cur_op == LYD_DIFF_OP_CREATE) && lysc_is_dup_inst_list(diff_node->schema)) { |
2911 | | /* special case of creating duplicate (leaf-)list instances */ |
2912 | 0 | goto add_diff; |
2913 | 0 | } |
2914 | | |
2915 | 0 | ret = lyd_diff_merge_create(&diff_node, diff, cur_op, src_diff, options); |
2916 | 0 | break; |
2917 | 0 | case LYD_DIFF_OP_DELETE: |
2918 | 0 | ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff); |
2919 | 0 | break; |
2920 | 0 | case LYD_DIFF_OP_NONE: |
2921 | | /* key-less list can never have "none" operation since all its descendants are acting as "keys" */ |
2922 | 0 | assert((src_diff->schema->nodetype != LYS_LIST) || !lysc_is_dup_inst_list(src_diff->schema)); |
2923 | 0 | ret = lyd_diff_merge_none(diff_node, cur_op, src_diff); |
2924 | 0 | break; |
2925 | 0 | default: |
2926 | 0 | LOGINT_RET(LYD_CTX(src_diff)); |
2927 | 0 | } |
2928 | 0 | if (ret) { |
2929 | 0 | LOGERR(LYD_CTX(src_diff), LY_EOTHER, "Merging operation \"%s\" failed.", lyd_diff_op2str(src_op)); |
2930 | 0 | return ret; |
2931 | 0 | } |
2932 | | |
2933 | | /* merge any metadata */ |
2934 | 0 | LY_CHECK_RET(lyd_diff_merge_metadata_r(src_diff, diff_node, lysc_is_dup_inst_list(src_diff->schema) ? 0 : 1)); |
2935 | |
|
2936 | 0 | if (diff_cb) { |
2937 | | /* call callback */ |
2938 | 0 | LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data)); |
2939 | 0 | } |
2940 | | |
2941 | | /* update diff parent */ |
2942 | 0 | diff_parent = diff_node; |
2943 | | |
2944 | | /* for diff purposes, all key-less list descendants actually act as keys (identifying the same instances), |
2945 | | * so there is nothing to merge for these "keys" */ |
2946 | 0 | if (!lysc_is_dup_inst_list(src_diff->schema)) { |
2947 | | /* merge src_diff recursively */ |
2948 | 0 | LY_LIST_FOR(lyd_child_no_keys(src_diff), child) { |
2949 | 0 | ret = lyd_diff_merge_r(child, diff_parent, diff_cb, cb_data, &child_dup_inst, options, diff); |
2950 | 0 | if (ret) { |
2951 | 0 | break; |
2952 | 0 | } |
2953 | 0 | } |
2954 | 0 | lyd_dup_inst_free(child_dup_inst); |
2955 | 0 | LY_CHECK_RET(ret); |
2956 | 0 | } |
2957 | 0 | } else { |
2958 | 0 | add_diff: |
2959 | | /* add new diff node with all descendants */ |
2960 | 0 | diff_opts = LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS; |
2961 | 0 | if (lysc_is_userordered(src_diff->schema)) { |
2962 | 0 | diff_opts |= LYD_DUP_NO_LYDS; |
2963 | 0 | } |
2964 | 0 | LY_CHECK_RET(lyd_dup_single(src_diff, diff_parent, diff_opts, &diff_node)); |
2965 | | |
2966 | | /* insert node into diff if not already */ |
2967 | 0 | if (!diff_parent) { |
2968 | 0 | lyd_diff_insert_sibling(*diff, diff_node, diff); |
2969 | 0 | } |
2970 | | |
2971 | | /* update operation */ |
2972 | 0 | LY_CHECK_RET(lyd_diff_change_op(diff_node, src_op)); |
2973 | |
|
2974 | 0 | if (diff_cb) { |
2975 | | /* call callback with no source diff node since it was duplicated and just added */ |
2976 | 0 | LY_CHECK_RET(diff_cb(NULL, diff_node, cb_data)); |
2977 | 0 | } |
2978 | | |
2979 | | /* update diff parent */ |
2980 | 0 | diff_parent = diff_node; |
2981 | 0 | } |
2982 | | |
2983 | | /* remove any redundant nodes */ |
2984 | 0 | if (lyd_diff_is_redundant(diff_parent)) { |
2985 | 0 | if (diff_parent == *diff) { |
2986 | 0 | *diff = (*diff)->next; |
2987 | 0 | } |
2988 | 0 | lyd_free_tree(diff_parent); |
2989 | 0 | } |
2990 | |
|
2991 | 0 | return LY_SUCCESS; |
2992 | 0 | } |
2993 | | |
2994 | | LIBYANG_API_DEF LY_ERR |
2995 | | lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff, const struct lys_module *mod, |
2996 | | lyd_diff_cb diff_cb, void *cb_data, uint16_t options) |
2997 | 0 | { |
2998 | 0 | const struct lyd_node *src_root; |
2999 | 0 | struct ly_ht *dup_inst = NULL; |
3000 | 0 | LY_ERR ret = LY_SUCCESS; |
3001 | |
|
3002 | 0 | LY_LIST_FOR(src_diff, src_root) { |
3003 | 0 | if (mod && (lyd_owner_module(src_root) != mod)) { |
3004 | | /* skip data nodes from different modules */ |
3005 | 0 | continue; |
3006 | 0 | } |
3007 | | |
3008 | | /* apply relevant nodes from the diff datatree */ |
3009 | 0 | LY_CHECK_GOTO(ret = lyd_diff_merge_r(src_root, NULL, diff_cb, cb_data, &dup_inst, options, diff), cleanup); |
3010 | 0 | } |
3011 | | |
3012 | 0 | cleanup: |
3013 | 0 | lyd_dup_inst_free(dup_inst); |
3014 | 0 | return ret; |
3015 | 0 | } |
3016 | | |
3017 | | LIBYANG_API_DEF LY_ERR |
3018 | | lyd_diff_merge_tree(struct lyd_node **diff_first, struct lyd_node *diff_parent, const struct lyd_node *src_sibling, |
3019 | | lyd_diff_cb diff_cb, void *cb_data, uint16_t options) |
3020 | 0 | { |
3021 | 0 | LY_ERR ret; |
3022 | 0 | struct ly_ht *dup_inst = NULL; |
3023 | |
|
3024 | 0 | if (!src_sibling) { |
3025 | 0 | return LY_SUCCESS; |
3026 | 0 | } |
3027 | | |
3028 | 0 | ret = lyd_diff_merge_r(src_sibling, diff_parent, diff_cb, cb_data, &dup_inst, options, diff_first); |
3029 | 0 | lyd_dup_inst_free(dup_inst); |
3030 | 0 | return ret; |
3031 | 0 | } |
3032 | | |
3033 | | LIBYANG_API_DEF LY_ERR |
3034 | | lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint16_t options) |
3035 | 0 | { |
3036 | 0 | return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options); |
3037 | 0 | } |
3038 | | |
3039 | | /** |
3040 | | * @brief Reverse diff value meta by switching it for the node value. |
3041 | | * |
3042 | | * @param[in] node Parent meta node. |
3043 | | * @param[in] mod Meta module. |
3044 | | * @return LY_ERR value. |
3045 | | */ |
3046 | | static LY_ERR |
3047 | | lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod) |
3048 | 0 | { |
3049 | 0 | LY_ERR ret = LY_SUCCESS; |
3050 | 0 | struct lyd_meta *meta; |
3051 | 0 | const char *val1 = NULL; |
3052 | 0 | char *val2; |
3053 | 0 | uint32_t flags; |
3054 | |
|
3055 | 0 | assert(node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA)); |
3056 | | |
3057 | 0 | meta = lyd_find_meta(node->meta, mod, "orig-value"); |
3058 | 0 | LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), "orig-value", node), LY_EINVAL); |
3059 | | |
3060 | | /* orig-value */ |
3061 | 0 | val1 = lyd_get_meta_value(meta); |
3062 | | |
3063 | | /* current value */ |
3064 | 0 | if (node->schema->nodetype == LYS_LEAF) { |
3065 | 0 | val2 = strdup(lyd_get_value(node)); |
3066 | 0 | } else { |
3067 | 0 | LY_CHECK_RET(lyd_any_value_str(node, LYD_XML, &val2)); |
3068 | 0 | } |
3069 | | |
3070 | | /* switch values, keep default flag */ |
3071 | 0 | flags = node->flags; |
3072 | 0 | if (node->schema->nodetype == LYS_LEAF) { |
3073 | 0 | LY_CHECK_GOTO(ret = lyd_change_term(node, val1), cleanup); |
3074 | 0 | } else { |
3075 | 0 | LY_CHECK_GOTO(ret = lyd_any_copy_value(node, NULL, val1, 0), cleanup); |
3076 | 0 | } |
3077 | 0 | node->flags = flags; |
3078 | 0 | LY_CHECK_GOTO(ret = lyd_change_meta(meta, val2), cleanup); |
3079 | |
|
3080 | 0 | cleanup: |
3081 | 0 | free(val2); |
3082 | 0 | return ret; |
3083 | 0 | } |
3084 | | |
3085 | | /** |
3086 | | * @brief Reverse diff default meta by switching it for the node dflt flag. |
3087 | | * |
3088 | | * @param[in] node Parent meta node. |
3089 | | * @param[in] mod Meta module. |
3090 | | * @return LY_ERR value. |
3091 | | */ |
3092 | | static LY_ERR |
3093 | | lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod) |
3094 | 0 | { |
3095 | 0 | struct lyd_meta *meta; |
3096 | 0 | uint32_t flag1, flag2; |
3097 | |
|
3098 | 0 | meta = lyd_find_meta(node->meta, mod, "orig-default"); |
3099 | 0 | LY_CHECK_ERR_RET(!meta, LOGINT(mod->ctx), LY_EINT); |
3100 | | |
3101 | | /* orig-default */ |
3102 | 0 | if (meta->value.boolean) { |
3103 | 0 | flag1 = LYD_DEFAULT; |
3104 | 0 | } else { |
3105 | 0 | flag1 = 0; |
3106 | 0 | } |
3107 | | |
3108 | | /* current default */ |
3109 | 0 | flag2 = node->flags & LYD_DEFAULT; |
3110 | |
|
3111 | 0 | if (flag1 == flag2) { |
3112 | | /* no default state change so nothing to reverse */ |
3113 | 0 | return LY_SUCCESS; |
3114 | 0 | } |
3115 | | |
3116 | | /* switch defaults */ |
3117 | 0 | node->flags &= ~LYD_DEFAULT; |
3118 | 0 | node->flags |= flag1; |
3119 | 0 | LY_CHECK_RET(lyd_change_meta(meta, flag2 ? "true" : "false")); |
3120 | |
|
3121 | 0 | return LY_SUCCESS; |
3122 | 0 | } |
3123 | | |
3124 | | /** |
3125 | | * @brief Reverse diff meta by switching their values. |
3126 | | * |
3127 | | * @param[in] node Parent meta node. |
3128 | | * @param[in] mod Meta module. |
3129 | | * @param[in] name1 First meta name. |
3130 | | * @param[in] name2 Second meta name. |
3131 | | * @return LY_ERR value. |
3132 | | */ |
3133 | | static LY_ERR |
3134 | | lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2) |
3135 | 0 | { |
3136 | 0 | LY_ERR ret = LY_SUCCESS; |
3137 | 0 | struct lyd_meta *meta1, *meta2; |
3138 | 0 | const char *val1 = NULL; |
3139 | 0 | char *val2 = NULL; |
3140 | |
|
3141 | 0 | meta1 = lyd_find_meta(node->meta, mod, name1); |
3142 | 0 | LY_CHECK_ERR_RET(!meta1, LOGERR_META(LYD_CTX(node), name1, node), LY_EINVAL); |
3143 | |
|
3144 | 0 | meta2 = lyd_find_meta(node->meta, mod, name2); |
3145 | 0 | LY_CHECK_ERR_RET(!meta2, LOGERR_META(LYD_CTX(node), name2, node), LY_EINVAL); |
3146 | | |
3147 | | /* value1 */ |
3148 | 0 | val1 = lyd_get_meta_value(meta1); |
3149 | | |
3150 | | /* value2 */ |
3151 | 0 | val2 = strdup(lyd_get_meta_value(meta2)); |
3152 | | |
3153 | | /* switch values */ |
3154 | 0 | LY_CHECK_GOTO(ret = lyd_change_meta(meta1, val2), cleanup); |
3155 | 0 | LY_CHECK_GOTO(ret = lyd_change_meta(meta2, val1), cleanup); |
3156 | |
|
3157 | 0 | cleanup: |
3158 | 0 | free(val2); |
3159 | 0 | return ret; |
3160 | 0 | } |
3161 | | |
3162 | | /** |
3163 | | * @brief Rename diff meta by deleting the old meta and creating a new one. |
3164 | | * |
3165 | | * @param[in] node Parent meta node. |
3166 | | * @param[in] mod Meta module. |
3167 | | * @param[in] src_name Current meta name. |
3168 | | * @param[in] trg_name New meta name. |
3169 | | * @return LY_ERR value. |
3170 | | */ |
3171 | | static LY_ERR |
3172 | | lyd_diff_rename_meta(struct lyd_node *node, const struct lys_module *mod, const char *src_name, const char *trg_name) |
3173 | 0 | { |
3174 | 0 | struct lyd_meta *meta; |
3175 | | |
3176 | | /* find the old meta */ |
3177 | 0 | meta = lyd_find_meta(node->meta, mod, src_name); |
3178 | 0 | LY_CHECK_ERR_RET(!meta, LOGERR_META(LYD_CTX(node), src_name, node), LY_EINVAL); |
3179 | | |
3180 | | /* create the new meta */ |
3181 | 0 | LY_CHECK_RET(lyd_new_meta(LYD_CTX(node), node, mod, trg_name, lyd_get_meta_value(meta), LYD_NEW_VAL_STORE_ONLY, NULL)); |
3182 | | |
3183 | | /* delete the old meta */ |
3184 | 0 | lyd_free_meta_single(meta); |
3185 | |
|
3186 | 0 | return LY_SUCCESS; |
3187 | 0 | } |
3188 | | |
3189 | | /** |
3190 | | * @brief Reverse all metadata diff meta. |
3191 | | * |
3192 | | * @param[in,out] diff Diff node with metadata diff to reverse. |
3193 | | * @return LY_ERR value. |
3194 | | */ |
3195 | | static LY_ERR |
3196 | | lyd_diff_reverse_metadata_diff(struct lyd_node *node) |
3197 | 0 | { |
3198 | 0 | LY_ERR rc = LY_SUCCESS; |
3199 | 0 | struct lyd_meta *m, **meta_create = NULL, **meta_delete = NULL, **meta_replace = NULL, **meta_orig = NULL; |
3200 | 0 | uint32_t i, mc_count = 0, md_count = 0, mr_count = 0, mo_count = 0; |
3201 | 0 | const struct lys_module *mod; |
3202 | 0 | const char *val1; |
3203 | |
|
3204 | 0 | mod = ly_ctx_get_module_implemented(LYD_CTX(node), "yang"); |
3205 | 0 | assert(mod); |
3206 | | |
3207 | | /* collect all the metadata so we can safely modify them */ |
3208 | 0 | LY_LIST_FOR(node->meta, m) { |
3209 | 0 | if (m->annotation->module != mod) { |
3210 | 0 | continue; |
3211 | 0 | } |
3212 | | |
3213 | 0 | if (!strcmp(m->name, "meta-create")) { |
3214 | 0 | LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_create, &mc_count), cleanup); |
3215 | 0 | } else if (!strcmp(m->name, "meta-delete")) { |
3216 | 0 | LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_delete, &md_count), cleanup); |
3217 | 0 | } else if (!strcmp(m->name, "meta-replace")) { |
3218 | 0 | LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_replace, &mr_count), cleanup); |
3219 | 0 | } else if (!strcmp(m->name, "meta-orig")) { |
3220 | 0 | LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_orig, &mo_count), cleanup); |
3221 | 0 | } |
3222 | 0 | } |
3223 | | |
3224 | | /* make sure meta_replace and meta_orig arrays are aligned */ |
3225 | 0 | LY_CHECK_GOTO(rc = lyd_diff_metadata_replace_orig_align(meta_replace, mr_count, meta_orig, mo_count), cleanup); |
3226 | | |
3227 | | /* reverse all the meta-create metadata */ |
3228 | 0 | for (i = 0; i < mc_count; ++i) { |
3229 | 0 | rc = lyd_new_meta(NULL, node, mod, "meta-delete", lyd_get_meta_value(meta_create[i]), 0, NULL); |
3230 | 0 | LY_CHECK_GOTO(rc, cleanup); |
3231 | 0 | lyd_free_meta_single(meta_create[i]); |
3232 | 0 | } |
3233 | | |
3234 | | /* reverse all the meta-replace and meta-orig metadata */ |
3235 | 0 | for (i = 0; i < mr_count; ++i) { |
3236 | 0 | LY_CHECK_GOTO(rc = lydict_dup(LYD_CTX(node), lyd_get_meta_value(meta_replace[i]), &val1), cleanup); |
3237 | |
|
3238 | 0 | rc = lyd_change_meta(meta_replace[i], lyd_get_meta_value(meta_orig[i])); |
3239 | 0 | if (rc) { |
3240 | 0 | lydict_remove(LYD_CTX(node), val1); |
3241 | 0 | goto cleanup; |
3242 | 0 | } |
3243 | | |
3244 | 0 | rc = lyd_change_meta(meta_orig[i], val1); |
3245 | 0 | lydict_remove(LYD_CTX(node), val1); |
3246 | 0 | LY_CHECK_GOTO(rc, cleanup); |
3247 | 0 | } |
3248 | | |
3249 | | /* reverse all the meta-delete metadata */ |
3250 | 0 | for (i = 0; i < md_count; ++i) { |
3251 | 0 | rc = lyd_new_meta(NULL, node, mod, "meta-create", lyd_get_meta_value(meta_delete[i]), 0, NULL); |
3252 | 0 | LY_CHECK_GOTO(rc, cleanup); |
3253 | 0 | lyd_free_meta_single(meta_delete[i]); |
3254 | 0 | } |
3255 | | |
3256 | 0 | cleanup: |
3257 | 0 | free(meta_create); |
3258 | 0 | free(meta_delete); |
3259 | 0 | free(meta_replace); |
3260 | 0 | free(meta_orig); |
3261 | 0 | return rc; |
3262 | 0 | } |
3263 | | |
3264 | | /** |
3265 | | * @brief Process a user-ordered node for a reverse diff. |
3266 | | * |
3267 | | * @param[in] node Reversed diff user-ordered node. NULL if only collected nodes should be reversed. |
3268 | | * @param[in,out] schema_p Current siblings user-ordered schema node. |
3269 | | * @param[in,out] nodes_p Collected diff nodes of @p schema_p whose order is to be reversed. |
3270 | | * @return LY_ERR value. |
3271 | | */ |
3272 | | static LY_ERR |
3273 | | lyd_diff_reverse_userord(struct lyd_node *node, const struct lysc_node **schema_p, struct lyd_node ***nodes_p) |
3274 | 0 | { |
3275 | 0 | LY_ERR rc = LY_SUCCESS; |
3276 | 0 | struct lyd_node **ptr, *anchor; |
3277 | 0 | LY_ARRAY_COUNT_TYPE u; |
3278 | |
|
3279 | 0 | assert(node || *schema_p); |
3280 | | |
3281 | | /* all the schema node instances were collected, reverse their order */ |
3282 | 0 | if (!node || (*schema_p && (node->schema != *schema_p))) { |
3283 | | /* unlink all the nodes except for the last */ |
3284 | 0 | for (u = 0; u < LY_ARRAY_COUNT(*nodes_p) - 1; ++u) { |
3285 | 0 | lyd_unlink_tree((*nodes_p)[u]); |
3286 | 0 | } |
3287 | | |
3288 | | /* use the last as the anchor, becomes the first node */ |
3289 | 0 | anchor = (*nodes_p)[u]; |
3290 | | |
3291 | | /* link them in reverse order back */ |
3292 | 0 | if (u) { |
3293 | 0 | do { |
3294 | 0 | --u; |
3295 | 0 | LY_CHECK_GOTO(rc = lyd_insert_after(anchor, (*nodes_p)[u]), cleanup); |
3296 | 0 | anchor = (*nodes_p)[u]; |
3297 | 0 | } while (u); |
3298 | 0 | } |
3299 | | |
3300 | | /* clear the collected nodes */ |
3301 | 0 | *schema_p = NULL; |
3302 | 0 | LY_ARRAY_FREE(*nodes_p); |
3303 | 0 | *nodes_p = NULL; |
3304 | 0 | } |
3305 | | |
3306 | 0 | if (!node) { |
3307 | | /* nothing more to do */ |
3308 | 0 | goto cleanup; |
3309 | 0 | } |
3310 | | |
3311 | | /* first node */ |
3312 | 0 | if (!*schema_p) { |
3313 | 0 | *schema_p = node->schema; |
3314 | 0 | } |
3315 | 0 | assert(*schema_p == node->schema); |
3316 | | |
3317 | | /* collect it */ |
3318 | 0 | LY_ARRAY_NEW_GOTO(LYD_CTX(node), *nodes_p, ptr, rc, cleanup); |
3319 | 0 | *ptr = node; |
3320 | |
|
3321 | 0 | cleanup: |
3322 | 0 | return rc; |
3323 | 0 | } |
3324 | | |
3325 | | /** |
3326 | | * @brief Reverse all sibling diff nodes, recursively. |
3327 | | * |
3328 | | * @param[in,out] sibling First sibling to reverse. |
3329 | | * @param[in] yang_mod YANG module 'yang' to use for metadata. |
3330 | | * @return LY_ERR value. |
3331 | | */ |
3332 | | static LY_ERR |
3333 | | lyd_diff_reverse_siblings_r(struct lyd_node *sibling, const struct lys_module *yang_mod) |
3334 | 0 | { |
3335 | 0 | LY_ERR rc = LY_SUCCESS; |
3336 | 0 | struct lyd_node *iter, *iter2, **userord = NULL; |
3337 | 0 | const struct lysc_node *userord_schema = NULL; |
3338 | 0 | enum lyd_diff_op op; |
3339 | |
|
3340 | 0 | LY_LIST_FOR(sibling, iter) { |
3341 | | /* skip all keys */ |
3342 | 0 | if (lysc_is_key(iter->schema)) { |
3343 | 0 | continue; |
3344 | 0 | } |
3345 | | |
3346 | | /* update module if needed */ |
3347 | 0 | if (LYD_CTX(iter) != yang_mod->ctx) { |
3348 | 0 | yang_mod = ly_ctx_get_module_implemented(LYD_CTX(iter), "yang"); |
3349 | 0 | assert(yang_mod); |
3350 | 0 | } |
3351 | | |
3352 | | /* find operation attribute, if any */ |
3353 | 0 | LY_CHECK_GOTO(rc = lyd_diff_get_op(iter, &op, NULL), cleanup); |
3354 | |
|
3355 | 0 | switch (op) { |
3356 | 0 | case LYD_DIFF_OP_CREATE: |
3357 | | /* reverse create to delete */ |
3358 | 0 | LY_CHECK_GOTO(rc = lyd_diff_change_op(iter, LYD_DIFF_OP_DELETE), cleanup); |
3359 | | |
3360 | | /* reverse user-ordered metadata */ |
3361 | 0 | if (lysc_is_userordered(iter->schema)) { |
3362 | 0 | if (lysc_is_dup_inst_list(iter->schema)) { |
3363 | 0 | LY_CHECK_GOTO(rc = lyd_diff_rename_meta(iter, yang_mod, "position", "orig-position"), cleanup); |
3364 | 0 | } else if (iter->schema->nodetype == LYS_LEAFLIST) { |
3365 | 0 | LY_CHECK_GOTO(rc = lyd_diff_rename_meta(iter, yang_mod, "value", "orig-value"), cleanup); |
3366 | 0 | } else { |
3367 | 0 | assert(iter->schema->nodetype == LYS_LIST); |
3368 | 0 | LY_CHECK_GOTO(rc = lyd_diff_rename_meta(iter, yang_mod, "key", "orig-key"), cleanup); |
3369 | 0 | } |
3370 | 0 | } |
3371 | | |
3372 | | /* keep the operation for all the children, handled recursively */ |
3373 | 0 | LY_LIST_FOR(lyd_child_no_keys(iter), iter2) { |
3374 | 0 | LY_CHECK_GOTO(rc = lyd_diff_change_op(iter2, LYD_DIFF_OP_CREATE), cleanup); |
3375 | 0 | } |
3376 | 0 | break; |
3377 | | |
3378 | 0 | case LYD_DIFF_OP_DELETE: |
3379 | | /* reverse delete to create */ |
3380 | 0 | LY_CHECK_GOTO(rc = lyd_diff_change_op(iter, LYD_DIFF_OP_CREATE), cleanup); |
3381 | | |
3382 | | /* reverse user-ordered metadata */ |
3383 | 0 | if (lysc_is_userordered(iter->schema)) { |
3384 | 0 | if (lysc_is_dup_inst_list(iter->schema)) { |
3385 | 0 | LY_CHECK_GOTO(rc = lyd_diff_rename_meta(iter, yang_mod, "orig-position", "position"), cleanup); |
3386 | 0 | } else if (iter->schema->nodetype == LYS_LEAFLIST) { |
3387 | 0 | LY_CHECK_GOTO(rc = lyd_diff_rename_meta(iter, yang_mod, "orig-value", "value"), cleanup); |
3388 | 0 | } else { |
3389 | 0 | assert(iter->schema->nodetype == LYS_LIST); |
3390 | 0 | LY_CHECK_GOTO(rc = lyd_diff_rename_meta(iter, yang_mod, "orig-key", "key"), cleanup); |
3391 | 0 | } |
3392 | 0 | } |
3393 | | |
3394 | | /* keep the operation for all the children, handled recursively */ |
3395 | 0 | LY_LIST_FOR(lyd_child_no_keys(iter), iter2) { |
3396 | 0 | LY_CHECK_GOTO(rc = lyd_diff_change_op(iter2, LYD_DIFF_OP_DELETE), cleanup); |
3397 | 0 | } |
3398 | 0 | break; |
3399 | | |
3400 | 0 | case LYD_DIFF_OP_REPLACE: |
3401 | 0 | switch (iter->schema->nodetype) { |
3402 | 0 | case LYS_LEAF: |
3403 | | /* leaf value change */ |
3404 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_value(iter, yang_mod), cleanup); |
3405 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_default(iter, yang_mod), cleanup); |
3406 | 0 | break; |
3407 | 0 | case LYS_ANYXML: |
3408 | 0 | case LYS_ANYDATA: |
3409 | | /* any value change */ |
3410 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_value(iter, yang_mod), cleanup); |
3411 | 0 | break; |
3412 | 0 | case LYS_LEAFLIST: |
3413 | | /* leaf-list move */ |
3414 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_default(iter, yang_mod), cleanup); |
3415 | 0 | if (lysc_is_dup_inst_list(iter->schema)) { |
3416 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(iter, yang_mod, "orig-position", "position"), cleanup); |
3417 | 0 | } else { |
3418 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(iter, yang_mod, "orig-value", "value"), cleanup); |
3419 | 0 | } |
3420 | 0 | break; |
3421 | 0 | case LYS_LIST: |
3422 | | /* list move */ |
3423 | 0 | if (lysc_is_dup_inst_list(iter->schema)) { |
3424 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(iter, yang_mod, "orig-position", "position"), cleanup); |
3425 | 0 | } else { |
3426 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(iter, yang_mod, "orig-key", "key"), cleanup); |
3427 | 0 | } |
3428 | 0 | break; |
3429 | 0 | default: |
3430 | 0 | LOGINT(LYD_CTX(iter)); |
3431 | 0 | rc = LY_EINT; |
3432 | 0 | goto cleanup; |
3433 | 0 | } |
3434 | 0 | break; |
3435 | | |
3436 | 0 | case LYD_DIFF_OP_NONE: |
3437 | 0 | switch (iter->schema->nodetype) { |
3438 | 0 | case LYS_LEAF: |
3439 | 0 | case LYS_LEAFLIST: |
3440 | | /* default flag change */ |
3441 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_default(iter, yang_mod), cleanup); |
3442 | 0 | break; |
3443 | 0 | default: |
3444 | | /* nothing to do */ |
3445 | 0 | break; |
3446 | 0 | } |
3447 | 0 | break; |
3448 | 0 | } |
3449 | | |
3450 | | /* reverse any metadata diff */ |
3451 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_metadata_diff(iter), cleanup); |
3452 | | |
3453 | | /* revursively reverse all descendants */ |
3454 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_siblings_r(lyd_child(iter), yang_mod), cleanup); |
3455 | |
|
3456 | 0 | if (lysc_is_userordered(iter->schema)) { |
3457 | | /* special user-ordered nodes processing (collect all the nodes) */ |
3458 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_userord(iter, &userord_schema, &userord), cleanup); |
3459 | 0 | } |
3460 | 0 | } |
3461 | | |
3462 | 0 | if (userord_schema) { |
3463 | | /* finish user-ordered nodes processing - reverse the order of the nodes */ |
3464 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_userord(NULL, &userord_schema, &userord), cleanup); |
3465 | 0 | } |
3466 | | |
3467 | 0 | cleanup: |
3468 | 0 | LY_ARRAY_FREE(userord); |
3469 | 0 | return rc; |
3470 | 0 | } |
3471 | | |
3472 | | LIBYANG_API_DEF LY_ERR |
3473 | | lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff) |
3474 | 0 | { |
3475 | 0 | LY_ERR rc = LY_SUCCESS; |
3476 | 0 | const struct lys_module *mod = NULL; |
3477 | |
|
3478 | 0 | LY_CHECK_ARG_RET(NULL, diff, LY_EINVAL); |
3479 | |
|
3480 | 0 | if (!src_diff) { |
3481 | 0 | *diff = NULL; |
3482 | 0 | return LY_SUCCESS; |
3483 | 0 | } |
3484 | | |
3485 | | /* duplicate diff */ |
3486 | 0 | LY_CHECK_GOTO(rc = lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE, diff), cleanup); |
3487 | | |
3488 | | /* find 'yang' module */ |
3489 | 0 | mod = ly_ctx_get_module_implemented(LYD_CTX(src_diff), "yang"); |
3490 | 0 | assert(mod); |
3491 | | |
3492 | | /* reverse it */ |
3493 | 0 | LY_CHECK_GOTO(rc = lyd_diff_reverse_siblings_r(*diff, mod), cleanup); |
3494 | | |
3495 | | /* changing the order of user-ordered nodes may have changed the first node */ |
3496 | 0 | *diff = lyd_first_sibling(*diff); |
3497 | |
|
3498 | 0 | cleanup: |
3499 | 0 | if (rc) { |
3500 | 0 | lyd_free_siblings(*diff); |
3501 | | *diff = NULL; |
3502 | 0 | } |
3503 | 0 | return rc; |
3504 | 0 | } |