Coverage Report

Created: 2026-01-21 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libyang/src/in.c
Line
Count
Source
1
/**
2
 * @file in.c
3
 * @author Radek Krejci <rkrejci@cesnet.cz>
4
 * @brief libyang input functions.
5
 *
6
 * Copyright (c) 2015 - 2020 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
#define _POSIX_C_SOURCE 200809L /* strdup, strndup */
17
18
#ifdef __APPLE__
19
#define _DARWIN_C_SOURCE /* F_GETPATH */
20
#endif
21
22
#if defined (__NetBSD__) || defined (__OpenBSD__)
23
/* realpath */
24
#define _XOPEN_SOURCE 1
25
#define _XOPEN_SOURCE_EXTENDED 1
26
#endif
27
28
#include "in.h"
29
#include "in_internal.h"
30
31
#include <errno.h>
32
#include <fcntl.h>
33
#include <limits.h>
34
#include <stdint.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <unistd.h>
39
40
#include "common.h"
41
#include "compat.h"
42
#include "dict.h"
43
#include "log.h"
44
#include "parser_data.h"
45
#include "parser_internal.h"
46
#include "set.h"
47
#include "tree.h"
48
#include "tree_data.h"
49
#include "tree_data_internal.h"
50
#include "tree_schema.h"
51
#include "tree_schema_internal.h"
52
53
API LY_IN_TYPE
54
ly_in_type(const struct ly_in *in)
55
0
{
56
0
    LY_CHECK_ARG_RET(NULL, in, LY_IN_ERROR);
57
0
    return in->type;
58
0
}
59
60
API LY_ERR
61
ly_in_new_fd(int fd, struct ly_in **in)
62
0
{
63
0
    size_t length;
64
0
    char *addr;
65
66
0
    LY_CHECK_ARG_RET(NULL, fd >= 0, in, LY_EINVAL);
67
68
0
    LY_CHECK_RET(ly_mmap(NULL, fd, &length, (void **)&addr));
69
0
    if (!addr) {
70
0
        LOGERR(NULL, LY_EINVAL, "Empty input file.");
71
0
        return LY_EINVAL;
72
0
    }
73
74
0
    *in = calloc(1, sizeof **in);
75
0
    LY_CHECK_ERR_RET(!*in, LOGMEM(NULL); ly_munmap(addr, length), LY_EMEM);
76
77
0
    (*in)->type = LY_IN_FD;
78
0
    (*in)->method.fd = fd;
79
0
    (*in)->current = (*in)->start = (*in)->func_start = addr;
80
0
    (*in)->line = 1;
81
0
    (*in)->length = length;
82
83
0
    return LY_SUCCESS;
84
0
}
85
86
API int
87
ly_in_fd(struct ly_in *in, int fd)
88
0
{
89
0
    int prev_fd;
90
0
    size_t length;
91
0
    const char *addr;
92
93
0
    LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FD, -1);
94
95
0
    prev_fd = in->method.fd;
96
97
0
    if (fd != -1) {
98
0
        LY_CHECK_RET(ly_mmap(NULL, fd, &length, (void **)&addr), -1);
99
0
        if (!addr) {
100
0
            LOGERR(NULL, LY_EINVAL, "Empty input file.");
101
0
            return -1;
102
0
        }
103
104
0
        ly_munmap((char *)in->start, in->length);
105
106
0
        in->method.fd = fd;
107
0
        in->current = in->start = addr;
108
0
        in->line = 1;
109
0
        in->length = length;
110
0
    }
111
112
0
    return prev_fd;
113
0
}
114
115
API LY_ERR
116
ly_in_new_file(FILE *f, struct ly_in **in)
117
0
{
118
0
    LY_CHECK_ARG_RET(NULL, f, in, LY_EINVAL);
119
120
0
    LY_CHECK_RET(ly_in_new_fd(fileno(f), in));
121
122
    /* convert the LY_IN_FD input handler into the LY_IN_FILE */
123
0
    (*in)->type = LY_IN_FILE;
124
0
    (*in)->method.f = f;
125
126
0
    return LY_SUCCESS;
127
0
}
128
129
API FILE *
130
ly_in_file(struct ly_in *in, FILE *f)
131
0
{
132
0
    FILE *prev_f;
133
134
0
    LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FILE, NULL);
135
136
0
    prev_f = in->method.f;
137
138
0
    if (f) {
139
        /* convert LY_IN_FILE handler into LY_IN_FD to be able to update it via ly_in_fd() */
140
0
        in->type = LY_IN_FD;
141
0
        in->method.fd = fileno(prev_f);
142
0
        if (ly_in_fd(in, fileno(f)) == -1) {
143
0
            in->type = LY_IN_FILE;
144
0
            in->method.f = prev_f;
145
0
            return NULL;
146
0
        }
147
148
        /* if success, convert the result back */
149
0
        in->type = LY_IN_FILE;
150
0
        in->method.f = f;
151
0
    }
