blob: 80c750103bae254d5e6b78cabea2e3a0e7eb88a6 [file] [log] [blame]
/**
* @file parser_lyb.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief LYB data parser for libyang
*
* Copyright (c) 2018 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#ifdef __APPLE__
# include <libkern/OSByteOrder.h>
# define le16toh(x) OSSwapLittleToHostInt16(x)
# define le32toh(x) OSSwapLittleToHostInt32(x)
# define le64toh(x) OSSwapLittleToHostInt64(x)
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
# include <sys/endian.h>
#elif defined(__sun__)
# include <endian.h>
# include <sys/byteorder.h>
# if defined(_BIG_ENDIAN)
# define le16toh(x) BSWAP_16(x)
# define le32toh(x) BSWAP_32(x)
# define le64toh(x) BSWAP_64(x)
# define htole64(x) le64toh(x)
# define htole32(x) le32toh(x)
# define htole16(x) le16toh(x)
# else
# define le16toh(x) (x)
# define le32toh(x) (x)
# define le64toh(x) (x)
# define htole64(x) (x)
# define htole32(x) (x)
# define htole16(x) (x)
# endif
#else
# include <endian.h>
#endif
#include "libyang.h"
#include "common.h"
#include "context.h"
#include "parser.h"
#include "tree_internal.h"
#define LYB_HAVE_READ_GOTO(r, d, go) if (r < 0) goto go; d += r;
#define LYB_HAVE_READ_RETURN(r, d, ret) if (r < 0) return ret; d += r;
static int
lyb_read(const char *data, uint8_t *buf, size_t count, struct lyb_state *lybs)
{
int ret = 0, i, empty_chunk_i;
size_t to_read;
uint8_t meta_buf[LYB_META_BYTES];
assert(data && lybs);
while (1) {
/* check for fully-read (empty) data chunks */
to_read = count;
empty_chunk_i = -1;
for (i = 0; i < lybs->used; ++i) {
/* we want the innermost chunks resolved first, so replace previous empty chunks,
* also ignore chunks that are completely finished, there is nothing for us to do */
if ((lybs->written[i] <= to_read) && lybs->position[i]) {
/* empty chunk, do not read more */
to_read = lybs->written[i];
empty_chunk_i = i;
}
}
if ((empty_chunk_i == -1) && !count) {
break;
}
/* we are actually reading some data, not just finishing another chunk */
if (to_read) {
if (buf) {
memcpy(buf, data + ret, to_read);
}
for (i = 0; i < lybs->used; ++i) {
/* decrease all written counters */
lybs->written[i] -= to_read;
assert(lybs->written[i] <= LYB_SIZE_MAX);
}
/* decrease count/buf */
count -= to_read;
if (buf) {
buf += to_read;
}
ret += to_read;
}
if (empty_chunk_i > -1) {
/* read the next chunk meta information */
memcpy(meta_buf, data + ret, LYB_META_BYTES);
lybs->written[empty_chunk_i] = meta_buf[0];
lybs->inner_chunks[empty_chunk_i] = meta_buf[1];
/* remember whether there is a following chunk or not */
lybs->position[empty_chunk_i] = (lybs->written[empty_chunk_i] == LYB_SIZE_MAX ? 1 : 0);
ret += LYB_META_BYTES;
}
}
return ret;
}
static int
lyb_read_number(uint64_t *num, size_t bytes, const char *data, struct lyb_state *lybs)
{
int r, ret = 0;
size_t i;
uint8_t byte;
for (i = 0; i < bytes; ++i) {
ret += (r = lyb_read(data, &byte, 1, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
*(((uint8_t *)num) + i) = byte;
}
return ret;
}
static int
lyb_read_enum(uint64_t *enum_idx, uint32_t count, const char *data, struct lyb_state *lybs)
{
int ret = 0;
size_t bytes;
uint64_t tmp_enum = 0;
if (count < (1 << 8)) {
bytes = 1;
} else if (count < (1 << 16)) {
bytes = 2;
} else if (count < (1 << 24)) {
bytes = 3;
} else {
bytes = 4;
}
/* The enum is always read into a uint64_t buffer */
ret = lyb_read_number(&tmp_enum, bytes, data, lybs);
*enum_idx = le64toh(tmp_enum);
return ret;
}
static int
lyb_read_string(const char *data, char **str, int with_length, struct lyb_state *lybs)
{
int next_chunk = 0, r, ret = 0;
size_t len = 0, cur_len;
if (with_length) {
ret += (r = lyb_read_number(&len, 2, data, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
} else {
/* read until the end of this subtree */
len = lybs->written[lybs->used - 1];
if (lybs->position[lybs->used - 1]) {
next_chunk = 1;
}
}
*str = malloc((len + 1) * sizeof **str);
LY_CHECK_ERR_RETURN(!*str, LOGMEM(lybs->ctx), -1);
ret += (r = lyb_read(data, (uint8_t *)*str, len, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
while (next_chunk) {
cur_len = lybs->written[lybs->used - 1];
if (lybs->position[lybs->used - 1]) {
next_chunk = 1;
} else {
next_chunk = 0;
}
*str = ly_realloc(*str, (len + cur_len + 1) * sizeof **str);
LY_CHECK_ERR_RETURN(!*str, LOGMEM(lybs->ctx), -1);
ret += (r = lyb_read(data, ((uint8_t *)*str) + len, cur_len, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
len += cur_len;
}
((char *)*str)[len] = '\0';
return ret;
error:
free((char *)*str);
*str = NULL;
return -1;
}
static void
lyb_read_stop_subtree(struct lyb_state *lybs)
{
if (lybs->written[lybs->used - 1]) {
LOGINT(lybs->ctx);
}
--lybs->used;
}
static int
lyb_read_start_subtree(const char *data, struct lyb_state *lybs)
{
uint8_t meta_buf[LYB_META_BYTES];
if (lybs->used == lybs->size) {
lybs->size += LYB_STATE_STEP;
lybs->written = ly_realloc(lybs->written, lybs->size * sizeof *lybs->written);
lybs->position = ly_realloc(lybs->position, lybs->size * sizeof *lybs->position);
lybs->inner_chunks = ly_realloc(lybs->inner_chunks, lybs->size * sizeof *lybs->inner_chunks);
LY_CHECK_ERR_RETURN(!lybs->written || !lybs->position || !lybs->inner_chunks, LOGMEM(lybs->ctx), -1);
}
memcpy(meta_buf, data, LYB_META_BYTES);
++lybs->used;
lybs->written[lybs->used - 1] = meta_buf[0];
lybs->inner_chunks[lybs->used - 1] = meta_buf[LYB_SIZE_BYTES];
lybs->position[lybs->used - 1] = (lybs->written[lybs->used - 1] == LYB_SIZE_MAX ? 1 : 0);
return LYB_META_BYTES;
}
static int
lyb_parse_model(const char *data, const struct lys_module **mod, struct lyb_state *lybs)
{
int r, ret = 0;
char *mod_name = NULL, mod_rev[11];
uint16_t rev = 0;
uint8_t tmp_buf[2];
/* model name */
ret += (r = lyb_read_string(data, &mod_name, 1, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
/* revision */
ret += (r = lyb_read(data, tmp_buf, sizeof tmp_buf, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
rev = tmp_buf[0] | (tmp_buf[1] << 8);
if (rev) {
sprintf(mod_rev, "%04u-%02u-%02u", ((rev & 0xFE00) >> 9) + 2000, (rev & 0x01E0) >> 5, (rev & 0x001F));
*mod = ly_ctx_get_module(lybs->ctx, mod_name, mod_rev, 0);
} else {
*mod = ly_ctx_get_module(lybs->ctx, mod_name, NULL, 0);
}
if (lybs->ctx->data_clb) {
if (!*mod) {
*mod = lybs->ctx->data_clb(lybs->ctx, mod_name, NULL, 0, lybs->ctx->data_clb_data);
} else if (!(*mod)->implemented) {
*mod = lybs->ctx->data_clb(lybs->ctx, mod_name, (*mod)->ns, LY_MODCLB_NOT_IMPLEMENTED, lybs->ctx->data_clb_data);
}
}
if (!*mod) {
LOGERR(lybs->ctx, LY_EINVAL, "Invalid context for LYB data parsing, missing module \"%s%s%s\".",
mod_name, rev ? "@" : "", rev ? mod_rev : "");
goto error;
} else if (!(*mod)->implemented) {
LOGERR(lybs->ctx, LY_EINVAL, "Invalid context for LYB data parsing, module \"%s%s%s\" not implemented.",
mod_name, rev ? "@" : "", rev ? mod_rev : "");
goto error;
}
free(mod_name);
return ret;
error:
free(mod_name);
return -1;
}
static struct lyd_node *
lyb_new_node(const struct lys_node *schema)
{
struct lyd_node *node;
switch (schema->nodetype) {
case LYS_CONTAINER:
case LYS_LIST:
case LYS_NOTIF:
case LYS_RPC:
case LYS_ACTION:
node = calloc(sizeof(struct lyd_node), 1);
break;
case LYS_LEAF:
case LYS_LEAFLIST:
node = calloc(sizeof(struct lyd_node_leaf_list), 1);
if (((struct lys_node_leaf *)schema)->type.base == LY_TYPE_LEAFREF) {
node->validity |= LYD_VAL_LEAFREF;
}
break;
case LYS_ANYDATA:
case LYS_ANYXML:
node = calloc(sizeof(struct lyd_node_anydata), 1);
break;
default:
return NULL;
}
LY_CHECK_ERR_RETURN(!node, LOGMEM(schema->module->ctx), NULL);
/* fill basic info */
node->schema = (struct lys_node *)schema;
if (resolve_applies_when(schema, 0, NULL)) {
/* this data are considered trusted so if this node exists, it means its when must have been true */
node->when_status = LYD_WHEN | LYD_WHEN_TRUE;
}
node->prev = node;
return node;
}
static int
lyb_parse_anydata(struct lyd_node *node, const char *data, struct lyb_state *lybs)
{
int r, ret = 0;
char *str = NULL;
struct lyd_node_anydata *any = (struct lyd_node_anydata *)node;
/* read value type */
ret += (r = lyb_read(data, (uint8_t *)&any->value_type, sizeof any->value_type, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
/* read anydata content */
if (any->value_type == LYD_ANYDATA_DATATREE) {
/* invalid situation */
LOGINT(node->schema->module->ctx);
return -1;
} else if (any->value_type == LYD_ANYDATA_LYB) {
ret += (r = lyb_read_string(data, &any->value.mem, 0, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
} else {
ret += (r = lyb_read_string(data, &str, 0, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
/* add to dictionary */
any->value.str = lydict_insert_zc(node->schema->module->ctx, str);
}
return ret;
}
/* generally, fill lyd_val value union */
static int
lyb_parse_val_1(struct lys_type *type, LY_DATA_TYPE value_type, uint8_t value_flags, const char *data,
const char **value_str, lyd_val *value, struct lyb_state *lybs)
{
int r, ret;
size_t i;
char *str = NULL;
uint8_t byte;
uint64_t num;
if (value_flags & LY_VALUE_USER) {
/* just read value_str */
ret = lyb_read_string(data, &str, 0, lybs);
if (ret > -1) {
*value_str = lydict_insert_zc(lybs->ctx, str);
}
return ret;
}
/* find the correct structure, go through leafrefs and typedefs */
switch (value_type) {
case LY_TYPE_ENUM:
for (; type->base == LY_TYPE_LEAFREF; type = &type->info.lref.target->type);
for (; !type->info.enums.count; type = &type->der->type);
break;
case LY_TYPE_BITS:
for (; type->base == LY_TYPE_LEAFREF; type = &type->info.lref.target->type);
for (; !type->info.bits.count; type = &type->der->type);
break;
default:
break;
}
switch (value_type) {
case LY_TYPE_INST:
case LY_TYPE_IDENT:
case LY_TYPE_UNION:
/* we do not actually fill value now, but value_str */
ret = lyb_read_string(data, &str, 0, lybs);
if (ret > -1) {
*value_str = lydict_insert_zc(lybs->ctx, str);
}
break;
case LY_TYPE_BINARY:
case LY_TYPE_STRING:
case LY_TYPE_UNKNOWN:
/* read string */
ret = lyb_read_string(data, &str, 0, lybs);
if (ret > -1) {
value->string = lydict_insert_zc(lybs->ctx, str);
}
break;
case LY_TYPE_BITS:
value->bit = calloc(type->info.bits.count, sizeof *value->bit);
LY_CHECK_ERR_RETURN(!value->bit, LOGMEM(lybs->ctx), -1);
/* read values */
ret = 0;
for (i = 0; i < type->info.bits.count; ++i) {
if (i % 8 == 0) {
/* read another byte */
ret += (r = lyb_read(data + ret, &byte, sizeof byte, lybs));
if (r < 0) {
return -1;
}
}
if (byte & (0x01 << (i % 8))) {
/* bit is set */
value->bit[i] = &type->info.bits.bit[i];
}
}
break;
case LY_TYPE_BOOL:
/* read byte */
ret = lyb_read(data, &byte, sizeof byte, lybs);
if ((ret > 0) && byte) {
value->bln = 1;
}
break;
case LY_TYPE_EMPTY:
/* nothing to read */
ret = 0;
break;
case LY_TYPE_ENUM:
num = 0;
ret = lyb_read_enum(&num, type->info.enums.count, data, lybs);
if (ret > 0) {
assert(num < type->info.enums.count);
value->enm = &type->info.enums.enm[num];
}
break;
case LY_TYPE_INT8:
case LY_TYPE_UINT8:
ret = lyb_read_number((uint64_t *)&value->uint8, 1, data, lybs);
break;
case LY_TYPE_INT16:
case LY_TYPE_UINT16:
ret = lyb_read_number((uint64_t *)&value->uint16, 2, data, lybs);
value->uint16 = le16toh(value->uint16);
break;
case LY_TYPE_INT32:
case LY_TYPE_UINT32:
ret = lyb_read_number((uint64_t *)&value->uint32, 4, data, lybs);
value->uint32 = le32toh(value->uint32);
break;
case LY_TYPE_DEC64:
case LY_TYPE_INT64:
case LY_TYPE_UINT64:
ret = lyb_read_number((uint64_t *)&value->uint64, 8, data, lybs);
value->uint64 = le64toh(value->uint64);
break;
default:
return -1;
}
return ret;
}
/* generally, fill value_str */
static int
lyb_parse_val_2(struct lys_type *type, struct lyd_node_leaf_list *leaf, struct lyd_attr *attr, struct unres_data *unres)
{
struct ly_ctx *ctx;
struct lys_module *mod;
struct lys_type *rtype = NULL;
char num_str[22], *str;
int64_t frac;
uint32_t i, str_len;
uint8_t *value_flags, dig;
const char **value_str;
LY_DATA_TYPE value_type;
lyd_val *value;
if (leaf) {
ctx = leaf->schema->module->ctx;
mod = lys_node_module(leaf->schema);
value = &leaf->value;
value_str = &leaf->value_str;
value_flags = &leaf->value_flags;
value_type = leaf->value_type;
} else {
ctx = attr->annotation->module->ctx;
mod = lys_main_module(attr->annotation->module);
value = &attr->value;
value_str = &attr->value_str;
value_flags = &attr->value_flags;
value_type = attr->value_type;
}
if (*value_flags & LY_VALUE_USER) {
/* unfortunately, we need to also fill the value properly, so just parse it again */
*value_flags &= ~LY_VALUE_USER;
if (!lyp_parse_value(type, value_str, NULL, leaf, attr, NULL, 1, (leaf ? leaf->dflt : 0), 1)) {
return -1;
}
if (!(*value_flags & LY_VALUE_USER)) {
LOGWRN(ctx, "Value \"%s\" was stored as a user type, but it is not in the current context.", value_str);
}
return 0;
}
/* we are parsing leafref/ptr union stored as the target type,
* so we first parse it into string and then resolve the leafref/ptr union */
if ((type->base == LY_TYPE_LEAFREF) || (type->base == LY_TYPE_INST)
|| ((type->base == LY_TYPE_UNION) && type->info.uni.has_ptr_type)) {
if ((value_type == LY_TYPE_INST) || (value_type == LY_TYPE_IDENT) || (value_type == LY_TYPE_UNION)) {
/* we already have a string */
goto parse_reference;
}
}
/* find the correct structure, go through leafrefs and typedefs */
switch (value_type) {
case LY_TYPE_BITS:
for (rtype = type; rtype->base == LY_TYPE_LEAFREF; rtype = &rtype->info.lref.target->type);
for (; !rtype->info.bits.count; rtype = &rtype->der->type);
break;
case LY_TYPE_DEC64:
for (rtype = type; rtype->base == LY_TYPE_LEAFREF; rtype = &type->info.lref.target->type);
break;
default:
break;
}
switch (value_type) {
case LY_TYPE_IDENT:
/* fill the identity pointer now */
value->ident = resolve_identref(type, *value_str, (struct lyd_node *)leaf, mod, (leaf ? leaf->dflt : 0));
if (!value->ident) {
return -1;
}
break;
case LY_TYPE_INST:
/* unresolved instance-identifier, keep value NULL */
value->instance = NULL;
break;
case LY_TYPE_BINARY:
case LY_TYPE_STRING:
case LY_TYPE_UNKNOWN:
/* just re-assign it */
*value_str = value->string;
break;
case LY_TYPE_BITS:
/* print the set bits */
str = malloc(1);
LY_CHECK_ERR_RETURN(!str, LOGMEM(ctx), -1);
str[0] = '\0';
str_len = 0;
for (i = 0; i < rtype->info.bits.count; ++i) {
if (value->bit[i]) {
str = ly_realloc(str, str_len + strlen(value->bit[i]->name) + (str_len ? 1 : 0) + 1);
LY_CHECK_ERR_RETURN(!str, LOGMEM(ctx), -1);
str_len += sprintf(str + str_len, "%s%s", str_len ? " " : "", value->bit[i]->name);
}
}
*value_str = lydict_insert_zc(ctx, str);
break;
case LY_TYPE_BOOL:
*value_str = lydict_insert(ctx, (value->bln ? "true" : "false"), 0);
break;
case LY_TYPE_EMPTY:
*value_str = lydict_insert(ctx, "", 0);
break;
case LY_TYPE_UNION:
if (attr) {
/* we do not support union type attribute */
LOGINT(ctx);
return -1;
}
if (resolve_union(leaf, type, 1, 2, NULL)) {
return -1;
}
break;
case LY_TYPE_ENUM:
/* print the value */
*value_str = lydict_insert(ctx, value->enm->name, 0);
break;
case LY_TYPE_INT8:
sprintf(num_str, "%d", value->int8);
*value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_UINT8:
sprintf(num_str, "%u", value->uint8);
*value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_INT16:
sprintf(num_str, "%d", value->int16);
*value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_UINT16:
sprintf(num_str, "%u", value->uint16);
*value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_INT32:
sprintf(num_str, "%d", value->int32);
*value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_UINT32:
sprintf(num_str, "%u", value->uint32);
*value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_INT64:
sprintf(num_str, "%"PRId64, value->int64);
*value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_UINT64:
sprintf(num_str, "%"PRIu64, value->uint64);
*value_str = lydict_insert(ctx, num_str, 0);
break;
case LY_TYPE_DEC64:
frac = value->dec64 % rtype->info.dec64.div;
dig = rtype->info.dec64.dig;
/* remove trailing zeros */
while ((dig > 1) && !(frac % 10)) {
frac /= 10;
--dig;
}
sprintf(num_str, "%"PRId64".%.*"PRId64, value->dec64 / (int64_t)rtype->info.dec64.div, dig, frac);
*value_str = lydict_insert(ctx, num_str, 0);
break;
default:
return -1;
}
if ((type->base == LY_TYPE_LEAFREF) || (type->base == LY_TYPE_INST)
|| ((type->base == LY_TYPE_UNION) && type->info.uni.has_ptr_type)) {
parse_reference:
assert(*value_str);
if (attr) {
/* we do not support reference types of attributes */
LOGINT(ctx);
return -1;
}
if (type->base == LY_TYPE_INST) {
if (unres_data_add(unres, (struct lyd_node *)leaf, UNRES_INSTID)) {
return -1;
}
} else if (type->base == LY_TYPE_LEAFREF) {
if (unres_data_add(unres, (struct lyd_node *)leaf, UNRES_LEAFREF)) {
return -1;
}
} else {
if (unres_data_add(unres, (struct lyd_node *)leaf, UNRES_UNION)) {
return -1;
}
}
}
return 0;
}
static int
lyb_parse_value(struct lys_type *type, struct lyd_node_leaf_list *leaf, struct lyd_attr *attr, const char *data,
struct unres_data *unres, struct lyb_state *lybs)
{
int r, ret = 0;
uint8_t start_byte;
const char **value_str;
lyd_val *value;
LY_DATA_TYPE *value_type;
uint8_t *value_flags;
assert((leaf || attr) && (!leaf || !attr));
if (leaf) {
value_str = &leaf->value_str;
value = &leaf->value;
value_type = &leaf->value_type;
value_flags = &leaf->value_flags;
} else {
value_str = &attr->value_str;
value = &attr->value;
value_type = &attr->value_type;
value_flags = &attr->value_flags;
}
/* read value type and flags on the first byte */
ret += (r = lyb_read(data, &start_byte, sizeof start_byte, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
/* fill value type, flags */
*value_type = start_byte & 0x1F;
if (start_byte & 0x80) {
assert(leaf);
leaf->dflt = 1;
}
if (start_byte & 0x40) {
*value_flags |= LY_VALUE_USER;
}
if (start_byte & 0x20) {
*value_flags |= LY_VALUE_UNRES;
}
ret += (r = lyb_parse_val_1(type, *value_type, *value_flags, data, value_str, value, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
/* union is handled specially */
if ((type->base == LY_TYPE_UNION) && !(*value_flags & LY_VALUE_USER)) {
assert(*value_type == LY_TYPE_STRING);
*value_str = value->string;
value->string = NULL;
*value_type = LY_TYPE_UNION;
}
ret += (r = lyb_parse_val_2(type, leaf, attr, unres));
LYB_HAVE_READ_RETURN(r, data, -1);
return ret;
}
static int
lyb_parse_attr_name(const struct lys_module *mod, const char *data, struct lys_ext_instance_complex **ext, int options,
struct lyb_state *lybs)
{
int r, ret = 0, pos, i, j, k;
const struct lys_submodule *submod = NULL;
char *attr_name = NULL;
/* attr name */
ret += (r = lyb_read_string(data, &attr_name, 1, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
/* search module */
pos = -1;
for (i = 0, j = 0; i < mod->ext_size; i = i + j + 1) {
j = lys_ext_instance_presence(&mod->ctx->models.list[0]->extensions[0], &mod->ext[i], mod->ext_size - i);
if (j == -1) {
break;
}
if (ly_strequal(mod->ext[i + j]->arg_value, attr_name, 0)) {
pos = i + j;
break;
}
}
/* try submodules */
if (pos == -1) {
for (k = 0; k < mod->inc_size; ++k) {
submod = mod->inc[k].submodule;
for (i = 0, j = 0; i < submod->ext_size; i = i + j + 1) {
j = lys_ext_instance_presence(&mod->ctx->models.list[0]->extensions[0], &submod->ext[i], submod->ext_size - i);
if (j == -1) {
break;
}
if (ly_strequal(submod->ext[i + j]->arg_value, attr_name, 0)) {
pos = i + j;
break;
}
}
}
}
if (pos == -1) {
*ext = NULL;
} else {
*ext = submod ? (struct lys_ext_instance_complex *)submod->ext[pos] : (struct lys_ext_instance_complex *)mod->ext[pos];
}
if (!*ext && (options & LYD_OPT_STRICT)) {
LOGVAL(mod->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Failed to find annotation \"%s\" in \"%s\".", attr_name, mod->name);
free(attr_name);
return -1;
}
free(attr_name);
return ret;
}
static int
lyb_parse_attributes(struct lyd_node *node, const char *data, int options, struct unres_data *unres, struct lyb_state *lybs)
{
int r, ret = 0;
uint8_t i, count = 0;
const struct lys_module *mod;
struct lys_type **type;
struct lyd_attr *attr = NULL;
struct lys_ext_instance_complex *ext;
/* read number of attributes stored */
ret += (r = lyb_read(data, &count, 1, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
/* read attributes */
for (i = 0; i < count; ++i) {
ret += (r = lyb_read_start_subtree(data, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
/* find model */
ret += (r = lyb_parse_model(data, &mod, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
if (mod) {
/* annotation name */
ret += (r = lyb_parse_attr_name(mod, data, &ext, options, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
}
if (!mod || !ext) {
/* unknown attribute, skip it */
do {
ret += (r = lyb_read(data, NULL, lybs->written[lybs->used - 1], lybs));
LYB_HAVE_READ_GOTO(r, data, error);
} while (lybs->written[lybs->used - 1]);
goto stop_subtree;
}
/* allocate new attribute */
if (!attr) {
assert(!node->attr);
attr = calloc(1, sizeof *attr);
LY_CHECK_ERR_GOTO(!attr, LOGMEM(lybs->ctx), error);
node->attr = attr;
} else {
attr->next = calloc(1, sizeof *attr);
LY_CHECK_ERR_GOTO(!attr->next, LOGMEM(lybs->ctx), error);
attr = attr->next;
}
/* attribute annotation */
attr->annotation = ext;
/* attribute name */
attr->name = lydict_insert(lybs->ctx, attr->annotation->arg_value, 0);
/* get the type */
type = (struct lys_type **)lys_ext_complex_get_substmt(LY_STMT_TYPE, attr->annotation, NULL);
if (!type || !(*type)) {
goto error;
}
/* attribute value */
ret += (r = lyb_parse_value(*type, NULL, attr, data, unres, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
stop_subtree:
lyb_read_stop_subtree(lybs);
}
return ret;
error:
lyd_free_attr(lybs->ctx, node, node->attr, 1);
return -1;
}
static int
lyb_is_schema_hash_match(struct lys_node *sibling, LYB_HASH *hash, uint8_t hash_count)
{
LYB_HASH sibling_hash;
uint8_t i;
/* compare all the hashes starting from collision ID 0 */
for (i = 0; i < hash_count; ++i) {
sibling_hash = lyb_hash(sibling, i);
if (sibling_hash != hash[i]) {
return 0;
}
}
return 1;
}
static int
lyb_parse_schema_hash(const struct lys_node *sparent, const struct lys_module *mod, const char *data, const char *yang_data_name,
int options, struct lys_node **snode, struct lyb_state *lybs)
{
int r, ret = 0;
uint8_t i, j;
struct lys_node *sibling;
LYB_HASH hash[LYB_HASH_BITS - 1];
assert((sparent || mod) && (!sparent || !mod));
/* read the first hash */
ret += (r = lyb_read(data, &hash[0], sizeof *hash, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
/* based on the first hash read all the other ones, if any */
for (i = 0; !(hash[0] & (LYB_HASH_COLLISION_ID >> i)); ++i) {
if (i > LYB_HASH_BITS) {
return -1;
}
}
/* move the first hash on its accurate position */
hash[i] = hash[0];
/* read the rest of hashes */
for (j = i; j; --j) {
ret += (r = lyb_read(data, &hash[j - 1], sizeof *hash, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
/* correct collision ID */
assert(hash[j - 1] & (LYB_HASH_COLLISION_ID >> (j - 1)));
/* preceded with zeros */
assert(!(hash[j - 1] & (LYB_HASH_MASK << (LYB_HASH_BITS - (j - 1)))));
}
/* handle yang data templates */
if ((options & LYD_OPT_DATA_TEMPLATE) && yang_data_name && mod) {
sparent = lyp_get_yang_data_template(mod, yang_data_name, strlen(yang_data_name));
if (!sparent) {
sibling = NULL;
goto finish;
}
}
/* handle RPC/action input/output */
if (sparent && (sparent->nodetype & (LYS_RPC | LYS_ACTION))) {
sibling = NULL;
while ((sibling = (struct lys_node *)lys_getnext(sibling, sparent, NULL, LYS_GETNEXT_WITHINOUT))) {
if ((sibling->nodetype == LYS_INPUT) && (options & LYD_OPT_RPC)) {
break;
}
if ((sibling->nodetype == LYS_OUTPUT) && (options & LYD_OPT_RPCREPLY)) {
break;
}
}
if (!sibling) {
/* fail */
goto finish;
}
/* use only input/output children nodes */
sparent = sibling;
}
/* find our node with matching hashes */
sibling = NULL;
while ((sibling = (struct lys_node *)lys_getnext(sibling, sparent, mod, 0))) {
/* skip schema nodes from models not present during printing */
if (lyb_has_schema_model(sibling, lybs->models, lybs->mod_count) && lyb_is_schema_hash_match(sibling, hash, i + 1)) {
/* match found */
break;
}
}
finish:
*snode = sibling;
if (!sibling && (options & LYD_OPT_STRICT)) {
if (mod) {
LOGVAL(lybs->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Failed to find matching hash for a top-level node from \"%s\".",
mod->name);
} else {
LOGVAL(lybs->ctx, LYE_SPEC, LY_VLOG_LYS, sparent, "Failed to find matching hash for a child of \"%s\".",
sparent->name);
}
return -1;
}
return ret;
}
static int
lyb_parse_subtree(const char *data, struct lyd_node *parent, struct lyd_node **first_sibling, const char *yang_data_name,
int options, struct unres_data *unres, struct lyb_state *lybs)
{
int r, ret = 0;
struct lyd_node *node = NULL, *iter;
const struct lys_module *mod;
struct lys_node *snode;
assert((parent && !first_sibling) || (!parent && first_sibling));
/* register a new subtree */
ret += (r = lyb_read_start_subtree(data, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
if (!parent) {
/* top-level, read module name */
ret += (r = lyb_parse_model(data, &mod, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
if (mod) {
/* read hash, find the schema node starting from mod, possibly yang_data_name */
r = lyb_parse_schema_hash(NULL, mod, data, yang_data_name, options, &snode, lybs);
}
} else {
mod = lyd_node_module(parent);
/* read hash, find the schema node starting from parent schema */
r = lyb_parse_schema_hash(parent->schema, NULL, data, NULL, options, &snode, lybs);
}
ret += r;
LYB_HAVE_READ_GOTO(r, data, error);
if (!mod || !snode) {
/* unknown data subtree, skip it whole */
do {
ret += (r = lyb_read(data, NULL, lybs->written[lybs->used - 1], lybs));
/* also skip the meta information inside */
r = lybs->inner_chunks[lybs->used - 1] * LYB_META_BYTES;
data += r;
ret += r;
} while (lybs->written[lybs->used - 1]);
goto stop_subtree;
}
/*
* read the node
*/
node = lyb_new_node(snode);
if (!node) {
goto error;
}
ret += (r = lyb_parse_attributes(node, data, options, unres, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
/* read node content */
switch (snode->nodetype) {
case LYS_CONTAINER:
case LYS_LIST:
case LYS_NOTIF:
case LYS_RPC:
case LYS_ACTION:
/* nothing to read */
break;
case LYS_LEAF:
case LYS_LEAFLIST:
ret += (r = lyb_parse_value(&((struct lys_node_leaf *)node->schema)->type, (struct lyd_node_leaf_list *)node,
NULL, data, unres, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
break;
case LYS_ANYXML:
case LYS_ANYDATA:
ret += (r = lyb_parse_anydata(node, data, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
break;
default:
goto error;
}
/* insert into data tree, manually */
if (parent) {
if (!parent->child) {
/* only child */
parent->child = node;
} else {
/* last child */
parent->child->prev->next = node;
node->prev = parent->child->prev;
parent->child->prev = node;
}
node->parent = parent;
} else if (*first_sibling) {
/* last sibling */
(*first_sibling)->prev->next = node;
node->prev = (*first_sibling)->prev;
(*first_sibling)->prev = node;
} else {
/* only sibling */
*first_sibling = node;
}
/* read all descendants */
while (lybs->written[lybs->used - 1]) {
ret += (r = lyb_parse_subtree(data, node, NULL, NULL, options, unres, lybs));
LYB_HAVE_READ_GOTO(r, data, error);
}
/* make containers default if should be */
if ((node->schema->nodetype == LYS_CONTAINER) && !((struct lys_node_container *)node->schema)->presence) {
LY_TREE_FOR(node->child, iter) {
if (!iter->dflt) {
break;
}
}
if (!iter) {
node->dflt = 1;
}
}
#ifdef LY_ENABLED_CACHE
/* calculate the hash and insert it into parent (list with keys is handled when its keys are inserted) */
if ((node->schema->nodetype != LYS_LIST) || !((struct lys_node_list *)node->schema)->keys_size) {
lyd_hash(node);
lyd_insert_hash(node);
}
#endif
stop_subtree:
/* end the subtree */
lyb_read_stop_subtree(lybs);
return ret;
error:
lyd_free(node);
if (first_sibling && (*first_sibling == node)) {
*first_sibling = NULL;
}
return -1;
}
static int
lyb_parse_data_models(const char *data, struct lyb_state *lybs)
{
int i, r, ret = 0;
/* read model count */
ret += (r = lyb_read_number((uint64_t *)&lybs->mod_count, 2, data, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
lybs->models = malloc(lybs->mod_count * sizeof *lybs->models);
LY_CHECK_ERR_RETURN(!lybs->models, LOGMEM(lybs->ctx), -1);
/* read modules */
for (i = 0; i < lybs->mod_count; ++i) {
ret += (r = lyb_parse_model(data, &lybs->models[i], lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
}
return ret;
}
static int
lyb_parse_magic_number(const char *data, struct lyb_state *lybs)
{
int r, ret = 0;
char magic_byte = 0;
ret += (r = lyb_read(data, (uint8_t *)&magic_byte, 1, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
if (magic_byte != 'l') {
LOGERR(lybs->ctx, LY_EINVAL, "Invalid first magic number byte \"0x%02x\".", magic_byte);
return -1;
}
ret += (r = lyb_read(data, (uint8_t *)&magic_byte, 1, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
if (magic_byte != 'y') {
LOGERR(lybs->ctx, LY_EINVAL, "Invalid second magic number byte \"0x%02x\".", magic_byte);
return -1;
}
ret += (r = lyb_read(data, (uint8_t *)&magic_byte, 1, lybs));
LYB_HAVE_READ_RETURN(r, data, -1);
if (magic_byte != 'b') {
LOGERR(lybs->ctx, LY_EINVAL, "Invalid third magic number byte \"0x%02x\".", magic_byte);
return -1;
}
return ret;
}
static int
lyb_parse_header(const char *data, struct lyb_state *lybs)
{
int ret = 0;
uint8_t byte = 0;
/* TODO version, any flags? */
ret += lyb_read(data, (uint8_t *)&byte, sizeof byte, lybs);
return ret;
}
struct lyd_node *
lyd_parse_lyb(struct ly_ctx *ctx, const char *data, int options, const struct lyd_node *data_tree,
const char *yang_data_name, int *parsed)
{
int r = 0, ret = 0;
struct lyd_node *node = NULL, *next, *act_notif = NULL;
struct unres_data *unres = NULL;
struct lyb_state lybs;
if (!ctx || !data) {
LOGARG;
return NULL;
}
lybs.written = malloc(LYB_STATE_STEP * sizeof *lybs.written);
lybs.position = malloc(LYB_STATE_STEP * sizeof *lybs.position);
lybs.inner_chunks = malloc(LYB_STATE_STEP * sizeof *lybs.inner_chunks);
LY_CHECK_ERR_GOTO(!lybs.written || !lybs.position || !lybs.inner_chunks, LOGMEM(ctx), finish);
lybs.used = 0;
lybs.size = LYB_STATE_STEP;
lybs.models = NULL;
lybs.mod_count = 0;
lybs.ctx = ctx;
unres = calloc(1, sizeof *unres);
LY_CHECK_ERR_GOTO(!unres, LOGMEM(ctx), finish);
/* read magic number */
ret += (r = lyb_parse_magic_number(data, &lybs));
LYB_HAVE_READ_GOTO(r, data, finish);
/* read header */
ret += (r = lyb_parse_header(data, &lybs));
LYB_HAVE_READ_GOTO(r, data, finish);
/* read used models */
ret += (r = lyb_parse_data_models(data, &lybs));
LYB_HAVE_READ_GOTO(r, data, finish);
/* read subtree(s) */
while (data[0]) {
ret += (r = lyb_parse_subtree(data, NULL, &node, yang_data_name, options, unres, &lybs));
if (r < 0) {
lyd_free_withsiblings(node);
node = NULL;
goto finish;
}
data += r;
}
/* read the last zero, parsing finished */
++ret;
r = ret;
if (options & LYD_OPT_DATA_ADD_YANGLIB) {
if (lyd_merge(node, ly_ctx_info(ctx), LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) {
LOGERR(ctx, LY_EINT, "Adding ietf-yang-library data failed.");
lyd_free_withsiblings(node);
node = NULL;
goto finish;
}
}
/* resolve any unresolved instance-identifiers */
if (unres->count) {
if (options & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF)) {
LY_TREE_DFS_BEGIN(node, next, act_notif) {
if (act_notif->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
break;
}
LY_TREE_DFS_END(node, next, act_notif);
}
}
if (lyd_defaults_add_unres(&node, options, ctx, NULL, 0, data_tree, act_notif, unres, 0)) {
lyd_free_withsiblings(node);
node = NULL;
goto finish;
}
}
finish:
free(lybs.written);
free(lybs.position);
free(lybs.inner_chunks);
free(lybs.models);
if (unres) {
free(unres->node);
free(unres->type);
free(unres);
}
if (parsed) {
*parsed = r;
}
return node;
}