Coverage Report

Created: 2025-07-18 06:09

/src/libyang/src/plugins_exts/schema_mount.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file schema_mount.c
3
 * @author Tadeas Vintrlik <xvintr04@stud.fit.vutbr.cz>
4
 * @brief libyang extension plugin - Schema Mount (RFC 8528)
5
 *
6
 * Copyright (c) 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
15
#define _GNU_SOURCE
16
17
#include <assert.h>
18
#include <pthread.h>
19
#include <stdint.h>
20
#include <stdlib.h>
21
#include <string.h>
22
23
#include "compat.h"
24
#include "dict.h"
25
#include "libyang.h"
26
#include "log.h"
27
#include "ly_common.h"
28
#include "parser_data.h"
29
#include "plugins_exts.h"
30
#include "plugins_types.h"
31
#include "tree_data.h"
32
#include "tree_schema.h"
33
34
/**
35
 * @brief Internal schema mount data structure for holding all the contexts of parsed data.
36
 */
37
struct lyplg_ext_sm {
38
    pthread_mutex_t lock;               /**< lock for accessing this shared structure */
39
40
    struct lyplg_ext_sm_shared {
41
        struct {
42
            struct ly_ctx *ctx;         /**< context shared between all data of this mount point */
43
            const char *mount_point;    /**< mount point name */
44
            const char *content_id;     /**< yang-library content-id (alternatively module-set-id),
45
                                             stored in the dictionary of the ext instance context */
46
        } *schemas;                     /**< array of shared schema schemas */
47
        uint32_t schema_count;          /**< length of schemas array */
48
        uint32_t ref_count;             /**< number of references to this structure (mount-points with the same name
49
                                             in the module) */
50
    } *shared;                          /**< shared schema mount points */
51
52
    struct lyplg_ext_sm_inln {
53
        struct {
54
            struct ly_ctx *ctx;         /**< context created for inline schema data, may be reused if possible */
55
        } *schemas;                     /**< array of inline schemas */
56
        uint32_t schema_count;          /**< length of schemas array */
57
    } inln;                             /**< inline mount points */
58
};
59
60
struct sprinter_tree_priv {
61
    struct ly_ctx *ext_ctx;
62
    struct ly_set *refs;
63
};
64
65
#define EXT_LOGERR_MEM_RET(cctx, ext) \
66
0
        lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s:%d).", __FILE__, __LINE__); \
67
0
        return LY_EMEM
68
69
#define EXT_LOGERR_MEM_GOTO(cctx, ext, rc, label) \
70
0
        lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EMEM, "Memory allocation failed (%s:%d).", __FILE__, __LINE__); \
71
0
        rc = LY_EMEM; \
72
0
        goto label
73
74
#define EXT_LOGERR_INT_RET(cctx, ext) \
75
0
        lyplg_ext_compile_log(cctx, ext, LY_LLERR, LY_EINT, "Internal error (%s:%d).", __FILE__, __LINE__); \
76
0
        return LY_EINT
77
78
/**
79
 * @brief Check if given mount point is unique among its siblings
80
 *
81
 * @param[in] pctx Parse context.
82
 * @param[in] ext Parsed extension instance.
83
 * @return LY_SUCCESS if is unique;
84
 * @return LY_EINVAL otherwise.
85
 */
86
static LY_ERR
87
schema_mount_parse_unique_mp(struct lysp_ctx *pctx, const struct lysp_ext_instance *ext)
88
0
{
89
0
    struct lysp_ext_instance *exts;
90
0
    LY_ARRAY_COUNT_TYPE u;
91
0
    struct lysp_node *parent;
92
93
    /* check if it is the only instance of the mount-point among its siblings */
94
0
    parent = ext->parent;
95
0
    exts = parent->exts;
96
0
    LY_ARRAY_FOR(exts, u) {
97
0
        if (&exts[u] == ext) {
98
0
            continue;
99
0
        }
100
101
0
        if (!strcmp(exts[u].name, ext->name)) {
102
0
            lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Multiple extension \"%s\" instances.", ext->name);
103
0
            return LY_EINVAL;
104
0
        }
105
0
    }
106
0
    return LY_SUCCESS;
107
0
}
108
109
/**
110
 * @brief Schema mount parse.
111
 * Checks if it can be a valid extension instance for yang schema mount.
112
 *
113
 * Implementation of ::lyplg_ext_parse_clb callback set as lyext_plugin::parse.
114
 */
115
static LY_ERR
116
schema_mount_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
117
0
{
118
    /* check YANG version 1.1 */
119
0
    if (lyplg_ext_parse_get_cur_pmod(pctx)->version != LYS_VERSION_1_1) {
120
0
        lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension \"%s\" instance not allowed in YANG version 1 module.",
121
0
                ext->name);
122
0
        return LY_EINVAL;
123
0
    }
124
125
    /* check parent nodetype */
126
0
    if ((ext->parent_stmt != LY_STMT_CONTAINER) && (ext->parent_stmt != LY_STMT_LIST)) {
127
0
        lyplg_ext_parse_log(pctx, ext, LY_LLERR, LY_EVALID, "Extension \"%s\" instance allowed only in container or list statement.",
128
0
                ext->name);
129
0
        return LY_EINVAL;
130
0
    }
131
132
    /* check uniqueness */
133
0
    if (schema_mount_parse_unique_mp(pctx, ext)) {
134
0
        return LY_EINVAL;
135
0
    }
136
137
    /* nothing to actually parse */
138
0
    return LY_SUCCESS;
139
0
}
140
141
struct lyplg_ext_sm_shared_cb_data {
142
    const struct lysc_ext_instance *ext;
143
    struct lyplg_ext_sm_shared *sm_shared;
144
};
145
146
static LY_ERR
147
schema_mount_compile_mod_dfs_cb(struct lysc_node *node, void *data, ly_bool *UNUSED(dfs_continue))
148
0
{
149
0
    struct lyplg_ext_sm_shared_cb_data *cb_data = data;
150
0
    struct lyplg_ext_sm *sm_data;
151
0
    struct lysc_ext_instance *exts;
152
0
    LY_ARRAY_COUNT_TYPE u;
153
154
0
    if (node == cb_data->ext->parent) {
155
        /* parent of the current compiled extension, skip */
156
0
        return LY_SUCCESS;
157
0
    }
158
159
    /* find the same mount point */
160
0
    exts = node->exts;
161
0
    LY_ARRAY_FOR(exts, u) {
162
0
        if (!strcmp(exts[u].def->module->name, "ietf-yang-schema-mount") && !strcmp(exts[u].def->name, "mount-point") &&
163
0
                (exts[u].argument == cb_data->ext->argument)) {
164
            /* same mount point, break the DFS search */
165
0
            sm_data = exts[u].compiled;
166
0
            cb_data->sm_shared = sm_data->shared;
167
0
            return LY_EEXIST;
168
0
        }
169
0
    }
170
171
    /* not found, continue search */
172
0
    return LY_SUCCESS;
173
0
}
174
175
static struct lyplg_ext_sm_shared *
176
schema_mount_compile_find_shared(const struct lys_module *mod, const struct lysc_ext_instance *ext)
177
0
{
178
0
    struct lyplg_ext_sm_shared_cb_data cb_data;
179
0
    LY_ERR r;
180
181
    /* prepare cb_data */
182
0
    cb_data.ext = ext;
183
0
    cb_data.sm_shared = NULL;
184
185
    /* try to find the same mount point */
186
0
    r = lysc_module_dfs_full(mod, schema_mount_compile_mod_dfs_cb, &cb_data);
187
0
    (void)r;
188
0
    assert((!r && !cb_data.sm_shared) || ((r == LY_EEXIST) && cb_data.sm_shared));
189
190
0
    return cb_data.sm_shared;
191
0
}
192
193
/**
194
 * @brief Schema mount compile.
195
 * Checks if it can be a valid extension instance for yang schema mount.
196
 *
197
 * Implementation of ::lyplg_ext_compile_clb callback set as lyext_plugin::compile.
198
 */