152
153
0
    return prev_f;
154
0
}
155
156
API LY_ERR
157
ly_in_new_memory(const char *str, struct ly_in **in)
158
0
{
159
0
    LY_CHECK_ARG_RET(NULL, str, in, LY_EINVAL);
160
161
0
    *in = calloc(1, sizeof **in);
162
0
    LY_CHECK_ERR_RET(!*in, LOGMEM(NULL), LY_EMEM);
163
164
0
    (*in)->type = LY_IN_MEMORY;
165
0
    (*in)->start = (*in)->current = (*in)->func_start = str;
166
0
    (*in)->line = 1;
167
168
0
    return LY_SUCCESS;
169
0
}
170
171
API const char *
172
ly_in_memory(struct ly_in *in, const char *str)
173
0
{
174
0
    const char *data;
175
176
0
    LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_MEMORY, NULL);
177
178
0
    data = in->current;
179
180
0
    if (str) {
181
0
        in->start = in->current = str;
182
0
        in->line = 1;
183
0
    }
184
185
0
    return data;
186
0
}
187
188
API LY_ERR
189
ly_in_reset(struct ly_in *in)
190
0
{
191
0
    LY_CHECK_ARG_RET(NULL, in, LY_EINVAL);
192
193
0
    in->current = in->func_start = in->start;
194
0
    in->line = 1;
195
0
    return LY_SUCCESS;
196
0
}
197
198
API LY_ERR
199
ly_in_new_filepath(const char *filepath, size_t len, struct ly_in **in)
200
0
{
201
0
    LY_ERR ret;
202
0
    char *fp;
203
0
    int fd;
204
205
0
    LY_CHECK_ARG_RET(NULL, filepath, in, LY_EINVAL);
206
207
0
    if (len) {
208
0
        fp = strndup(filepath, len);
209
0
    } else {
210
0
        fp = strdup(filepath);
211
0
    }
212
213
0
    fd = open(fp, O_RDONLY);
214
0
    LY_CHECK_ERR_RET(fd == -1, LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", fp, strerror(errno)); free(fp),
215
0
            LY_ESYS);
216
217
0
    LY_CHECK_ERR_RET(ret = ly_in_new_fd(fd, in), free(fp), ret);
218
219
    /* convert the LY_IN_FD input handler into the LY_IN_FILE */
220
0
    (*in)->type = LY_IN_FILEPATH;
221
0
    (*in)->method.fpath.fd = fd;
222
0
    (*in)->method.fpath.filepath = fp;
223
224
0
    return LY_SUCCESS;
225
0
}
226
227
API const char *
228
ly_in_filepath(struct ly_in *in, const char *filepath, size_t len)
229
0
{
230
0
    int fd, prev_fd;
231
0
    char *fp = NULL;
232
233
0
    LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FILEPATH, filepath ? NULL : ((void *)-1));
234
235
0
    if (!filepath) {
236
0
        return in->method.fpath.filepath;
237
0
    }
238
239
0
    if (len) {
240
0
        fp = strndup(filepath, len);
241
0
    } else {
242
0
        fp = strdup(filepath);
243
0
    }
244
245
    /* replace filepath */
246
0
    fd = open(fp, O_RDONLY);
