Coverage Report

Created: 2026-05-16 07:04

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