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