From fa7f7e3d7be49f97ae796dd3faa9b9140dd64a73 Mon Sep 17 00:00:00 2001
From: Ludwig Krispenz <lkrispen@redhat.com>
Date: Fri, 9 Jun 2017 10:23:59 +0200
Subject: [PATCH 3/4] ticket 49043 - part3 - hanle complex scenarios

---
 ldap/servers/plugins/replication/repl5_plugins.c |  11 +-
 ldap/servers/plugins/replication/repl5_ruv.c     |   4 +-
 ldap/servers/plugins/replication/urp.c           | 389 +++++++++++++++++++----
 ldap/servers/plugins/replication/urp.h           |   5 +
 ldap/servers/plugins/replication/urp_tombstone.c | 200 ++++++++++++
 ldap/servers/slapd/back-ldbm/ldbm_add.c          | 110 ++++++-
 ldap/servers/slapd/back-ldbm/ldbm_delete.c       |  45 ++-
 ldap/servers/slapd/back-ldbm/ldbm_modify.c       |   5 +-
 ldap/servers/slapd/pblock.c                      |  10 +
 ldap/servers/slapd/pblock_v3.h                   |   1 +
 ldap/servers/slapd/slapi-plugin.h                |   4 +
 11 files changed, 688 insertions(+), 96 deletions(-)

diff --git a/ldap/servers/plugins/replication/repl5_plugins.c b/ldap/servers/plugins/replication/repl5_plugins.c
index f283b21..4a9e6e3 100644
--- a/ldap/servers/plugins/replication/repl5_plugins.c
+++ b/ldap/servers/plugins/replication/repl5_plugins.c
@@ -747,6 +747,8 @@ multimaster_mmr_postop (Slapi_PBlock *pb, int flags)
 		rc = multimaster_be_betxnpostop_modrdn(pb);
 		break;
 	}
+	slapi_log_err(SLAPI_LOG_REPL, REPLICATION_SUBSYSTEM,
+                     "multimaster_mmr_postop - error %d for oparation %d.\n", rc, flags);
 	return rc;
 }
 
@@ -1209,6 +1211,7 @@ write_changelog_and_ruv (Slapi_PBlock *pb)
 			op_params->p.p_modify.modify_mods != NULL)
 		{
 			void *txn = NULL;
+			char csn_str[CSN_STRSIZE];
 			if (cl5_is_diskfull() && !cl5_diskspace_is_available()) 
 			{
 				slapi_log_err(SLAPI_LOG_CRIT, repl_plugin_name,
@@ -1216,11 +1219,17 @@ write_changelog_and_ruv (Slapi_PBlock *pb)
 				goto common_return;
 			}
 			slapi_pblock_get(pb, SLAPI_TXN, &txn);
+			slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name,
+					"write_changelog_and_ruv - Writing change for "
+					"%s (uniqid: %s, optype: %lu) to changelog csn %s\n",
+					REPL_GET_DN(&op_params->target_address),
+					op_params->target_address.uniqueid,
+					op_params->operation_type,
+					csn_as_string(op_params->csn, PR_FALSE, csn_str));
 			rc = cl5WriteOperationTxn(repl_name, repl_gen, op_params, 
 									  !operation_is_flag_set(op, OP_FLAG_REPLICATED), txn);
 			if (rc != CL5_SUCCESS)
 			{
-    			char csn_str[CSN_STRSIZE];
 			    /* ONREPL - log error */
         		slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name,
 					"write_changelog_and_ruv - Can't add a change for "
diff --git a/ldap/servers/plugins/replication/repl5_ruv.c b/ldap/servers/plugins/replication/repl5_ruv.c
index d59e6d2..c549b8e 100644
--- a/ldap/servers/plugins/replication/repl5_ruv.c
+++ b/ldap/servers/plugins/replication/repl5_ruv.c
@@ -1637,7 +1637,7 @@ int ruv_add_csn_inprogress (RUV *ruv, const CSN *csn)
     {
         if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
             slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "ruv_add_csn_inprogress - "
-                            "The csn %s has already be seen - ignoring\n",
+                            "The csn %s has already be seen (in ruv) - ignoring\n",
                             csn_as_string (csn, PR_FALSE, csn_str));
         }
         rc = RUV_COVERS_CSN;
@@ -1653,7 +1653,7 @@ int ruv_add_csn_inprogress (RUV *ruv, const CSN *csn)
     {
         if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
             slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "ruv_add_csn_inprogress - "
-                            "The csn %s has already be seen - ignoring\n",
+                            "The csn %s has already be seen (in csnpl) - ignoring\n",
                             csn_as_string (csn, PR_FALSE, csn_str));
         }
         set_thread_primary_csn(NULL);
diff --git a/ldap/servers/plugins/replication/urp.c b/ldap/servers/plugins/replication/urp.c
index b82418f..5a33e86 100644
--- a/ldap/servers/plugins/replication/urp.c
+++ b/ldap/servers/plugins/replication/urp.c
@@ -24,19 +24,22 @@
 extern int slapi_log_urp;
 
 static int urp_add_resolve_parententry (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *entry, Slapi_Entry *parententry, CSN *opcsn);
-static int urp_add_check_tombstone (char *sessionid, Slapi_Entry *entry, CSN *opcsn);
+static int urp_add_check_tombstone (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *entry, CSN *opcsn);
+static int urp_delete_check_conflict (char *sessionid, Slapi_Entry *tombstone_entry, CSN *opcsn);
 static int urp_add_new_entry_to_conflict (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *addentry, CSN *opcsn);
 static int urp_annotate_dn (char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const char *optype, char **conflict_dn);
 static int urp_conflict_to_glue (char *sessionid, const Slapi_Entry *entry, Slapi_DN *parentdn, CSN *opcsn);
 static char *urp_find_tombstone_for_glue (Slapi_PBlock *pb, char *sessionid, const Slapi_Entry *entry, Slapi_DN *parentdn, CSN *opcsn);
+static char *urp_find_valid_entry_to_delete(Slapi_PBlock *pb, const Slapi_Entry *deleteentry, char *sessionid, CSN *opcsn);
 static int urp_naming_conflict_removal (Slapi_PBlock *pb, char *sessionid, CSN *opcsn, const char *optype);
 static int mod_namingconflict_attr (const char *uniqueid, const Slapi_DN *entrysdn, const Slapi_DN *conflictsdn, CSN *opcsn, const char *optype);
 static int mod_objectclass_attr (const char *uniqueid, const Slapi_DN *entrysdn, const Slapi_DN *conflictsdn, CSN *opcsn, const char *optype);
 static int del_replconflict_attr (const Slapi_Entry *entry, CSN *opcsn, int opflags);
 static char *get_dn_plus_uniqueid(char *sessionid,const Slapi_DN *oldsdn,const char *uniqueid);
 static int is_suffix_entry (Slapi_PBlock *pb, Slapi_Entry *entry, Slapi_DN **parenddn);
+static int is_renamed_entry ( Slapi_PBlock *pb, Slapi_Entry *entry, CSN *opcsn);
 static int urp_fixup_add_cenotaph (Slapi_PBlock *pb, char *sessionid, CSN *opcsn);
-static int is_conflict_entry(const Slapi_Entry *entry);
+static int is_deleted_at_csn(const Slapi_Entry *entry, CSN *opcsn);
 static char * urp_get_valid_parent_nsuniqueid ( Slapi_DN *parentdn );
 static char * urp_get_conflict_dn_for_nsuniqueid ( char *nsuniqueid, Slapi_DN *parentdn );
 static int urp_rename_conflict_children(char *old_parent, const Slapi_DN *new_parent);
@@ -148,13 +151,24 @@ urp_add_operation( Slapi_PBlock *pb )
 	 * in that case the new entry will become a conflict entry independent 
 	 * of the case if the other entry still exists
 	 */