199
static LY_ERR
200
schema_mount_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *UNUSED(extp), struct lysc_ext_instance *ext)
201
0
{
202
0
    const struct lysc_node *node;
203
0
    struct lyplg_ext_sm *sm_data;
204
205
    /* init internal data */
206
0
    sm_data = calloc(1, sizeof *sm_data);
207
0
    if (!sm_data) {
208
0
        EXT_LOGERR_MEM_RET(cctx, ext);
209
0
    }
210
0
    pthread_mutex_init(&sm_data->lock, NULL);
211
0
    ext->compiled = sm_data;
212
213
    /* find the owner module */
214
0
    node = ext->parent;
215
0
    while (node->parent) {
216
0
        node = node->parent;
217
0
    }
218
219
    /* reuse/init shared schema */
220
0
    sm_data->shared = schema_mount_compile_find_shared(node->module, ext);
221
0
    if (sm_data->shared) {
222
0
        ++sm_data->shared->ref_count;
223
0
    } else {
224
0
        sm_data->shared = calloc(1, sizeof *sm_data->shared);
225
0
        if (!sm_data->shared) {
226
0
            free(sm_data);
227
0
            EXT_LOGERR_MEM_RET(cctx, ext);
228
0
        }
229
0
        sm_data->shared->ref_count = 1;
230
0
    }
231
232
0
    return LY_SUCCESS;
233
0
}
234
235
/**
236
 * @brief Learn details about the current mount point.
237
 *
238
 * @param[in] ext Compiled extension instance.
239
 * @param[in] ext_data Extension data retrieved by the callback.
240
 * @param[out] config Whether the whole schema should keep its config or be set to false.
241
 * @param[out] shared Optional flag whether the schema is shared or inline.
242
 * @return LY_ERR value.
243
 */
244
static LY_ERR
245
schema_mount_get_smount(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool *config,
246
        ly_bool *shared)
247
0
{
248
0
    struct lyd_node *mpoint, *node;
249
0
    char *path = NULL;
250
0
    LY_ERR r;
251
252
    /* find the mount point */
253
0
    if (asprintf(&path, "/ietf-yang-schema-mount:schema-mounts/mount-point[module='%s'][label='%s']", ext->module->name,
254
0
            ext->argument) == -1) {
255
0
        EXT_LOGERR_MEM_RET(NULL, ext);
256
0
    }
257
0
    r = ext_data ? lyd_find_path(ext_data, path, 0, &mpoint) : LY_ENOTFOUND;
258
0
    free(path);
259
0
    if (r) {
260
        /* missing mount-point, cannot be data for this extension (https://datatracker.ietf.org/doc/html/rfc8528#page-10) */
261
0
        return LY_ENOT;
262
0
    }
263
264
    /* check config */
265
0
    *config = 1;
266
0
    if (!lyd_find_path(mpoint, "config", 0, &node) && !strcmp(lyd_get_value(node), "false")) {
267
0
        *config = 0;
268
0
    }
269
0
    assert((ext->parent_stmt == LY_STMT_CONTAINER) || (ext->parent_stmt == LY_STMT_LIST));
270
0
    if (((struct lysc_node *)ext->parent)->flags & LYS_CONFIG_R) {
271
0
        *config = 0;
272
0
    }
273
274
0
    if (shared) {
275
        /* check schema-ref */
276
0
        if (lyd_find_path(mpoint, "shared-schema", 0, NULL)) {
277
0
            if (lyd_find_path(mpoint, "inline", 0, NULL)) {
278
0
                EXT_LOGERR_INT_RET(NULL, ext);
279
0
            }
280
0
            *shared = 0;
281
0
        } else {
282
0
            *shared = 1;
283
0
        }
284
0
    }
285
286
0
    return LY_SUCCESS;
287
0
}
288
289
/**
290
 * @brief Create schema (context) based on retrieved extension data.
291
 *
292
 * @param[in] ext Compiled extension instance.
293
 * @param[in] ext_data Extension data retrieved by the callback.
294
 * @param[in] config Whether the whole schema should keep its config or be set to false.
295
 * @param[out] ext_ctx Schema to use for parsing the data.
296
 * @return LY_ERR value.
297
 */
298
static LY_ERR
299
schema_mount_create_ctx(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config,
300
        struct ly_ctx **ext_ctx)
301
0
{
302
0
    LY_ERR rc = LY_SUCCESS;
303
0
    const char * const *searchdirs;
304
0
    char *sdirs = NULL;
305
0
    const struct lys_module *mod;
306
0
    struct lysc_node *root, *node;
307
0
    uint32_t i, idx = 0;
308
309
    /* get searchdirs from the current context */
310
0
    searchdirs = ly_ctx_get_searchdirs(ext->module->ctx);
311
312
0
    if (searchdirs) {
313
        /* append them all into a single string */
314
0
        for (i = 0; searchdirs[i]; ++i) {
315
0
            if ((rc = ly_strcat(&sdirs, "%s" PATH_SEPARATOR, searchdirs[i]))) {
316
0
                goto cleanup;
317
0
            }
318
0
        }
319
0
    }
320
321
    /* create the context based on the data */
322
0
    if ((rc = ly_ctx_new_yldata(sdirs, ext_data, ly_ctx_get_options(ext->module->ctx), ext_ctx))) {
323
0
        lyplg_ext_compile_log(NULL, ext, LY_LLERR, rc, "Failed to create context for the schema-mount data.");
324
0
        goto cleanup;
325
0
    }
326
327
0
    if (!config) {
328
        /* manually change the config of all schema nodes in all the modules */
329
0
        while ((mod = ly_ctx_get_module_iter(*ext_ctx, &idx))) {
330
0
            if (!mod->implemented) {
331
0
                continue;
332
0
            }
333
334
0
            LY_LIST_FOR(mod->compiled->data, root) {
335
0
                LYSC_TREE_DFS_BEGIN(root, node) {
336
0
                    node->flags &= ~LYS_CONFIG_W;
337
0
                    node->flags |= LYS_CONFIG_R;
338
339
0
                    LYSC_TREE_DFS_END(root, node);
340
0
                }
341
0
            }
342
0
        }
343
0
    }
344
345
0
cleanup:
346
0
    free(sdirs);
347
0
    return rc;
348
0
}
349
350
/**
351
 * @brief Get ietf-yang-library context-id from its data.
352
 *
353
 * @param[in] ext Compiled extension instance for logging.
354
 * @param[in] ext_data Extension data retrieved by the callback with the yang-library data.
355
 * @param[out] content_id Content ID in @p ext_data.
356
 * @return LY_ERR value.
357
 */
