Coverage Report

Created: 2025-10-08 06:07

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