-	if (urp_add_check_tombstone(sessionid, addentry, opcsn)) {
+	rc = urp_add_check_tombstone(pb, sessionid, addentry, opcsn);
+	if (rc == 1) {
 		/* the new entry is conflicting with an already deleted entry
 		 * transform to conflict entry
 		 */
 		rc = urp_add_new_entry_to_conflict(pb, sessionid, addentry, opcsn);
 		slapi_log_err (SLAPI_LOG_ERR, sessionid,
-		          "urp_add_operation - add conflictentry (%s).\n",
+		          "urp_add_operation - new entry to conflictentry (%s).\n",
+			slapi_entry_get_dn_const(addentry));
+		goto bailout;
+	} else if (rc == 2) {
+		/* the tombstone is newer than the added entry
+		 * inform the caller that this needs to become a tombstone operation
+		 * and turn the tombstone to a conflict
+		 */
+		rc = SLAPI_PLUGIN_NOOP_TOMBSTONE;
+		slapi_log_err (SLAPI_LOG_ERR, sessionid,
+		          "urp_add_operation - new entry to tombstone (%s).\n",
 			slapi_entry_get_dn_const(addentry));
 		goto bailout;
 	}
@@ -322,22 +336,36 @@ urp_modrdn_operation( Slapi_PBlock *pb )
 		 */
 
 		/* Turn the tombstone to glue before rename it */
-		/*
-		op_result = tombstone_to_glue (pb, sessionid, target_entry,
-			slapi_entry_get_sdn (target_entry), "renameTombstone", opcsn);
-		*/
-		op_result = LDAP_NO_SUCH_OBJECT;
-		slapi_log_err(SLAPI_LOG_REPL, sessionid,
+		/* check if the delete was after the modrdn */
+		char *del_str = slapi_entry_attr_get_charptr(target_entry, "nstombstonecsn");
+		CSN *del_csn = csn_new_by_string(del_str);
+		if (csn_compare(del_csn,opcsn)>0) {
+			char *glue_dn = slapi_entry_attr_get_charptr(target_entry, "nscpentrydn");
+			Slapi_DN *glue_sdn = slapi_sdn_new_dn_byval(glue_dn);
+			op_result = tombstone_to_glue (pb, sessionid, target_entry,
+					glue_sdn, "renameTombstone", opcsn, NULL);
+			slapi_log_err(SLAPI_LOG_REPL, sessionid,
+		                "urp_modrdn_operation  - Target_entry %s is a tombstone; Renaming since delete was after rename.\n",
+		                slapi_entry_get_dn((Slapi_Entry *)target_entry));
+			slapi_ch_free_string(&glue_dn);
+			slapi_sdn_free(&glue_sdn);
+		} else {
+			op_result = LDAP_NO_SUCH_OBJECT;
+			slapi_log_err(SLAPI_LOG_REPL, sessionid,
 		                "urp_modrdn_operation  - Target_entry %s is a tombstone; returning LDAP_NO_SUCH_OBJECT.\n",
 		                slapi_entry_get_dn((Slapi_Entry *)target_entry));
+		}
+		slapi_ch_free_string(&del_str);
+		csn_free(&del_csn);
+
 		slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
 		if (op_result == 0)
 		{
 			/*
 			 * Remember to turn this entry back to tombstone in post op.
-			 * We'll just borrow an obsolete pblock type here.
+			 * We'll just borrow an obsolete pblock type here. ???
 			 */
-			slapi_pblock_set (pb, SLAPI_URP_TOMBSTONE_UNIQUEID, slapi_ch_strdup(op_uniqueid));
+			/* no. slapi_pblock_set (pb, SLAPI_URP_TOMBSTONE_UNIQUEID, slapi_ch_strdup(op_uniqueid)); */
 			rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_TARGET_ENTRY);
 			rc = 0;
 		}
@@ -577,13 +605,42 @@ urp_delete_operation( Slapi_PBlock *pb )
 	}
 	else if(is_tombstone_entry(deleteentry))
 	{
-		/* The entry is already a Tombstone, ignore this delete. */
-		op_result= LDAP_ALREADY_EXISTS;
-		slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
-		rc = SLAPI_PLUGIN_NOOP; /* Don't apply the Delete */
-		slapi_log_err (slapi_log_urp, sessionid,
-		                "urp_delete_operation - Entry \"%s\" is already a Tombstone.\n",
+		/* The entry is already a Tombstone,
+		 * either because the operation was already applied,
+		 * then ignore this delete.
+		 * check if the tombstone csn matches the operationcsn
+		 */
+		if (is_deleted_at_csn(deleteentry, opcsn)) {
+			op_result= LDAP_ALREADY_EXISTS;
+			slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+			rc = SLAPI_PLUGIN_NOOP; /* Don't apply the Delete */
+			slapi_log_err (slapi_log_urp, sessionid,
+ 		                "urp_delete_operation - Entry \"%s\" is already a Tombstone.\n",
+ 		                slapi_entry_get_dn_const(deleteentry));
+		} else if (urp_delete_check_conflict (sessionid, deleteentry, opcsn)) {
+		/* the other option is that an entry was turned into a conflict
+		 * when a conflicting  add was handled for an already delted entry
+		 * This means that now the conflict has also to be turned into a tombstone
+		 * Check if such a conflict exists - and turn to a tombstone
+		 */
+			op_result= LDAP_SUCCESS;
+			slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+			rc = SLAPI_PLUGIN_NOOP_COMMIT; /* Don't apply the Delete */
+			slapi_log_err (slapi_log_urp, sessionid,
+		                "urp_delete_operation - Deleted conflict entry instead of tombstone \"%s\"\n",
 		                slapi_entry_get_dn_const(deleteentry));
+		} else {
+			/* no already deleted tombstone and no alternative to delete
+			 * do nothing
+			 */
+			op_result= LDAP_OPERATIONS_ERROR;
+			slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+			rc = SLAPI_PLUGIN_NOOP; /* Don't apply the Delete */
+			slapi_log_err (slapi_log_urp, sessionid,
+		                "urp_delete_operation - Entry \"%s\" cannot be deleted.\n",
+		                slapi_entry_get_dn_const(deleteentry));
+		}
+
 		PROFILE_POINT; /* Delete Operation; Already a Tombstone. */
 	}
 	else /* The entry to be deleted exists and is not a tombstone */
@@ -596,8 +653,29 @@ urp_delete_operation( Slapi_PBlock *pb )
 			/* Remove possible conflict attributes */
 			rc = SLAPI_PLUGIN_SUCCESS; /* OK, to delete the entry */
 			if (is_conflict_entry(deleteentry)) {
-				del_replconflict_attr (deleteentry, opcsn, 0);
-				rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+				Slapi_DN *sdn = NULL;
+				slapi_pblock_get(pb, SLAPI_DELETE_TARGET_SDN, &sdn);
+				if (0 == slapi_sdn_compare(sdn, slapi_entry_get_sdn(deleteentry))) {
+					/* the delete directly targetd the conflict entry, just continue */
+				} else {
+				/* check if there is a valid entry added before the delete,
+				 * then we need to delete this entry
+				 */
+				char *valid_uniqueid = urp_find_valid_entry_to_delete(pb, deleteentry, sessionid, opcsn);
+				if (valid_uniqueid) {
+					/* do we need to free SLAPI_TARGET_UNIQUEID ? */
+					slapi_pblock_set( pb, SLAPI_TARGET_UNIQUEID, (void*)valid_uniqueid);
+					rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_UNIQUEID_ENTRY);
+				} else { 
+					del_replconflict_attr (deleteentry, opcsn, 0);
+					rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+				}
+				}
+			} else if (is_renamed_entry(pb, deleteentry, opcsn)) {
+				/* the entry was renamed before the delete, ignore the delete */
+				op_result= LDAP_SUCCESS;
+				slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+				rc = SLAPI_PLUGIN_NOOP; /* Don't apply the Delete */
 			}
 			PROFILE_POINT; /* Delete Operation; OK. */
 		}
@@ -654,52 +732,43 @@ urp_post_add_operation( Slapi_PBlock *pb )
 	Slapi_Entry *addentry;
 	char *conflict_dn = NULL;
 	const char *valid_dn = NULL;
+	int rc = 0;
 
 	slapi_pblock_get( pb, SLAPI_URP_NAMING_COLLISION_DN, &conflict_dn );
