From 63384e60246e15c27eaf064f97b33c919fb0357e Mon Sep 17 00:00:00 2001
From: Jan Kadlec <jan.kadlec@nic.cz>
Date: Fri, 4 Apr 2014 15:38:50 +0200
Subject: [PATCH] new_node: Do not reuse old pointers in zone copy.

 - node->new_node is thus obosolete.
 - since hattrie_dup made almost a full copy anyway, it's feasible to
   let the zone contents recreate the zone anew, with proper pointers.
(new node was used to set only parent pointer, others were reset anyway
in adjusting, all this will die in the new zone API, but at least like
this it's easier to maintain.)
---
 src/common/hattrie/hat-trie.c |   4 +
 src/knot/nameserver/update.c  |  20 ++---
 src/knot/server/zones.c       |   7 +-
 src/knot/updates/xfr-in.c     | 119 ++++------------------------
 src/knot/updates/xfr-in.h     |   6 +-
 src/knot/zone/node.c          |  70 ++---------------
 src/knot/zone/node.h          |  10 ---
 src/knot/zone/zone-contents.c | 143 ++++++++++++++++++++++++++--------
 src/knot/zone/zone-tree.c     |  53 -------------
 src/knot/zone/zone-tree.h     |  19 -----
 src/libknot/rr.c              |   2 +-
 11 files changed, 154 insertions(+), 299 deletions(-)

diff --git a/src/common/hattrie/hat-trie.c b/src/common/hattrie/hat-trie.c
index 692365ce7..099363502 100644
--- a/src/common/hattrie/hat-trie.c
+++ b/src/common/hattrie/hat-trie.c
@@ -317,6 +317,10 @@ hattrie_t* hattrie_dup(const hattrie_t* T, value_t (*nval)(value_t))
 {
     hattrie_t *N = hattrie_create_n(T->bsize, &T->mm);
 
+    if (nval == NULL) {
+        return N;
+    }
+
     /* assignment */
     if (!nval) nval = hattrie_setval;
 
diff --git a/src/knot/nameserver/update.c b/src/knot/nameserver/update.c
index 26a29d9ae..1c91e0e5f 100644
--- a/src/knot/nameserver/update.c
+++ b/src/knot/nameserver/update.c
@@ -280,7 +280,7 @@ static int zones_process_update_auth(struct query_data *qdata)
 		sec_chs = knot_changesets_create();
 		sec_ch = knot_changesets_create_changeset(sec_chs);
 		if (sec_chs == NULL || sec_ch == NULL) {
-			xfrin_rollback_update(chgsets, old_contents, &new_contents);
+			xfrin_rollback_update(chgsets, &new_contents);
 			knot_changesets_free(&chgsets);
 			free(msg);
 			return KNOT_ENOMEM;
@@ -313,7 +313,7 @@ static int zones_process_update_auth(struct query_data *qdata)
 			log_zone_error("%s: Failed to sign incoming update (%s)"
 			               "\n", msg, knot_strerror(ret));
 			1 == 1; // TODO: rollback
-			xfrin_rollback_update(chgsets, old_contents, &new_contents);
+			xfrin_rollback_update(chgsets, &new_contents);
 			knot_changesets_free(&chgsets);
 			knot_changesets_free(&sec_chs);
 			free(msg);
@@ -328,7 +328,7 @@ static int zones_process_update_auth(struct query_data *qdata)
 	if (ret != KNOT_EOK) {
 		log_zone_error("%s: Failed to save new entry to journal (%s)\n",
 		               msg, knot_strerror(ret));
-		xfrin_rollback_update(chgsets, old_contents, &new_contents);
+		xfrin_rollback_update(chgsets, &new_contents);
 		zones_free_merged_changesets(chgsets, sec_chs);
 		free(msg);
 		return ret;
@@ -337,10 +337,10 @@ static int zones_process_update_auth(struct query_data *qdata)
 	bool new_signatures = !knot_changeset_is_empty(sec_ch);
 	// Apply DNSSEC changeset
 	if (new_signatures) {
-		ret = xfrin_apply_changesets_dnssec_ddns(zone, old_contents,
-		                                    new_contents,
-		                                    sec_chs,
-		                                    chgsets);
+		ret = xfrin_apply_changesets_dnssec_ddns(zone,
+		                                         new_contents,
+		                                         sec_chs,
+		                                         chgsets);
 		if (ret != KNOT_EOK) {
 			log_zone_error("%s: Failed to sign incoming update (%s)"
 			               "\n", msg, knot_strerror(ret));
@@ -366,7 +366,7 @@ static int zones_process_update_auth(struct query_data *qdata)
 		ret = knot_zone_contents_adjust_nsec3_pointers(new_contents);
 		if (ret != KNOT_EOK) {
 			zones_store_changesets_rollback(transaction);
-			xfrin_rollback_update(chgsets, old_contents, &new_contents);
+			xfrin_rollback_update(chgsets, &new_contents);
 			zones_free_merged_changesets(chgsets, sec_chs);
 			free(msg);
 			return ret;
@@ -379,7 +379,7 @@ static int zones_process_update_auth(struct query_data *qdata)
 		if (ret != KNOT_EOK) {
 			log_zone_error("%s: Failed to commit new journal entry "
 			               "(%s).\n", msg, knot_strerror(ret));
-			xfrin_rollback_update(chgsets, old_contents, &new_contents);
+			xfrin_rollback_update(chgsets, &new_contents);
 			zones_free_merged_changesets(chgsets, sec_chs);
 			free(msg);
 			return ret;
@@ -395,7 +395,7 @@ static int zones_process_update_auth(struct query_data *qdata)
 		log_zone_error("%s: Failed to replace current zone (%s)\n",
 		               msg, knot_strerror(ret));
 		// Cleanup old and new contents
-		xfrin_rollback_update(chgsets, old_contents, &new_contents);
+		xfrin_rollback_update(chgsets, &new_contents);
 
 		/* Free changesets, but not the data. */
 		zones_free_merged_changesets(chgsets, sec_chs);
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index de0d814d1..2cb7736d0 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -1470,7 +1470,7 @@ int zones_store_and_apply_chgsets(knot_changesets_t *chs,
 	/* Commit transaction. */
 	ret = zones_store_changesets_commit(transaction);
 	if (ret != KNOT_EOK) {
-		xfrin_rollback_update(chs, zone->contents, new_contents);
+		xfrin_rollback_update(chs, new_contents);
 		log_zone_error("%s Failed to commit stored changesets.\n", msgpref);
 		knot_changesets_free(&chs);
 		return ret;
@@ -1486,7 +1486,7 @@ int zones_store_and_apply_chgsets(knot_changesets_t *chs,
 	if (switch_ret != KNOT_EOK) {
 		log_zone_error("%s Failed to replace current zone.\n", msgpref);
 		// Cleanup old and new contents
-		xfrin_rollback_update(chs, zone->contents, new_contents);
+		xfrin_rollback_update(chs, new_contents);
 
 		/* Free changesets, but not the data. */
 		knot_changesets_free(&chs);
@@ -1930,7 +1930,6 @@ int zones_journal_apply(zone_t *zone)
 
 					// Cleanup old and new contents
 					xfrin_rollback_update(chsets,
-					                      zone->contents,
 					                      &contents);
 				}
 			}
@@ -2151,7 +2150,7 @@ static int store_chgsets_after_load(zone_t *old_zone, zone_t *zone,
 			               "switching zone (%s).\n",
 			               zone->conf->name, knot_strerror(ret));
 			// Cleanup old and new contents
-			xfrin_rollback_update(diff_chs, zone->contents, &new_contents);
+			xfrin_rollback_update(diff_chs, &new_contents);
 			return ret;
 		}
 
diff --git a/src/knot/updates/xfr-in.c b/src/knot/updates/xfr-in.c
index 2dd755e3e..32e13b127 100644
--- a/src/knot/updates/xfr-in.c
+++ b/src/knot/updates/xfr-in.c
@@ -597,24 +597,6 @@ cleanup:
 /* Applying changesets to zone                                                */
 /*----------------------------------------------------------------------------*/
 
-void xfrin_zone_contents_free(knot_zone_contents_t **contents)
-{
-	/*! \todo This should be all in some API!! */
-
-	// free the zone tree with nodes
-	dbg_zone("Destroying zone tree.\n");
-	knot_zone_tree_deep_free(&(*contents)->nodes);
-	dbg_zone("Destroying NSEC3 zone tree.\n");
-	knot_zone_tree_deep_free(&(*contents)->nsec3_nodes);
-
-	knot_nsec3_params_free(&(*contents)->nsec3_params);
-
-	free(*contents);
-	*contents = NULL;
-}
-
-/*----------------------------------------------------------------------------*/
-
 void xfrin_cleanup_successful_update(knot_changesets_t *chgs)
 {
 	if (chgs == NULL) {
@@ -632,46 +614,10 @@ void xfrin_cleanup_successful_update(knot_changesets_t *chgs)
 	};
 }
 
-/*----------------------------------------------------------------------------*/
-/* New changeset applying                                                     */
-/*----------------------------------------------------------------------------*/
-
-static int xfrin_switch_nodes_in_node(knot_node_t **node, void *data)
-{
-	UNUSED(data);
-
-	assert(node && *node);
-	assert(knot_node_new_node(*node) == NULL);
-
-	knot_node_update_refs(*node);
-
-	return KNOT_EOK;
-}
-
 /*----------------------------------------------------------------------------*/
 
-static int xfrin_switch_nodes(knot_zone_contents_t *contents_copy)
+static void xfrin_zone_contents_free(knot_zone_contents_t **contents)
 {
-	assert(contents_copy != NULL);
-
-	// Traverse the trees and for each node check every reference
-	// stored in that node. The node itself should be new.
-	int ret = knot_zone_tree_apply(contents_copy->nodes,
-	                               xfrin_switch_nodes_in_node, NULL);
-	if (ret == KNOT_EOK) {
-		ret = knot_zone_tree_apply(contents_copy->nsec3_nodes,
-		                           xfrin_switch_nodes_in_node, NULL);
-	}
-
-	return ret;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static void xfrin_zone_contents_free2(knot_zone_contents_t **contents)
-{
-	/*! \todo This should be all in some API!! */
-
 	// free the zone tree, but only the structure
 	// (nodes are already destroyed)
 	dbg_zone("Destroying zone tree.\n");
@@ -687,44 +633,22 @@ static void xfrin_zone_contents_free2(knot_zone_contents_t **contents)
 
 /*----------------------------------------------------------------------------*/
 
-static int xfrin_cleanup_old_nodes(knot_node_t **node, void *data)
+static void xfrin_cleanup_failed_update(knot_zone_contents_t **new_contents)
 {
-	UNUSED(data);
-	assert(node && *node);
-
-	knot_node_set_new_node(*node, NULL);
-
-	return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static void xfrin_cleanup_failed_update(knot_zone_contents_t *old_contents,
-                                        knot_zone_contents_t **new_contents)
-{
-	if (old_contents == NULL && new_contents == NULL) {
+	if (new_contents == NULL) {
 		return;
 	}
 
 	if (*new_contents != NULL) {
 		// destroy the shallow copy of zone
-		xfrin_zone_contents_free2(new_contents);
+		xfrin_zone_contents_free(new_contents);
 	}
 
-	if (old_contents != NULL) {
-		// cleanup old zone tree - reset pointers to new node to NULL
-		knot_zone_tree_apply(old_contents->nodes, xfrin_cleanup_old_nodes,
-				     NULL);
-
-		knot_zone_tree_apply(old_contents->nsec3_nodes, xfrin_cleanup_old_nodes,
-				     NULL);
-	}
 }
 
 /*----------------------------------------------------------------------------*/
 
 void xfrin_rollback_update(knot_changesets_t *chgs,
-                           knot_zone_contents_t *old_contents,
                            knot_zone_contents_t **new_contents)
 {
 	if (chgs != NULL) {
@@ -734,11 +658,11 @@ void xfrin_rollback_update(knot_changesets_t *chgs,
 			rrs_list_clear(&change->new_data, NULL);
 			// Keep old RR data
 			ptrlist_free(&change->old_data, NULL);
-				init_list(&change->new_data);
-		init_list(&change->old_data);
+			init_list(&change->new_data);
+			init_list(&change->old_data);
 		};
 	}
-	xfrin_cleanup_failed_update(old_contents, new_contents);
+	xfrin_cleanup_failed_update(new_contents);
 }
 
 /*----------------------------------------------------------------------------*/
@@ -999,7 +923,7 @@ static int xfrin_apply_changeset(list_t *old_rrs, list_t *new_rrs,
 	if (soa == NULL || knot_rrs_soa_serial(soa)
 			   != chset->serial_from) {
 		dbg_xfrin("SOA serials do not match!!\n");
-		return KNOT_ERROR;
+		return KNOT_EINVAL;
 	}
 
 	int ret = xfrin_apply_remove(contents, chset, old_rrs, new_rrs);
@@ -1140,7 +1064,7 @@ int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents,
 	 * updated.
 	 *
 	 * This will create new zone contents structures (normal nodes' tree,
-	 * NSEC3 tree, hash table, domain name table), and copy all nodes.
+	 * NSEC3 tree), and copy all nodes.
 	 * The data in the nodes (RRSets) remain the same though.
 	 */
 	knot_zone_contents_t *contents_copy = NULL;
@@ -1155,19 +1079,6 @@ int xfrin_prepare_zone_copy(knot_zone_contents_t *old_contents,
 
 	assert(knot_zone_contents_apex(contents_copy) != NULL);
 
-	/*
-	 * Fix references to new nodes. Some references in new nodes may point
-	 * to old nodes. Hash table contains only old nodes.
-	 */
-	dbg_xfrin("Switching ptrs pointing to old nodes to the new nodes.\n");
-	ret = xfrin_switch_nodes(contents_copy);
-	if (ret != KNOT_EOK) {
-		dbg_xfrin("Failed to switch pointers in nodes.\n");
-		knot_zone_contents_free(&contents_copy);
-		return ret;
-	}
-	assert(knot_zone_contents_apex(contents_copy) != NULL);
-
 	*new_contents = contents_copy;
 
 	return KNOT_EOK;
@@ -1246,12 +1157,11 @@ int xfrin_apply_changesets_directly(knot_zone_contents_t *contents,
 
 /* Post-DDNS application, no need to shallow copy. */
 int xfrin_apply_changesets_dnssec_ddns(zone_t *zone,
-                                       knot_zone_contents_t *z_old,
                                        knot_zone_contents_t *z_new,
                                        knot_changesets_t *sec_chsets,
                                        knot_changesets_t *chsets)
 {
-	if (zone == NULL || z_old == NULL || z_new == NULL ||
+	if (zone == NULL || z_new == NULL ||
 	    sec_chsets == NULL || chsets == NULL) {
 		return KNOT_EINVAL;
 	}
@@ -1262,7 +1172,7 @@ int xfrin_apply_changesets_dnssec_ddns(zone_t *zone,
 	/* Apply changes. */
 	int ret = xfrin_apply_changesets_directly(z_new, sec_chsets);
 	if (ret != KNOT_EOK) {
-		xfrin_rollback_update(sec_chsets, z_old, &z_new);
+		xfrin_rollback_update(sec_chsets, &z_new);
 		dbg_xfrin("Failed to apply changesets to zone: "
 		          "%s\n", knot_strerror(ret));
 		return ret;
@@ -1273,7 +1183,7 @@ int xfrin_apply_changesets_dnssec_ddns(zone_t *zone,
 	if (ret != KNOT_EOK) {
 		dbg_xfrin("Failed to finalize updated zone: %s\n",
 		          knot_strerror(ret));
-		xfrin_rollback_update(sec_chsets, z_old, &z_new);
+		xfrin_rollback_update(sec_chsets, &z_new);
 		return ret;
 	}
 
@@ -1320,8 +1230,7 @@ int xfrin_apply_changesets(zone_t *zone,
 		                            &set->new_data,
 		                            contents_copy, set);
 		if (ret != KNOT_EOK) {
-			xfrin_rollback_update(chsets, old_contents,
-			                      &contents_copy);
+			xfrin_rollback_update(chsets, &contents_copy);
 			dbg_xfrin("Failed to apply changesets to zone: "
 				  "%s\n", knot_strerror(ret));
 			return ret;
@@ -1338,7 +1247,7 @@ int xfrin_apply_changesets(zone_t *zone,
 	if (ret != KNOT_EOK) {
 		dbg_xfrin("Failed to finalize updated zone: %s\n",
 			  knot_strerror(ret));
-		xfrin_rollback_update(chsets, old_contents, &contents_copy);
+		xfrin_rollback_update(chsets, &contents_copy);
 		return ret;
 	}
 
diff --git a/src/knot/updates/xfr-in.h b/src/knot/updates/xfr-in.h
index ab3f95187..e6e772129 100644
--- a/src/knot/updates/xfr-in.h
+++ b/src/knot/updates/xfr-in.h
@@ -155,7 +155,6 @@ int xfrin_apply_changesets(zone_t *zone,
 /*!
  * \brief Applies DNSSEC changesets after DDNS.
  *
- * \param z_old           Old contents for possible rollbacks.
  * \param z_new           Post DDNS/reload zone.
  * \param sec_chsets      Changes with RRSIGs/NSEC(3)s.
  * \param chsets          DDNS/reload changes, for rollback.
@@ -165,7 +164,7 @@ int xfrin_apply_changesets(zone_t *zone,
  * by the UPDATE-processing function. It uses new and old zones from this
  * operation.
  */
-int xfrin_apply_changesets_dnssec_ddns(zone_t *zone, knot_zone_contents_t *z_old,
+int xfrin_apply_changesets_dnssec_ddns(zone_t *zone,
                                        knot_zone_contents_t *z_new,
                                        knot_changesets_t *sec_chsets,
                                        knot_changesets_t *chsets);
@@ -199,7 +198,7 @@ int xfrin_switch_zone(zone_t *zone,
                       knot_zone_contents_t *new_contents,
                       int transfer_type);
 
-void xfrin_rollback_update(knot_changesets_t *chgs, knot_zone_contents_t *old_contents,
+void xfrin_rollback_update(knot_changesets_t *chgs,
                            knot_zone_contents_t **new_contents);
 
 int xfrin_copy_rrset(knot_node_t *node, uint16_t type,
@@ -211,7 +210,6 @@ int xfrin_replace_rrset_in_node(knot_node_t *node,
                                 knot_rrset_t *rrset_new,
                                 knot_zone_contents_t *contents);
 
-void xfrin_zone_contents_free(knot_zone_contents_t **contents);
 void xfrin_cleanup_successful_update(knot_changesets_t *chgs);
 
 #endif /* _KNOTXFR_IN_H_ */
diff --git a/src/knot/zone/node.c b/src/knot/zone/node.c
index ae8cfda74..6e26eee43 100644
--- a/src/knot/zone/node.c
+++ b/src/knot/zone/node.c
@@ -401,63 +401,6 @@ const knot_node_t *knot_node_wildcard_child(const knot_node_t *node)
 
 /*----------------------------------------------------------------------------*/
 
-const knot_node_t *knot_node_new_node(const knot_node_t *node)
-{
-	if (node == NULL) {
-		return NULL;
-	}
-
-	return node->new_node;
-}
-
-/*----------------------------------------------------------------------------*/
-
-knot_node_t *knot_node_get_new_node(const knot_node_t *node)
-{
-	if (node == NULL) {
-		return NULL;
-	}
-
-	return node->new_node;
-}
-
-/*----------------------------------------------------------------------------*/
-
-void knot_node_set_new_node(knot_node_t *node,
-                              knot_node_t *new_node)
-{
-	if (node == NULL) {
-		return;
-	}
-
-	node->new_node = new_node;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static void knot_node_update_ref(knot_node_t **ref)
-{
-	if (*ref != NULL && (*ref)->new_node != NULL) {
-		*ref = (*ref)->new_node;
-	}
-}
-
-/*----------------------------------------------------------------------------*/
-
-void knot_node_update_refs(knot_node_t *node)
-{
-	// reference to previous node
-	knot_node_update_ref(&node->prev);
-	// reference to parent
-	knot_node_update_ref(&node->parent);
-	// reference to wildcard child
-	knot_node_update_ref(&node->wildcard_child);
-	// reference to NSEC3 node
-	knot_node_update_ref(&node->nsec3_node);
-}
-
-/*----------------------------------------------------------------------------*/
-
 void knot_node_set_deleg_point(knot_node_t *node)
 {
 	if (node == NULL) {
@@ -638,18 +581,21 @@ int knot_node_shallow_copy(const knot_node_t *from, knot_node_t **to)
 	if (*to == NULL) {
 		return KNOT_ENOMEM;
 	}
+	memset(*to, 0, sizeof(knot_node_t));
 
-	// do not use the API function to set parent, so that children count
-	// is not changed
-	memcpy(*to, from, sizeof(knot_node_t));
+	// Copy owner
 	(*to)->owner = knot_dname_copy(from->owner, NULL);
+	if ((*to)->owner == NULL) {
+		free(*to);
+		return KNOT_ENOMEM;
+	}
 
 	// copy RRSets
+	(*to)->rrset_count = from->rrset_count;
 	size_t rrlen = sizeof(struct rr_data) * from->rrset_count;
 	(*to)->rrs = malloc(rrlen);
 	if ((*to)->rrs == NULL) {
-		free(*to);
-		*to = NULL;
+		knot_node_free(to);
 		return KNOT_ENOMEM;
 	}
 	memcpy((*to)->rrs, from->rrs, rrlen);
diff --git a/src/knot/zone/node.h b/src/knot/zone/node.h
index b374bdd5e..df636b274 100644
--- a/src/knot/zone/node.h
+++ b/src/knot/zone/node.h
@@ -68,8 +68,6 @@ struct knot_node {
 	 */
 	struct knot_node *nsec3_node;
 
-	struct knot_node *new_node;
-
 	unsigned int children;
 
 	uint16_t rrset_count; /*!< Number of RRSets stored in the node. */
@@ -286,14 +284,6 @@ void knot_node_set_wildcard_child(knot_node_t *node,
 
 knot_node_t *knot_node_get_wildcard_child(const knot_node_t *node);
 
-const knot_node_t *knot_node_new_node(const knot_node_t *node);
-
-knot_node_t *knot_node_get_new_node(const knot_node_t *node);
-
-void knot_node_set_new_node(knot_node_t *node, knot_node_t *new_node);
-
-void knot_node_update_refs(knot_node_t *node);
-
 /*!
  * \brief Mark the node as a delegation point.
  *
diff --git a/src/knot/zone/zone-contents.c b/src/knot/zone/zone-contents.c
index 467e12de1..3645ef62d 100644
--- a/src/knot/zone/zone-contents.c
+++ b/src/knot/zone/zone-contents.c
@@ -566,7 +566,6 @@ dbg_zone_exec_detail(
 
 			/* Insert node to a tree. */
 			dbg_zone_detail("Inserting new node to zone tree.\n");
-			assert(knot_zone_contents_find_node(zone, parent) == NULL);
 			ret = knot_zone_tree_insert(zone->nodes, next_node);
 			if (ret != KNOT_EOK) {
 				knot_node_free(&next_node);
@@ -599,13 +598,9 @@ dbg_zone_exec_detail(
 
 /*----------------------------------------------------------------------------*/
 
-int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *zone,
-                                        knot_node_t *node, int create_parents,
-                                        uint8_t flags)
+static int knot_zone_contents_add_nsec3_node(knot_zone_contents_t *zone,
+                                             knot_node_t *node)
 {
-	UNUSED(create_parents);
-	UNUSED(flags);
-
 	if (zone == NULL || node == NULL) {
 		return KNOT_EINVAL;
 	}
@@ -684,7 +679,7 @@ static int insert_rr(knot_zone_contents_t *z,
 			if (*n == NULL) {
 				return KNOT_ENOMEM;
 			}
-			ret = nsec3 ? knot_zone_contents_add_nsec3_node(z, *n, true, 0) :
+			ret = nsec3 ? knot_zone_contents_add_nsec3_node(z, *n) :
 			              knot_zone_contents_add_node(z, *n, true, 0);
 			if (ret != KNOT_EOK) {
 				knot_node_free(n);
@@ -695,6 +690,97 @@ static int insert_rr(knot_zone_contents_t *z,
 	return knot_node_add_rrset(*n, rr);
 }
 
+static int recreate_normal_tree(const knot_zone_contents_t *z,
+                                knot_zone_contents_t *out)
+{
+	out->nodes = hattrie_dup(z->nodes, NULL);
+	if (out->nodes == NULL) {
+		return KNOT_ENOMEM;
+	}
+
+	// Insert APEX first.
+	knot_node_t *apex_cpy;
+	int ret = knot_node_shallow_copy(z->apex, &apex_cpy);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+
+	// Normal additions need apex ... so we need to insert directly.
+	ret = knot_zone_tree_insert(out->nodes, apex_cpy);
+	if (ret != KNOT_EOK) {
+		knot_node_free(&apex_cpy);
+		return ret;
+	}
+
+	out->apex = apex_cpy;
+
+	hattrie_iter_t *itt = hattrie_iter_begin(z->nodes, true);
+	if (itt == NULL) {
+		return KNOT_ENOMEM;
+	}
+	while (!hattrie_iter_finished(itt)) {
+		const knot_node_t *to_cpy = (knot_node_t *)*hattrie_iter_val(itt);
+		if (to_cpy == z->apex) {
+			// Inserted already.
+			hattrie_iter_next(itt);
+			continue;
+		}
+		knot_node_t *to_add;
+		int ret = knot_node_shallow_copy(to_cpy, &to_add);
+		if (ret != KNOT_EOK) {
+			hattrie_iter_free(itt);
+			return ret;
+		}
+		ret = knot_zone_contents_add_node(out, to_add, true, 0);
+		if (ret != KNOT_EOK) {
+			knot_node_free(&to_add);
+			hattrie_iter_free(itt);
+			return ret;
+		}
+		hattrie_iter_next(itt);
+	}
+
+	hattrie_iter_free(itt);
+	hattrie_build_index(out->nodes);
+
+	return KNOT_EOK;
+}
+
+static int recreate_nsec3_tree(const knot_zone_contents_t *z,
+                               knot_zone_contents_t *out)
+{
+	out->nsec3_nodes = hattrie_dup(z->nsec3_nodes, NULL);
+	if (out->nsec3_nodes == NULL) {
+		return KNOT_ENOMEM;
+	}
+
+	hattrie_iter_t *itt = hattrie_iter_begin(z->nsec3_nodes, false);
+	if (itt == NULL) {
+		return KNOT_ENOMEM;
+	}
+	while (!hattrie_iter_finished(itt)) {
+		const knot_node_t *to_cpy = (knot_node_t *)*hattrie_iter_val(itt);
+		knot_node_t *to_add;
+		int ret = knot_node_shallow_copy(to_cpy, &to_add);
+		if (ret != KNOT_EOK) {
+			hattrie_iter_free(itt);
+			return ret;
+		}
+		ret = knot_zone_contents_add_nsec3_node(out, to_add);
+		if (ret != KNOT_EOK) {
+			hattrie_iter_free(itt);
+			knot_node_free(&to_add);
+			return ret;
+		}
+		hattrie_iter_next(itt);
+	}
+
+	hattrie_iter_free(itt);
+	hattrie_build_index(out->nsec3_nodes);
+
+	return KNOT_EOK;
+}
+
 int knot_zone_contents_add_rr(knot_zone_contents_t *z,
                               const knot_rrset_t *rr, knot_node_t **n)
 {
@@ -1249,41 +1335,36 @@ int knot_zone_contents_shallow_copy(const knot_zone_contents_t *from,
 		return KNOT_EINVAL;
 	}
 
-	int ret = KNOT_EOK;
-
-	knot_zone_contents_t *contents = (knot_zone_contents_t *)calloc(
-					     1, sizeof(knot_zone_contents_t));
+	knot_zone_contents_t *contents = calloc(1, sizeof(knot_zone_contents_t));
 	if (contents == NULL) {
 		ERR_ALLOC_FAILED;
 		return KNOT_ENOMEM;
 	}
 
-	//contents->apex = from->apex;
-
-	contents->node_count = from->node_count;
 	contents->flags = from->flags;
-	// set the 'new' flag
 	knot_zone_contents_set_gen_new(contents);
 
-	if ((ret = knot_zone_tree_deep_copy(from->nodes,
-					    &contents->nodes)) != KNOT_EOK
-	    || (ret = knot_zone_tree_deep_copy(from->nsec3_nodes,
-					  &contents->nsec3_nodes)) != KNOT_EOK) {
-		goto cleanup;
+	int ret = recreate_normal_tree(from, contents);
+	if (ret != KNOT_EOK) {
+		knot_zone_tree_free(&contents->nodes);
+		free(contents);
+		return ret;
 	}
 
-	contents->apex = knot_node_get_new_node(from->apex);
-
-	dbg_zone("knot_zone_contents_shallow_copy: finished OK\n");
+	if (contents->nsec3_nodes) {
+		ret = recreate_nsec3_tree(from, contents);
+		if (ret != KNOT_EOK) {
+			knot_zone_tree_free(&contents->nodes);
+			knot_zone_tree_free(&contents->nsec3_nodes);
+			free(contents);
+			return ret;
+		}
+	} else {
+		contents->nsec3_nodes = NULL;
+	}
 
 	*to = contents;
 	return KNOT_EOK;
-
-cleanup:
-	knot_zone_tree_free(&contents->nodes);
-	knot_zone_tree_free(&contents->nsec3_nodes);
-	free(contents);
-	return ret;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -1368,7 +1449,7 @@ knot_node_t *zone_contents_get_node_for_rr(knot_zone_contents_t *zone,
 		if (!nsec3) {
 			ret = knot_zone_contents_add_node(zone, node, 1, 0);
 		} else {
-			ret = knot_zone_contents_add_nsec3_node(zone, node, 1, 0);
+			ret = knot_zone_contents_add_nsec3_node(zone, node);
 		}
 		if (ret != KNOT_EOK) {
 			knot_node_free(&node);
diff --git a/src/knot/zone/zone-tree.c b/src/knot/zone/zone-tree.c
index 362869487..af36361e8 100644
--- a/src/knot/zone/zone-tree.c
+++ b/src/knot/zone/zone-tree.c
@@ -23,23 +23,6 @@
 #include "common/debug.h"
 #include "common/hattrie/hat-trie.h"
 
-/*----------------------------------------------------------------------------*/
-/* Non-API functions                                                          */
-/*----------------------------------------------------------------------------*/
-
-static value_t knot_zone_node_copy(value_t v)
-{
-	return v;
-}
-
-static value_t knot_zone_node_deep_copy(value_t v)
-{
-	knot_node_t *n = NULL;
-	knot_node_shallow_copy((knot_node_t *)v, &n);
-	knot_node_set_new_node((knot_node_t *)v, n);
-	return (value_t)n;
-}
-
 /*----------------------------------------------------------------------------*/
 /* API functions                                                              */
 /*----------------------------------------------------------------------------*/
@@ -279,42 +262,6 @@ int knot_zone_tree_apply(knot_zone_tree_t *tree,
 
 /*----------------------------------------------------------------------------*/
 
-int knot_zone_tree_shallow_copy(knot_zone_tree_t *from,
-                                  knot_zone_tree_t **to)
-{
-	if (to == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	if (from != NULL) {
-		*to = hattrie_dup(from, knot_zone_node_copy);
-	} else {
-		*to = NULL;
-	}
-
-	return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
-int knot_zone_tree_deep_copy(knot_zone_tree_t *from,
-                             knot_zone_tree_t **to)
-{
-	if (to == NULL) {
-		return KNOT_EINVAL;
-	}
-
-	if (from != NULL) {
-		*to = hattrie_dup(from, knot_zone_node_deep_copy);
-	} else {
-		*to = NULL;
-	}
-
-	return KNOT_EOK;
-}
-
-/*----------------------------------------------------------------------------*/
-
 void knot_zone_tree_free(knot_zone_tree_t **tree)
 {
 	if (tree == NULL || *tree == NULL) {
diff --git a/src/knot/zone/zone-tree.h b/src/knot/zone/zone-tree.h
index cbc234ead..07fcae54e 100644
--- a/src/knot/zone/zone-tree.h
+++ b/src/knot/zone/zone-tree.h
@@ -207,25 +207,6 @@ int knot_zone_tree_apply_inorder(knot_zone_tree_t *tree,
 int knot_zone_tree_apply(knot_zone_tree_t *tree,
                          knot_zone_tree_apply_cb_t function, void *data);
 
-/*!
- * \brief Copies the whole zone tree structure (but not the data contained
- *        within).
- *
- * \warning This function does not check if the target zone tree is empty,
- *          it just replaces the root pointer.
- *
- * \param from Original zone tree.
- * \param to Zone tree to copy the original one into.
- *
- * \retval KNOT_EOK
- * \retval KNOT_ENOMEM
- */
-int knot_zone_tree_shallow_copy(knot_zone_tree_t *from,
-                                  knot_zone_tree_t **to);
-
-int knot_zone_tree_deep_copy(knot_zone_tree_t *from,
-                             knot_zone_tree_t **to);
-
 /*!
  * \brief Destroys the zone tree, not touching the saved data.
  *
diff --git a/src/libknot/rr.c b/src/libknot/rr.c
index eded54e76..328f6d7dc 100644
--- a/src/libknot/rr.c
+++ b/src/libknot/rr.c
@@ -117,7 +117,7 @@ static int add_rr_at(knot_rrs_t *rrs, const knot_rr_t *rr, size_t pos,
 		knot_rr_t *new_rr = knot_rrs_rr(rrs, pos);
 		knot_rr_set_size(new_rr, size);
 		knot_rr_set_ttl(new_rr, ttl);
-		memcpy(knot_rr_rdata, rdata, size);
+		memcpy(knot_rr_rdata(new_rr), rdata, size);
 		return KNOT_EOK;
 	}
 
-- 
GitLab