schema compile CHANGE check if-features applicable to leafref and its target
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 4ac55c1..ad66bfb 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -1876,6 +1876,62 @@
}
/**
+ * @brief Check the features used in if-feature statements applicable to the leafref and its target.
+ *
+ * The set of features used for target must be a subset of features used for the leafref.
+ * This is not a perfect, we should compare the truth tables but it could require too much resources
+ * and RFC 7950 does not require it explicitely, so we simplify that.
+ *
+ * @param[in] refnode The leafref node.
+ * @param[in] target Tha target node of the leafref.
+ * @return LY_SUCCESS or LY_EVALID;
+ */
+static LY_ERR
+lys_compile_leafref_features_validate(const struct lysc_node *refnode, const struct lysc_node *target)
+{
+ LY_ERR ret = LY_EVALID;
+ const struct lysc_node *iter;
+ struct lysc_iffeature **iff;
+ unsigned int u, v, count;
+ struct ly_set features = {0};
+
+ for (iter = refnode; iter; iter = iter->parent) {
+ iff = lysc_node_iff(iter);
+ if (iff && *iff) {
+ LY_ARRAY_FOR(*iff, u) {
+ LY_ARRAY_FOR((*iff)[u].features, v) {
+ LY_CHECK_GOTO(ly_set_add(&features, (*iff)[u].features[v], 0) == -1, cleanup);
+ }
+ }
+ }
+ }
+
+ /* we should have, in features set, a superset of features applicable to the target node.
+ * So when adding features applicable to the target into the features set, we should not be
+ * able to actually add any new feature, otherwise it is not a subset of features applicable
+ * to the leafref itself. */
+ count = features.count;
+ for (iter = target; iter; iter = iter->parent) {
+ iff = lysc_node_iff(iter);
+ if (iff && *iff) {
+ LY_ARRAY_FOR(*iff, u) {
+ LY_ARRAY_FOR((*iff)[u].features, v) {
+ if ((unsigned int)ly_set_add(&features, (*iff)[u].features[v], 0) >= count) {
+ /* new feature was added (or LY_EMEM) */
+ goto cleanup;
+ }
+ }
+ }
+ }
+ }
+ ret = LY_SUCCESS;
+
+cleanup:
+ ly_set_erase(&features, NULL);
+ return ret;
+}
+
+/**
* @brief Validate the leafref path.
* @param[in] ctx Compile context
* @param[in] startnode Path context node (where the leafref path begins/is placed).
@@ -1979,6 +2035,14 @@
}
}
+ /* check if leafref and its target are under common if-features */
+ if (lys_compile_leafref_features_validate(startnode, node)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path \"%s\" - set of features applicable to the leafref target is not a subset of features applicable to the leafref itself.",
+ leafref->path);
+ return LY_EVALID;
+ }
+
return LY_SUCCESS;
}
@@ -2500,7 +2564,6 @@
COMPILE_MEMBER_GOTO(ctx, leaf_p->when, leaf->when, options, lys_compile_when, ret, done);
COMPILE_ARRAY_GOTO(ctx, leaf_p->iffeatures, leaf->iffeatures, options, u, lys_compile_iffeature, ret, done);
-
COMPILE_ARRAY_GOTO(ctx, leaf_p->musts, leaf->musts, options, u, lys_compile_must, ret, done);
ret = lys_compile_type(ctx, leaf_p, options, &leaf->type);
LY_CHECK_GOTO(ret, done);
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 1e52dd8..24ad2db 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -460,6 +460,22 @@
}
static void
+lysc_when_free(struct ly_ctx *ctx, struct lysc_when *w)
+{
+ lyxp_expr_free(ctx, w->cond);
+ FREE_ARRAY(ctx, w->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_must_free(struct ly_ctx *ctx, struct lysc_must *must)
+{
+ lyxp_expr_free(ctx, must->cond);
+ FREE_STRING(ctx, must->emsg);
+ FREE_STRING(ctx, must->eapptag);
+ FREE_ARRAY(ctx, must->exts, lysc_ext_instance_free);
+}
+
+static void
lysc_import_free(struct ly_ctx *ctx, struct lysc_import *import)
{
/* imported module is freed directly from the context's list */
@@ -581,17 +597,27 @@
{
struct lysc_node *child, *child_next;
+ FREE_MEMBER(ctx, node->when, lysc_when_free);
+ FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
LY_LIST_FOR_SAFE(node->child, child_next, child) {
lysc_node_free(ctx, child);
}
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+
+ /* TODO actions, notifs */
}
static void
lysc_node_leaf_free(struct ly_ctx *ctx, struct lysc_node_leaf *node)
{
+ FREE_MEMBER(ctx, node->when, lysc_when_free);
+ FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
if (node->type) {
lysc_type_free(ctx, node->type);
}
+ FREE_STRING(ctx, node->units);
+ FREE_STRING(ctx, node->dflt);
}
void
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 5484999..b815bbc 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -968,10 +968,6 @@
/* TODO
case LYS_LIST:
return &((struct lysc_node_list*)node)->iffeatures;
- case LYS_CASE:
- return &((struct lysc_node_case*)node)->iffeatures;
- case LYS_USES:
- return &((struct lysc_node_uses*)node)->iffeatures;
case LYS_NOTIF:
return &((struct lysc_notif*)node)->child;
*/
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index ebf36eb..9323bd9 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -1350,6 +1350,20 @@
assert_ptr_not_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
assert_int_equal(1, ((struct lysc_type_leafref* )type)->require_instance);
+ /* conditional leafrefs */
+ assert_non_null(mod = lys_parse_mem(ctx, "module d {yang-version 1.1;namespace urn:d;prefix d;feature f1; feature f2;"
+ "leaf ref1 {if-feature 'f1 and f2';type leafref {path /target;}}"
+ "leaf target {if-feature f1; type boolean;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/target", ((struct lysc_type_leafref* )type)->path);
+ assert_ptr_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
+ assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+ assert_int_equal(LY_TYPE_BOOL, ((struct lysc_type_leafref*)type)->realtype->basetype);
+
/* TODO target in list with predicates */
@@ -1391,6 +1405,12 @@
assert_int_equal(LY_EVALID, lys_compile(mod, 0));
logbuf_assert("Missing path substatement for leafref type mytype.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module jj {namespace urn:jj;prefix jj;feature f;"
+ "leaf ref {type leafref {path /target;}}leaf target {if-feature f;type string;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path \"/target\" - set of features applicable to the leafref target is not a subset of features "
+ "applicable to the leafref itself.");
+
/* circular chain */
assert_non_null(mod = lys_parse_mem(ctx, "module aaa {namespace urn:aaa;prefix aaa;"
"leaf ref1 {type leafref {path /ref2;}}"