358
static LY_ERR
359
schema_mount_get_content_id(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, const char **content_id)
360
0
{
361
0
    struct lyd_node *node = NULL;
362
363
0
    *content_id = NULL;
364
365
    /* get yang-library content-id or module-set-id */
366
0
    if (ext_data) {
367
0
        lyd_find_path(ext_data, "/ietf-yang-library:yang-library/content-id", 0, &node);
368
0
        if (!node) {
369
0
            lyd_find_path(ext_data, "/ietf-yang-library:modules-state/module-set-id", 0, &node);
370
0
        }
371
0
        if (node) {
372
0
            *content_id = lyd_get_value(node);
373
0
        }
374
0
    }
375
376
0
    if (!*content_id) {
377
0
        lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EVALID,
378
0
                "Missing \"content-id\" or \"module-set-id\" in ietf-yang-library data.");
379
0
        return LY_EVALID;
380
0
    }
381
0
    return LY_SUCCESS;
382
0
}
383
384
/**
385
 * @brief Get schema (context) for a shared-schema mount point.
386
 *
387
 * @param[in] ext Compiled extension instance.
388
 * @param[in] ext_data Extension data retrieved by the callback.
389
 * @param[in] config Whether the whole schema should keep its config or be set to false.
390
 * @param[out] ext_ctx Schema to use for parsing the data.
391
 * @return LY_ERR value.
392
 */
393
static LY_ERR
394
schema_mount_get_ctx_shared(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config,
395
        const struct ly_ctx **ext_ctx)
396
0
{
397
0
    struct lyplg_ext_sm *sm_data = ext->compiled;
398
0
    LY_ERR rc = LY_SUCCESS, r;
399
0
    struct ly_ctx *new_ctx = NULL;
400
0
    uint32_t i;
401
0
    const char *content_id;
402
0
    void *mem;
403
404
0
    assert(sm_data && sm_data->shared);
405
406
    /* get yang-library content-id or module-set-id */
407
0
    if ((r = schema_mount_get_content_id(ext, ext_data, &content_id))) {
408
0
        return r;
409
0
    }
410
411
    /* LOCK */
412
0
    if ((r = pthread_mutex_lock(&sm_data->lock))) {
413
0
        lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_ESYS, "Mutex lock failed (%s).", strerror(r));
414
0
        return LY_ESYS;
415
0
    }
416
417
    /* try to find this mount point */
418
0
    for (i = 0; i < sm_data->shared->schema_count; ++i) {
419
0
        if (ext->argument == sm_data->shared->schemas[i].mount_point) {
420
0
            break;
421
0
        }
422
0
    }
423
424
0
    if (i < sm_data->shared->schema_count) {
425
        /* schema exists already */
426
0
        if (strcmp(content_id, sm_data->shared->schemas[i].content_id)) {
427
0
            lyplg_ext_compile_log_path("/ietf-yang-library:yang-library/content-id", ext, LY_LLERR, LY_EVALID,
428
0
                    "Shared-schema yang-library content-id \"%s\" differs from \"%s\" used previously.",
429
0
                    content_id, sm_data->shared->schemas[i].content_id);
430
0
            rc = LY_EVALID;
431
0
            goto cleanup;
432
0
        }
433
0
    } else {
434
        /* no schema found, create it */
435
0
        if ((r = schema_mount_create_ctx(ext, ext_data, config, &new_ctx))) {
436
0
            rc = r;
437
0
            goto cleanup;
438
0
        }
439
440
        /* new entry */
441
0
        mem = realloc(sm_data->shared->schemas, (i + 1) * sizeof *sm_data->shared->schemas);
442
0
        if (!mem) {
443
0
            ly_ctx_destroy(new_ctx);
444
0
            EXT_LOGERR_MEM_GOTO(NULL, ext, rc, cleanup);
445
0
        }
446
0
        sm_data->shared->schemas = mem;
447
0
        ++sm_data->shared->schema_count;
448
449
        /* fill entry */
450
0
        sm_data->shared->schemas[i].ctx = new_ctx;
451
0
        sm_data->shared->schemas[i].mount_point = ext->argument;
452
0
        lydict_insert(ext->module->ctx, content_id, 0, &sm_data->shared->schemas[i].content_id);
453
0
    }
454
455
    /* use the context */
456
0
    *ext_ctx = sm_data->shared->schemas[i].ctx;
457
458
0
cleanup:
459
    /* UNLOCK */
460
0
    pthread_mutex_unlock(&sm_data->lock);
461
462
0
    return rc;
463
0
}
464
465
/**
466
 * @brief Check whether ietf-yang-library data describe an existing context meaning whether it includes
467
 * at least exactly all the mentioned modules.
468
 *
469
 * @param[in] ext Compiled extension instance for logging.
470
 * @param[in] ext_data Extension data retrieved by the callback with the yang-library data.
471
 * @param[in] ctx Context to consider.
472
 * @return LY_SUCCESS if the context matches.
473
 * @return LY_ENOT if the context differs.
474
 * @return LY_ERR on error.
475
 */
476
static LY_ERR
477
schema_mount_ctx_match(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, const struct ly_ctx *ctx)
478
0
{
479
0
    struct ly_set *impl_mods = NULL, *imp_mods = NULL;
480
0
    struct lyd_node *node;
481
0
    const struct lys_module *mod;
482
0
    const char *name, *revision;
483
0
    LY_ERR rc = LY_ENOT, r;
484
0
    uint32_t i;
485
486
    /* collect all the implemented and imported modules, we do not really care about content-id */
487
0
    if (!lyd_find_path(ext_data, "/ietf-yang-library:yang-library/content-id", 0, NULL)) {
488
0
        if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:yang-library/module-set[1]/module", &impl_mods))) {
489
0
            rc = r;
490
0
            goto cleanup;
491
0
        }
492
0
        if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:yang-library/module-set[1]/import-only-module", &imp_mods))) {
493
0
            rc = r;
494
0
            goto cleanup;
495
0
        }
496
0
    } else {
497
0
        if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:modules-state/module[conformance-type='implement']", &impl_mods))) {
498
0
            rc = r;
499
0
            goto cleanup;
500
0
        }
501
0
        if ((r = lyd_find_xpath(ext_data, "/ietf-yang-library:modules-state/module[conformance-type='import']", &imp_mods))) {
502
0
            rc = r;
503
0
            goto cleanup;
504
0
        }
505
0
    }
