plugins exts FEATURE validation of operations
diff --git a/src/plugins_exts.h b/src/plugins_exts.h
index 747aa51..75dbf85 100644
--- a/src/plugins_exts.h
+++ b/src/plugins_exts.h
@@ -1,9 +1,10 @@
 /**
  * @file plugins_exts.h
  * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
  * @brief libyang support for YANG extensions implementation.
  *
- * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
  *
  * This source code is licensed under BSD 3-Clause License (the "License").
  * You may not use this file except in compliance with the License.
@@ -16,6 +17,7 @@
 #define LY_PLUGINS_EXTS_H_
 
 #include "log.h"
+#include "parser_data.h"
 #include "plugins.h"
 #include "tree_data.h"
 #include "tree_edit.h"
@@ -100,7 +102,7 @@
 /**
  * @brief Extensions API version
  */
-#define LYPLG_EXT_API_VERSION 3
+#define LYPLG_EXT_API_VERSION 4
 
 /**
  * @brief Macro to define plugin information in external plugins
@@ -187,13 +189,16 @@
  *
  * @param[in] ext Compiled extension instance.
  * @param[in] sibling First sibling with schema node returned by ::lyplg_ext_data_snode_clb.
+ * @param[in] dep_tree Tree to be used for validating references from the operation subtree, if operation.
+ * @param[in] data_type Validated data type, can be ::LYD_TYPE_DATA_YANG, ::LYD_TYPE_RPC_YANG, ::LYD_TYPE_NOTIF_YANG,
+ * or ::LYD_TYPE_REPLY_YANG.
  * @param[in] val_opts Validation options, see @ref datavalidationoptions.
  * @param[out] diff Optional diff with any changes made by the validation.
  * @return LY_SUCCESS on success.
  * @return LY_ERR on error.
  */
-typedef LY_ERR (*lyplg_ext_data_validate_clb)(struct lysc_ext_instance *ext, struct lyd_node *sibling, uint32_t val_opts,
-        struct lyd_node **diff);
+typedef LY_ERR (*lyplg_ext_data_validate_clb)(struct lysc_ext_instance *ext, struct lyd_node *sibling,
+        const struct lyd_node *dep_tree, enum lyd_type data_type, uint32_t val_opts, struct lyd_node **diff);
 
 /**
  * @brief Extension plugin implementing various aspects of a YANG extension
diff --git a/src/plugins_exts/schema_mount.c b/src/plugins_exts/schema_mount.c
index f655e06..b823a8c 100644
--- a/src/plugins_exts/schema_mount.c
+++ b/src/plugins_exts/schema_mount.c
@@ -20,9 +20,11 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "common.h"
 #include "dict.h"
 #include "libyang.h"
 #include "log.h"
+#include "parser_data.h"
 #include "plugins_exts.h"
 #include "plugins_types.h"
 #include "tree_data.h"
@@ -604,13 +606,14 @@
         LYD_VALUE_GET(&term->value, xp_val);
         if ((ret = lyd_find_xpath4(ctx_node, ctx_node, lyxp_get_expr(xp_val->exp), xp_val->format, xp_val->prefix_data,
                 NULL, &par_set))) {
+            lyplg_ext_log(ext, LY_LLERR, ret, NULL, "Parent reference \"%s\" evaluation failed.", lyxp_get_expr(xp_val->exp));
             goto cleanup;
         }
 
         for (j = 0; j < par_set->count; ++j) {
             /* duplicate with parents in the context of the mounted data */
             if ((ret = lyd_dup_single_to_ctx(par_set->dnodes[j], trg_ctx, NULL,
-                    LYD_DUP_RECURSIVE | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup))) {
+                    LYD_DUP_RECURSIVE | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS | LYD_DUP_NO_EXT, &dup))) {
                 goto cleanup;
             }
 
@@ -669,12 +672,13 @@
  * @brief Validate callback for schema mount.
  */
 static LY_ERR
-schema_mount_validate(struct lysc_ext_instance *ext, struct lyd_node *sibling, uint32_t val_opts, struct lyd_node **diff)
+schema_mount_validate(struct lysc_ext_instance *ext, struct lyd_node *sibling, const struct lyd_node *dep_tree,
+        enum lyd_type data_type, uint32_t val_opts, struct lyd_node **diff)
 {
     LY_ERR ret = LY_SUCCESS;
     uint32_t old_log_opts, i;
     struct ly_err_item *err;
-    struct lyd_node *iter, *ext_data = NULL, *ref_first = NULL, *orig_parent = lyd_parent(sibling);
+    struct lyd_node *iter, *ext_data = NULL, *ref_first = NULL, *orig_parent = lyd_parent(sibling), *op_tree;
     struct lyd_node *ext_diff = NULL, *diff_parent = NULL;
     ly_bool ext_data_free = 0;
     struct ly_set *ref_set = NULL;
@@ -703,6 +707,11 @@
         goto cleanup;
     }
 