247
0
    LY_CHECK_ERR_RET(!fd, LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", fp, strerror(errno)); free(fp), NULL);
248
249
    /* convert LY_IN_FILEPATH handler into LY_IN_FD to be able to update it via ly_in_fd() */
250
0
    in->type = LY_IN_FD;
251
0
    prev_fd = ly_in_fd(in, fd);
252
0
    LY_CHECK_ERR_RET(prev_fd == -1, in->type = LY_IN_FILEPATH; free(fp), NULL);
253
254
    /* and convert the result back */
255
0
    in->type = LY_IN_FILEPATH;
256
0
    close(prev_fd);
257
0
    free(in->method.fpath.filepath);
258
0
    in->method.fpath.fd = fd;
259
0
    in->method.fpath.filepath = fp;
260
261
0
    return NULL;
262
0
}
263
264
void
265
lys_parser_fill_filepath(struct ly_ctx *ctx, struct ly_in *in, const char **filepath)
266
0
{
267
0
    char path[PATH_MAX];
268
269
0
#ifndef __APPLE__
270
0
    char proc_path[32];
271
0
    int len;
272
0
#endif
273
274
0
    LY_CHECK_ARG_RET(NULL, ctx, in, filepath, );
275
0
    if (*filepath) {
276
        /* filepath already set */
277
0
        return;
278
0
    }
279
280
0
    switch (in->type) {
281
0
    case LY_IN_FILEPATH:
282
0
        if (realpath(in->method.fpath.filepath, path) != NULL) {
283
0
            lydict_insert(ctx, path, 0, filepath);
284
0
        } else {
285
0
            lydict_insert(ctx, in->method.fpath.filepath, 0, filepath);
286
0
        }
287
288
0
        break;
289
0
    case LY_IN_FD:
290
#ifdef __APPLE__
291
        if (fcntl(in->method.fd, F_GETPATH, path) != -1) {
292
            lydict_insert(ctx, path, 0, filepath);
293
        }
294
#else
295
        /* get URI if there is /proc */
296
0
        sprintf(proc_path, "/proc/self/fd/%d", in->method.fd);
297
0
        if ((len = readlink(proc_path, path, PATH_MAX - 1)) > 0) {
298
0
            lydict_insert(ctx, path, len, filepath);
299
0
        }
300
0
#endif
301
0
        break;
302
0
    case LY_IN_MEMORY:
303
0
    case LY_IN_FILE:
304
        /* nothing to do */
305
0
        break;
306
0
    default:
307
0
        LOGINT(ctx);
308
0
        break;
309
0
    }
310
311
0
}
312
313
API void
314
ly_in_free(struct ly_in *in, ly_bool destroy)
315
0
{
316
0
    if (!in) {
317
0
        return;
318
0
    } else if (in->type == LY_IN_ERROR) {
319
0
        LOGINT(NULL);
320
0
        return;
321
0
    }
322
323
0
    if (destroy) {
324
0
        if (in->type == LY_IN_MEMORY) {
325
0
            free((char *)in->start);
326
0
        } else {
327
0
            ly_munmap((char *)in->start, in->length);
328
329
0
            if (in->type == LY_IN_FILE) {
330
0
                fclose(in->method.f);
331
0
            } else {
332
0
                close(in->method.fd);
333
334
0
                if (in->type == LY_IN_FILEPATH) {
335
0
                    free(in->method.fpath.filepath);
336
0
                }
337
0
            }
338
0
        }
339
0
    } else if (in->type != LY_IN_MEMORY) {
340
0
        ly_munmap((char *)in->start, in->length);
341
342
0
        if (in->type == LY_IN_FILEPATH) {
343
0
            close(in->method.fpath.fd);
344
0
            free(in->method.fpath.filepath);
345
0
        }
346
0
    }
347
348
0
    free(in);
349
0
}
350
351
LY_ERR
352
ly_in_read(struct ly_in *in, void *buf, size_t count)
353
0
{
354
0
    if (in->length && (in->length - (in->current - in->start) < count)) {
355
        /* EOF */
356
0
        return LY_EDENIED;
357
0
    }
358
359
0
    memcpy(buf, in->current, count);
360
0
    in->current += count;
361
0
    return LY_SUCCESS;
362
0
}
363
364
API size_t
365
ly_in_parsed(const struct ly_in *in)
366
0
{
367
0
    return in->current - in->func_start;
368
0
}
369
370
LY_ERR
371
ly_in_skip(struct ly_in *in, size_t count)
372
0
{
373
0
    if (in->length && (in->length - (in->current - in->start) < count)) {
374
        /* EOF */
375
0
        return LY_EDENIED;
376
0
    }
377
378
0
    in->current += count;
379
0
    return LY_SUCCESS;
380
0
}
381
382
void
383
lyd_ctx_free(struct lyd_ctx *lydctx)
384
0
{
385
0
    ly_set_erase(&lydctx->node_types, NULL);
386
0
    ly_set_erase(&lydctx->meta_types, NULL);
387
0
    ly_set_erase(&lydctx->node_when, NULL);
388
0
    ly_set_erase(&lydctx->node_exts, NULL);
389
0
}
390
391
LY_ERR
392
lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op)
393
0
{
394
0
    const struct lyd_node *iter;
395
396
0
    *op = NULL;
397
398
0
    if (!parent) {
399
        /* no parent, nothing to look for */
400
0
        return LY_SUCCESS;
401
0
    }
402
403
    /* we need to find the operation node if it already exists */
404
0
    for (iter = parent; iter; iter = lyd_parent(iter)) {
405
0
        if (iter->schema && (iter->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))) {
406
0
            break;
407
0
        }
408
0
    }
409
410
0
    if (!iter) {
411
        /* no operation found */
412
0
        return LY_SUCCESS;
413
0
    }