506
507
0
    if (!impl_mods->count) {
508
0
        lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EVALID, "No implemented modules included in ietf-yang-library data.");
509
0
        rc = LY_EVALID;
510
0
        goto cleanup;
511
0
    }
512
513
    /* check all the implemented modules */
514
0
    for (i = 0; i < impl_mods->count; ++i) {
515
0
        lyd_find_path(impl_mods->dnodes[i], "name", 0, &node);
516
0
        name = lyd_get_value(node);
517
518
0
        lyd_find_path(impl_mods->dnodes[i], "revision", 0, &node);
519
0
        if (node && strlen(lyd_get_value(node))) {
520
0
            revision = lyd_get_value(node);
521
0
        } else {
522
0
            revision = NULL;
523
0
        }
524
525
0
        if (!(mod = ly_ctx_get_module(ctx, name, revision)) || !mod->implemented) {
526
            /* unsuitable module */
527
0
            goto cleanup;
528
0
        }
529
0
    }
530
531
    /* check all the imported modules */
532
0
    for (i = 0; i < imp_mods->count; ++i) {
533
0
        lyd_find_path(imp_mods->dnodes[i], "name", 0, &node);
534
0
        name = lyd_get_value(node);
535
536
0
        lyd_find_path(imp_mods->dnodes[i], "revision", 0, &node);
537
0
        if (node && strlen(lyd_get_value(node))) {
538
0
            revision = lyd_get_value(node);
539
0
        } else {
540
0
            revision = NULL;
541
0
        }
542
543
0
        if (!ly_ctx_get_module(ctx, name, revision)) {
544
            /* unsuitable module */
545
0
            goto cleanup;
546
0
        }
547
0
    }
548
549
    /* context matches and can be reused */
550
0
    rc = LY_SUCCESS;
551
552
0
cleanup:
553
0
    ly_set_free(impl_mods, NULL);
554
0
    ly_set_free(imp_mods, NULL);
555
0
    return rc;
556
0
}
557
558
/**
559
 * @brief Get schema (context) for an inline mount point.
560
 *
561
 * @param[in] ext Compiled extension instance.
562
 * @param[in] ext_data Extension data retrieved by the callback.
563
 * @param[in] config Whether the whole schema should keep its config or be set to false.
564
 * @param[out] ext_ctx Schema to use for parsing the data.
565
 * @return LY_ERR value.
566
 */
567
static LY_ERR
568
schema_mount_get_ctx_inline(struct lysc_ext_instance *ext, const struct lyd_node *ext_data, ly_bool config,
569
        const struct ly_ctx **ext_ctx)
570
0
{
571
0
    struct lyplg_ext_sm *sm_data = ext->compiled;
572
0
    struct ly_ctx *new_ctx = NULL;
573
0
    uint32_t i;
574
0
    void *mem;
575
0
    LY_ERR rc = LY_SUCCESS, r;
576
577
0
    assert(sm_data && sm_data->shared);
578
579
    /* LOCK */
580
0
    if ((r = pthread_mutex_lock(&sm_data->lock))) {
581
0
        lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_ESYS, "Mutex lock failed (%s).", strerror(r));
582
0
        return LY_ESYS;
583
0
    }
584
585
    /* try to find a context we can reuse */
586
0
    for (i = 0; i < sm_data->inln.schema_count; ++i) {
587
0
        r = schema_mount_ctx_match(ext, ext_data, sm_data->inln.schemas[i].ctx);
588
0
        if (!r) {
589
            /* match */
590
0
            *ext_ctx = sm_data->inln.schemas[i].ctx;
591
0
            goto cleanup;
592
0
        } else if (r != LY_ENOT) {
593
            /* error */
594
0
            rc = r;
595
0
            goto cleanup;
596
0
        }
597
0
    }
598
599
    /* new schema required, create context */
600
0
    if ((r = schema_mount_create_ctx(ext, ext_data, config, &new_ctx))) {
601
0
        rc = r;
602
0
        goto cleanup;
603
0
    }
604
605
    /* new entry */
606
0
    mem = realloc(sm_data->inln.schemas, (i + 1) * sizeof *sm_data->inln.schemas);
607
0
    if (!mem) {
608
0
        ly_ctx_destroy(new_ctx);
609
0
        EXT_LOGERR_MEM_GOTO(NULL, ext, rc, cleanup);
610
0
    }
611
0
    sm_data->inln.schemas = mem;
612
0
    ++sm_data->inln.schema_count;
613
614
    /* fill entry */
615
0
    sm_data->inln.schemas[i].ctx = new_ctx;
616
617
    /* use the context */
618
0
    *ext_ctx = sm_data->inln.schemas[i].ctx;
619
620
0
cleanup:
621
    /* UNLOCK */
622
0
    pthread_mutex_unlock(&sm_data->lock);
623
624
0
    return rc;