-	if (conflict_dn == NULL) {
-		/* there was no conflicting entry, done */
-		return 0;
-	}
-#if 0
-	Slapi_Entry *existing_dn_entry;
-	Slapi_DN *parentdn = NULL;
-	char *nsuid = NULL;
-	if( is_suffix_entry (pb, existing_dn_entry, &parentdn) )
-	{
-		/* should not happen, just used to get parentdn */
-		return 0;
-	}
-	/* there was a conflict, but we lost info if the new or existing entry
-	 * was transformed into a conflict entry
-	 * if it was the new entry we are done.
-	 * if it was the existing entry we need to check for child entries and
-	 * move them to the valid entry
-	 */
-
-	nsuid = slapi_entry_attr_get_charptr(existing_dn_entry, "nsuniqueid");
-
-	/* search the entry */
-	conflict_dn = urp_get_conflict_dn_for_nsuniqueid(nsuid, parentdn);
 
-	if (conflict_dn == NULL) {
-		/* existing entry is not a conflict dn, done */
-		return 0;
-	}
-#endif
-
-	slapi_pblock_get( pb, SLAPI_OPERATION, &op);
-	get_repl_session_id (pb, sessionid, &opcsn);
-	slapi_log_err(SLAPI_LOG_ERR, sessionid,
+	if (conflict_dn) {
+		/* the existing entry was turned into a conflict,
+		 * move children to the valid entry
+		 */
+		slapi_pblock_get( pb, SLAPI_OPERATION, &op);
+		get_repl_session_id (pb, sessionid, &opcsn);
+		slapi_log_err(SLAPI_LOG_ERR, sessionid,
 			                "urp_post_add_operation - Entry %s is conflict entry, check for children\n",
 			                conflict_dn);
-	slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &addentry );
-	valid_dn = slapi_entry_get_dn_const(addentry);
-	slapi_log_err(SLAPI_LOG_ERR, sessionid,
+		slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &addentry );
+		valid_dn = slapi_entry_get_dn_const(addentry);
+		slapi_log_err(SLAPI_LOG_ERR, sessionid,
 			                "urp_post_add_operation - Entry %s is valid entry, check for children\n",
 			                valid_dn);
-	urp_rename_conflict_children(conflict_dn, slapi_entry_get_sdn_const(addentry));
+		rc = urp_rename_conflict_children(conflict_dn, slapi_entry_get_sdn_const(addentry));
+	}
+	slapi_ch_free_string(&conflict_dn);
+	slapi_pblock_set( pb, SLAPI_URP_NAMING_COLLISION_DN, NULL );
+	slapi_pblock_get( pb, SLAPI_URP_TOMBSTONE_CONFLICT_DN, &conflict_dn );
 
-	return 0;
+	if (conflict_dn) {
+		/* a tombstone was tirned into a conflict
+		 * move it to a valid parent if parent is also a conflict
+		 */
+		Slapi_DN *conflict_sdn = slapi_sdn_new_dn_byval(conflict_dn);
+		slapi_pblock_get( pb, SLAPI_OPERATION, &op);
+		get_repl_session_id (pb, sessionid, &opcsn);
+		slapi_log_err(SLAPI_LOG_ERR, sessionid,
+			                "urp_post_add_operation - Entry %s is conflict from tombstone, check parent\n",
+			                conflict_dn);
+		rc = tombstone_to_conflict_check_parent(sessionid, slapi_dn_parent(conflict_dn),NULL, NULL, opcsn, conflict_sdn);
+	}
+	return rc;
 }
 
 int urp_post_modrdn_operation (Slapi_PBlock *pb)
@@ -841,11 +910,14 @@ urp_fixup_add_cenotaph (Slapi_PBlock *pb, char *sessionid, CSN *opcsn)
 		return -1;
 	}
 
-	pre_sdn = slapi_entry_get_sdn(pre_entry);
+	slapi_pblock_get(pb, SLAPI_MODRDN_TARGET_SDN, &pre_sdn);
+	entrydn = slapi_sdn_get_ndn(pre_sdn);
+	/* pre_sdn = slapi_entry_get_sdn(pre_entry); 
+	entrydn = slapi_entry_get_ndn (pre_entry);*/
 	uniqueid = slapi_entry_get_uniqueid (pre_entry);
-	entrydn = slapi_entry_get_ndn (pre_entry);
 	parentdn = slapi_dn_parent(entrydn);
 	slapi_sdn_get_rdn(pre_sdn, rdn);
+	slapi_rdn_remove_attr (rdn, SLAPI_ATTR_UNIQUEID );
 	slapi_rdn_add(rdn, "cenotaphID", uniqueid);
 	newdn = slapi_ch_smprintf("%s,%s", slapi_rdn_get_rdn(rdn), parentdn);
 	slapi_ch_free_string(&parentdn);
@@ -923,6 +995,52 @@ urp_fixup_add_entry (Slapi_Entry *e, const char *target_uniqueid, const char *pa
 }
 
 int
