context UPDATE support for storing leafref references (#2155)
* Adding leafref references and API to lyd_node_term
* Adjusted to require context flag
* Extended the utest for tree_data
* Fixed typo in docs
* Refactored based on PR discussion
* Refactored to use hash table for leafref nodes
diff --git a/src/common.h b/src/common.h
index 0fedeae..0157d26 100644
--- a/src/common.h
+++ b/src/common.h
@@ -373,6 +373,7 @@
void *ext_clb_data; /**< optional private data for ::ly_ctx.ext_clb */
struct ly_ht *err_ht; /**< hash table of thread-specific list of errors related to the context */
pthread_mutex_t lyb_hash_lock; /**< lock for storing LYB schema hashes in schema nodes */
+ struct ly_ht *leafref_links_ht; /**< hash table of leafref links between term data nodes */
};
/**
diff --git a/src/context.c b/src/context.c
index 7a14203..0c41019 100644
--- a/src/context.c
+++ b/src/context.c
@@ -239,6 +239,30 @@
return !memcmp(&err1->tid, &err2->tid, sizeof err1->tid);
}
+/**
+ * @brief Hash table value-equal callback for comparing leafref links hash table record.
+ */
+static ly_bool
+ly_ctx_ht_leafref_links_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ struct lyd_leafref_links_rec *rec1 = val1_p, *rec2 = val2_p;
+
+ return rec1->node == rec2->node;
+}
+
+/**
+ * @brief Callback for freeing leafref links recorcd internal resources.
+ *
+ * @param[in] val_p Pointer to leafref links record
+ */
+static void
+ly_ctx_ht_leafref_links_rec_free(void *val_p)
+{
+ struct lyd_leafref_links_rec *rec = val_p;
+
+ lyd_free_leafref_links_rec(rec);
+}
+
LIBYANG_API_DEF LY_ERR
ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx)
{
@@ -262,6 +286,11 @@
/* plugins */
LY_CHECK_ERR_GOTO(lyplg_init(), LOGINT(NULL); rc = LY_EINT, cleanup);
+ if (options & LY_CTX_LEAFREF_LINKING) {
+ ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec), ly_ctx_ht_leafref_links_equal_cb, NULL, 1);
+ LY_CHECK_ERR_GOTO(!ctx->leafref_links_ht, rc = LY_EMEM, cleanup);
+ }
+
/* initialize thread-specific error hash table */
ctx->err_ht = lyht_new(1, sizeof(struct ly_ctx_err_rec), ly_ctx_ht_err_equal_cb, NULL, 1);
LY_CHECK_ERR_GOTO(!ctx->err_ht, rc = LY_EMEM, cleanup);
@@ -588,6 +617,11 @@
LY_CHECK_ERR_RET((option & LY_CTX_NO_YANGLIBRARY) && !(ctx->flags & LY_CTX_NO_YANGLIBRARY),
LOGARG(ctx, option), LY_EINVAL);
+ if (!(ctx->flags & LY_CTX_LEAFREF_LINKING) && (option & LY_CTX_LEAFREF_LINKING)) {
+ ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec), ly_ctx_ht_leafref_links_equal_cb, NULL, 1);
+ LY_CHECK_ERR_RET(!ctx->leafref_links_ht, LOGARG(ctx, option), LY_EMEM);
+ }
+
if (!(ctx->flags & LY_CTX_SET_PRIV_PARSED) && (option & LY_CTX_SET_PRIV_PARSED)) {
ctx->flags |= LY_CTX_SET_PRIV_PARSED;
/* recompile the whole context to set the priv pointers */
@@ -628,6 +662,11 @@
LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
LY_CHECK_ERR_RET(option & LY_CTX_NO_YANGLIBRARY, LOGARG(ctx, option), LY_EINVAL);
+ if ((ctx->flags & LY_CTX_LEAFREF_LINKING) && (option & LY_CTX_LEAFREF_LINKING)) {
+ lyht_free(ctx->leafref_links_ht, ly_ctx_ht_leafref_links_rec_free);
+ ctx->leafref_links_ht = NULL;
+ }
+
if ((ctx->flags & LY_CTX_SET_PRIV_PARSED) && (option & LY_CTX_SET_PRIV_PARSED)) {
struct lys_module *mod;
uint32_t index;
@@ -1318,6 +1357,11 @@
/* leftover unres */
lys_unres_glob_erase(&ctx->unres);
+ /* clean the leafref links hash table */
+ if (ctx->leafref_links_ht) {
+ lyht_free(ctx->leafref_links_ht, ly_ctx_ht_leafref_links_rec_free);
+ }
+
/* clean the error hash table */
lyht_free(ctx->err_ht, ly_ctx_ht_err_rec_free);
diff --git a/src/context.h b/src/context.h
index a6367f5..318c614 100644
--- a/src/context.h
+++ b/src/context.h
@@ -199,6 +199,10 @@
enabled. */
#define LY_CTX_LEAFREF_EXTENDED 0x0200 /**< By default, path attribute of leafref accepts only path as defined in RFC 7950.
By using this option, the path attribute will also allow using XPath functions as deref() */
+#define LY_CTX_LEAFREF_LINKING 0x0400 /**< Link valid leafref nodes with its target during validation if leafref node is not using
+ 'require-instance false;'. It also enables usage of
+ [lyd_leafref_get_links](@ref lyd_leafref_get_links) and
+ [lyd_leafref_link_node_tree](@ref lyd_leafref_link_node_tree) APIs. */
/** @} contextoptions */
diff --git a/src/plugins_types/leafref.c b/src/plugins_types/leafref.c
index 8ab3fc5..8e60f98 100644
--- a/src/plugins_types/leafref.c
+++ b/src/plugins_types/leafref.c
@@ -25,6 +25,7 @@
#include "common.h"
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
+#include "tree_data_internal.h" /* lyd_link_leafref_node */
/**
* @page howtoDataLYB LYB Binary Format
@@ -63,12 +64,13 @@
}
LIBYANG_API_DEF LY_ERR
-lyplg_type_validate_leafref(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *type, const struct lyd_node *ctx_node,
+lyplg_type_validate_leafref(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *ctx_node,
const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err)
{
LY_ERR ret;
struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type;
char *errmsg = NULL, *path;
+ struct lyd_node *target = NULL;
*err = NULL;
@@ -78,13 +80,17 @@
}
/* check leafref target existence */
- if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, NULL, &errmsg)) {
+ if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, &target, &errmsg)) {
path = lyd_path(ctx_node, LYD_PATH_STD, NULL, 0);
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, path, strdup("instance-required"), "%s", errmsg);
free(errmsg);
return ret;
}
+ if (ly_ctx_get_options(ctx) & LY_CTX_LEAFREF_LINKING) {
+ return lyd_link_leafref_node((struct lyd_node_term *)target, (struct lyd_node_term *)ctx_node);
+ }
+
return LY_SUCCESS;
}
diff --git a/src/tree_data.c b/src/tree_data.c
index 2b007ef..9ccf994 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -911,6 +911,11 @@
/* update hashes while still linked into the tree */
lyd_unlink_hash(node);
+ /* unlink leafref nodes */
+ if (node->schema && (node->schema->nodetype & LYD_NODE_TERM)) {
+ lyd_free_leafref_nodes((struct lyd_node_term *)node);
+ }
+
/* unlink from siblings */
if (node->prev->next) {
node->prev->next = node->next;
@@ -3268,3 +3273,148 @@
return NULL;
}
+
+LY_ERR
+lyd_get_or_create_leafref_links_record(const struct lyd_node_term *node, struct lyd_leafref_links_rec **record, ly_bool create)
+{
+ struct ly_ht *ht;
+ uint32_t hash;
+ struct lyd_leafref_links_rec rec;
+
+ assert(node);
+ assert(record);
+
+ if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) {
+ *record = NULL;
+ return LY_EDENIED;
+ }
+
+ rec.node = node;
+ rec.leafref_nodes = NULL;
+ rec.target_node = NULL;
+
+ ht = LYD_CTX(node)->leafref_links_ht;
+ hash = lyht_hash((const char *)&node, sizeof & node);
+ if (lyht_find(ht, &rec, hash, (void **)record) == LY_ENOTFOUND) {
+ if (create) {
+ LY_CHECK_RET(lyht_insert_no_check(ht, &rec, hash, (void **)record));
+ } else {
+ *record = NULL;
+ return LY_ENOTFOUND;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_leafref_get_links(const struct lyd_node_term *node, const struct lyd_leafref_links_rec **record)
+{
+ LY_CHECK_ARG_RET(NULL, node, record, LY_EINVAL);
+
+ return lyd_get_or_create_leafref_links_record(node, (struct lyd_leafref_links_rec **)record, 0);
+}
+
+LY_ERR
+lyd_link_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ const struct lyd_node_term **item = NULL;
+ struct lyd_leafref_links_rec *rec;
+
+ assert(node);
+ assert(leafref_node);
+
+ if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) {
+ return LY_EDENIED;
+ }
+
+ LY_CHECK_RET(lyd_get_or_create_leafref_links_record(node, &rec, 1));
+ LY_ARRAY_FOR(rec->leafref_nodes, u) {
+ if (rec->leafref_nodes[u] == leafref_node) {
+ return LY_SUCCESS;
+ }
+ }
+
+ LY_ARRAY_NEW_RET(LYD_CTX(node), rec->leafref_nodes, item, LY_EMEM);
+ *item = leafref_node;
+ LY_CHECK_RET(lyd_get_or_create_leafref_links_record(leafref_node, &rec, 1));
+ rec->target_node = node;
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_leafref_link_node_tree(const struct lyd_node *tree)
+{
+ const struct lyd_node *sibling, *elem;
+ struct lyd_node *target;
+ char *errmsg;
+ struct lyd_node_term *leafref_node;
+ struct lysc_node_leaf *leaf_schema;
+ struct lysc_type_leafref *lref;
+ LY_ERR ret;
+
+ LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL);
+
+ if (!(ly_ctx_get_options(LYD_CTX(tree)) & LY_CTX_LEAFREF_LINKING)) {
+ return LY_EDENIED;
+ }
+
+ LY_LIST_FOR(tree, sibling) {
+ LYD_TREE_DFS_BEGIN(sibling, elem) {
+ if (elem->schema->nodetype & LYD_NODE_TERM) {
+ leafref_node = (struct lyd_node_term *)elem;
+ leaf_schema = (struct lysc_node_leaf *)elem->schema;
+ if (leaf_schema->type->basetype == LY_TYPE_LEAFREF) {
+ lref = (struct lysc_type_leafref *)leaf_schema->type;
+ if (lyplg_type_resolve_leafref(lref, elem, &leafref_node->value, tree, &target, &errmsg)) {
+ free(errmsg);
+ } else if (target->schema->nodetype & LYD_NODE_TERM) {
+ ret = lyd_link_leafref_node((struct lyd_node_term *)target, leafref_node);
+ if (ret != LY_SUCCESS) {
+ return ret;
+ }
+ }
+ }
+ }
+ LYD_TREE_DFS_END(sibling, elem);
+ }
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_unlink_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node)
+{
+ LY_ERR ret;
+ struct lyd_leafref_links_rec *rec;
+
+ assert(node);
+ assert(leafref_node);
+
+ if (!(ly_ctx_get_options(LYD_CTX(node)) & LY_CTX_LEAFREF_LINKING)) {
+ return LY_EDENIED;
+ }
+
+ ret = lyd_get_or_create_leafref_links_record(node, &rec, 0);
+ if (ret == LY_SUCCESS) {
+ LY_ARRAY_REMOVE_VALUE(rec->leafref_nodes, leafref_node);
+ if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (rec->target_node == NULL)) {
+ lyd_free_leafref_nodes(node);
+ }
+ } else if (ret != LY_ENOTFOUND) {
+ return ret;
+ }
+
+ ret = lyd_get_or_create_leafref_links_record(leafref_node, &rec, 0);
+ if (ret == LY_SUCCESS) {
+ rec->target_node = NULL;
+ if ((LY_ARRAY_COUNT(rec->leafref_nodes) == 0) && (rec->target_node == NULL)) {
+ lyd_free_leafref_nodes(leafref_node);
+ }
+ } else if (ret != LY_ENOTFOUND) {
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
diff --git a/src/tree_data.h b/src/tree_data.h
index 4d87fba..0cce979 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -998,6 +998,22 @@
};
/**
+ * @brief Structure of leafref links record.
+ */
+struct lyd_leafref_links_rec {
+ const struct lyd_node_term *node; /** pointer to the data node itself */
+ const struct lyd_node_term **leafref_nodes; /** list of the leafref pointing to this data node [sized array](@ref sizedarrays)),
+ By default it is empty. It is filled automatically by validation function of
+ leafref nodes, which are valid and are not using 'require-instance false;'.
+ It can also be populated based on manual request using
+ [link api](@ref lyd_leafref_link_node_tree). Freeing of the resources is
+ automatic. */
+ const struct lyd_node_term *target_node; /** pointer to leafref target data node, by default is NULL. The logic
+ is the same as for [leafref_nodes](@ref leafref_nodes) and is filled only
+ for leafrefs */
+};
+
+/**
* @brief Get the generic parent pointer of a data node.
*
* @param[in] node Node whose parent pointer to get.
@@ -2702,6 +2718,29 @@
*/
LIBYANG_API_DECL LY_ERR ly_time_ts2str(const struct timespec *ts, char **str);
+/**
+ * @brief Gets the leafref links record for given node
+ *
+ * This API requires usage of LY_CTX_LEAFREF_LINKING context flag.
+ *
+ * @param[in] node The term data node.
+ * @param[out] record The leafref links record
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_leafref_get_links(const struct lyd_node_term *node, const struct lyd_leafref_links_rec **record);
+
+/**
+ * @brief Traverse through data tree including root node siblings and adds leafrefs links to the given nodes
+ *
+ * This API requires usage of LY_CTX_LEAFREF_LINKING context flag.
+ *
+ * @param[in] tree The data tree root node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_leafref_link_node_tree(const struct lyd_node *tree);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index 0281ae5..100cf49 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -138,6 +138,53 @@
lyd_free_attr(ctx, attr, 1);
}
+void
+lyd_free_leafref_links_rec(struct lyd_leafref_links_rec *rec)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct lyd_leafref_links_rec *leafref_rec;
+
+ assert(rec);
+
+ /* remove stored leafref nodes */
+ LY_ARRAY_FOR(rec->leafref_nodes, u) {
+ if (lyd_get_or_create_leafref_links_record(rec->leafref_nodes[u], &leafref_rec, 0) == LY_SUCCESS) {
+ leafref_rec->target_node = NULL;
+ if ((LY_ARRAY_COUNT(leafref_rec->leafref_nodes) == 0) && (leafref_rec->target_node == NULL)) {
+ lyd_free_leafref_nodes(rec->leafref_nodes[u]);
+ }
+ }
+ }
+ LY_ARRAY_FREE(rec->leafref_nodes);
+ rec->leafref_nodes = NULL;
+ /* remove stored target node */
+ if (rec->target_node) {
+ lyd_unlink_leafref_node(rec->target_node, rec->node);
+ }
+}
+
+void
+lyd_free_leafref_nodes(const struct lyd_node_term *node)
+{
+ struct ly_ht *ht;
+ uint32_t hash;
+ struct lyd_leafref_links_rec *rec;
+
+ assert(node);
+
+ if (lyd_get_or_create_leafref_links_record(node, &rec, 0) != LY_SUCCESS) {
+ return;
+ }
+
+ /* free entry content */
+ lyd_free_leafref_links_rec(rec);
+
+ /* free entry itself from hash table */
+ ht = LYD_CTX(node)->leafref_links_ht;
+ hash = lyht_hash((const char *)&node, sizeof & node);
+ lyht_remove(ht, rec, hash);
+}
+
/**
* @brief Free Data (sub)tree.
* @param[in] node Data node to be freed.
@@ -177,7 +224,10 @@
/* only frees the value this way */
lyd_any_copy_value(node, NULL, 0);
} else if (node->schema->nodetype & LYD_NODE_TERM) {
- ((struct lysc_node_leaf *)node->schema)->type->plugin->free(LYD_CTX(node), &((struct lyd_node_term *)node)->value);
+ struct lyd_node_term *node_term = (struct lyd_node_term *)node;
+
+ ((struct lysc_node_leaf *)node->schema)->type->plugin->free(LYD_CTX(node), &node_term->value);
+ lyd_free_leafref_nodes(node_term);
}
if (!node->schema) {
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index cfb93f1..d639591 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -591,4 +591,55 @@
*/
LY_ERR ly_set_rm_index_ordered(struct ly_set *set, uint32_t index, void (*destructor)(void *obj));
+/**
+ * @brief Frees data within leafref links record
+ *
+ * @param[in] rec The leafref links record
+ */
+void lyd_free_leafref_links_rec(struct lyd_leafref_links_rec *rec);
+
+/**
+ * @brief Frees all leafref nodes and target node of given data node
+ *
+ * @param[in] node The data node, which leafref nodes and/or target node should be cleared.
+ */
+void lyd_free_leafref_nodes(const struct lyd_node_term *node);
+
+/**
+ * @brief Gets or creates the leafref links record.
+ *
+ * @param[in] node The term data node.
+ * @param[out] record The leafref links record.
+ * @param[in] create Whether to create record if not exists.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LY_ERR lyd_get_or_create_leafref_links_record(const struct lyd_node_term *node, struct lyd_leafref_links_rec **record, ly_bool create);
+
+/**
+ * @brief Adds links between leafref adn data node.
+ *
+ * If the links were already added, it will not be added again.
+ * This API requires usage of LY_CTX_LEAFREF_LINKING context flag.
+ *
+ * @param[in] node Data node to which, the leafref is pointing to.
+ * @param[in] leafref_node The leafref, which points to given node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LY_ERR lyd_link_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node);
+
+/**
+ * @brief Removes links between leafref adn data node.
+ *
+ * If the links were never added, it will be silently ignored.
+ * This API requires usage of LY_CTX_LEAFREF_LINKING context flag.
+ *
+ * @param[in] node Data node to which, the leafref is pointing to.
+ * @param[in] leafref_node The leafref, which points to given node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LY_ERR lyd_unlink_leafref_node(const struct lyd_node_term *node, const struct lyd_node_term *leafref_node);
+
#endif /* LY_TREE_DATA_INTERNAL_H_ */
diff --git a/src/tree_data_new.c b/src/tree_data_new.c
index 5d9f429..e56e61e 100644
--- a/src/tree_data_new.c
+++ b/src/tree_data_new.c
@@ -1315,6 +1315,11 @@
val_change = 0;
}
+ /* clear links to leafref nodes */
+ if (ly_ctx_get_options(LYD_CTX(term)) & LY_CTX_LEAFREF_LINKING) {
+ lyd_free_leafref_nodes(t);
+ }
+
/* always clear the default flag */
if (term->flags & LYD_DEFAULT) {
for (parent = term; parent; parent = lyd_parent(parent)) {
diff --git a/src/tree_edit.h b/src/tree_edit.h
index 951d95d..113c9e3 100644
--- a/src/tree_edit.h
+++ b/src/tree_edit.h
@@ -232,6 +232,26 @@
if (ARRAY){free((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1);}
/**
+ * @brief Remove item from array based on value
+ *
+ * @param[in, out] ARRAY A ([sized array](@ref sizedarrays)) to be modified.
+ * @param[in] VALUE The item value to be removed. Only the first occurence will be removed.
+ */
+#define LY_ARRAY_REMOVE_VALUE(ARRAY, VALUE) \
+ { \
+ LY_ARRAY_COUNT_TYPE index__; \
+ LY_ARRAY_FOR(ARRAY, index__) { \
+ if (ARRAY[index__] == VALUE) { \
+ if (index__ != LY_ARRAY_COUNT(ARRAY) - 1) { \
+ memmove(&(ARRAY[index__]), &(ARRAY[LY_ARRAY_COUNT(ARRAY) - 1]), sizeof *(ARRAY)); \
+ } \
+ LY_ARRAY_DECREMENT(ARRAY); \
+ break; \
+ } \
+ } \
+ }
+
+/**
* @brief Insert item into linked list.
*
* @param[in,out] LIST Linked list to add to.