625
0
}
626
627
LY_ERR
628
lyplg_ext_schema_mount_get_ctx(struct lysc_ext_instance *ext, const struct ly_ctx **ext_ctx)
629
0
{
630
0
    LY_ERR ret = LY_SUCCESS, r;
631
0
    struct lyd_node *iter, *ext_data = NULL;
632
0
    ly_bool ext_data_free = 0, config, shared;
633
634
0
    *ext_ctx = NULL;
635
636
    /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
637
0
    if ((r = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
638
0
        ret = r;
639
0
        goto cleanup;
640
0
    }
641
642
0
    LY_LIST_FOR(ext_data, iter) {
643
0
        if (iter->flags & LYD_NEW) {
644
            /* must be validated for the parent-reference prefix data to be stored */
645
0
            lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Provided ext data have not been validated.");
646
0
            ret = LY_EINVAL;
647
0
            goto cleanup;
648
0
        }
649
0
    }
650
651
    /* learn about this mount point */
652
0
    if ((r = schema_mount_get_smount(ext, ext_data, &config, &shared))) {
653
0
        ret = r;
654
0
        goto cleanup;
655
0
    }
656
657
    /* create/get the context for parsing the data */
658
0
    if (shared) {
659
0
        r = schema_mount_get_ctx_shared(ext, ext_data, config, ext_ctx);
660
0
    } else {
661
0
        r = schema_mount_get_ctx_inline(ext, ext_data, config, ext_ctx);
662
0
    }
663
0
    if (r) {
664
0
        ret = r;
665
0
        goto cleanup;
666
0
    }
667
668
0
cleanup:
669
0
    if (ext_data_free) {
670
0
        lyd_free_all(ext_data);
671
0
    }
672
0
    return ret;
673
0
}
674
675
/**
676
 * @brief Snode callback for schema mount.
677
 * Check if data are valid for schema mount and returns their schema node.
678
 */
679
static LY_ERR
680
schema_mount_snode(struct lysc_ext_instance *ext, const struct lyd_node *parent, const struct lysc_node *sparent,
681
        const char *prefix, size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name, size_t name_len,
682
        const struct lysc_node **snode)
683
0
{
684
0
    LY_ERR r;
685
0
    const struct lys_module *mod;
686
0
    const struct ly_ctx *ext_ctx = NULL;
687
688
    /* get context based on ietf-yang-library data */
689
0
    if ((r = lyplg_ext_schema_mount_get_ctx(ext, &ext_ctx))) {
690
0
        return r;
691
0
    }
692
693
    /* get the module */
694
0
    mod = lyplg_type_identity_module(ext_ctx, parent ? parent->schema : sparent, prefix, prefix_len, format, prefix_data);
695
0
    if (!mod) {
696
0
        return LY_ENOT;
697
0
    }
698
699
    /* get the top-level schema node */
700
0
    *snode = lys_find_child(NULL, mod, name, name_len, 0, 0);
701
0
    return *snode ? LY_SUCCESS : LY_ENOT;
702
0
}
703
704
static LY_ERR
705
schema_mount_get_parent_ref(const struct lysc_ext_instance *ext, const struct lyd_node *ext_data,
706
        struct ly_set **set)
707
0
{
708
0
    LY_ERR ret = LY_SUCCESS;
709
0
    char *path = NULL;
710
711
    /* get all parent references of this mount point */
712
0
    if (asprintf(&path, "/ietf-yang-schema-mount:schema-mounts/mount-point[module='%s'][label='%s']"
713
0
            "/shared-schema/parent-reference", ext->module->name, ext->argument) == -1) {
714
0
        EXT_LOGERR_MEM_GOTO(NULL, ext, ret, cleanup);
715
0
    }
716
0
    if ((ret = lyd_find_xpath(ext_data, path, set))) {
717
0
        goto cleanup;
718
0
    }
719
720
0
cleanup:
721
0
    free(path);
722
0
    return ret;
723
0
}
724
725
/**
726
 * @brief Duplicate all accessible parent references for a shared-schema mount point.
727
 *
728
 * @param[in] ext Compiled extension instance.
729
 * @param[in] ctx_node Context node for evaluating the parent-reference XPath expressions.
730
 * @param[in] ext_data Extension data retrieved by the callback.
731
 * @param[in] trg_ctx Mounted data context to use for duplication.
732
 * @param[out] ref_set Set of all top-level parent-ref subtrees connected to each other, may be empty.
733
 * @return LY_ERR value.
734
 */
735
static LY_ERR
736
schema_mount_dup_parent_ref(const struct lysc_ext_instance *ext, const struct lyd_node *ctx_node,
737
        const struct lyd_node *ext_data, const struct ly_ctx *trg_ctx, struct ly_set **ref_set)
738
0
{
739
0
    LY_ERR ret = LY_SUCCESS;
740
0
    char *path = NULL;
741
0
    struct ly_set *set = NULL, *par_set = NULL;
742
0
    struct lyd_node_term *term;
743
0
    struct lyd_node *dup = NULL, *top_node, *first;
744
0
    struct lyd_value_xpath10 *xp_val;
745
0
    uint32_t i, j;
746
747
0
    *ref_set = NULL;
748
749
0
    if (!ext_data) {
750
        /* we expect the same ext data as before and there must be some for data to be parsed */
751
0
        lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "No ext data provided.");
752
0
        ret = LY_EINVAL;
753
0
        goto cleanup;
754
0
    }
755
756
0
    if ((ret = schema_mount_get_parent_ref(ext, ext_data, &set))) {
757
0
        goto cleanup;
758
0
    }
759
760
    /* prepare result set */
761
0
    if ((ret = ly_set_new(ref_set))) {
762
0
        goto cleanup;
763
0
    }
764
765
0
    first = NULL;
766
0
    for (i = 0; i < set->count; ++i) {
767
0
        term = set->objs[i];
768
769
        /* get the referenced nodes (subtrees) */
770
0
        LYD_VALUE_GET(&term->value, xp_val);
771
0
        if ((ret = lyd_find_xpath3(ctx_node, ctx_node, lyxp_get_expr(xp_val->exp), xp_val->format, xp_val->prefix_data,
772
0
                NULL, &par_set))) {
773
0
            lyplg_ext_compile_log(NULL, ext, LY_LLERR, ret, "Parent reference \"%s\" evaluation failed.",
774
0
                    lyxp_get_expr(xp_val->exp));
775
0
            goto cleanup;
776
0
        }
777
778
0
        for (j = 0; j < par_set->count; ++j) {
779
            /* duplicate with parents in the context of the mounted data */
780
0
            if ((ret = lyd_dup_single_to_ctx(par_set->dnodes[j], trg_ctx, NULL,
781
0
                    LYD_DUP_RECURSIVE | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS | LYD_DUP_NO_EXT, &dup))) {
782
0
                goto cleanup;
783
0
            }
784
785
            /* go top-level */
786
0
            while (dup->parent) {
787
0
                dup = lyd_parent(dup);
788
0
            }
789
790
            /* check whether the top-level node exists */
791
0
            if (first) {
792
0
                if ((ret = lyd_find_sibling_first(first, dup, &top_node)) && (ret != LY_ENOTFOUND)) {
793
0
                    goto cleanup;
794
0
                }
795
0
            } else {
796
0
                top_node = NULL;
797
0
            }
798
799
0
            if (top_node) {
800
                /* merge */
801
0
                ret = lyd_merge_tree(&first, dup, LYD_MERGE_DESTRUCT);
802
0
                dup = NULL;
803
0
                if (ret) {
804
0
                    goto cleanup;
805
0
                }
806
0
            } else {
807
                /* insert */
808
0
                if ((ret = lyd_insert_sibling(first, dup, &first))) {
809
0
                    goto cleanup;
810
0
                }
811
812
                /* add into the result set because a new top-level node was added */
813
0
                if ((ret = ly_set_add(*ref_set, dup, 1, NULL))) {
814
0
                    goto cleanup;
815
0
                }
816
0
                dup = NULL;
817
0
            }
818
0
        }
819
0
    }
820
821
0
cleanup:
822
0
    free(path);
823
0
    ly_set_free(set, NULL);
824
0
    ly_set_free(par_set, NULL);
825
0
    lyd_free_tree(dup);
826
0
    if (ret && *ref_set) {
827
0
        if ((*ref_set)->count) {
828
0
            lyd_free_siblings((*ref_set)->dnodes[0]);
829
0
        }
830
0
        ly_set_free(*ref_set, NULL);
831
0
        *ref_set = NULL;
832
0
    }
833
0
    return ret;
834
0
}
835
836
LIBYANG_API_DEF LY_ERR
837
lyplg_ext_schema_mount_get_parent_ref(const struct lysc_ext_instance *ext, struct ly_set **refs)
838
0
{
839
0
    LY_ERR rc;
840
0
    struct ly_set *pref_set = NULL;
841
0
    struct ly_set *snode_set = NULL;
842
0
    struct ly_set *results_set = NULL;
843
0
    struct lyd_node *ext_data;
844
0
    ly_bool ext_data_free;
845
846
    /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
847
0
    if ((rc = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
848
0
        return rc;
849
0
    }
850
851
0
    LY_CHECK_GOTO(rc = schema_mount_get_parent_ref(ext, ext_data, &pref_set), cleanup);
852
0
    if (pref_set->count == 0) {
853
0
        goto cleanup;
854
0
    }
855
856
0
    LY_CHECK_GOTO(rc = ly_set_new(&results_set), cleanup);
857
858
0
    for (uint32_t i = 0; i < pref_set->count; ++i) {
859
0
        struct lyd_node_term *term;
860
0
        struct lyd_value_xpath10 *xp_val;
861
0
        char *value;
862
0
        struct ly_err_item *err;
863
864
0
        term = (struct lyd_node_term *)pref_set->dnodes[i];
865
0
        LYD_VALUE_GET(&term->value, xp_val);
866
0
        LY_CHECK_GOTO(rc = lyplg_type_print_xpath10_value(xp_val, LY_VALUE_JSON, NULL, &value, &err), cleanup);
867
0
        LY_CHECK_ERR_GOTO(rc = lys_find_xpath(ext->module->ctx, NULL, value, 0, &snode_set), free(value), cleanup);
868
0
        free(value);
869
0
        for (uint32_t sn = 0; sn < snode_set->count; sn++) {
870
0
            LY_CHECK_GOTO(rc = ly_set_add(results_set, snode_set->snodes[sn], 0, NULL), cleanup);
871
0
        }
872
0
        ly_set_free(snode_set, NULL);
873
0
        snode_set = NULL;
874
0
    }
875
876
0
    *refs = results_set;
877
878
0
cleanup:
879
0
    if (rc) {
880
0
        ly_set_free(results_set, NULL);
881
0
    }
882
0
    ly_set_free(snode_set, NULL);
883
0
    if (ext_data_free) {
884
0
        lyd_free_all(ext_data);
885
0
    }
886
0
    ly_set_free(pref_set, NULL);
887
888
0
    return rc;
889
0
}
890
891
/**
892
 * @brief Validate callback for schema mount.
893
 */
894
static LY_ERR
895
schema_mount_validate(struct lysc_ext_instance *ext, struct lyd_node *sibling, const struct lyd_node *dep_tree,
896
        enum lyd_type data_type, uint32_t val_opts, struct lyd_node **diff)
897
0
{
898
0
    LY_ERR ret = LY_SUCCESS;
899
0
    uint32_t *prev_lo, temp_lo = LY_LOSTORE_LAST, i;
900
0
    const struct ly_err_item *err;
901
0
    struct lyd_node *iter, *ext_data = NULL, *ref_first = NULL, *orig_parent = lyd_parent(sibling), *op_tree;
902
0
    struct lyd_node *ext_diff = NULL, *diff_parent = NULL;
903
0
    ly_bool ext_data_free = 0;
904
0
    struct ly_set *ref_set = NULL;
905
906
0
    if (!sibling) {
907
        /* some data had to be parsed for this callback to be called */
908
0
        EXT_LOGERR_INT_RET(NULL, ext);
909
0
    }
910
911
    /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
912
0
    if ((ret = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
913
0
        goto cleanup;
914
0
    }
915
916
0
    LY_LIST_FOR(ext_data, iter) {
917
0
        if (iter->flags & LYD_NEW) {
918
            /* must be validated for the parent-reference prefix data to be stored */
919
0
            lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Provided ext data have not been validated.");
920
0
            ret = LY_EINVAL;
921
0
            goto cleanup;
922
0
        }
923
0
    }
924
925
    /* duplicate the referenced parent nodes into ext context */
926
0
    if ((ret = schema_mount_dup_parent_ref(ext, orig_parent, ext_data, LYD_CTX(sibling), &ref_set))) {
927
0
        goto cleanup;
928
0
    }
929
930
0
    if (data_type != LYD_TYPE_DATA_YANG) {
931
        /* remember the operation data tree, it may be moved */
932
0
        op_tree = sibling;
933
0
    }
934
935
    /* create accessible tree, remove LYD_EXT to not call this callback recursively */
936
0
    LY_CHECK_GOTO(lyd_unlink_siblings(sibling), cleanup);
937
0
    LY_LIST_FOR(sibling, iter) {
938
0
        iter->flags &= ~LYD_EXT;
939
0
    }
940
0
    if (ref_set->count) {
941
0
        if ((ret = lyd_insert_sibling(sibling, ref_set->dnodes[0], &sibling))) {
942
0
            goto cleanup;
943
0
        }
944
0
    }
945
946
    /* only store messages in the context, log as an extension */
947
0
    prev_lo = ly_temp_log_options(&temp_lo);
948
949
0
    if (data_type == LYD_TYPE_DATA_YANG) {
950
        /* validate all the modules with data */
951
0
        ret = lyd_validate_all(&sibling, NULL, val_opts | LYD_VALIDATE_PRESENT, diff ? &ext_diff : NULL);
952
0
    } else {
953
        /* validate the operation */
954
0
        ret = lyd_validate_op(op_tree, dep_tree, data_type, diff ? &ext_diff : NULL);
955
0
    }
956
957
    /* restore logging */
958
0
    ly_temp_log_options(prev_lo);
959
960
    /* restore sibling tree */
961
0
    for (i = 0; i < ref_set->count; ++i) {
962
0
        if (ref_set->dnodes[i] == sibling) {
963
0
            sibling = sibling->next;
964
0
        }
965
0
        lyd_free_tree(ref_set->dnodes[i]);
966
0
    }
967
0
    LY_LIST_FOR(sibling, iter) {
968
0
        iter->flags |= LYD_EXT;
969
0
    }
970
0
    lyplg_ext_insert(orig_parent, sibling);
971
972
0
    if (ret) {
973
        /* log the error in the original context */
974
0
        err = ly_err_first(LYD_CTX(sibling));
975
0
        if (!err) {
976
0
            lyplg_ext_compile_log(NULL, ext, LY_LLERR, ret, "Unknown validation error (err code %d).", ret);
977
0
        } else {
978
0
            lyplg_ext_compile_log_err(err, ext);
979
0
        }
980
0
        goto cleanup;
981
0
    }
982
983
    /* create proper diff */
984
0
    if (diff && ext_diff) {
985
        /* diff nodes from an extension instance */
986
0
        LY_LIST_FOR(ext_diff, iter) {
987
0
            iter->flags |= LYD_EXT;
988
0
        }
989
990
        /* create the parent and insert the diff */
991
0
        if ((ret = lyd_dup_single(lyd_parent(sibling), NULL, LYD_DUP_WITH_PARENTS | LYD_DUP_NO_META, &diff_parent))) {
992
0
            goto cleanup;
993
0
        }
994
0
        if ((ret = lyplg_ext_insert(diff_parent, ext_diff))) {
995
0
            goto cleanup;
996
0
        }
997
0
        ext_diff = NULL;
998
999
        /* go top-level and set the operation */
1000
0
        while (lyd_parent(diff_parent)) {
1001
0
            diff_parent = lyd_parent(diff_parent);
1002
0
        }
1003
0
        if ((ret = lyd_new_meta(LYD_CTX(diff_parent), diff_parent, NULL, "yang:operation", "none", 0, NULL))) {
1004
0
            goto cleanup;
1005
0
        }
1006
1007
        /* finally merge into the global diff */
1008
0
        if ((ret = lyd_diff_merge_all(diff, diff_parent, LYD_DIFF_MERGE_DEFAULTS))) {
1009
0
            goto cleanup;
1010
0
        }
1011
0
    }
1012
1013
0
cleanup:
1014
0
    ly_set_free(ref_set, NULL);
1015
0
    lyd_free_siblings(ref_first);
1016
0
    lyd_free_tree(ext_diff);
1017
0
    lyd_free_all(diff_parent);
1018
0
    if (ext_data_free) {
1019
0
        lyd_free_all(ext_data);
1020
0
    }
1021
0
    return ret;
1022
0
}
1023
1024
/**
1025
 * @brief Schema mount compile free.
1026
 *
1027
 * Implementation of ::lyplg_ext_compile_free_clb callback set as ::lyext_plugin::cfree.
1028
 */
1029
static void
1030
schema_mount_cfree(const struct ly_ctx *ctx, struct lysc_ext_instance *ext)
1031
0
{
1032
0
    struct lyplg_ext_sm *sm_data = ext->compiled;
1033
0
    uint32_t i;
1034
1035
0
    if (!sm_data) {
1036
0
        return;
1037
0
    }
1038
1039
0
    if (!--sm_data->shared->ref_count) {
1040
0
        for (i = 0; i < sm_data->shared->schema_count; ++i) {
1041
0
            ly_ctx_destroy(sm_data->shared->schemas[i].ctx);
1042
0
            lydict_remove(ctx, sm_data->shared->schemas[i].content_id);
1043
0
        }
1044
0
        free(sm_data->shared->schemas);
1045
0
        free(sm_data->shared);
1046
0
    }
1047
1048
0
    for (i = 0; i < sm_data->inln.schema_count; ++i) {
1049
0
        ly_ctx_destroy(sm_data->inln.schemas[i].ctx);
1050
0
    }
1051
0
    free(sm_data->inln.schemas);
1052
1053
0
    pthread_mutex_destroy(&sm_data->lock);
1054
0
    free(sm_data);
1055
0
}
1056
1057
LIBYANG_API_DEF LY_ERR
1058
lyplg_ext_schema_mount_create_context(const struct lysc_ext_instance *ext, struct ly_ctx **ctx)
1059
0
{
1060
0
    struct lyd_node *ext_data = NULL;
1061
0
    ly_bool ext_data_free = 0, config;
1062
0
    LY_ERR rc = LY_SUCCESS;
1063
1064
0
    if (!ext->module->ctx->ext_clb) {
1065
0
        return LY_EINVAL;
1066
0
    }
1067
1068
0
    if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) {
1069
0
        return LY_EINVAL;
1070
0
    }
1071
1072
    /* get operational data with ietf-yang-library and ietf-yang-schema-mount data */
1073
0
    if ((rc = lyplg_ext_get_data(ext->module->ctx, ext, (void **)&ext_data, &ext_data_free))) {
1074
0
        return rc;
1075
0
    }
1076
1077
    /* learn about this mount point */
1078
0
    if ((rc = schema_mount_get_smount(ext, ext_data, &config, NULL))) {
1079
0
        goto cleanup;
1080
0
    }
1081
1082
    /* create the context */
1083
0
    rc = schema_mount_create_ctx(ext, ext_data, config, ctx);
1084
1085
0
cleanup:
1086
0
    if (ext_data_free) {
1087
0
        lyd_free_all(ext_data);
1088
0
    }
1089
0
    return rc;
1090
0
}
1091
1092
static void
1093
schema_mount_spriter_tree_free(void *priv)
1094
0
{
1095
0
    struct sprinter_tree_priv *st_priv;
1096
1097
0
    st_priv = priv;
1098
0
    ly_set_free(st_priv->refs, NULL);
1099
0
    ly_ctx_destroy(st_priv->ext_ctx);
1100
0
    free(st_priv);
1101
0
}
1102
1103
static LY_ERR
1104
schema_mount_sprinter_tree_cnode_override_mounted(const struct lysc_node *node, const void *UNUSED(plugin_priv),
1105
        ly_bool *UNUSED(skip), const char **UNUSED(flags), const char **add_opts)
1106
0
{
1107
0
    if (!node->parent) {
1108
0
        *add_opts = "/";
1109
0
    }
1110
1111
0
    return LY_SUCCESS;
1112
0
}
1113
1114
static LY_ERR
1115
schema_mount_sprinter_tree_pnode_override_mounted(const struct lysp_node *node, const void *UNUSED(plugin_priv),
1116
        ly_bool *UNUSED(skip), const char **UNUSED(flags), const char **add_opts)
1117
0
{
1118
0
    if (!node->parent) {
1119
0
        *add_opts = "/";
1120
0
    }
1121
1122
0
    return LY_SUCCESS;
1123
0
}
1124
1125
static LY_ERR
1126
schema_mount_sprinter_tree_node_override_parent_refs(const struct lysc_node *node, const void *plugin_priv,
1127
        ly_bool *skip, const char **UNUSED(flags), const char **add_opts)
1128
0
{
1129
0
    uint32_t i;
1130
0
    const struct ly_set *refs;
1131
0
    const struct lysc_module *mod;
1132
0
    struct lysc_node *ref, *iter;
1133
1134
0
    refs = ((struct sprinter_tree_priv *)plugin_priv)->refs;
1135
0
    mod = node->module->compiled;
1136
1137
    /* Assume the @p node will be skipped. */
1138
0
    *skip = 1;
1139
0
    for (i = 0; (i < refs->count) && *skip; i++) {
1140
0
        ref = refs->snodes[i];
1141
0
        if (ref->module->compiled != mod) {
1142
            /* parent-reference points to different module */
1143
0
            continue;
1144
0
        }
1145
1146
0
        for (iter = ref; iter; iter = iter->parent) {
1147
0
            if (iter == node) {
1148
                /* @p node is not skipped because it is parent-rererence node or his parent */
1149
0
                *skip = 0;
1150
0
                break;
1151
0
            }
1152
0
        }
1153
0
    }
1154
1155
0
    if (!*skip && !node->parent) {
1156
        /* top-node has additional opts */
1157
0
        *add_opts = "@";
1158
0
    }
1159
1160
0
    return LY_SUCCESS;
1161
0
}
1162
1163
/**
1164
 * @brief Schema mount schema parsed tree printer.
1165
 *
1166
 * Implementation of ::lyplg_ext_sprinter_ptree_clb callback set as lyext_plugin::printer_ptree.
1167
 */
1168
static LY_ERR
1169
schema_mount_sprinter_ptree(struct lysp_ext_instance *UNUSED(ext), const struct lyspr_tree_ctx *ctx,
1170
        const char **flags, const char **UNUSED(add_opts))
1171
0
{
1172
0
    if (!ctx) {
1173
0
        *flags = "mp";
1174
0
    }
1175
1176
0
    return LY_SUCCESS;
1177
0
}
1178
1179
/**
1180
 * @brief Schema mount schema compiled tree printer.
1181
 *
1182
 * Implementation of ::lyplg_ext_sprinter_ctree_clb callback set as lyext_plugin::printer_ctree.
1183
 */
1184
static LY_ERR
1185
schema_mount_sprinter_ctree(struct lysc_ext_instance *ext, const struct lyspr_tree_ctx *ctx,
1186
        const char **flags, const char **UNUSED(add_opts))
1187
0
{
1188
0
    LY_ERR rc = LY_SUCCESS;
1189
0
    struct ly_ctx *ext_ctx = NULL;
1190
0
    const struct lys_module *mod;
1191
0
    struct ly_set *refs = NULL;
1192
0
    struct lysc_node *tree1, *tree2;
1193
0
    uint32_t i, j;
1194
0
    ly_bool from_parent_ref, is_first;
1195
0
    struct sprinter_tree_priv *st_priv;
1196
1197
0
    if (!ctx) {
1198
0
        *flags = "mp";
1199
0
        return LY_SUCCESS;
1200
0
    }
1201
1202
0
    if (lyplg_ext_schema_mount_create_context(ext, &ext_ctx)) {
1203
        /* Void mount point */
1204
0
        return LY_SUCCESS;
1205
0
    }
1206
1207
0
    rc = lyplg_ext_schema_mount_get_parent_ref(ext, &refs);
1208
0
    LY_CHECK_GOTO(rc, cleanup);
1209
1210
    /* build new list of modules to print. This list will omit internal
1211
     * modules, modules with no nodes (e.g., iana-if-types) and modules
1212
     * that were loaded as the result of a parent-reference.
1213
     */
1214
0
    i = ly_ctx_internal_modules_count(ext_ctx);
1215
0
    while ((mod = ly_ctx_get_module_iter(ext_ctx, &i))) {
1216
0
        from_parent_ref = 0;
1217
1218
0
        for (j = 0; refs && j < refs->count; j++) {
1219
0
            if (!strcmp(mod->ns, refs->snodes[j]->module->ns)) {
1220
0
                from_parent_ref = 1;
1221
0
                break;
1222
0
            }
1223
0
        }
1224
0
        if (from_parent_ref) {
1225
            /* Modules loaded as the result of a parent-reference are added later. */
1226
0
            continue;
1227
0
        }
1228
1229
        /* Add data nodes, rpcs and notifications. */
1230
0
        if ((ext_ctx->flags & LY_CTX_SET_PRIV_PARSED) && mod->compiled) {
1231
            /* For compiled module. */
1232
0
            rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, mod->compiled->data,
1233
0
                    schema_mount_sprinter_tree_cnode_override_mounted);
1234
0
            LY_CHECK_GOTO(rc, cleanup);
1235
0
            if (mod->compiled->rpcs) {
1236
0
                rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, &mod->compiled->rpcs->node,
1237
0
                        schema_mount_sprinter_tree_cnode_override_mounted);
1238
0
            }
1239
0
            LY_CHECK_GOTO(rc, cleanup);
1240
0
            if (mod->compiled->notifs) {
1241
0
                rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, &mod->compiled->notifs->node,
1242
0
                        schema_mount_sprinter_tree_cnode_override_mounted);
1243
0
            }
1244
0
            LY_CHECK_GOTO(rc, cleanup);
1245
0
        } else {
1246
            /* For parsed module. */
1247
0
            rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, mod->parsed->data,
1248
0
                    schema_mount_sprinter_tree_pnode_override_mounted);
