diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 6c246d7d..61c834d0 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -667,17 +667,17 @@ from_client_edit_config(clixon_handle h, goto done; /* Disable duplicate check in NETCONF messages. */ if (clicon_option_bool(h, "CLICON_NETCONF_DUPLICATE_ALLOW")){ - if (xml_duplicate_remove_recurse(xc) < 0) + if ((ret = xml_duplicate_detect(xc, 1, NULL)) < 0) goto done; } else { - if ((ret = xml_yang_validate_unique_recurse(xc, &xret)) < 0) + if ((ret = xml_duplicate_detect(xc, 0, &xret)) < 0) goto done; - if (ret == 0){ - if (clixon_xml2cbuf(cbret, xret, 0, 0, NULL, -1, 0) < 0) - goto done; - goto ok; - } + } + if (ret == 0){ + if (clixon_xml2cbuf(cbret, xret, 0, 0, NULL, -1, 0) < 0) + goto done; + goto ok; } /* xmldb_put (difflist handling) requires list keys */ if ((ret = xml_yang_validate_list_key_only(xc, &xret)) < 0) diff --git a/lib/clixon/clixon_validate_minmax.h b/lib/clixon/clixon_validate_minmax.h index 7b2c2bdd..e645d37e 100644 --- a/lib/clixon/clixon_validate_minmax.h +++ b/lib/clixon/clixon_validate_minmax.h @@ -44,9 +44,6 @@ * Prototypes */ int xml_yang_validate_minmax(cxobj *xt, int presence, cxobj **xret); -int xml_yang_validate_minmax_recurse(cxobj *xt, cxobj **xret); -int xml_yang_validate_unique(cxobj *xt, cxobj **xret); -int xml_yang_validate_unique_recurse(cxobj *xt, cxobj **xret); -int xml_duplicate_remove_recurse(cxobj *xt); +int xml_duplicate_detect(cxobj *xt, int rm, cxobj **xret); #endif /* _CLIXON_VALIDATE_MINMAX_H_ */ diff --git a/lib/src/clixon_validate_minmax.c b/lib/src/clixon_validate_minmax.c index 57c898a3..a54be2dd 100644 --- a/lib/src/clixon_validate_minmax.c +++ b/lib/src/clixon_validate_minmax.c @@ -35,6 +35,9 @@ * * Check YANG validation for min/max-elements and unique + * For duplicate detection, there is an (older) mechanism in check_unique_list_direct + * and a new more optimized in xml_duplicate_detect + * TODO: use the new mechanism for all purposes, but need some restructuring */ #ifdef HAVE_CONFIG_H #include "clixon_config.h" /* generated by config & autoconf */ @@ -46,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -214,7 +216,6 @@ check_insert_duplicate(char **vec, * @param[in] xt The parent of x (a list) * @param[in] y Its yang spec (Y_LIST) * @param[in] yu A yang unique (Y_UNIQUE) for unique schema node ids or (Y_LIST) for list keys - * @param[in] mark If set, a flag mask to mark (prior) duplicate object with * @param[out] xret Error XML tree. Free with xml_free after use * @retval 1 Validation OK * @retval 0 Validation failed (cbret set) @@ -232,13 +233,13 @@ check_insert_duplicate(char **vec, * The combined values of all the leafs specified in the key are used to * uniquely identify a list entry. All key leafs MUST be given values * when a list entry is created. + * @see xml_duplicate_detect1 optimized variant */ static int check_unique_list_direct(cxobj *x, cxobj *xt, yang_stmt *y, yang_stmt *yu, - int mark, cxobj **xret) { int retval = -1; @@ -303,8 +304,6 @@ check_unique_list_direct(cxobj *x, if (cvi==NULL){ /* Last element (i) is newly inserted, see if it is already there */ if (check_insert_duplicate(vec, i, clen, sorted, &dupl) < 0){ - if (mark) - xml_flag_set(xvec[dupl], mark); if (xret && netconf_data_not_unique_xml(xret, x, cvk) < 0) goto done; goto fail; @@ -333,7 +332,6 @@ check_unique_list_direct(cxobj *x, * @param[in] xt The parent of x (a list) * @param[in] y Its yang spec (Y_LIST) * @param[in] yu A yang unique (Y_UNIQUE) for unique schema node ids or (Y_LIST) for list keys - * @param[in] mark If set, a flag mask to mark (prior) duplicate object with * @param[out] xret Error XML tree. Free with xml_free after use * @retval 1 Validation OK * @retval 0 Validation failed (xret set) @@ -357,24 +355,23 @@ check_unique_list(cxobj *x, cxobj *xt, yang_stmt *y, yang_stmt *yu, - uint16_t mark, cxobj **xret) { - int retval = -1; - cg_var *cvi; /* unique node name */ - char **svec = NULL; /* vector of search results */ - size_t slen = 0; - char *xpath0 = NULL; - char *xpath1 = NULL; - int ret; - cvec *cvk; - cvec *nsc0 = NULL; - cvec *nsc1 = NULL; + int retval = -1; + cg_var *cvi; /* unique node name */ + char **svec = NULL; /* vector of search results */ + size_t slen = 0; + char *xpath0 = NULL; + char *xpath1 = NULL; + int ret; + cvec *cvk; + cvec *nsc0 = NULL; + cvec *nsc1 = NULL; /* Check if multiple direct children */ cvk = yang_cvec_get(yu); if (cvec_len(cvk) > 1){ - retval = check_unique_list_direct(x, xt, y, yu, mark, xret); + retval = check_unique_list_direct(x, xt, y, yu, xret); goto done; } cvi = cvec_i(cvk, 0); @@ -384,7 +381,7 @@ check_unique_list(cxobj *x, } /* Check if direct schmeanode-id , ie not xpath */ if (index(xpath0, '/') == NULL){ - retval = check_unique_list_direct(x, xt, y, yu, mark, xret); + retval = check_unique_list_direct(x, xt, y, yu, xret); goto done; } /* Here proper xpath with at least one slash (can there be a descendant schemanodeid w/o slash?) */ @@ -520,50 +517,38 @@ check_empty_list_minmax(cxobj *xt, goto done; } -/*! Check duplicates/unique in list +/*! Check YANG unique constraint * - * @param[in] x XML LIST node + * Here is a list w unique constraints identified by: + * its first element x, its yang spec y, its parent xt, and + * a unique yang spec yu, + * Two cases: + * 1) multiple direct children (no prefixes), eg "a b" + * 2) single xpath with canonical prefixes, eg "/ex:a/ex:b" + * @param[in] x XML LIST node * @param[in] xt XML parent * @param[in] y YANG of x - * @param[in] mark If set, a flag mask to mark (prior) duplicate object with * @param[out] xret Error as XML if ret = 0 * @retval 1 Validation OK * @retval 0 Validation failed (xret set) * @retval -1 Error */ static int -xml_yang_minmax_new_list(cxobj *x, - cxobj *xt, - yang_stmt *y, - int mark, - cxobj **xret) +xml_unique_detect(cxobj *x, + cxobj *xt, + yang_stmt *y, + cxobj **xret) { int retval = -1; - yang_stmt *yu; int inext; + yang_stmt *yu; int ret; - /* Here new (first element) of lists only - * First check unique keys direct children - */ - if ((ret = check_unique_list_direct(x, xt, y, y, mark, xret)) < 0) - goto done; - if (ret == 0) - goto fail; - /* Check if there is a unique constraint on the list - */ inext = 0; while ((yu = yn_iter(y, &inext)) != NULL) { if (yang_keyword_get(yu) != Y_UNIQUE) continue; - /* Here is a list w unique constraints identified by: - * its first element x, its yang spec y, its parent xt, and - * a unique yang spec yu, - * Two cases: - * 1) multiple direct children (no prefixes), eg "a b" - * 2) single xpath with canonical prefixes, eg "/ex:a/ex:b" - */ - if ((ret = check_unique_list(x, xt, y, yu, 0, xret)) < 0) + if ((ret = check_unique_list(x, xt, y, yu, xret)) < 0) goto done; if (ret == 0) goto fail; @@ -576,67 +561,6 @@ xml_yang_minmax_new_list(cxobj *x, goto done; } -/*! Check duplicates in leaf-list - * - * @param[in] x0 XML LIST node - * @param[in] xt XML parent - * @param[in] y0 YANG of x0 - * @param[in] mark If set, a flag mask to mark (prior) duplicate object with - * @param[out] xret Error as XML if ret = 0 - * @retval 1 Validation OK - * @retval 0 Validation failed (xret set) - * @retval -1 Error - * @note works for both ordered-by user and system. But worst case quadratic - */ -static int -xml_yang_minmax_new_leaf_list(cxobj *x0, - cxobj *xt, - yang_stmt *y0, - int mark, - cxobj **xret) -{ - int retval = -1; - cxobj *xi; - cxobj *xj; - char *bi; - char *bj; - cvec *cvv = NULL; - - xi = x0; - do { - if ((bi = xml_body(xi)) == NULL) - continue; - xj = xi; - while ((xj = xml_child_each(xt, xj, CX_ELMNT)) != NULL && - xml_spec(xj) == y0) { - if ((bj = xml_body(xj)) == NULL) - continue; - if (bi && bj && strcmp(bi, bj) == 0){ - if (mark) - xml_flag_set(xi, mark); - if ((cvv = cvec_new(0)) == NULL){ - clixon_err(OE_UNIX, errno, "cvec_new"); - goto done; - } - cvec_add_string(cvv, "name", bi); - if (xret && netconf_data_not_unique_xml(xret, xi, cvv) < 0) - goto done; - goto fail; - } - } - } - while ((xi = xml_child_each(xt, xi, CX_ELMNT)) != NULL && - xml_spec(xi) == y0); - retval = 1; - done: - if (cvv) - cvec_free(cvv); - return retval; - fail: - retval = 0; - goto done; -} - /*! Perform gap analysis in a child-vector interval [ye,y] * * Gap analysis here meaning if there is a list x with min-element constraint but there are no @@ -805,15 +729,15 @@ xml_yang_validate_minmax(cxobj *xt, /* new list check */ if (ret){ if (keyw == Y_LIST){ - if ((ret = xml_yang_minmax_new_list(x, xt, y, 0, xret)) < 0) + if ((ret = check_unique_list_direct(x, xt, y, y, xret)) < 0) goto done; - } -#ifdef NOTYET /* XXX This is enforced in xml_yang_validate_unique instead */ - else if (keyw == Y_LEAF_LIST){ - if ((ret = xml_yang_minmax_new_leaf_list(x, xt, y, xret)) < 0) + if (ret == 0) + goto fail; + if ((ret = xml_unique_detect(x, xt, y, xret)) < 0) goto done; + if (ret == 0) + goto fail; } -#endif } if (ret == 0) goto fail; @@ -888,168 +812,6 @@ xml_yang_validate_minmax(cxobj *xt, goto done; } -/*! Recursive minmax check - * - * Callback function type for xml_apply - * @param[in] x XML node - * @param[in] arg General-purpose argument - * @retval 2 Locally abort this subtree, continue with others - * @retval 1 Abort, dont continue with others, return 1 to end user - * @retval 0 OK, continue - * @retval -1 Error, aborted at first error encounter, return -1 to end user - */ -static int -xml_yang_minmax_apply(cxobj *x, - void *arg) -{ - int ret; - cxobj **xret = (cxobj **)arg; - - if ((ret = xml_yang_validate_minmax(x, 1, xret)) < 0) - return -1; - if (ret == 0){ /* Validation failed (xret set) */ - return 1; /* Abort dont continue */ - } - return 0; -} - -/*! Recursive YANG Minmax check - * - * @param[in] xt XML parent (may have lists w unique constraints as child) - * @param[out] xret Error XML tree. Free with xml_free after use - * @retval 1 Validation OK - * @retval 0 Validation failed (xret set) - * @retval -1 Error - */ -int -xml_yang_validate_minmax_recurse(cxobj *xt, - cxobj **xret) -{ - int retval = -1; - int ret; - - if ((ret = xml_apply0(xt, CX_ELMNT, xml_yang_minmax_apply, xret)) < 0) - goto done; - if (ret == 1) - goto fail; - retval = 1; - done: - return retval; - fail: - retval = 0; - goto done; -} - -/*! YANG unique check, no recursion - * - * Assume xt:s children are sorted and yang populated. - * @param[in] xt XML parent (may have lists w unique constraints as child) - * @param[out] xret Error XML tree. Free with xml_free after use - * @retval 1 Validation OK - * @retval 0 Validation failed (xret set) - * @retval -1 Error - * @see xml_yang_validate_minmax which include these unique tests - */ -int -xml_yang_validate_unique(cxobj *xt, - cxobj **xret) -{ - int retval = -1; - cxobj *x = NULL; - yang_stmt *y; - yang_stmt *yprev = NULL; - enum rfc_6020 keyw; - int ret; - - while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){ - if ((y = xml_spec(x)) == NULL) - continue; - keyw = yang_keyword_get(y); - if (keyw == Y_LIST || keyw == Y_LEAF_LIST){ - /* equal: just continue*/ - if (y == yprev){ - continue; - } - /* new list check */ - switch (keyw){ - case Y_LIST: - if ((ret = xml_yang_minmax_new_list(x, xt, y, 0, xret)) < 0) - goto done; - if (ret == 0) - goto fail; - break; - case Y_LEAF_LIST: - if ((ret = xml_yang_minmax_new_leaf_list(x, xt, y, 0, xret)) < 0) - goto done; - if (ret == 0) - goto fail; - break; - default: - break; - } - yprev = y; - } - } - retval = 1; - done: - return retval; - fail: - retval = 0; - goto done; -} - -/*! Recursive unique check - * - * Callback function type for xml_apply - * @param[in] x XML node - * @param[in] arg General-purpose argument - * @retval 2 Locally abort this subtree, continue with others - * @retval 1 Abort, dont continue with others, return 1 to end user - * @retval 0 OK, continue - * @retval -1 Error, aborted at first error encounter, return -1 to end user - */ -static int -xml_yang_unique_apply(cxobj *x, - void *arg) -{ - int ret; - cxobj **xret = (cxobj **)arg; - - if ((ret = xml_yang_validate_unique(x, xret)) < 0) - return -1; - if (ret == 0){ /* Validation failed (xret set) */ - return 1; /* Abort dont continue */ - } - return 0; -} - -/*! Recursive YANG unique check - * - * @param[in] xt XML parent (may have lists w unique constraints as child) - * @param[out] xret Error XML tree. Free with xml_free after use - * @retval 1 Validation OK - * @retval 0 Validation failed (xret set) - * @retval -1 Error - */ -int -xml_yang_validate_unique_recurse(cxobj *xt, - cxobj **xret) -{ - int retval = -1; - int ret; - - if ((ret = xml_apply0(xt, CX_ELMNT, xml_yang_unique_apply, xret)) < 0) - goto done; - if (ret == 1) - goto fail; - retval = 1; - done: - return retval; - fail: - retval = 0; - goto done; -} - /*----------- New linear vector code -----------------*/ static int @@ -1081,8 +843,6 @@ cmp_list_qsort(const void *arg1, eq = 0; for (i=0; ivo_slen; i++){ - assert(v1->vo_strvec[i]); - assert(v2->vo_strvec[i]); if ((eq = strcmp(v1->vo_strvec[i], v2->vo_strvec[i])) != 0) break; } @@ -1099,11 +859,23 @@ cmp_list_qsort(const void *arg1, } /*! Remove duplicates list + * + * @param[in] vec Ordered vector of string vectors + * @param[in] vlen Length of vec + * @param[in] rm 0: return 0 on first duplicate, 1: remove all duplicates + * @param[in] cvv Vector of keys (for error) + * @param[out] xret Error XML tree. Free with xml_free after use + * @retval 1 OK, no duplicates + * @retval 0 Validation failed (xret set) (only if rm=0) + * @retval -1 Error */ static int -remove_duplicates_list(struct vec_order *vec, - size_t vlen, - int *nr) +remove_duplicates_list(yang_stmt *y, + struct vec_order *vec, + size_t vlen, + int rm, + int *nr, + cxobj **xret) { int retval = -1; int v; @@ -1117,48 +889,95 @@ remove_duplicates_list(struct vec_order *vec, break; } if (i==vec[v-1].vo_slen){ - if (xml_purge(vec[v-1].vo_xml) < 0) - goto done; - if (nr) - (*nr)++; + if (rm){ + if (xml_purge(vec[v-1].vo_xml) < 0) + goto done; + if (nr) + (*nr)++; + } + else{ + cvec *cvk = NULL; + if (yang_keyword_get(y) == Y_LEAF_LIST){ + if ((cvk = cvec_new(0)) == NULL){ + clixon_err(OE_UNIX, errno, "cvec_new"); + goto done; + } + cvec_add_string(cvk, "name", vec[v].vo_strvec[0]); + } + else + cvk = yang_cvec_get(y); + if (xret && netconf_data_not_unique_xml(xret, vec[0].vo_xml, cvk) < 0) + goto done; + if (yang_keyword_get(y) == Y_LEAF_LIST && cvk != NULL) + cvec_free(cvk); + goto fail; + } } } - retval = 0; + retval = 1; done: return retval; + fail: + retval = 0; + goto done; } +/*! Analyze sorted list: detect and potentially remove duplicates + * + * @param[in] y YANG node of list segment + * @param[in] vec Ordered vector of string vectors + * @param[in] vlen Length of vec + * @param[in] x Most recent XML element, for adjustment + * @param[in] rm 0: return 0 on first duplicate, 1: remove all duplicates + * @param[out] xret Error XML tree. Free with xml_free after use + * @retval 1 OK, no duplicates + * @retval 0 Validation failed (xret set) (only if rm=0) + * @retval -1 Error + */ static int vec_order_analyze(yang_stmt *y, struct vec_order *vec, size_t vlen, - cxobj *x) + cxobj *x, + int rm, + cxobj **xret) { - int retval = -1; - int nr = 0; + int retval = -1; + int nr = 0; + int ret; if (yang_find(y, Y_ORDERED_BY, "user") != NULL) qsort(vec, vlen, sizeof(*vec), cmp_list_qsort); - if (remove_duplicates_list(vec, vlen, &nr) < 0) + if ((ret = remove_duplicates_list(y, vec, vlen, rm, &nr, xret)) < 0) goto done; + if (ret == 0) { + goto fail; + } if (x && nr) xml_vector_decrement(x, nr); - retval = 0; + retval = 1; done: return retval; + fail: + retval = 0; + goto done; } -/*! YANG unique check and remove duplicates, keep last +/*! YANG unique check and remove duplicates single node, keep last * * Assume xt:s children are sorted and yang populated. * @param[in] xt XML parent (may have lists w unique constraints as child) + * @param[in] rm 0: return 0 on first duplicate, 1: remove all duplicates * @param[out] xret Error XML tree. Free with xml_free after use - * @retval 0 OK + * @retval 1 OK, no duplicates + * @retval 0 Validation failed (xret set) (only if rm=0) * @retval -1 Error * @see xml_yang_validate_minmax which include these unique tests */ static int -xml_duplicate_remove(cxobj *xt) +xml_duplicate_detect1(cxobj *xt, + int rm, + cxobj **xret) { int retval = -1; cxobj *x; @@ -1175,6 +994,7 @@ xml_duplicate_remove(cxobj *xt) char *str; cxobj *xi; int v; + int ret; xml_enumerate_children(xt); // Could be done in-line y0 = NULL; @@ -1184,8 +1004,10 @@ xml_duplicate_remove(cxobj *xt) if ((y = xml_spec(x)) == NULL) continue; if (y != y0 && vec != NULL){ /* New */ - if (vec_order_analyze(y0, vec, vlen, x) < 0) + if ((ret = vec_order_analyze(y0, vec, vlen, x, rm, xret)) < 0) goto done; + if (ret == 0) + goto fail; if (vec_free(vec, vlen) < 0) goto done; vec = NULL; @@ -1203,7 +1025,6 @@ xml_duplicate_remove(cxobj *xt) clixon_err(OE_YANG, 0, "List key vector mismatch %lu != %lu", slen0, clen); goto done; } - /* see check_unique_list_direct */ if ((vec = realloc(vec, (vlen+1)*sizeof(*vec))) == NULL){ clixon_err(OE_UNIX, errno, "cvec_new"); goto done; @@ -1235,6 +1056,11 @@ xml_duplicate_remove(cxobj *xt) slen0 = clen; vlen++; } + /* Special case of YANG unique statement */ + if ((ret = xml_unique_detect(x, xt, y, xret)) < 0) + goto done; + if (ret == 0) + goto fail; break; case Y_LEAF_LIST: if (vec>0 && slen0 != 1){ /* Sanity check */ @@ -1262,41 +1088,55 @@ xml_duplicate_remove(cxobj *xt) y0 = y; } if (y0 && vec != NULL){ - if (vec_order_analyze(y0, vec, vlen, NULL) < 0) + if ((ret = vec_order_analyze(y0, vec, vlen, NULL, rm, xret)) < 0) goto done; - if (vec_free(vec, vlen) < 0) - goto done; - vec = NULL; - vlen = 0; + if (ret == 0) + goto fail; } - retval = 0; + retval = 1; done: if (vec) - free(vec); + vec_free(vec, vlen); return retval; + fail: + retval = 0; + goto done; } -/*! Recursive YANG unique check and remove duplicates, keep last +/*! Recursive YANG unique check and potential remove duplicates, keep last * * @param[in] xt XML parent (may have lists w unique constraints as child) - * @retval 0 Validation OK + * @param[in] rm 0: return 0 on first duplicate, 1: remove all duplicates + * @param[out] xret Error XML tree. Free with xml_free after use + * @retval 1 OK, no duplicates + * @retval 0 Validation failed (xret set) (only if rm=0) * @retval -1 Error * @see xml_yang_validate_unique_recurse This function destructively removes */ int -xml_duplicate_remove_recurse(cxobj *xt) +xml_duplicate_detect(cxobj *xt, + int rm, + cxobj **xret) { int retval = -1; cxobj *x; + int ret; - if (xml_duplicate_remove(xt) < 0) + if ((ret = xml_duplicate_detect1(xt, rm, xret)) < 0) goto done; + if (ret == 0) + goto fail; x = NULL; while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { - if (xml_duplicate_remove_recurse(x) < 0) + if ((ret = xml_duplicate_detect(x, rm, xret)) < 0) goto done; + if (ret == 0) + goto fail; } - retval = 0; + retval = 1; done: return retval; + fail: + retval = 0; + goto done; }