blob: c11c910a9b81cb09da91aa38715f57fef42e7b7b [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 Krejci3e0addb2015-08-27 16:37:59 +020026#include "xml.h"
Radek Krejcieab784a2015-08-27 09:56:53 +020027#include "common.h"
Radek Krejcib1c12512015-08-11 11:22:04 +020028
Radek Krejcieab784a2015-08-27 09:56:53 +020029static struct lys_node_leaf *
Radek Krejci27aaa732015-09-04 15:24:04 +020030lyv_keys_present(struct lyd_node *list)
Radek Krejcib1c12512015-08-11 11:22:04 +020031{
32 struct lyd_node *aux;
33 struct lys_node_list *schema;
34 int i;
35
36 schema = (struct lys_node_list *)list->schema;
37
38 for (i = 0; i < schema->keys_size; i++) {
39 for (aux = list->child; aux; aux = aux->next) {
40 if (aux->schema == (struct lys_node *)schema->keys[i]) {
41 break;
42 }
43 }
44 if (!aux) {
45 /* key not found in the data */
Radek Krejci1073cc02015-08-12 20:37:39 +020046 return schema->keys[i];
Radek Krejcib1c12512015-08-11 11:22:04 +020047 }
48 }
49
50 return EXIT_SUCCESS;
51}
Radek Krejcieab784a2015-08-27 09:56:53 +020052
Radek Krejci3e0addb2015-08-27 16:37:59 +020053/**
54 * @brief Compare filter nodes
55 *
56 * @param[in] first The first data node to compare
57 * @param[in] second The second node to compare
58 * @return 0 if both filter nodes selects the same data.
59 */
60static int
61filter_compare(struct lyd_node *first, struct lyd_node *second)
62{
63 struct lyd_node *diter1, *diter2;
64 int match, c1, c2;
65
66 assert(first);
67 assert(second);
68
69 if (first->schema != second->schema) {
70 return 1;
71 }
72
73
74 switch (first->schema->nodetype) {
75 case LYS_CONTAINER:
76 case LYS_LIST:
77 /* check if all the content match nodes are the same */
78 c1 = 0;
79 LY_TREE_FOR(first->child, diter1) {
80 if (!(diter1->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
81 continue;
Michal Vasko4c183312015-09-25 10:41:47 +020082 } else if (!((struct lyd_node_leaf_list *)diter1)->value_str) {
Radek Krejci3e0addb2015-08-27 16:37:59 +020083 /* selection node */
84 continue;
85 }
86
87 match = 0;
88 LY_TREE_FOR(second->child, diter2) {
89 if (diter2->schema != diter1->schema) {
90 continue;
Michal Vasko4c183312015-09-25 10:41:47 +020091 } else if (((struct lyd_node_leaf_list *)diter1)->value_str !=
92 ((struct lyd_node_leaf_list *)diter2)->value_str) {
Radek Krejci3e0addb2015-08-27 16:37:59 +020093 continue;
94 }
95 match = 1;
96 c1++;
97 }
98 if (!match) {
99 return 1;
100 }
101 }
102 /* get number of content match nodes in the second to get know if there are some
103 * that are not present in first
104 */
105 c2 = 0;
106 LY_TREE_FOR(second->child, diter2) {
107 if (!(diter2->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
108 continue;
Michal Vasko4c183312015-09-25 10:41:47 +0200109 } else if (!((struct lyd_node_leaf_list *)diter2)->value_str) {
Radek Krejci3e0addb2015-08-27 16:37:59 +0200110 /* selection node */
111 continue;
112 }
113 c2++;
114 }
115 if (c1 != c2) {
116 return 1;
117 }
118 break;
119 case LYS_LEAF:
120 case LYS_LEAFLIST:
Michal Vasko4c183312015-09-25 10:41:47 +0200121 if (((struct lyd_node_leaf_list *)first)->value_str != ((struct lyd_node_leaf_list *)second)->value_str) {
Radek Krejci3e0addb2015-08-27 16:37:59 +0200122 return 1;
123 }
124 break;
125 default:
126 /* no more tests are needed */
127 break;
128 }
129 return 0;
130}
131
132static int
133filter_merge(struct lyd_node *to, struct lyd_node *from)
134{
135 struct lyd_node *diter1, *diter2;
136 unsigned int i, j;
137 struct lyd_set *s1 = NULL, *s2 = NULL;
138 int copy;
139
140 if (!to || !from || to->schema != from->schema) {
141 ly_errno = LY_EINVAL;
142 return EXIT_FAILURE;
143 }
144
145 switch(to->schema->nodetype) {
146 case LYS_LIST:
147 case LYS_CONTAINER:
148 if (!from->child) {
149 /* from is selection node, so we want to make the to selection node now */
150 while (to->child) {
151 lyd_free(to->child);
152 }
153 } else if (to->child) {
154 /* both to and from are containment nodes and it was already checked
155 * (by calling filter_compare()) that they selects the same target.
156 * Therefore we can skip the content match nodes (they are the same in
157 * both of them) and merge only the selection and containment nodes */
158
159 /* first, get know if to and from contain some selection or containment
160 * nodes. Because if one of them does not contain any such a node it
161 * selects all the data so it does not make sense to limit it by any
162 * selection/containment node.
163 */
164 s1 = lyd_set_new();
165 s2 = lyd_set_new();
166 LY_TREE_FOR(to->child, diter1) {
167 /* is selection node */
Michal Vasko4c183312015-09-25 10:41:47 +0200168 if ((diter1->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))
169 && !((struct lyd_node_leaf_list *)diter1)->value_str) {
Radek Krejci3e0addb2015-08-27 16:37:59 +0200170 lyd_set_add(s1, diter1);
171 } else if ((diter1->schema->nodetype == LYS_ANYXML) && !((struct lyd_node_anyxml *)diter1)->value->child) {
172 lyd_set_add(s1, diter1);
173 } else if (diter1->schema->nodetype & (LYS_CONTAINER | LYS_LIST)) {
174 /* or containment node */
175 lyd_set_add(s1, diter1);
176 }
177 }
178
179 LY_TREE_FOR(from->child, diter2) {
180 /* is selection node */
Michal Vasko4c183312015-09-25 10:41:47 +0200181 if ((diter2->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))
182 && !((struct lyd_node_leaf_list *)diter2)->value_str) {
Radek Krejci3e0addb2015-08-27 16:37:59 +0200183 lyd_set_add(s2, diter2);
184 } else if ((diter2->schema->nodetype == LYS_ANYXML) && !((struct lyd_node_anyxml *)diter2)->value->child) {
185 lyd_set_add(s2, diter2);
186 } else if (diter2->schema->nodetype & (LYS_CONTAINER | LYS_LIST)) {
187 /* or containment node */
188 lyd_set_add(s2, diter2);
189 }
190 }
191
192 if (!s1->number) {
193 /* to already selects all content, so nothing is needed */
194 break;
195 } else if (!s2->number) {
196 /* from selects all content, so make to select it too by
197 * removing all selection and containment nodes
198 */
199 for (i = 0; i < s1->number; i++) {
200 lyd_free(s1->set[i]);
201 }
202 break;
203 } else {
204 /* both contain some selection or containment node(s), so merge them */
205 for (j = 0; j < s2->number; j++) { /* from */
206 copy = 0;
207 for (i = 0; i < s1->number; i++) { /* to */
208 if (s1->set[i]->schema != s2->set[j]->schema) {
209 continue;
210 }
211
212 /* we have something similar to diter1, explore it more */
213 switch (s2->set[j]->schema->nodetype) {
214 case LYS_LIST:
215 case LYS_CONTAINER:
216 if (!filter_compare(s2->set[j], s1->set[i])) {
217 /* merge the two containers into the to */
218 filter_merge(s1->set[i], s2->set[j]);
219 } else {
220 /* check that some of them is not a selection node */
221 if (!s2->set[j]->child) {
222 /* from is selection node, so keep only it because to selects subset */
223 lyd_free(s1->set[i]);
224 /* set the flag to copy the from child at the end */
225 copy = 1;
226 continue;
227 } else if (!s1->set[i]->child) {
228 /* to is already selection node, so ignore the from child */
229 } else {
230 /* they are different so keep trying to search for some other matching instance */
231 continue;
232 }
233 }
234
235 break;
236 case LYS_ANYXML:
237 case LYS_LEAFLIST:
238 case LYS_LEAF:
239 /* here it can be only a selection node, so do not duplicate it (keep i < s1->number) */
240 break;
241 default:
242 /* keep compiler silent */
243 break;
244 }
245
246 /* we have a match, so do not duplicate the current from child and go to check next from child */
247 /* i < s1->number */
248 break;
249 }
250
251 if (copy || i == s1->number) {
252 /* the node is not yet present in to, so move it there */
253 lyd_unlink(s2->set[j]);
254 if (to->child) {
255 to->child->prev->next = s2->set[j];
256 s2->set[j]->prev = to->child->prev;
257 to->child->prev = s2->set[j];
258 } else {
259 to->child = s2->set[j];
260 }
261 s2->set[j]->parent = to;
262 }
263 }
264 }
265 } /* else from is empty, so nothing to do */
266
267 break;
268
269 default:
270 /* no other type needed to cover,
271 * keep the default branch to make compiler silent */
272 break;
273 }
274
275 lyd_set_free(s1);
276 lyd_set_free(s2);
277
278 return EXIT_SUCCESS;
279}
280
Radek Krejcieab784a2015-08-27 09:56:53 +0200281int
282lyv_data_context(struct lys_node *schema, unsigned int line, int options)
283{
284 assert(schema);
285
286 /* check if the node instance is enabled by if-feature */
287 if (lys_is_disabled(schema, 2)) {
288 LOGVAL(LYE_INELEM, line, schema->name);
289 return EXIT_FAILURE;
290 }
291
292 /* check for (non-)presence of status data in edit-config data */
293 if ((options & LYD_OPT_EDIT) && (schema->flags & LYS_CONFIG_R)) {
294 LOGVAL(LYE_INELEM, line, schema->name);
295 return EXIT_FAILURE;
296 }
297
298 return EXIT_SUCCESS;
299}
300int
301lyv_data_content(struct lyd_node *node, unsigned int line, int options)
302{
303 struct lys_node *schema, *siter;
304 struct lys_node *cs, *ch;
305 struct lyd_node *diter, *start;
306
307 assert(node);
308 assert(node->schema);
309
310 schema = node->schema; /* shortcut */
311
312 /* check presence of all keys in case of list */
313 if (schema->nodetype == LYS_LIST && !(options & LYD_OPT_FILTER)) {
Radek Krejci27aaa732015-09-04 15:24:04 +0200314 siter = (struct lys_node *)lyv_keys_present(node);
Radek Krejcieab784a2015-08-27 09:56:53 +0200315 if (siter) {
316 /* key not found in the data */
317 LOGVAL(LYE_MISSELEM, line, siter->name, schema->name);
318 return EXIT_FAILURE;
319 }
320 }
321
322 /* mandatory children */
323 if ((schema->nodetype & (LYS_CONTAINER | LYS_LIST)) && !(options & (LYD_OPT_FILTER | LYD_OPT_EDIT))) {
324 siter = ly_check_mandatory(node);
325 if (siter) {
326 if (siter->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
327 LOGVAL(LYE_SPEC, line, "Number of \"%s\" instances in \"%s\" does not follow min/max constraints.",
328 siter->name, siter->parent->name);
329 } else {
330 LOGVAL(LYE_MISSELEM, line, siter->name, siter->parent->name);
331 }
332 return EXIT_FAILURE;
333 }
334 }
335
336 /* get the first sibling */
337 if (node->parent) {
338 start = node->parent->child;
339 } else {
340 for (start = node; start->prev->next; start = start->prev);
341 }
342
Radek Krejcieab784a2015-08-27 09:56:53 +0200343 /* check that there are no data from different choice case */
Radek Krejci37bda002015-08-27 11:23:56 +0200344 if (!(options & LYD_OPT_FILTER)) {
345 /* init loop condition */
346 ch = schema;
347
348 while (ch->parent && (ch->parent->nodetype & (LYS_CASE | LYS_CHOICE))) {
349 if (ch->parent->nodetype == LYS_CHOICE) {
350 cs = NULL;
351 ch = ch->parent;
352 } else { /* ch->parent->nodetype == LYS_CASE */
353 cs = ch->parent;
354 ch = ch->parent->parent;
Radek Krejcieab784a2015-08-27 09:56:53 +0200355 }
356
Radek Krejci37bda002015-08-27 11:23:56 +0200357 for (diter = start; diter; diter = diter->next) {
358 if (diter == node) {
359 continue;
360 }
361
362 /* find correct level to compare */
363 for (siter = diter->schema->parent; siter; siter = siter->parent) {
364 if (siter->nodetype == LYS_CHOICE) {
365 if (siter == ch) {
366 LOGVAL(LYE_MCASEDATA, line, ch->name);
367 return EXIT_FAILURE;
368 } else {
369 continue;
370 }
371 }
372
373 if (siter->nodetype == LYS_CASE) {
374 if (siter->parent != ch) {
375 continue;
376 } else if (!cs || cs != siter) {
377 LOGVAL(LYE_MCASEDATA, line, ch->name);
378 return EXIT_FAILURE;
379 }
380 }
381
382 /* diter is from something else choice (subtree) */
383 break;
384 }
Radek Krejcieab784a2015-08-27 09:56:53 +0200385 }
386 }
387 }
388
Radek Krejci3e0addb2015-08-27 16:37:59 +0200389 /* keep this check the last since in case of filter it affects the data and can modify the tree */
390 /* check number of instances (similar to list uniqueness) for non-list nodes */
391 if (schema->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_ANYXML)) {
392 /* find duplicity */
393 for (diter = start; diter; diter = diter->next) {
394 if (diter->schema == schema && diter != node) {
395 if (options & LYD_OPT_FILTER) {
396 /* normalize the filter if needed */
397 switch (schema->nodetype) {
398 case LYS_CONTAINER:
399 if (!filter_compare(diter, node)) {
400 /* merge the two containers, diter will be kept ... */
401 filter_merge(diter, node);
402 /* ... and node will be removed (ly_errno is not set) */
403 return EXIT_FAILURE;
404 } else {
405 /* check that some of them is not a selection node */
406 if (!diter->child) {
407 /* keep diter since it selects all such containers
408 * and let remove the node since it selects just a subset */
409 return EXIT_FAILURE;
410 } else if (!node->child) {
411 /* keep the node and remove diter since it selects subset
412 * of what is selected by node */
413 lyd_free(diter);
414 }
415 /* keep them as they are */
416 return EXIT_SUCCESS;
417 }
418 break;
419 case LYS_LEAF:
Michal Vasko4c183312015-09-25 10:41:47 +0200420 if (!((struct lyd_node_leaf_list *)diter)->value_str
421 && ((struct lyd_node_leaf_list *)node)->value_str) {
Radek Krejci3e0addb2015-08-27 16:37:59 +0200422 /* the first instance is selection node but the new instance is content match node ->
423 * since content match node also works as selection node. keep only the new instance
424 */
425 lyd_free(diter);
426 /* return success to keep the node in the tree */
427 return EXIT_SUCCESS;
Michal Vasko4c183312015-09-25 10:41:47 +0200428 } else if (!((struct lyd_node_leaf_list *)node)->value_str
429 || ((struct lyd_node_leaf_list *)diter)->value_str ==
430 ((struct lyd_node_leaf_list *)node)->value_str) {
Radek Krejci3e0addb2015-08-27 16:37:59 +0200431 /* keep the previous instance and remove the current one ->
432 * return failure but do not set ly_errno */
433 return EXIT_FAILURE;
434 }
435 break;
436 case LYS_ANYXML:
437 /* filtering according to the anyxml content is not allowed,
438 * so anyxml is always a selection node with no content.
439 * Therefore multiple instances of anyxml does not make sense
440 */
441 /* failure is returned but no ly_errno is set */
442 return EXIT_FAILURE;
443 default:
444 /* not possible, but necessary to silence compiler warnings */
445 break;
446 }
447 /* we are done */
448 break;
449 } else {
450 LOGVAL(LYE_TOOMANY, line, schema->name, schema->parent ? schema->parent->name : "data tree");
451 return EXIT_FAILURE;
452 }
453 }
454 }
Radek Krejci27aaa732015-09-04 15:24:04 +0200455 } else if (schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
456 /* uniqueness of list/leaflist instances */
Radek Krejci3e0addb2015-08-27 16:37:59 +0200457
Radek Krejci27aaa732015-09-04 15:24:04 +0200458 /* get the first list/leaflist instance sibling */
459 if (node->parent) {
460 start = node->parent->child;
461 } else {
462 start = NULL;
463 for (diter = node; diter->prev->next; diter = diter->prev) {
464 if (diter->schema == node->schema) {
465 /* the same list instance */
466 start = diter;
Radek Krejci3e0addb2015-08-27 16:37:59 +0200467 }
Radek Krejcieab784a2015-08-27 09:56:53 +0200468 }
469 }
Radek Krejci3e0addb2015-08-27 16:37:59 +0200470
Radek Krejci27aaa732015-09-04 15:24:04 +0200471 /* check uniqueness of the list/leaflist instances (compare values) */
472 for (diter = start; diter; diter = diter->next) {
473 if (diter->schema != node->schema || diter == node) {
Radek Krejcieab784a2015-08-27 09:56:53 +0200474 continue;
475 }
476
477 if (options & LYD_OPT_FILTER) {
478 /* compare content match nodes */
Radek Krejci3e0addb2015-08-27 16:37:59 +0200479 if (!filter_compare(diter, node)) {
Radek Krejcieab784a2015-08-27 09:56:53 +0200480 /* merge both nodes */
481 /* add selection and containment nodes from result into the diter,
482 * but only in case the diter already contains some selection nodes,
483 * otherwise it already will return all the data */
Radek Krejci3e0addb2015-08-27 16:37:59 +0200484 filter_merge(diter, node);
Radek Krejcieab784a2015-08-27 09:56:53 +0200485
486 /* not the error, just return no data */
Radek Krejcieab784a2015-08-27 09:56:53 +0200487 /* failure is returned but no ly_errno is set */
488 return EXIT_FAILURE;
Radek Krejci27aaa732015-09-04 15:24:04 +0200489 } else if (node->schema->nodetype == LYS_LEAFLIST) {
490 /* in contrast to lists, leaflists can be still safely optimized if one of them
491 * is selection node. In that case wee need to keep the other node, which is content
492 * match node and it somehow limit the data to be filtered.
493 */
Michal Vasko4c183312015-09-25 10:41:47 +0200494 if (!((struct lyd_node_leaf_list *)diter)->value_str) {
Radek Krejci27aaa732015-09-04 15:24:04 +0200495 /* the other instance is selection node, keep the new one whatever it is */
496 lyd_free(diter);
497 break;
Michal Vasko4c183312015-09-25 10:41:47 +0200498 } else if (!((struct lyd_node_leaf_list *)node)->value_str) {
Radek Krejci27aaa732015-09-04 15:24:04 +0200499 /* the new instance is selection node, keep the previous instance which is
500 * content match node */
501 /* failure is returned but no ly_errno is set */
502 return EXIT_FAILURE;
503 }
Radek Krejcieab784a2015-08-27 09:56:53 +0200504 }
Radek Krejci27aaa732015-09-04 15:24:04 +0200505 } else if (!lyd_compare(diter, node, 1)) { /* comparing keys and unique combinations */
506 LOGVAL(LYE_DUPLIST, line, schema->name);
507 return EXIT_FAILURE;
Radek Krejcieab784a2015-08-27 09:56:53 +0200508 }
509 }
510 }
511
512 return EXIT_SUCCESS;
513}