+urp_fixup_modrdn_entry (const Slapi_DN *entrydn, const char *newrdn, const Slapi_DN *newsuperior, const char *entryuniqueid, const char *parentuniqueid, CSN *opcsn, int opflags)
+{
+	Slapi_PBlock *newpb;
+	Slapi_Operation *op;
+	int op_result;
+
+	slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name,
+					"urp_fixup_modrdn_entry: moving entry (%s) to (%s) as (%s)\n",
+					slapi_sdn_get_dn(entrydn), slapi_sdn_get_dn(newsuperior),newrdn);
+	/* log only
+	return 0; */
+	newpb = slapi_pblock_new();
+
+	slapi_rename_internal_set_pb_ext (
+					newpb,
+					entrydn,
+					newrdn,
+					newsuperior,
+					0,
+					NULL,
+					entryuniqueid,
+					repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+					OP_FLAG_REPLICATED | OP_FLAG_REPL_FIXUP | opflags);
+
+	/* set operation csn if provided */
+	if (opcsn) {
+		slapi_pblock_get (newpb, SLAPI_OPERATION, &op);
+		operation_set_csn (op, opcsn);
+	}
+	if (parentuniqueid)
+	{
+		struct slapi_operation_parameters *op_params;
+		slapi_pblock_get( newpb, SLAPI_OPERATION_PARAMETERS, &op_params );
+		op_params->p.p_modrdn.modrdn_newsuperior_address.uniqueid = (char*)parentuniqueid;
+	}
+	slapi_modrdn_internal_pb(newpb); 
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+
+	slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name,
+					"urp_fixup_modrdn_entry: moving entry (%s) result %d\n",
+					slapi_sdn_get_dn(entrydn), op_result);
+	slapi_pblock_destroy(newpb);
+	return op_result;
+}
+
+int
 urp_fixup_rename_entry (const Slapi_Entry *entry, const char *newrdn, const char *parentuniqueid, int opflags)
 {
 	Slapi_PBlock *newpb;
@@ -1306,19 +1424,19 @@ urp_add_new_entry_to_conflict (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *a
 }
 
 static int
-urp_add_check_tombstone (char *sessionid, Slapi_Entry *entry, CSN *opcsn)
+urp_add_check_tombstone (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *entry, CSN *opcsn)
 {
 	int rc = 0;
 	int op_result;
 	Slapi_Entry **entries = NULL;
 	Slapi_PBlock *newpb;
 	char *basedn = slapi_entry_get_ndn (entry);
+	const Slapi_DN *suffix = slapi_get_suffix_by_dn(slapi_entry_get_sdn (entry));
 
 	char *filter = slapi_filter_sprintf("(&(objectclass=nstombstone)(nscpentrydn=%s))", basedn);
 	newpb = slapi_pblock_new();
-	char *parent_dn = slapi_dn_parent (basedn);
 	slapi_search_internal_set_pb(newpb,
-								 parent_dn, /* Base DN */
+								 slapi_sdn_get_dn(suffix), /* Base DN */
 								 LDAP_SCOPE_SUBTREE,
 								 filter,
 								 NULL, /* Attrs */
@@ -1337,16 +1455,19 @@ urp_add_check_tombstone (char *sessionid, Slapi_Entry *entry, CSN *opcsn)
 	}
 	for (int i = 0; entries && (entries[i] != NULL); i++) {
 		/* we need to distinguish between normal tombstones abd cenotaphs */
+		int is_cenotaph;
 		CSN *from_csn = NULL;
 		CSN *to_csn = NULL;
 		char *to_value = NULL;
 		char *from_value = slapi_entry_attr_get_charptr(entries[i], "cenotaphfrom");
 		if (from_value) {
+			is_cenotaph = 1;
 			from_csn = csn_new_by_string(from_value);
 			to_value = slapi_entry_attr_get_charptr(entries[i], "cenotaphto");
 			to_csn = csn_new_by_string(to_value);
 			slapi_ch_free_string(&from_value);
 		} else {
+			is_cenotaph = 0;
 			from_csn = csn_dup(entry_get_dncsn(entries[i]));
 			to_value = slapi_entry_attr_get_charptr(entries[i], "nstombstonecsn");
 			to_csn = csn_new_by_string(to_value);
@@ -1357,6 +1478,28 @@ urp_add_check_tombstone (char *sessionid, Slapi_Entry *entry, CSN *opcsn)
 		          "urp_add_check_tombstone - found conflicting tombstone (%s).\n",
 			slapi_entry_get_dn_const(entries[i]));
 			rc = 1;
+		} else if (!is_cenotaph && csn_compare(opcsn, from_csn) < 0) {
+			/* the tombstone is from an entry added later than the received one */
+			Slapi_Value *tomb_value;
+			const char *adduniqueid = slapi_entry_get_uniqueid (entries[i]);
+			const char *basedn = slapi_entry_get_dn_const (entry);
+			char *newrdn = get_rdn_plus_uniqueid ( sessionid, basedn, adduniqueid );
+			char *conflict_dn = slapi_ch_smprintf("%s,%s",newrdn,
+						slapi_dn_parent_ext(slapi_entry_get_dn_const(entries[i]),1));
+			slapi_log_err (SLAPI_LOG_ERR, sessionid,
+				"urp_add_check_tombstone - found tombstone for newer entry(%s) create conflict (%s).\n",
+				slapi_entry_get_dn_const(entries[i]),conflict_dn);
+			Slapi_DN *conflict_sdn = slapi_sdn_new_dn_byval(conflict_dn);
+			/* need to add the tombstone value here since we need the deletion csn */
+			tomb_value = slapi_value_new_string(SLAPI_ATTR_VALUE_TOMBSTONE);
+			value_update_csn(tomb_value, CSN_TYPE_VALUE_UPDATED, entry_get_deletion_csn(entries[i]));
+			slapi_entry_add_value(entry, SLAPI_ATTR_OBJECTCLASS, tomb_value);
+			slapi_value_free(&tomb_value);
+
+			/* now turn the existing tombstone into a conflict */
+			tombstone_to_conflict(sessionid, slapi_entry_dup(entries[i]), conflict_sdn, "tombstoneToConflict", opcsn, NULL);
+			slapi_pblock_set(pb, SLAPI_URP_TOMBSTONE_CONFLICT_DN, conflict_dn);
+			rc = 2;
 		}
 		slapi_ch_free_string(&to_value);
 		csn_free(&from_csn);
@@ -1369,6 +1512,99 @@ done:
 	return rc;
 }
 
+static int
+urp_delete_check_conflict (char *sessionid, Slapi_Entry *tombstone_entry, CSN *opcsn)
+{
+	int rc = 0;
+	int op_result = 0;
+	char *filter = NULL;
+	Slapi_PBlock *newpb = NULL;
+	Slapi_Entry **entries = NULL;
+	char *validdn = slapi_entry_attr_get_charptr(tombstone_entry, "nscpentrydn");
+	char *parent_dn = slapi_dn_parent (validdn);
+
+	filter = slapi_filter_sprintf("(&(objectclass=ldapsubentry)(%s=%s (ADD) %s%s))", ATTR_NSDS5_REPLCONFLICT, REASON_ANNOTATE_DN,
+			ESC_NEXT_VAL, validdn);
+	newpb = slapi_pblock_new();
+	slapi_search_internal_set_pb(newpb,
+					 parent_dn,
+					 LDAP_SCOPE_SUBTREE,
+					 filter,
+					 NULL, /* Attrs */
+					 0, /* AttrOnly */
+					 NULL, /* Controls */
+					 NULL, /* UniqueID */
+					repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+					 0);
+	slapi_search_internal_pb(newpb);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+
+	if ( (op_result != LDAP_SUCCESS) || (entries == NULL) )
+	{
+		/* Log a message */
+		goto done;
+	}
+
+	conflict_to_tombstone(sessionid, slapi_entry_dup(entries[0]), opcsn);
+	rc = 1;
+done:
+	slapi_free_search_results_internal(newpb);
+	slapi_pblock_destroy(newpb);
+	newpb = NULL;
+	if (filter) {
+		PR_smprintf_free(filter);
+	}
+
+	slapi_ch_free_string(&validdn);
+	slapi_ch_free_string(&parent_dn);
+	return rc;
+}
+
+static char*
+urp_find_valid_entry_to_delete(Slapi_PBlock *pb, const Slapi_Entry *deleteentry, char *sessionid, CSN *opcsn)
+{
+	Slapi_DN *sdnp = NULL;
+	const char *dn;
+	char *delete_nsuniqueid = NULL;
+	Slapi_PBlock *newpb;
+	const CSN *dncsn;
+	Slapi_Entry **entries = NULL;
+	int op_result;
+
+	slapi_pblock_get( pb, SLAPI_DELETE_TARGET_SDN, &sdnp );
+	dn = slapi_sdn_get_dn(sdnp);
+	newpb = slapi_pblock_new();
+	slapi_search_internal_set_pb(newpb,
+								 dn, /* Base DN */
+								 LDAP_SCOPE_BASE,
+								 "objectclass=*",
+								 NULL, /* Attrs */
+								 0, /* AttrOnly */
+								 NULL, /* Controls */
+								 NULL, /* UniqueID */
+								 repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+								 0);
+	slapi_search_internal_pb(newpb);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+	if ( (op_result != LDAP_SUCCESS) || (entries == NULL) || entries[0] == NULL )
+	{
+		/* Log a message */
+		goto done;
+	}
+	dncsn = entry_get_dncsn(entries[0]);
+	if (dncsn && (csn_compare(dncsn, opcsn) < 0 )) {
+		delete_nsuniqueid = slapi_entry_attr_get_charptr(entries[0], "nsuniqueid");
+		slapi_log_err (SLAPI_LOG_ERR, sessionid,
+					"urp_find_valid_entry_to_delete - found (%s) (%s).\n",
+			delete_nsuniqueid, slapi_entry_get_dn_const(entries[0]));
+	}
+
+done:
+	return delete_nsuniqueid;
+}
+
 static char*
 urp_find_tombstone_for_glue (Slapi_PBlock *pb, char *sessionid, const Slapi_Entry *entry, Slapi_DN *parentdn, CSN *opcsn)
 {
@@ -1922,6 +2158,20 @@ bail:
 }
 
 static int
+is_deleted_at_csn(const Slapi_Entry *entry, CSN *opcsn)
+{
+	int rc = 0;
+	char *tombstone_csnstr = slapi_entry_attr_get_charptr(entry, "nstombstonecsn" );
+	CSN *tombstone_csn = csn_new_by_string(tombstone_csnstr);
+
+	if (csn_compare (tombstone_csn, opcsn) == 0) rc = 1;
+
+	slapi_ch_free_string(&tombstone_csnstr);
+	csn_free(&tombstone_csn);
+
+	return rc;
+}
+int
 is_conflict_entry(const Slapi_Entry *entry)
 {
 	int is_conflict = 0;
@@ -1933,6 +2183,19 @@ is_conflict_entry(const Slapi_Entry *entry)
 
 	return is_conflict;
 }
+static int
+is_renamed_entry (Slapi_PBlock *pb, Slapi_Entry *entry, CSN *opcsn)
+{
+	int rc = 0;
+	Slapi_DN *del_dn = NULL;
+	slapi_pblock_get(pb, SLAPI_DELETE_TARGET_SDN, &del_dn);
+	if (slapi_sdn_compare(del_dn, slapi_entry_get_sdn(entry))) {
+		/* the existing entry was renamed, check if it was before the delete */
+		if (csn_compare (entry_get_dncsn(entry), opcsn) < 0) rc = 1;
+	}
+
+	return rc;
+}
 
 static int
 is_suffix_entry ( Slapi_PBlock *pb, Slapi_Entry *entry, Slapi_DN **parentdn )
diff --git a/ldap/servers/plugins/replication/urp.h b/ldap/servers/plugins/replication/urp.h
index ee50664..77f19c7 100644
--- a/ldap/servers/plugins/replication/urp.h
+++ b/ldap/servers/plugins/replication/urp.h
@@ -34,6 +34,7 @@ int urp_fixup_add_entry (Slapi_Entry *e, const char *target_uniqueid, const char
 int urp_fixup_delete_entry (const char *uniqueid, const char *dn, CSN *opcsn, int opflags);
 int urp_fixup_rename_entry (const Slapi_Entry *entry, const char *newrdn, const char *parentuniqueid, int opflags);
 int urp_fixup_modify_entry (const char *uniqueid, const Slapi_DN *sdn, CSN *opcsn, Slapi_Mods *smods, int opflags);
+int urp_fixup_modrdn_entry (const Slapi_DN *entrydn, const char *newrdn, const Slapi_DN *newsuperior, const char *entryuniqueid, const char *parentuniqueid, CSN *opcsn, int opflags);
 
 int is_suffix_dn (Slapi_PBlock *pb, const Slapi_DN *dn, Slapi_DN **parenddn);
 int is_suffix_dn_ext (Slapi_PBlock *pb, const Slapi_DN *dn, Slapi_DN **parenddn, int is_tombstone);
@@ -42,6 +43,7 @@ int is_suffix_dn_ext (Slapi_PBlock *pb, const Slapi_DN *dn, Slapi_DN **parenddn,
  * urp_glue.c
  */
 int is_glue_entry(const Slapi_Entry* entry);
+int is_conflict_entry(const Slapi_Entry *entry);
 int create_glue_entry ( Slapi_PBlock *pb, char *sessionid, Slapi_DN *dn, const char *uniqueid, CSN *opcsn );
 int entry_to_glue(char *sessionid, const Slapi_Entry* entry, const char *reason, CSN *opcsn);
 int glue_to_entry (Slapi_PBlock *pb, Slapi_Entry *entry );
@@ -52,5 +54,8 @@ PRBool get_glue_csn(const Slapi_Entry *entry, const CSN **gluecsn);
  */
 int is_tombstone_entry(const Slapi_Entry* entry);
 int tombstone_to_glue(Slapi_PBlock *pb, char *sessionid, Slapi_Entry *entry, const Slapi_DN *parentdn, const char *reason, CSN *opcsn,     Slapi_DN **newparentdn);
+int tombstone_to_conflict(char *sessionid, Slapi_Entry *entry, const Slapi_DN *conflictdn, const char *reason, CSN *opcsn,     Slapi_DN **newparentdn);
+int conflict_to_tombstone(char *sessionid, Slapi_Entry *entry, CSN *opcsn);
+int tombstone_to_conflict_check_parent ( char *sessionid, char *parentdn, const char *uniqueid, const char *parentuniqueid, CSN *opcsn, Slapi_DN *conflictdn);
 int entry_to_tombstone ( Slapi_PBlock *pb, Slapi_Entry *entry );
 PRBool get_tombstone_csn(const Slapi_Entry *entry, const CSN **delcsn);
diff --git a/ldap/servers/plugins/replication/urp_tombstone.c b/ldap/servers/plugins/replication/urp_tombstone.c
index b5621a6..a41c880 100644
--- a/ldap/servers/plugins/replication/urp_tombstone.c
+++ b/ldap/servers/plugins/replication/urp_tombstone.c
@@ -52,6 +52,94 @@ get_tombstone_csn(const Slapi_Entry *entry, const CSN **delcsn)
 	return ists;
 }
 
+static Slapi_DN*
+get_valid_parent_for_conflict(Slapi_Entry *entry)
+{
+	char *replconflict = slapi_entry_attr_get_charptr(entry,ATTR_NSDS5_REPLCONFLICT );
+	char *validdn = NULL;
+	Slapi_DN *valid_DN = NULL;
+
+	if (replconflict) {
+		validdn = strstr(replconflict, " (ADD) ");
+		if (validdn) {
+			validdn += 7;
+			valid_DN = slapi_sdn_new_dn_byval(validdn);
+			slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name,
+				"get_valid_parent_for_conflict - valid entry dn: %s\n",
+				validdn);
+		}
+	}
+	return valid_DN;
+}
+
+int
+tombstone_to_conflict_check_parent (
+	char *sessionid,
+	char *parentdn,
+	const char *uniqueid,
+	const char *parentuniqueid,
+	CSN *opcsn,
+	Slapi_DN *conflictdn)
+{
+	int rc = 0;
+	int op_result;
+	Slapi_PBlock *newpb;
+
+	if (parentdn == 0) {
+		slapi_log_err(SLAPI_LOG_REPL, sessionid,
+				"tombstone_to_conflict_check_parent - no parent entry for: %s\n",
+				slapi_sdn_get_ndn(conflictdn));
+		return rc;
+	} else {
+		slapi_log_err(SLAPI_LOG_REPL, sessionid,
+				"tombstone_to_conflict_check_parent - checking parent entry: %s\n",
+				parentdn);
+	}
+	newpb= slapi_pblock_new();
+	slapi_search_internal_set_pb(
+					newpb,
+					parentdn,
+					LDAP_SCOPE_BASE,
+					"objectclass=*",
+					NULL, /*attrs*/
+					0, /*attrsonly*/
+					NULL, /*Controls*/
+					parentuniqueid, /*uniqueid*/
+					repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+					0);
+	slapi_search_internal_pb(newpb); 
+	slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+	switch(op_result)
+	{
+	case LDAP_SUCCESS:
+		{
+		Slapi_Entry **entries= NULL;
+		slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+		if(entries!=NULL && entries[0]!=NULL) {
+			if(is_conflict_entry(entries[0])) {
+				Slapi_DN *newsuperior = get_valid_parent_for_conflict(entries[0]);
+				Slapi_RDN *rdn = slapi_rdn_new();
+				slapi_sdn_get_rdn(conflictdn, rdn);
+				char *newrdn = slapi_rdn_get_rdn(rdn);
+				op_result= urp_fixup_modrdn_entry(
+					conflictdn,
+					newrdn,
+					newsuperior,
+					uniqueid,
+					NULL,
+					NULL,
+					OP_FLAG_NOOP);
+				slapi_rdn_free(&rdn);
+			}
+		}
+		}
+		break;
+	default:
+		break;
+	}
+	return rc;
+}
+
 static int
 tombstone_to_glue_resolve_parent (
 	Slapi_PBlock *pb,
@@ -112,6 +200,118 @@ tombstone_to_glue_resolve_parent (
 }
 
 /*
+ * transform a conflict entry to a tombstone of the original entry
+ * 1. rename the conflict to the original entry
+ * 2. remove conflict attrs
+ * 3. delete it
+ */
+
+int
+conflict_to_tombstone(char *sessionid, Slapi_Entry *entry, CSN *opcsn)
+{
+	int op_result = 0;
+	Slapi_RDN *srdn = slapi_rdn_new();
+	const char *uniqueid = slapi_entry_get_uniqueid ( entry );
+	const char *newrdn = NULL;
+	char *conflictdn = NULL;
+	char *replconflict = slapi_entry_attr_get_charptr(entry,ATTR_NSDS5_REPLCONFLICT );
+
+	if (replconflict) {
+		conflictdn = strstr(replconflict, " (ADD) ");
+		if (conflictdn == NULL) {
+			/* error, wrong type of conflict */
+			op_result = 1;
+		} else {
+			conflictdn += 7;
+			slapi_rdn_init_all_dn(srdn, conflictdn);
+			newrdn = slapi_rdn_get_nrdn(srdn);
+			slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name,
+				"conflict_to_tombstone - %s - valid entry dn: %s newrdn: %s\n",
+				sessionid, conflictdn, newrdn);
+		}
+	}
+	slapi_ch_free_string(&replconflict);
+
+	if (op_result) goto done;
+
+	slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name,
+			"conflict_to_tombstone - %s - Renaming entry %s to %s\n",
+			sessionid, slapi_entry_get_dn_const (entry), newrdn);
+	op_result = urp_fixup_rename_entry(entry, newrdn, NULL, OP_FLAG_NOOP);
+	if (op_result) goto done;
+
+	op_result = urp_fixup_delete_entry (uniqueid, slapi_entry_get_dn_const (entry), opcsn, 0);
+
+done: 
+	slapi_ch_free_string(&replconflict);
+	slapi_rdn_free(&srdn);
+	return op_result;
+}
+
+/*
+ * Convert a tombstone into a conflict entry.
+ */
+int
+tombstone_to_conflict (
+	char *sessionid,
+	Slapi_Entry *tombstoneentry,
+	const Slapi_DN *conflictdn,
+	const char *reason,
+	CSN *opcsn,
+	Slapi_DN **newparentdn)
+{
+	int op_result = 0;
+	Slapi_Mods smods;
+	char csnstr[CSN_STRSIZE+1];
+	char buf[BUFSIZ];
+	char *uniqueid = slapi_entry_attr_get_charptr(tombstoneentry, "nsuiqueid");
+	char *entrydn = slapi_entry_attr_get_charptr(tombstoneentry, "nscpentrydn");
+	char *parentuniqueid = slapi_entry_attr_get_charptr (tombstoneentry, SLAPI_ATTR_VALUE_PARENT_UNIQUEID); /* Allocated */
+	char *parentdn = slapi_dn_parent(slapi_sdn_get_ndn(conflictdn));
+	const CSN *dncsn = entry_get_dncsn(tombstoneentry);
+
+	slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name,
+			                 "tombstone_to_conflict - %s - trying to resurrect tombstone as '%s'.\n",
+			                 sessionid, slapi_sdn_get_ndn(conflictdn));
+	slapi_sdn_set_normdn_byval(slapi_entry_get_sdn(tombstoneentry), slapi_sdn_get_ndn(conflictdn));
+	/* not just e_sdn, e_rsdn needs to be updated. */
+	slapi_rdn_set_all_dn(slapi_entry_get_srdn(tombstoneentry), slapi_sdn_get_ndn(conflictdn));
+	op_result = urp_fixup_add_entry (tombstoneentry, NULL, NULL, opcsn, OP_FLAG_RESURECT_ENTRY|OP_FLAG_NOOP);
+
+	if (op_result) {
+		slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name,
+			"tombstone_to_conflict - %s - Can't resurrect tombstone, error=%d\n",
+			sessionid, op_result);
+		goto done;
+	}
+
+	slapi_mods_init (&smods, 5);
+	slapi_mods_add (&smods, LDAP_MOD_ADD, "objectclass", strlen("ldapsubentry"),"ldapsubentry");
+	slapi_mods_add (&smods, LDAP_MOD_DELETE, "objectclass", strlen("glue"),"glue");
+	slapi_mods_add (&smods, LDAP_MOD_REPLACE, "conflictcsn", strlen(csnstr),csn_as_string(dncsn, PR_FALSE, csnstr));
+	PR_snprintf (buf, sizeof(buf), "%s (%s) %s",
+	             REASON_ANNOTATE_DN, "ADD", entrydn);
+	slapi_mods_add (&smods, LDAP_MOD_ADD, ATTR_NSDS5_REPLCONFLICT, strlen(buf), buf);
+	op_result = urp_fixup_modify_entry (uniqueid, conflictdn, opcsn, &smods, 0);
+	slapi_mods_done (&smods);
+	if (op_result == LDAP_TYPE_OR_VALUE_EXISTS) {
+		/* the objectclass was already present */
+		op_result = LDAP_SUCCESS;
+	}
+	/* this will go to postop 
+	if (op_result == LDAP_SUCCESS) {
+		op_result = tombstone_to_conflict_check_parent(sessionid, parentdn, uniqueid, parentuniqueid, opcsn, conflictdn);
+	}
+	*/
+done:
+	slapi_ch_free_string(&uniqueid);
+	slapi_ch_free_string(&parentuniqueid);
+	slapi_ch_free_string(&parentdn);
+	slapi_ch_free_string(&entrydn);
+	return op_result;
+}
+
+/*
  * Convert a tombstone into a glue entry.
  */
 int
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c
index c8b9175..cd30cd6 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_add.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c
@@ -22,6 +22,7 @@ extern char *hassubordinates;
 
 static void delete_update_entrydn_operational_attributes(struct backentry *ep);
 