414
415
0
    if (!(int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY))) {
416
0
        LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent %s \"%s\" node when not parsing any operation.",
417
0
                lys_nodetype2str(iter->schema->nodetype), iter->schema->name);
418
0
        return LY_EINVAL;
419
0
    } else if ((iter->schema->nodetype == LYS_RPC) && !(int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY))) {
420
0
        LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent RPC \"%s\" node when not parsing RPC nor rpc-reply.",
421
0
                iter->schema->name);
422
0
        return LY_EINVAL;
423
0
    } else if ((iter->schema->nodetype == LYS_ACTION) && !(int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY))) {
424
0
        LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent action \"%s\" node when not parsing action nor rpc-reply.",
425
0
                iter->schema->name);
426
0
        return LY_EINVAL;
427
0
    } else if ((iter->schema->nodetype == LYS_NOTIF) && !(int_opts & LYD_INTOPT_NOTIF)) {
428
0
        LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent notification \"%s\" node when not parsing a notification.",
429
0
                iter->schema->name);
430
0
        return LY_EINVAL;
431
0
    }
432
433
0
    *op = (struct lyd_node *)iter;
434
0
    return LY_SUCCESS;
435
0
}
436
437
LY_ERR
438
lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode)
439
0
{
440
0
    LY_ERR rc = LY_SUCCESS;
441
442
0
    LOG_LOCSET(snode, NULL, NULL, NULL);
443
444
0
    if ((lydctx->parse_opts & LYD_PARSE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
445
0
        LOGVAL(lydctx->data_ctx->ctx, LY_VCODE_UNEXPNODE, "state", snode->name);
446
0
        rc = LY_EVALID;
447
0
        goto cleanup;
448
0
    }
449
450
0
    if (snode->nodetype == LYS_RPC) {
451
0
        if (lydctx->int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY)) {
452
0
            if (lydctx->op_node) {
453
0
                goto error_node_dup;
454
0
            }
455
0
        } else {
456
0
            goto error_node_inval;
457
0
        }
458
0
    } else if (snode->nodetype == LYS_ACTION) {
459
0
        if (lydctx->int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) {
460
0
            if (lydctx->op_node) {
461
0
                goto error_node_dup;
462
0
            }
463
0
        } else {
464
0
            goto error_node_inval;
465
0
        }
466
0
    } else if (snode->nodetype == LYS_NOTIF) {
467
0
        if (lydctx->int_opts & LYD_INTOPT_NOTIF) {
468
0
            if (lydctx->op_node) {
469
0
                goto error_node_dup;
470
0
            }
471
0
        } else {
472
0
            goto error_node_inval;
473
0
        }
474
0
    }
475
476
    /* success */
477
0
    goto cleanup;
478
479
0
error_node_dup:
480
0
    LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
481
0
            lys_nodetype2str(snode->nodetype), snode->name, lys_nodetype2str(lydctx->op_node->schema->nodetype),
482
0
            lydctx->op_node->schema->name);
483
0
    rc = LY_EVALID;
484
0
    goto cleanup;
485
486
0
error_node_inval:
487
0
    LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\".", lys_nodetype2str(snode->nodetype),
488
0
            snode->name);
489
0
    rc = LY_EVALID;
490
491
0
cleanup:
492
0
    LOG_LOCBACK(1, 0, 0, 0);
493
0
    return rc;
494
0
}
495
496
LY_ERR
497
lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const void *value, size_t value_len,
498
        ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, struct lyd_node **node)
499
0
{
500
0
    ly_bool incomplete;
501
502
0
    LY_CHECK_RET(lyd_create_term(schema, value, value_len, dynamic, format, prefix_data, hints, &incomplete, node));
503
504
0
    if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
505
0
        LY_CHECK_RET(ly_set_add(&lydctx->node_types, *node, 1, NULL));
506
0
    }
507
0
    return LY_SUCCESS;
508
0
}
509
510
LY_ERR
511
lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod,
512
        const char *name, size_t name_len, const void *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format,
513
        void *prefix_data, uint32_t hints)
514
0
{
515
0
    ly_bool incomplete;
516
0
    struct lyd_meta *first = NULL;
517
518
0
    if (meta && *meta) {
519
        /* remember the first metadata */
520
0
        first = *meta;
521
0
    }
522
523
0
    LY_CHECK_RET(lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, dynamic, format, prefix_data,
524
0
            hints, 0, &incomplete));
525
526
0
    if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
527
0
        LY_CHECK_RET(ly_set_add(&lydctx->meta_types, *meta, 1, NULL));
528
0
    }
529
530
0
    if (first) {
531
        /* always return the first metadata */
532
0
        *meta = first;
533
0
    }
534
535
0
    return LY_SUCCESS;
536
0
}