Coverage Report

Created: 2026-07-16 06:22

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