+static int set_error(Slapi_PBlock *pb, int retval, int ldap_result_code, char **ldap_result_message);
 #define ADD_SET_ERROR(rc, error, count)                                        \
 {                                                                              \
     (rc) = (error);                                                            \
@@ -84,10 +85,12 @@ ldbm_back_add( Slapi_PBlock *pb )
 	int is_tombstone_operation= 0;
 	int is_fixup_operation= 0;
 	int is_remove_from_cache= 0;
+	int op_plugin_call = 1;
 	int is_ruv = 0;				 /* True if the current entry is RUV */
 	CSN *opcsn = NULL;
 	entry_address addr = {0};
 	int not_an_error = 0;
+	int is_noop = 0;
 	int parent_switched = 0;
 	int noabort = 1;
 	int myrc = 0;
@@ -112,6 +115,7 @@ ldbm_back_add( Slapi_PBlock *pb )
 	is_fixup_operation = operation_is_flag_set(operation, OP_FLAG_REPL_FIXUP);
 	is_ruv = operation_is_flag_set(operation, OP_FLAG_REPL_RUV);
 	is_remove_from_cache = operation_is_flag_set(operation, OP_FLAG_NEVER_CACHE);
+	if (operation_is_flag_set(operation,OP_FLAG_NOOP)) op_plugin_call = 0;
 
 	inst = (ldbm_instance *) be->be_instance_info;
 	if (inst && inst->inst_ref_count) {
@@ -328,13 +332,19 @@ ldbm_back_add( Slapi_PBlock *pb )
 				ldap_result_code = LDAP_SUCCESS;
 				slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
 				rc = plugin_call_mmr_plugin_preop(pb, NULL,SLAPI_PLUGIN_BE_PRE_ADD_FN);
-				if (rc == 0) {
+				if (rc == 0  && op_plugin_call) {
 					rc = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_ADD_FN);
 				}
-				if (rc < 0) {
+				if (rc == SLAPI_PLUGIN_NOOP_TOMBSTONE) {
+					is_tombstone_operation = 1;
+					is_noop = 1;
+					op_plugin_call = 0;
+					rc = LDAP_SUCCESS;
+				} else if (rc < 0) {
 					int opreturn = 0;
 					if (SLAPI_PLUGIN_NOOP == rc) {
 						not_an_error = 1;
+						is_noop = 1;
 						rc = LDAP_SUCCESS;
 					}
 					/* 
@@ -616,6 +626,7 @@ ldbm_back_add( Slapi_PBlock *pb )
 				 * next_id will add this id to the list of ids that are pending
 				 * id2entry indexing.
 				 */
+				Slapi_DN nscpEntrySDN;
 				addingentry = backentry_init( e );
 				if ( ( addingentry->ep_id = next_id( be ) ) >= MAXID ) {
 				  slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_add ",
@@ -656,28 +667,50 @@ ldbm_back_add( Slapi_PBlock *pb )
 					 * 3) If the parent entry was found, set the nsparentuniqueid
 					 *	attribute to be the unique id of that parent.
 					 */
+					const char *entryuniqueid= slapi_entry_get_uniqueid(addingentry->ep_entry);
 					char *untombstoned_dn = slapi_entry_get_dn(e);
 					char *tombstoned_dn = NULL;
 					if (NULL == untombstoned_dn)
 					{
 						untombstoned_dn = "";
 					}
-					tombstoned_dn = compute_entry_tombstone_dn(untombstoned_dn, addr.uniqueid);
+					tombstoned_dn = compute_entry_tombstone_dn(untombstoned_dn, entryuniqueid);
+					slapi_log_err(SLAPI_LOG_ERR,
+							"ldbm_back_add", "(tombstone_operation for %s): calculated tombstone_dn "
+							"is (%s) \n", entryuniqueid, tombstoned_dn);
 					/*
 					 * This needs to be done before slapi_entry_set_dn call,
 					 * because untombstoned_dn is released in slapi_entry_set_dn.
 					 */
+					slapi_sdn_init(&nscpEntrySDN);
+					slapi_sdn_set_ndn_byval(&nscpEntrySDN, slapi_sdn_get_ndn(slapi_entry_get_sdn(addingentry->ep_entry)));
+	
 					if (entryrdn_get_switch())
 					{
-						Slapi_RDN srdn = {0};
-						rc = slapi_rdn_init_all_dn(&srdn, tombstoned_dn);
-						if (rc) {
-							slapi_log_err(SLAPI_LOG_TRACE,
-								"ldbm_back_add", "(tombstone_operation): failed to "
-								"decompose %s to Slapi_RDN\n", tombstoned_dn);
+						if (is_ruv) {
+							Slapi_RDN srdn = {0};
+							rc = slapi_rdn_init_all_dn(&srdn, tombstoned_dn);
+							if (rc) {
+								slapi_log_err(SLAPI_LOG_TRACE,
+									"ldbm_back_add", "(tombstone_operation): failed to "
+									"decompose %s to Slapi_RDN\n", tombstoned_dn);
+							} else {
+								slapi_entry_set_srdn(e, &srdn);
+								slapi_rdn_done(&srdn);
+							}
 						} else {
-							slapi_entry_set_srdn(e, &srdn);
-							slapi_rdn_done(&srdn);
+							/* immediate entry to tombstone */
+							Slapi_RDN *srdn = slapi_entry_get_srdn(addingentry->ep_entry);
+							slapi_rdn_init_all_sdn(srdn, slapi_entry_get_sdn_const(addingentry->ep_entry));
+							char *tombstone_rdn =
+					 			compute_entry_tombstone_rdn(slapi_entry_get_rdn_const(addingentry->ep_entry),
+								entryuniqueid);
+							slapi_log_err(SLAPI_LOG_ERR,
+								"ldbm_back_add", "(tombstone_operation for %s): calculated tombstone_rdn "
+								"is (%s) \n", entryuniqueid, tombstone_rdn);
+							/* e_srdn has "uniaqueid=..., <ORIG RDN>" */
+							slapi_rdn_replace_rdn(srdn, tombstone_rdn);
+							slapi_ch_free_string(&tombstone_rdn);
 						}
 					}
 					slapi_entry_set_dn(addingentry->ep_entry, tombstoned_dn);
@@ -709,6 +742,9 @@ ldbm_back_add( Slapi_PBlock *pb )
 									tombstone_csnstr);
 						}
 					}
+					if (attrlist_find( addingentry->ep_entry->e_attrs, SLAPI_ATTR_NSCP_ENTRYDN ) == NULL){
+						slapi_entry_add_string(addingentry->ep_entry, SLAPI_ATTR_NSCP_ENTRYDN, slapi_sdn_get_ndn(&nscpEntrySDN));
+					}
 
 					if (NULL != operation->o_params.p.p_add.parentuniqueid)
 					{
@@ -757,7 +793,11 @@ ldbm_back_add( Slapi_PBlock *pb )
 				pid = parententry->ep_id;
 
 				/* We may need to adjust the DN since parent could be a resurrected conflict entry... */
-				if (!slapi_sdn_isparent(slapi_entry_get_sdn_const(parententry->ep_entry),
+				/* TBD better handle tombstone parents, 
+				 * we have the entry dn as nsuniqueid=nnnn,<rdn>,parentdn
+				 * so is parent will return false
+				 */ 
+				if (!is_tombstone_operation && !slapi_sdn_isparent(slapi_entry_get_sdn_const(parententry->ep_entry),
 				                        slapi_entry_get_sdn_const(addingentry->ep_entry))) {
 					Slapi_DN adjustedsdn = {0};
 					char *adjusteddn = slapi_ch_smprintf("%s,%s", 
@@ -791,6 +831,9 @@ ldbm_back_add( Slapi_PBlock *pb )
 			{
 				/* Remove the entrydn operational attributes from the addingentry */
 				delete_update_entrydn_operational_attributes(addingentry);
+				if (!is_ruv) {
+					add_update_entry_operational_attributes(addingentry, pid);
+				}
 			}
 			else
 			{
@@ -851,8 +894,9 @@ ldbm_back_add( Slapi_PBlock *pb )
 
 		/* call the transaction pre add plugins just after the to-be-added entry
 		 * is prepared. */
-		retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_PRE_ADD_FN);
-		if (retval) {
+		if (op_plugin_call) {
+			retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_PRE_ADD_FN);
+			if (retval) {
 			int opreturn = 0;
 			if (SLAPI_PLUGIN_NOOP == retval) {
 				not_an_error = 1;
@@ -875,6 +919,14 @@ ldbm_back_add( Slapi_PBlock *pb )
 			slapi_log_err(SLAPI_LOG_DEBUG, "ldbm_back_add", "SLAPI_PLUGIN_BE_TXN_PRE_ADD_FN plugin failed: %d\n",
 			              ldap_result_code ? ldap_result_code : retval);
 			goto error_return;
+			}
+		}
+		if (is_tombstone_operation) {
+			int len = 0;
+			const char *rs = slapi_entry_get_rdn_const(addingentry->ep_entry);
+			char *es= slapi_entry2str_with_options(e, &len, SLAPI_DUMP_STATEINFO | SLAPI_DUMP_UNIQUEID);
+			slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_add", "now adding entry: %s\n %s\n", rs?rs:"no rdn", es);
+			slapi_ch_free_string(&es);
 		}
 
 		retval = id2entry_add_ext(be, addingentry, &txn, 1, &myrc);
@@ -1166,7 +1218,7 @@ ldbm_back_add( Slapi_PBlock *pb )
 	}
 
 	/* call the transaction post add plugins just before the commit */
-	if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_ADD_FN))) {
+	if (op_plugin_call && (retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_ADD_FN))) {
 		int opreturn = 0;
 		slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_add",
 			"SLAPI_PLUGIN_BE_TXN_POST_ADD_FN plugin "
@@ -1186,6 +1238,10 @@ ldbm_back_add( Slapi_PBlock *pb )
 		goto error_return;
 	}
 	retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_ADD_FN);
+	if (retval) {
+		set_error(pb, retval, ldap_result_code, &ldap_result_message);
+		goto error_return;
+	}
 
 	/* Release SERIAL LOCK */
 	retval = dblayer_txn_commit(be, &txn);
@@ -1263,7 +1319,8 @@ diskfull_return:
 				slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, &val );
 			}
 			/* call the transaction post add plugins just before the abort */
-			if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_ADD_FN))) {
+			/* but only if it is not a NOOP */
+			if (!is_noop && op_plugin_call && (retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_ADD_FN))) {
 				int opreturn = 0;
 				slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_add",
 					"SLAPI_PLUGIN_BE_TXN_POST_ADD_FN plugin "
@@ -1278,6 +1335,8 @@ diskfull_return:
 				}
 				slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
 			}
