blob: 9a3e93f773f9a1f86a6538a5680861a087a38df2 [file] [log] [blame]
/**
* @file validation.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief Data tree validation functions
*
* Copyright (c) 2015 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 <stdlib.h>
#include <string.h>
#include "common.h"
#include "validation.h"
#include "libyang.h"
#include "xpath.h"
#include "parser.h"
#include "resolve.h"
#include "tree_internal.h"
#include "xml_internal.h"
int
lyv_keys(const struct lyd_node *list)
{
struct lyd_node *child;
struct lys_node_list *schema = (struct lys_node_list *)list->schema; /* shortcut */
int i;
for (i = 0, child = list->child; i < schema->keys_size; i++, child = child->next) {
if (!child || child->schema != (struct lys_node *)schema->keys[i]) {
/* key not found on the correct place */
LOGVAL(LYE_MISSELEM, LY_VLOG_LYD, list, schema->keys[i]->name, schema->name);
for ( ; child; child = child->next) {
if (child->schema == (struct lys_node *)schema->keys[i]) {
LOGVAL(LYE_SPEC, LY_VLOG_LYD, child, "Invalid position of the key element.");
break;
}
}
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
int
lyv_data_context(const struct lyd_node *node, int options, struct unres_data *unres)
{
const struct lys_node *siter = NULL;
assert(node);
assert(unres);
/* check if the node instance is enabled by if-feature */
if (lys_is_disabled(node->schema, 2)) {
LOGVAL(LYE_INELEM, LY_VLOG_LYD, node, node->schema->name);
return EXIT_FAILURE;
}
/* check leafref/instance-identifier */
if ((node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) &&
!(options & (LYD_OPT_EDIT | LYD_OPT_GET | LYD_OPT_GETCONFIG))) {
/* remove possible unres flags from type */
((struct lyd_node_leaf_list *)node)->value_type &= LY_DATA_TYPE_MASK;
/* if leafref or instance-identifier, store the node for later resolving */
if (((struct lyd_node_leaf_list *)node)->value_type == LY_TYPE_LEAFREF &&
!((struct lyd_node_leaf_list *)node)->value.leafref) {
if (unres_data_add(unres, (struct lyd_node *)node, UNRES_LEAFREF)) {
return EXIT_FAILURE;
}
} else if (((struct lyd_node_leaf_list *)node)->value_type == LY_TYPE_INST) {
if (unres_data_add(unres, (struct lyd_node *)node, UNRES_INSTID)) {
return EXIT_FAILURE;
}
}
}
/* check all relevant when conditions */
if ((!(options & LYD_OPT_TYPEMASK) || (options & LYD_OPT_CONFIG)) && (node->when_status & LYD_WHEN)) {
if (unres_data_add(unres, (struct lyd_node *)node, UNRES_WHEN)) {
return EXIT_FAILURE;
}
}
/* check for (non-)presence of status data in edit-config data */
if ((options & (LYD_OPT_EDIT | LYD_OPT_GETCONFIG | LYD_OPT_CONFIG)) && (node->schema->flags & LYS_CONFIG_R)) {
LOGVAL(LYE_INELEM, LY_VLOG_LYD, node, node->schema->name);
return EXIT_FAILURE;
}
/* check elements order in case of RPC's input and output */
if (node->validity && lyp_is_rpc_action(node->schema)) {
if ((node->prev != node) && node->prev->next) {
for (siter = lys_getnext(node->schema, lys_parent(node->schema), node->schema->module, 0);
siter;
siter = lys_getnext(siter, lys_parent(siter), siter->module, 0)) {
if (siter == node->prev->schema) {
/* data predecessor has the schema node after
* the schema node of the data node being checked */
LOGVAL(LYE_INORDER, LY_VLOG_LYD, node, node->schema->name, siter->name);
return EXIT_FAILURE;
}
}
}
}
return EXIT_SUCCESS;
}
int
lyv_data_content(struct lyd_node *node, int options, struct unres_data *unres)
{
const struct lys_node *schema, *siter;
struct lyd_node *diter, *start;
struct lys_ident *ident;
struct lys_tpdf *tpdf;
assert(node);
assert(node->schema);
assert(unres);
schema = node->schema; /* shortcut */
if (node->validity) {
/* check presence and correct order of all keys in case of list */
if (schema->nodetype == LYS_LIST && !(options & (LYD_OPT_GET | LYD_OPT_GETCONFIG))) {
if (lyv_keys(node)) {
return EXIT_FAILURE;
}
}
/* mandatory children */
if ((schema->nodetype & (LYS_CONTAINER | LYS_LIST))
&& !(options & (LYD_OPT_EDIT | LYD_OPT_GET | LYD_OPT_GETCONFIG | LYD_OPT_ACTION))) {
if (ly_check_mandatory(node, NULL, (options & LYD_OPT_TYPEMASK) ? 0 : 1, (options & LYD_OPT_RPCREPLY) ? 1 : 0)) {
return EXIT_FAILURE;
}
}
/* get the first sibling */
if (node->parent) {
start = node->parent->child;
} else {
for (start = node; start->prev->next; start = start->prev);
}
/* keep this check the last since in case of filter it affects the data and can modify the tree */
/* check number of instances (similar to list uniqueness) for non-list nodes */
if (schema->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_ANYXML)) {
/* find duplicity */
for (diter = start; diter; diter = diter->next) {
if (diter->schema == schema && diter != node) {
LOGVAL(LYE_TOOMANY, LY_VLOG_LYD, node, schema->name,
lys_parent(schema) ? lys_parent(schema)->name : "data tree");
return EXIT_FAILURE;
}
}
} else if (schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
/* uniqueness of list/leaflist instances */
/* get the first list/leaflist instance sibling */
if (options & (LYD_OPT_GET | LYD_OPT_GETCONFIG)) {
/* skip key uniqueness check in case of get/get-config data */
start = NULL;
} else {
diter = start;
start = NULL;
while(diter) {
if (diter == node) {
diter = diter->next;
continue;
}
if (diter->schema == node->schema) {
/* the same list instance */
start = diter;
break;
}
diter = diter->next;
}
}
/* check uniqueness of the list/leaflist instances (compare values) */
for (diter = start; diter; diter = diter->next) {
if (diter->schema != node->schema || diter == node ||
diter->validity) { /* skip comparison that will be done in future when checking diter as node */
continue;
}
if (lyd_list_equal(diter, node, 1)) { /* comparing keys and unique combinations */
return EXIT_FAILURE;
}
}
}
/* status - of the node's schema node itself and all its parents that
* cannot have their own instance (like a choice statement) */
siter = node->schema;
do {
if (((siter->flags & LYS_STATUS_MASK) == LYS_STATUS_OBSLT) && (options & LYD_OPT_OBSOLETE)) {
LOGVAL(LYE_OBSDATA, LY_VLOG_LYD, node, schema->name);
return EXIT_FAILURE;
}
siter = lys_parent(siter);
} while (siter && !(siter->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML)));
/* status of the identity value */
if (schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
if (options & LYD_OPT_OBSOLETE) {
/* check that we are not instantiating obsolete type */
tpdf = ((struct lys_node_leaf *)node->schema)->type.der;
while (tpdf) {
if ((tpdf->flags & LYS_STATUS_MASK) == LYS_STATUS_OBSLT) {
LOGVAL(LYE_OBSTYPE, LY_VLOG_LYD, node, schema->name, tpdf->name);
return EXIT_FAILURE;
}
tpdf = tpdf->type.der;
}
}
if (((struct lyd_node_leaf_list *)node)->value_type == LY_TYPE_IDENT) {
ident = ((struct lyd_node_leaf_list *)node)->value.ident;
if (lyp_check_status(schema->flags, schema->module, schema->name,
ident->flags, ident->module, ident->name, NULL)) {
LOGPATH(LY_VLOG_LYD, node);
return EXIT_FAILURE;
}
}
}
}
/* check must conditions */
if (resolve_applies_must(node) && unres_data_add(unres, node, UNRES_MUST) == -1) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}