blob: 4482f2df832fc12a882970d089fb596aa22d6b6b [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>
Michal Vaskocf024702015-10-08 15:01:42 +020024#include <string.h>
Radek Krejcib1c12512015-08-11 11:22:04 +020025
Michal Vaskocf024702015-10-08 15:01:42 +020026#include "validation.h"
Radek Krejcib1c12512015-08-11 11:22:04 +020027#include "libyang.h"
Michal Vaskocf024702015-10-08 15:01:42 +020028#include "xpath.h"
29#include "resolve.h"
30#include "tree_internal.h"
Radek Krejci3e0addb2015-08-27 16:37:59 +020031#include "xml.h"
Radek Krejcieab784a2015-08-27 09:56:53 +020032#include "common.h"
Radek Krejcib1c12512015-08-11 11:22:04 +020033
Radek Krejcieab784a2015-08-27 09:56:53 +020034static struct lys_node_leaf *
Radek Krejci27aaa732015-09-04 15:24:04 +020035lyv_keys_present(struct lyd_node *list)
Radek Krejcib1c12512015-08-11 11:22:04 +020036{
37 struct lyd_node *aux;
38 struct lys_node_list *schema;
39 int i;
40
41 schema = (struct lys_node_list *)list->schema;
42
43 for (i = 0; i < schema->keys_size; i++) {
44 for (aux = list->child; aux; aux = aux->next) {
45 if (aux->schema == (struct lys_node *)schema->keys[i]) {
46 break;
47 }
48 }
49 if (!aux) {
50 /* key not found in the data */
Radek Krejci1073cc02015-08-12 20:37:39 +020051 return schema->keys[i];
Radek Krejcib1c12512015-08-11 11:22:04 +020052 }
53 }
54
55 return EXIT_SUCCESS;
56}
Radek Krejcieab784a2015-08-27 09:56:53 +020057
Radek Krejci3e0addb2015-08-27 16:37:59 +020058/**
59 * @brief Compare filter nodes
60 *
61 * @param[in] first The first data node to compare
62 * @param[in] second The second node to compare
63 * @return 0 if both filter nodes selects the same data.
64 */
65static int
66filter_compare(struct lyd_node *first, struct lyd_node *second)
67{
68 struct lyd_node *diter1, *diter2;
69 int match, c1, c2;
70
71 assert(first);
72 assert(second);
73
74 if (first->schema != second->schema) {
75 return 1;
76 }
77
78
79 switch (first->schema->nodetype) {
80 case LYS_CONTAINER:
81 case LYS_LIST:
82 /* check if all the content match nodes are the same */
83 c1 = 0;
84 LY_TREE_FOR(first->child, diter1) {
85 if (!(diter1->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
86 continue;
87 } else if (!((struct lyd_node_leaf *)diter1)->value_str) {
88 /* selection node */
89 continue;
90 }
91
92 match = 0;
93 LY_TREE_FOR(second->child, diter2) {
94 if (diter2->schema != diter1->schema) {
95 continue;
96 } else if (((struct lyd_node_leaf *)diter1)->value_str != ((struct lyd_node_leaf *)diter2)->value_str) {
97 continue;
98 }
99 match = 1;
100 c1++;
101 }
102 if (!match) {
103 return 1;
104 }
105 }
106 /* get number of content match nodes in the second to get know if there are some
107 * that are not present in first
108 */
109 c2 = 0;
110 LY_TREE_FOR(second->child, diter2) {
111 if (!(diter2->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
112 continue;
113 } else if (!((struct lyd_node_leaf *)diter2)->value_str) {
114 /* selection node */
115 continue;
116 }
117 c2++;
118 }
119 if (c1 != c2) {
120 return 1;
121 }
122 break;
123 case LYS_LEAF:
124 case LYS_LEAFLIST:
125 if (((struct lyd_node_leaf *)first)->value_str != ((struct lyd_node_leaf *)second)->value_str) {
126 return 1;
127 }
128 break;
129 default:
130 /* no more tests are needed */
131 break;
132 }
133 return 0;
134}
135
136static int
137filter_merge(struct lyd_node *to, struct lyd_node *from)
138{
139 struct lyd_node *diter1, *diter2;
140 unsigned int i, j;
141 struct lyd_set *s1 = NULL, *s2 = NULL;
142 int copy;
143
144 if (!to || !from || to->schema != from->schema) {
145 ly_errno = LY_EINVAL;
146 return EXIT_FAILURE;
147 }
148
149 switch(to->schema->nodetype) {
150 case LYS_LIST:
151 case LYS_CONTAINER:
152 if (!from->child) {
153 /* from is selection node, so we want to make the to selection node now */
154 while (to->child) {
155 lyd_free(to->child);
156 }
157 } else if (to->child) {
158 /* both to and from are containment nodes and it was already checked
159 * (by calling filter_compare()) that they selects the same target.
160 * Therefore we can skip the content match nodes (they are the same in
161 * both of them) and merge only the selection and containment nodes */
162
163 /* first, get know if to and from contain some selection or containment
164 * nodes. Because if one of them does not contain any such a node it
165 * selects all the data so it does not make sense to limit it by any
166 * selection/containment node.
167 */
168 s1 = lyd_set_new();
169 s2 = lyd_set_new();
170 LY_TREE_FOR(to->child, diter1) {
171 /* is selection node */
172 if ((diter1->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) && !((struct lyd_node_leaf *)diter1)->value_str) {
173 lyd_set_add(s1, diter1);
174 } else if ((diter1->schema->nodetype == LYS_ANYXML) && !((struct lyd_node_anyxml *)diter1)->value->child) {
175 lyd_set_add(s1, diter1);
176 } else if (diter1->schema->nodetype & (LYS_CONTAINER | LYS_LIST)) {
177 /* or containment node */
178 lyd_set_add(s1, diter1);
179 }
180 }
181
182 LY_TREE_FOR(from->child, diter2) {
183 /* is selection node */
184 if ((diter2->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) && !((struct lyd_node_leaf *)diter2)->value_str) {
185 lyd_set_add(s2, diter2);
186 } else if ((diter2->schema->nodetype == LYS_ANYXML) && !((struct lyd_node_anyxml *)diter2)->value->child) {
187 lyd_set_add(s2, diter2);
188 } else if (diter2->schema->nodetype & (LYS_CONTAINER | LYS_LIST)) {
189 /* or containment node */
190 lyd_set_add(s2, diter2);
191 }
192 }
193
194 if (!s1->number) {
195 /* to already selects all content, so nothing is needed */
196 break;
197 } else if (!s2->number) {
198 /* from selects all content, so make to select it too by
199 * removing all selection and containment nodes
200 */
201 for (i = 0; i < s1->number; i++) {
202 lyd_free(s1->set[i]);
203 }
204 break;
205 } else {
206 /* both contain some selection or containment node(s), so merge them */
207 for (j = 0; j < s2->number; j++) { /* from */
208 copy = 0;
209 for (i = 0; i < s1->number; i++) { /* to */
210 if (s1->set[i]->schema != s2->set[j]->schema) {
211 continue;
212 }
213
214 /* we have something similar to diter1, explore it more */
215 switch (s2->set[j]->schema->nodetype) {
216 case LYS_LIST:
217 case LYS_CONTAINER:
218 if (!filter_compare(s2->set[j], s1->set[i])) {
219 /* merge the two containers into the to */
220 filter_merge(s1->set[i], s2->set[j]);
221 } else {
222 /* check that some of them is not a selection node */
223 if (!s2->set[j]->child) {
224 /* from is selection node, so keep only it because to selects subset */
225 lyd_free(s1->set[i]);
226 /* set the flag to copy the from child at the end */
227 copy = 1;
228 continue;
229 } else if (!s1->set[i]->child) {
230 /* to is already selection node, so ignore the from child */
231 } else {
232 /* they are different so keep trying to search for some other matching instance */
233 continue;
234 }
235 }
236
237 break;
238 case LYS_ANYXML:
239 case LYS_LEAFLIST:
240 case LYS_LEAF:
241 /* here it can be only a selection node, so do not duplicate it (keep i < s1->number) */
242 break;
243 default:
244 /* keep compiler silent */
245 break;
246 }
247
248 /* we have a match, so do not duplicate the current from child and go to check next from child */
249 /* i < s1->number */
250 break;
251 }
252
253 if (copy || i == s1->number) {
254 /* the node is not yet present in to, so move it there */
255 lyd_unlink(s2->set[j]);
256 if (to->child) {
257 to->child->prev->next = s2->set[j];
258 s2->set[j]->prev = to->child->prev;
259 to->child->prev = s2->set[j];
260 } else {
261 to->child = s2->set[j];
262 }
263 s2->set[j]->parent = to;
264 }
265 }
266 }
267 } /* else from is empty, so nothing to do */
268
269 break;
270
271 default:
272 /* no other type needed to cover,
273 * keep the default branch to make compiler silent */
274 break;
275 }
276
277 lyd_set_free(s1);
278 lyd_set_free(s2);
279
280 return EXIT_SUCCESS;
281}
282
Radek Krejcieab784a2015-08-27 09:56:53 +0200283int
Michal Vaskocf024702015-10-08 15:01:42 +0200284lyv_data_context(struct lyd_node *node, int options, unsigned int line, struct unres_data *unres)
Radek Krejcieab784a2015-08-27 09:56:53 +0200285{
Michal Vaskocf024702015-10-08 15:01:42 +0200286 assert(node);
Radek Krejcieab784a2015-08-27 09:56:53 +0200287
288 /* check if the node instance is enabled by if-feature */
Michal Vaskocf024702015-10-08 15:01:42 +0200289 if (lys_is_disabled(node->schema, 2)) {
290 LOGVAL(LYE_INELEM, line, node->schema->name);
291 return EXIT_FAILURE;
292 }
293
294 /* check all relevant when conditions */
295 if (unres_data_add(unres, node, UNRES_WHEN, line) == -1) {
Radek Krejcieab784a2015-08-27 09:56:53 +0200296 return EXIT_FAILURE;
297 }
298
299 /* check for (non-)presence of status data in edit-config data */
Michal Vaskocf024702015-10-08 15:01:42 +0200300 if ((options & LYD_OPT_EDIT) && (node->schema->flags & LYS_CONFIG_R)) {
301 LOGVAL(LYE_INELEM, line, node->schema->name);
Radek Krejcieab784a2015-08-27 09:56:53 +0200302 return EXIT_FAILURE;
303 }
304
305 return EXIT_SUCCESS;
306}
Michal Vaskocf024702015-10-08 15:01:42 +0200307
Radek Krejcieab784a2015-08-27 09:56:53 +0200308int
Michal Vaskocf024702015-10-08 15:01:42 +0200309lyv_data_content(struct lyd_node *node, int options, unsigned int line, struct unres_data *unres)
Radek Krejcieab784a2015-08-27 09:56:53 +0200310{
311 struct lys_node *schema, *siter;
312 struct lys_node *cs, *ch;
313 struct lyd_node *diter, *start;
314
315 assert(node);
316 assert(node->schema);
317
318 schema = node->schema; /* shortcut */
319
320 /* check presence of all keys in case of list */
321 if (schema->nodetype == LYS_LIST && !(options & LYD_OPT_FILTER)) {
Radek Krejci27aaa732015-09-04 15:24:04 +0200322 siter = (struct lys_node *)lyv_keys_present(node);
Radek Krejcieab784a2015-08-27 09:56:53 +0200323 if (siter) {
324 /* key not found in the data */
325 LOGVAL(LYE_MISSELEM, line, siter->name, schema->name);
326 return EXIT_FAILURE;
327 }
328 }
329
330 /* mandatory children */
331 if ((schema->nodetype & (LYS_CONTAINER | LYS_LIST)) && !(options & (LYD_OPT_FILTER | LYD_OPT_EDIT))) {
332 siter = ly_check_mandatory(node);
333 if (siter) {
334 if (siter->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
335 LOGVAL(LYE_SPEC, line, "Number of \"%s\" instances in \"%s\" does not follow min/max constraints.",
336 siter->name, siter->parent->name);
337 } else {
338 LOGVAL(LYE_MISSELEM, line, siter->name, siter->parent->name);
339 }
340 return EXIT_FAILURE;
341 }
342 }
343
344 /* get the first sibling */
345 if (node->parent) {
346 start = node->parent->child;
347 } else {
348 for (start = node; start->prev->next; start = start->prev);
349 }
350
Radek Krejcieab784a2015-08-27 09:56:53 +0200351 /* check that there are no data from different choice case */
Radek Krejci37bda002015-08-27 11:23:56 +0200352 if (!(options & LYD_OPT_FILTER)) {
353 /* init loop condition */
354 ch = schema;
355
356 while (ch->parent && (ch->parent->nodetype & (LYS_CASE | LYS_CHOICE))) {
357 if (ch->parent->nodetype == LYS_CHOICE) {
358 cs = NULL;
359 ch = ch->parent;
360 } else { /* ch->parent->nodetype == LYS_CASE */
361 cs = ch->parent;
362 ch = ch->parent->parent;
Radek Krejcieab784a2015-08-27 09:56:53 +0200363 }
364
Radek Krejci37bda002015-08-27 11:23:56 +0200365 for (diter = start; diter; diter = diter->next) {
366 if (diter == node) {
367 continue;
368 }
369
370 /* find correct level to compare */
371 for (siter = diter->schema->parent; siter; siter = siter->parent) {
372 if (siter->nodetype == LYS_CHOICE) {
373 if (siter == ch) {
374 LOGVAL(LYE_MCASEDATA, line, ch->name);
375 return EXIT_FAILURE;
376 } else {
377 continue;
378 }
379 }
380
381 if (siter->nodetype == LYS_CASE) {
382 if (siter->parent != ch) {
383 continue;
384 } else if (!cs || cs != siter) {
385 LOGVAL(LYE_MCASEDATA, line, ch->name);
386 return EXIT_FAILURE;
387 }
388 }
389
390 /* diter is from something else choice (subtree) */
391 break;
392 }
Radek Krejcieab784a2015-08-27 09:56:53 +0200393 }
394 }
395 }
396
Radek Krejci3e0addb2015-08-27 16:37:59 +0200397 /* keep this check the last since in case of filter it affects the data and can modify the tree */
398 /* check number of instances (similar to list uniqueness) for non-list nodes */
399 if (schema->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_ANYXML)) {
400 /* find duplicity */
401 for (diter = start; diter; diter = diter->next) {
402 if (diter->schema == schema && diter != node) {
403 if (options & LYD_OPT_FILTER) {
404 /* normalize the filter if needed */
405 switch (schema->nodetype) {
406 case LYS_CONTAINER:
407 if (!filter_compare(diter, node)) {
408 /* merge the two containers, diter will be kept ... */
409 filter_merge(diter, node);
410 /* ... and node will be removed (ly_errno is not set) */
411 return EXIT_FAILURE;
412 } else {
413 /* check that some of them is not a selection node */
414 if (!diter->child) {
415 /* keep diter since it selects all such containers
416 * and let remove the node since it selects just a subset */
417 return EXIT_FAILURE;
418 } else if (!node->child) {
419 /* keep the node and remove diter since it selects subset
420 * of what is selected by node */
421 lyd_free(diter);
422 }
423 /* keep them as they are */
424 return EXIT_SUCCESS;
425 }
426 break;
427 case LYS_LEAF:
428 if (!((struct lyd_node_leaf *)diter)->value_str && ((struct lyd_node_leaf *)node)->value_str) {
429 /* the first instance is selection node but the new instance is content match node ->
430 * since content match node also works as selection node. keep only the new instance
431 */
432 lyd_free(diter);
433 /* return success to keep the node in the tree */
434 return EXIT_SUCCESS;
435 } else if (!((struct lyd_node_leaf *)node)->value_str ||
436 ((struct lyd_node_leaf *)diter)->value_str == ((struct lyd_node_leaf *)node)->value_str) {
437 /* keep the previous instance and remove the current one ->
438 * return failure but do not set ly_errno */
439 return EXIT_FAILURE;
440 }
441 break;
442 case LYS_ANYXML:
443 /* filtering according to the anyxml content is not allowed,
444 * so anyxml is always a selection node with no content.
445 * Therefore multiple instances of anyxml does not make sense
446 */
447 /* failure is returned but no ly_errno is set */
448 return EXIT_FAILURE;
449 default:
450 /* not possible, but necessary to silence compiler warnings */
451 break;
452 }
453 /* we are done */
454 break;
455 } else {
456 LOGVAL(LYE_TOOMANY, line, schema->name, schema->parent ? schema->parent->name : "data tree");
457 return EXIT_FAILURE;
458 }
459 }
460 }
Radek Krejci27aaa732015-09-04 15:24:04 +0200461 } else if (schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
462 /* uniqueness of list/leaflist instances */
Radek Krejci3e0addb2015-08-27 16:37:59 +0200463
Radek Krejci27aaa732015-09-04 15:24:04 +0200464 /* get the first list/leaflist instance sibling */
465 if (node->parent) {
466 start = node->parent->child;
467 } else {
468 start = NULL;
469 for (diter = node; diter->prev->next; diter = diter->prev) {
470 if (diter->schema == node->schema) {
471 /* the same list instance */
472 start = diter;
Radek Krejci3e0addb2015-08-27 16:37:59 +0200473 }
Radek Krejcieab784a2015-08-27 09:56:53 +0200474 }
475 }
Radek Krejci3e0addb2015-08-27 16:37:59 +0200476
Radek Krejci27aaa732015-09-04 15:24:04 +0200477 /* check uniqueness of the list/leaflist instances (compare values) */
478 for (diter = start; diter; diter = diter->next) {
479 if (diter->schema != node->schema || diter == node) {
Radek Krejcieab784a2015-08-27 09:56:53 +0200480 continue;
481 }
482
483 if (options & LYD_OPT_FILTER) {
484 /* compare content match nodes */
Radek Krejci3e0addb2015-08-27 16:37:59 +0200485 if (!filter_compare(diter, node)) {
Radek Krejcieab784a2015-08-27 09:56:53 +0200486 /* merge both nodes */
487 /* add selection and containment nodes from result into the diter,
488 * but only in case the diter already contains some selection nodes,
489 * otherwise it already will return all the data */
Radek Krejci3e0addb2015-08-27 16:37:59 +0200490 filter_merge(diter, node);
Radek Krejcieab784a2015-08-27 09:56:53 +0200491
492 /* not the error, just return no data */
Radek Krejcieab784a2015-08-27 09:56:53 +0200493 /* failure is returned but no ly_errno is set */
494 return EXIT_FAILURE;
Radek Krejci27aaa732015-09-04 15:24:04 +0200495 } else if (node->schema->nodetype == LYS_LEAFLIST) {
496 /* in contrast to lists, leaflists can be still safely optimized if one of them
497 * is selection node. In that case wee need to keep the other node, which is content
498 * match node and it somehow limit the data to be filtered.
499 */
500 if (!((struct lyd_node_leaflist *)diter)->value_str) {
501 /* the other instance is selection node, keep the new one whatever it is */
502 lyd_free(diter);
503 break;
504 } else if (!((struct lyd_node_leaflist *)node)->value_str) {
505 /* the new instance is selection node, keep the previous instance which is
506 * content match node */
507 /* failure is returned but no ly_errno is set */
508 return EXIT_FAILURE;
509 }
Radek Krejcieab784a2015-08-27 09:56:53 +0200510 }
Radek Krejci27aaa732015-09-04 15:24:04 +0200511 } else if (!lyd_compare(diter, node, 1)) { /* comparing keys and unique combinations */
512 LOGVAL(LYE_DUPLIST, line, schema->name);
513 return EXIT_FAILURE;
Radek Krejcieab784a2015-08-27 09:56:53 +0200514 }
515 }
516 }
517
518 return EXIT_SUCCESS;
519}