1249
0
            LY_CHECK_GOTO(rc, cleanup);
1250
0
            if (mod->parsed->rpcs) {
1251
0
                rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, &mod->parsed->rpcs->node,
1252
0
                        schema_mount_sprinter_tree_pnode_override_mounted);
1253
0
            }
1254
0
            LY_CHECK_GOTO(rc, cleanup);
1255
0
            if (mod->parsed->notifs) {
1256
0
                rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, &mod->parsed->notifs->node,
1257
0
                        schema_mount_sprinter_tree_pnode_override_mounted);
1258
0
            }
1259
0
            LY_CHECK_GOTO(rc, cleanup);
1260
0
        }
1261
0
    }
1262
1263
    /* Add modules loaded as the result of a parent-reference. */
1264
0
    for (i = 0; refs && (i < refs->count); i++) {
1265
0
        tree1 = refs->snodes[i]->module->compiled->data;
1266
1267
        /* Add data nodes from the module only once. */
1268
0
        is_first = 1;
1269
0
        for (j = 0; j < i; j++) {
1270
0
            tree2 = refs->snodes[j]->module->compiled->data;
1271
0
            if (tree1 == tree2) {
1272
0
                is_first = 0;
1273
0
                break;
1274
0
            }
1275
0
        }
1276
0
        if (is_first) {
1277
            /* Add all data nodes but unavailable nodes are skipped in the callback. */
1278
0
            rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, tree1, schema_mount_sprinter_tree_node_override_parent_refs);
1279
0
            LY_CHECK_GOTO(rc, cleanup);
1280
0
        }