+    if (data_type != LYD_TYPE_DATA_YANG) {
+        /* remember the operation data tree, it may be moved */
+        op_tree = sibling;
+    }
+
     /* create accessible tree, remove LYD_EXT to not call this callback recursively */
     lyd_unlink_siblings(sibling);
     LY_LIST_FOR(sibling, iter) {
@@ -717,8 +726,15 @@
     /* only store messages in the context, log as an extension */
     old_log_opts = ly_log_options(LY_LOSTORE_LAST);
 
-    /* validate all the data */
-    ret = lyd_validate_all(&sibling, NULL, val_opts, diff ? &ext_diff : NULL);
+    if (data_type == LYD_TYPE_DATA_YANG) {
+        /* validate all the data */
+        ret = lyd_validate_all(&sibling, NULL, val_opts, diff ? &ext_diff : NULL);
+    } else {
+        /* validate the operation */
+        ret = lyd_validate_op(op_tree, dep_tree, data_type, diff ? &ext_diff : NULL);
+    }
+
+    /* restore logging */
     ly_log_options(old_log_opts);
 
     /* restore sibling tree */
diff --git a/src/tree_data_new.c b/src/tree_data_new.c
index 74f353d..148562f 100644
--- a/src/tree_data_new.c
+++ b/src/tree_data_new.c
@@ -1711,7 +1711,8 @@
     }
 
     /* resolve when and remove any invalid defaults */
-    LY_CHECK_GOTO(ret = lyd_validate_unres(&tree, NULL, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, 0, diff), cleanup);
+    ret = lyd_validate_unres(&tree, NULL, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, 0, diff);
+    LY_CHECK_GOTO(ret, cleanup);
 
 cleanup:
     ly_set_erase(&node_when, NULL);
@@ -1780,7 +1781,7 @@
     LY_CHECK_GOTO(ret = lyd_new_implicit_r(NULL, tree, NULL, module, &node_when, NULL, implicit_options, diff), cleanup);
 
     /* resolve when and remove any invalid defaults */
-    LY_CHECK_GOTO(ret = lyd_validate_unres(tree, module, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, 0, diff),
+    LY_CHECK_GOTO(ret = lyd_validate_unres(tree, module, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, 0, diff),
             cleanup);
 
     /* process nested nodes */
diff --git a/src/validation.c b/src/validation.c
index 269079a..3b0b51b 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -259,9 +259,9 @@
 }
 
 LY_ERR
-lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when, uint32_t when_xp_opts,
-        struct ly_set *node_types, struct ly_set *meta_types, struct ly_set *ext_val, uint32_t val_opts,
-        struct lyd_node **diff)
+lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum lyd_type data_type, struct ly_set *node_when,
+        uint32_t when_xp_opts, struct ly_set *node_types, struct ly_set *meta_types, struct ly_set *ext_val,
+        uint32_t val_opts, struct lyd_node **diff)
 {
     LY_ERR ret = LY_SUCCESS;
     uint32_t i;
@@ -275,7 +275,7 @@
             struct lyd_ctx_ext_val *ext_v = ext_val->objs[i];
 
             /* validate extension data */
-            ret = ext_v->ext->def->plugin->validate(ext_v->ext, ext_v->sibling, val_opts, diff);
+            ret = ext_v->ext->def->plugin->validate(ext_v->ext, ext_v->sibling, *tree, data_type, val_opts, diff);
             LY_CHECK_RET(ret);
 
             /* remove this item from the set */
@@ -1607,7 +1607,8 @@
         }
 
         /* finish incompletely validated terminal values/attributes and when conditions */
-        ret = lyd_validate_unres(first2, mod, node_when_p, 0, node_types_p, meta_types_p, ext_val_p, val_opts, diff);
+        ret = lyd_validate_unres(first2, mod, LYD_TYPE_DATA_YANG, node_when_p, 0, node_types_p, meta_types_p, ext_val_p,
+                val_opts, diff);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* perform final validation that assumes the data tree is final */
@@ -1720,6 +1721,7 @@
  * @param[in] op_node Operation node itself.
  * @param[in] dep_tree Tree to be used for validating references from the operation subtree.
  * @param[in] int_opts Internal parser options.
+ * @param[in] data_type Type of validated data.
  * @param[in] validate_subtree Whether subtree was already validated (as part of data parsing) or not (separate validation).
  * @param[in] node_when_p Set of nodes with when conditions, if NULL a local set is used.
  * @param[in] node_types_p Set of unres node types, if NULL a local set is used.