+			/* the repl postop needs to be called for aborted operations */
+			retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_ADD_FN);
 			if ( addingentry ) {
 				if (inst && cache_is_in_cache(&inst->inst_cache, addingentry)) {
 					CACHE_REMOVE(&inst->inst_cache, addingentry);
@@ -1457,3 +1516,22 @@ delete_update_entrydn_operational_attributes(struct backentry *ep)
     slapi_entry_attr_delete( ep->ep_entry, LDBM_ENTRYDN_STR);
 }
 
+static int
+set_error(Slapi_PBlock *pb, int retval, int ldap_result_code, char **ldap_result_message)
+{
+	int opreturn = 0;
+	if (!ldap_result_code) {
+		slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
+	}
+	if (!ldap_result_code) {
+		ldap_result_code = LDAP_OPERATIONS_ERROR;
+		slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
+	}
+	slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn);
+	if (!opreturn) {
+		slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
+	}
+	slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
+
+	return opreturn;
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
index 25e8cfc..56b381d 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
@@ -270,6 +270,7 @@ ldbm_back_delete( Slapi_PBlock *pb )
 			 * deleted.  That is, the entry 'e' found with "addr" is a tomb-
 			 * stone.  If it is the case, we need to back off.
 			 */
+replace_entry:
 			if ((e = find_entry2modify(pb, be, addr, &txn, &result_sent)) == NULL)
 			{
 				ldap_result_code= LDAP_NO_SUCH_OBJECT;
@@ -330,9 +331,27 @@ ldbm_back_delete( Slapi_PBlock *pb )
 						rc = LDAP_SUCCESS;
 						retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN);
 						goto commit_return;
-				}else if(slapi_isbitset_int(retval,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY)) {
+				} else if(slapi_isbitset_int(retval,SLAPI_RTN_BIT_FETCH_EXISTING_UNIQUEID_ENTRY)) {
+					/* we need to delete an other entry, determined by urp */
+					done_with_pblock_entry(pb,SLAPI_DELETE_EXISTING_ENTRY); /* Could be through this multiple times */
+					if (cache_is_in_cache(&inst->inst_cache, e)) {
+						ep_id = e->ep_id; /* Otherwise, e might have been freed. */
+						CACHE_REMOVE(&inst->inst_cache, e);
+					}
+					cache_unlock_entry(&inst->inst_cache, e);
+					CACHE_RETURN(&inst->inst_cache, &e);
+					/*
+					 * e is unlocked and no longer in cache.
+					 * It could be freed at any moment.
+					 */
+					e = NULL;
+					goto replace_entry;
+				} else if(slapi_isbitset_int(retval,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY)) {
 					Slapi_Entry *e_to_delete = NULL;
 					done_with_pblock_entry(pb,SLAPI_DELETE_EXISTING_ENTRY); /* Could be through this multiple times */
+					slapi_log_err(SLAPI_LOG_REPL,
+								   "ldbm_back_delete", "reloading existing entry "
+								   "(%s)\n", addr->uniqueid );
 					ldap_result_code= get_copy_of_entry(pb, addr, &txn,
 								SLAPI_DELETE_EXISTING_ENTRY, !is_replicated_operation);
 					slapi_pblock_get(pb,SLAPI_DELETE_EXISTING_ENTRY, &e_to_delete);
@@ -1260,6 +1279,18 @@ ldbm_back_delete( Slapi_PBlock *pb )
 		slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
 		goto error_return;
 	}
