Radek Krejci | b1c1251 | 2015-08-11 11:22:04 +0200 | [diff] [blame] | 1 | /** |
| 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 Krejci | eab784a | 2015-08-27 09:56:53 +0200 | [diff] [blame^] | 22 | #include <assert.h> |
Radek Krejci | b1c1251 | 2015-08-11 11:22:04 +0200 | [diff] [blame] | 23 | #include <stdlib.h> |
| 24 | |
| 25 | #include "libyang.h" |
Radek Krejci | eab784a | 2015-08-27 09:56:53 +0200 | [diff] [blame^] | 26 | #include "common.h" |
Radek Krejci | b1c1251 | 2015-08-11 11:22:04 +0200 | [diff] [blame] | 27 | |
Radek Krejci | eab784a | 2015-08-27 09:56:53 +0200 | [diff] [blame^] | 28 | static struct lys_node_leaf * |
Radek Krejci | b1c1251 | 2015-08-11 11:22:04 +0200 | [diff] [blame] | 29 | lyv_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 Krejci | 1073cc0 | 2015-08-12 20:37:39 +0200 | [diff] [blame] | 45 | return schema->keys[i]; |
Radek Krejci | b1c1251 | 2015-08-11 11:22:04 +0200 | [diff] [blame] | 46 | } |
| 47 | } |
| 48 | |
| 49 | return EXIT_SUCCESS; |
| 50 | } |
Radek Krejci | eab784a | 2015-08-27 09:56:53 +0200 | [diff] [blame^] | 51 | |
| 52 | int |
| 53 | lyv_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 | } |
| 71 | int |
| 72 | lyv_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 | } |