1281
0
    }
1282
1283
    /* add private plugin data */
1284
0
    st_priv = calloc(1, sizeof(*st_priv));
1285
0
    LY_CHECK_ERR_GOTO(!st_priv, rc = LY_EMEM, cleanup);
1286
0
    st_priv->ext_ctx = ext_ctx;
1287
0
    st_priv->refs = refs;
1288
0
    rc = lyplg_ext_sprinter_tree_set_priv(ctx, st_priv, schema_mount_spriter_tree_free);
1289
1290
0
cleanup:
1291
0
    if (rc) {
1292
0
        ly_set_free(refs, NULL);
1293
0
        ly_ctx_destroy(ext_ctx);
1294
0
    }
1295
1296
0
    return rc;
1297
0
}
1298
1299
/**
1300
 * @brief Plugin descriptions for the Yang Schema Mount extension.
1301
 *
1302
 * Note that external plugins are supposed to use:
1303
 *
1304
 *   LYPLG_EXTENSIONS = {
1305
 */
1306
const struct lyplg_ext_record plugins_schema_mount[] = {
1307
    {
1308
        .module = "ietf-yang-schema-mount",
1309
        .revision = "2019-01-14",
1310
        .name = "mount-point",
1311
1312
        .plugin.id = "ly2 schema mount v1",
1313
        .plugin.parse = schema_mount_parse,
1314
        .plugin.compile = schema_mount_compile,
1315
        .plugin.printer_info = NULL,
1316
        .plugin.printer_ctree = schema_mount_sprinter_ctree,
1317
        .plugin.printer_ptree = schema_mount_sprinter_ptree,
1318
        .plugin.node = NULL,
1319
        .plugin.snode = schema_mount_snode,
1320
        .plugin.validate = schema_mount_validate,
1321
        .plugin.pfree = NULL,
1322
        .plugin.cfree = schema_mount_cfree
1323
    },
1324
    {0} /* terminating zeroed item */
1325
};