@@ -1730,12 +1732,12 @@
  * @return LY_ERR error on error.
  */
 static LY_ERR
-_lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struct lyd_node *dep_tree,
+_lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struct lyd_node *dep_tree, enum lyd_type data_type,
         uint32_t int_opts, ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p,
         struct ly_set *meta_types_p, struct ly_set *ext_val_p, struct lyd_node **diff)
 {
     LY_ERR rc = LY_SUCCESS;
-    struct lyd_node *tree_sibling, *tree_parent, *op_subtree, *op_parent, *child;
+    struct lyd_node *tree_sibling, *tree_parent, *op_subtree, *op_parent, *op_sibling_before, *op_sibling_after, *child;
     struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_val = {0};
 
     assert(op_tree && op_node);
@@ -1783,7 +1785,7 @@
 
     /* finish incompletely validated terminal values/attributes and when conditions on the full tree,
      * account for unresolved 'when' that may appear in the non-validated dependency data tree */
-    LY_CHECK_GOTO(rc = lyd_validate_unres((struct lyd_node **)&dep_tree, NULL, node_when_p, LYXP_IGNORE_WHEN,
+    LY_CHECK_GOTO(rc = lyd_validate_unres((struct lyd_node **)&dep_tree, NULL, data_type, node_when_p, LYXP_IGNORE_WHEN,
             node_types_p, meta_types_p, ext_val_p, 0, diff), cleanup);
 
     /* perform final validation of the operation/notification */
@@ -1814,10 +1816,11 @@
 {
     struct lyd_node *op_node;
     uint32_t int_opts;
+    struct ly_set ext_val = {0};
+    LY_ERR rc;
 
     LY_CHECK_ARG_RET(NULL, op_tree, !dep_tree || !dep_tree->parent, (data_type == LYD_TYPE_RPC_YANG) ||
             (data_type == LYD_TYPE_NOTIF_YANG) || (data_type == LYD_TYPE_REPLY_YANG), LY_EINVAL);
-    LY_CHECK_CTX_EQUAL_RET(LYD_CTX(op_tree), dep_tree ? LYD_CTX(dep_tree) : NULL, LY_EINVAL);
     if (diff) {
         *diff = NULL;
     }
@@ -1843,6 +1846,13 @@
         LYD_TREE_DFS_BEGIN(op_tree, op_node) {
             if (!op_node->schema) {
                 return lyd_parse_opaq_error(op_node);
+            } else if (op_node->flags & LYD_EXT) {
+                /* fully validate the rest using the extension instance callback */
+                LY_CHECK_RET(lyd_validate_nested_ext(op_node, &ext_val));
+                rc = lyd_validate_unres((struct lyd_node **)&dep_tree, NULL, data_type, NULL, 0, NULL, NULL, &ext_val,
+                        0, diff);
+                ly_set_erase(&ext_val, free);
+                return rc;
             }
 
             if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) &&
@@ -1868,5 +1878,5 @@
     }
 
     /* validate */
-    return _lyd_validate_op(op_tree, op_node, dep_tree, int_opts, 1, NULL, NULL, NULL, NULL, diff);
+    return _lyd_validate_op(op_tree, op_node, dep_tree, data_type, int_opts, 1, NULL, NULL, NULL, NULL, diff);
 }
diff --git a/src/validation.h b/src/validation.h
index abf7332..0fdd027 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -19,6 +19,7 @@
 
 #include "diff.h"
 #include "log.h"
+#include "parser_data.h"
 
 struct ly_ctx;
 struct ly_set;
@@ -45,6 +46,7 @@
  * @param[in] mod Module of the @p tree to take into consideration when deleting @p tree and moving it.
  * If set, it is expected @p tree should point to the first node of @p mod. Otherwise it will simply be
  * the first top-level sibling.
+ * @param[in] data_type Validate data type.
  * @param[in] node_when Set with nodes with "when" conditions, can be NULL.
  * @param[in] when_xp_opts Additional XPath options to use for evaluating "when".
  * @param[in] node_types Set with nodes with unresolved types, can be NULL
@@ -54,9 +56,9 @@
  * @param[in,out] diff Validation diff.
  * @return LY_ERR value.
  */
-LY_ERR lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when,
-        uint32_t when_xp_opts, struct ly_set *node_types, struct ly_set *meta_types, struct ly_set *ext_val,
-        uint32_t val_opts, struct lyd_node **diff);
+LY_ERR lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum lyd_type data_type,
+        struct ly_set *node_when, uint32_t when_xp_opts, struct ly_set *node_types, struct ly_set *meta_types,
+        struct ly_set *ext_val, uint32_t val_opts, struct lyd_node **diff);
 
 /**
  * @brief Validate new siblings. Specifically, check duplicated instances, autodelete default values and cases.