+	if (parent_found)
+	{
+		/* Replace the old parent entry with the newly modified one */
+		myrc = modify_switch_entries( &parent_modify_c,be);
+		slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_delete",
+		                "conn=%lu op=%d modify_switch_entries: old_entry=0x%p, new_entry=0x%p, rc=%d\n",
+		                conn_id, op_id, parent_modify_c.old_entry, parent_modify_c.new_entry, myrc);
+		if (myrc == 0) {
+			parent_switched = 1;
+		}
+	}
+
 	retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN);
 
 commit_return:
@@ -1325,18 +1356,6 @@ commit_return:
 		}
 	}
 
-	if (parent_found)
-	{
-		/* Replace the old parent entry with the newly modified one */
-		myrc = modify_switch_entries( &parent_modify_c,be);
-		slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_delete",
-		                "conn=%lu op=%d modify_switch_entries: old_entry=0x%p, new_entry=0x%p, rc=%d\n",
-		                conn_id, op_id, parent_modify_c.old_entry, parent_modify_c.new_entry, myrc);
-		if (myrc == 0) {
-			parent_switched = 1;
-		}
-	}
-
 	rc= 0;
 	goto common_return;
 
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
index 5f6b817..a40b694 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
@@ -410,6 +410,7 @@ ldbm_back_modify( Slapi_PBlock *pb )
 	int opreturn = 0;
 	int mod_count = 0;
 	int not_an_error = 0;
