blob: d63b4a041616dd7ed3468b714b583fc28d10d293 [file] [log] [blame]
Radek Krejcib1c12512015-08-11 11:22:04 +02001/**
2 * @file validation.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief Data tree validation functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 */
21
Radek Krejcieab784a2015-08-27 09:56:53 +020022#include <assert.h>
Radek Krejcib1c12512015-08-11 11:22:04 +020023#include <stdlib.h>
24
25#include "libyang.h"
Radek Krejcieab784a2015-08-27 09:56:53 +020026#include "common.h"
Radek Krejcib1c12512015-08-11 11:22:04 +020027
Radek Krejcieab784a2015-08-27 09:56:53 +020028static struct lys_node_leaf *
Radek Krejcib1c12512015-08-11 11:22:04 +020029lyv_keys_present(struct lyd_node_list *list)
30{
31 struct lyd_node *aux;
32 struct lys_node_list *schema;
33 int i;
34
35 schema = (struct lys_node_list *)list->schema;
36
37 for (i = 0; i < schema->keys_size; i++) {
38 for (aux = list->child; aux; aux = aux->next) {
39 if (aux->schema == (struct lys_node *)schema->keys[i]) {
40 break;
41 }
42 }
43 if (!aux) {
44 /* key not found in the data */
Radek Krejci1073cc02015-08-12 20:37:39 +020045 return schema->keys[i];
Radek Krejcib1c12512015-08-11 11:22:04 +020046 }
47 }
48
49 return EXIT_SUCCESS;
50}
Radek Krejcieab784a2015-08-27 09:56:53 +020051
52int
53lyv_data_context(struct lys_node *schema, unsigned int line, int options)
54{
55 assert(schema);
56
57 /* check if the node instance is enabled by if-feature */
58 if (lys_is_disabled(schema, 2)) {
59 LOGVAL(LYE_INELEM, line, schema->name);
60 return EXIT_FAILURE;
61 }
62
63 /* check for (non-)presence of status data in edit-config data */
64 if ((options & LYD_OPT_EDIT) && (schema->flags & LYS_CONFIG_R)) {
65 LOGVAL(LYE_INELEM, line, schema->name);
66 return EXIT_FAILURE;
67 }
68
69 return EXIT_SUCCESS;
70}
71int
72lyv_data_content(struct lyd_node *node, unsigned int line, int options)
73{
74 struct lys_node *schema, *siter;
75 struct lys_node *cs, *ch;
76 struct lyd_node *diter, *start;
77
78 assert(node);
79 assert(node->schema);
80
81 schema = node->schema; /* shortcut */
82
83 /* check presence of all keys in case of list */
84 if (schema->nodetype == LYS_LIST && !(options & LYD_OPT_FILTER)) {
85 siter = (struct lys_node *)lyv_keys_present((struct lyd_node_list *)node);
86 if (siter) {
87 /* key not found in the data */
88 LOGVAL(LYE_MISSELEM, line, siter->name, schema->name);
89 return EXIT_FAILURE;
90 }
91 }
92
93 /* mandatory children */
94 if ((schema->nodetype & (LYS_CONTAINER | LYS_LIST)) && !(options & (LYD_OPT_FILTER | LYD_OPT_EDIT))) {
95 siter = ly_check_mandatory(node);
96 if (siter) {
97 if (siter->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
98 LOGVAL(LYE_SPEC, line, "Number of \"%s\" instances in \"%s\" does not follow min/max constraints.",
99 siter->name, siter->parent->name);
100 } else {
101 LOGVAL(LYE_MISSELEM, line, siter->name, siter->parent->name);
102 }
103 return EXIT_FAILURE;
104 }
105 }
106
107 /* get the first sibling */
108 if (node->parent) {
109 start = node->parent->child;
110 } else {
111 for (start = node; start->prev->next; start = start->prev);
112 }
113
114 /* check number of instances for non-list nodes */
115 if (schema->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_ANYXML)) {
116 /* find duplicity */
117 for (diter = start; diter; diter = diter->next) {
118 if (diter->schema == schema && diter != node) {
119 if (options & LYD_OPT_FILTER) {
120 /* normalize the filter if needed */
121 switch (schema->nodetype) {
122 case LYS_CONTAINER:
123 if (!diter->child) {
124 /* previous instance is a selection node, so keep it
125 * and ignore the current instance - failure is returned
126 * but no ly_errno is set */
127 return EXIT_FAILURE;
128 }
129 if (!node->child) {
130 /* current instance is a selection node, so make the
131 * previous instance a a selection node (remove its
132 * children) and ignore the current instance */
133 while(diter->child) {
134 lyd_free(diter->child);
135 }
136 /* failure is returned but no ly_errno is set */
137 return EXIT_FAILURE;
138 }
139 /* TODO merging container used as a containment node */
140 break;
141 case LYS_LEAF:
142 if (((struct lyd_node_leaf *)diter)->value_str == ((struct lyd_node_leaf *)node)->value_str) {
143 /* failure is returned but no ly_errno is set */
144 return EXIT_FAILURE;
145 }
146 break;
147 case LYS_ANYXML:
148 /* filtering according to the anyxml content is not allowed,
149 * so anyxml is always a selection node with no content.
150 * Therefore multiple instances of anyxml does not make sense
151 */
152 /* failure is returned but no ly_errno is set */
153 return EXIT_FAILURE;
154 default:
155 /* not possible, but necessary to silence compiler warnings */
156 break;
157 }
158 /* we are done */
159 break;
160 } else {
161 LOGVAL(LYE_TOOMANY, line, schema->name, schema->parent ? schema->parent->name : "data tree");
162 return EXIT_FAILURE;
163 }
164 }
165 }
166 }
167
168 /* check that there are no data from different choice case */
169 if (!(options & LYD_OPT_FILTER) && schema->parent && (schema->parent->nodetype & (LYS_CASE | LYS_CHOICE))) {
170 if (schema->parent->nodetype == LYS_CHOICE) {
171 cs = NULL;
172 ch = schema->parent;
173 } else { /* schema->parent->nodetype == LYS_CASE */
174 cs = schema->parent;
175 ch = schema->parent->parent;
176 }
177 if (ch->parent && ch->parent->nodetype == LYS_CASE) {
178 /* TODO check schemas with a choice inside a case */
179 LOGWRN("Not checking parent branches of nested choice");
180 }
181 for (diter = start; diter; diter = diter->next) {
182 if (diter == node) {
183 continue;
184 }
185
186 if ((diter->schema->parent->nodetype == LYS_CHOICE && diter->schema->parent == ch) ||
187 (diter->schema->parent->nodetype == LYS_CASE && !cs) ||
188 (diter->schema->parent->nodetype == LYS_CASE && cs && diter->schema->parent != cs && diter->schema->parent->parent == ch)) {
189 LOGVAL(LYE_MCASEDATA, line, ch->name);
190 return EXIT_FAILURE;
191 }
192 }
193 }
194
195 /* uniqueness of (leaf-)list instances */
196 if (schema->nodetype == LYS_LEAFLIST) {
197 /* get the first leaf-list instance sibling */
198 for (start = node;
199 ((struct lyd_node_leaflist *)start)->lprev;
200 start = (struct lyd_node *)((struct lyd_node_leaflist *)start)->lprev);
201
202 /* check uniqueness of the leaf-list instances (compare values) */
203 for (diter = start; diter; diter = (struct lyd_node *)((struct lyd_node_leaflist *)diter)->lnext) {
204 if (diter == node) {
205 continue;
206 }
207
208 if (!lyd_compare(diter, node, 0)) {
209 if (options & LYD_OPT_FILTER) {
210 /* optimize filter and do not duplicate the same selection node,
211 * so this is not actually error, but the data are silently removed */
212 ((struct lyd_node_leaflist *)node)->lprev->lnext = NULL;
213 /* failure is returned but no ly_errno is set */
214 return EXIT_FAILURE;
215 } else {
216 LOGVAL(LYE_DUPLEAFLIST, line, schema->name, ((struct lyd_node_leaflist *)node)->value_str);
217 return EXIT_FAILURE;
218 }
219 }
220 }
221 } else if (schema->nodetype == LYS_LIST) {
222 /* get the first list instance sibling */
223 for (start = node;
224 ((struct lyd_node_list *)start)->lprev;
225 start = (struct lyd_node *)((struct lyd_node_list *)start)->lprev);
226
227 /* check uniqueness of the list instances */
228 for (diter = start; diter; diter = (struct lyd_node *)((struct lyd_node_list *)diter)->lnext) {
229 if (diter == node) {
230 continue;
231 }
232
233 if (options & LYD_OPT_FILTER) {
234 /* compare content match nodes */
235 if (!lyd_filter_compare(diter, node)) {
236 /* merge both nodes */
237 /* add selection and containment nodes from result into the diter,
238 * but only in case the diter already contains some selection nodes,
239 * otherwise it already will return all the data */
240 lyd_filter_merge(diter, node);
241
242 /* not the error, just return no data */
243 ((struct lyd_node_list *)node)->lprev->lnext = NULL;
244 /* failure is returned but no ly_errno is set */
245 return EXIT_FAILURE;
246 }
247 } else {
248 /* compare keys and unique combinations */
249 if (!lyd_compare(diter, node, 1)) {
250 LOGVAL(LYE_DUPLIST, line, schema->name);
251 return EXIT_FAILURE;
252 }
253 }
254 }
255 }
256
257 return EXIT_SUCCESS;
258}