+	int is_noop = 0;
 	int fixup_tombstone = 0;
 	int ec_locked = 0;
 	int result_sent = 0;
@@ -647,6 +648,7 @@ ldbm_back_modify( Slapi_PBlock *pb )
 				}
 				if (SLAPI_PLUGIN_NOOP == opreturn) {
 					not_an_error = 1;
+					is_noop = 1;
 					rc = opreturn = LDAP_SUCCESS;
 				} else if (!opreturn) {
 					opreturn = SLAPI_PLUGIN_FAILURE;
@@ -922,7 +924,7 @@ error_return:
 			   and skip processing if they don't want do anything - some plugins that
 			   keep track of a counter (usn, dna) may want to "rollback" the counter
 			   in this case */
-			if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN))) {
+			if (!is_noop && (retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN))) {
 				slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify",
 					"SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN plugin returned error code %d\n", retval );
 				slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
@@ -932,6 +934,7 @@ error_return:
 					slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
 				}
 			}
+			retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN);
 
 			/* It is safer not to abort when the transaction is not started. */
 			/* Release SERIAL LOCK */
diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c
index c686f8b..0c92e8f 100644
--- a/ldap/servers/slapd/pblock.c
+++ b/ldap/servers/slapd/pblock.c
@@ -2245,6 +2245,12 @@ int slapi_pblock_get(Slapi_PBlock *pblock, int arg, void *value) {
         }
         break;
 
+    case SLAPI_URP_TOMBSTONE_CONFLICT_DN:
+        if (pblock->pb_intop != NULL) {
+		(*(char **)value) = pblock->pb_intop->pb_urp_tombstone_conflict_dn;
+        }
+	break;
+		
     case SLAPI_SEARCH_CTRLS:
         if (pblock->pb_intop != NULL) {
             (*(LDAPControl ***)value) = pblock->pb_intop->pb_search_ctrls;
@@ -3922,6 +3928,10 @@ int slapi_pblock_set(Slapi_PBlock *pblock, int arg, void *value) {
         pblock->pb_intop->pb_urp_naming_collision_dn = (char *)value;
         break;
 
+    case SLAPI_URP_TOMBSTONE_CONFLICT_DN:
+	pblock->pb_intop->pb_urp_tombstone_conflict_dn = (char *)value;
+	break;
+
     case SLAPI_URP_TOMBSTONE_UNIQUEID:
         _pblock_assert_pb_intop(pblock);
         pblock->pb_intop->pb_urp_tombstone_uniqueid = (char *)value;
diff --git a/ldap/servers/slapd/pblock_v3.h b/ldap/servers/slapd/pblock_v3.h
index 1d84c25..30cc82d 100644
--- a/ldap/servers/slapd/pblock_v3.h
+++ b/ldap/servers/slapd/pblock_v3.h
@@ -136,6 +136,7 @@ typedef struct _slapi_pblock_intop {
     char * pb_result_text;      /* result text when available */
     char * pb_urp_naming_collision_dn;  /* replication naming conflict removal */
     char * pb_urp_tombstone_uniqueid;   /* replication change tombstone */
+    char * pb_urp_tombstone_conflict_dn; /* urp changed tombstone to conflict */
     int     pb_opreturn;
     /* controls we know about */
     int     pb_managedsait;
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index a9441a6..18359c6 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -6853,6 +6853,9 @@ time_t slapi_current_time( void );
 				  * main op should be noop, but plugin made
 				  * changes which need to be committed
 				  */
+#define SLAPI_PLUGIN_NOOP_TOMBSTONE    -4  /* Ignore the add and turn entry
+					    * into tombstone
+					    */
 
 /*
  * the following can be used as the second argument to the
@@ -6927,6 +6930,7 @@ time_t slapi_current_time( void );
 /* urp flags */
 #define SLAPI_URP_NAMING_COLLISION_DN	286
 #define SLAPI_URP_TOMBSTONE_UNIQUEID	288
+#define SLAPI_URP_TOMBSTONE_CONFLICT_DN	293
 
 /* common to all plugins */
 #define SLAPI_PLUGIN				3
-- 
2.5.5

