From f63949d1fbecf3071e8b5bb6cfcd77e8cc472657 Mon Sep 17 00:00:00 2001 From: Ludwig Krispenz Date: Sep 05 2017 11:53:47 +0000 Subject: Ticket 49043 - make replication conflicts transparent to clients Bug Description: replication conflicts are created mostly because of failures in manually applying updates on different servers or client applications. But once they are created many clients, applications and users have problems to understand and handle them properly Fix Description: - keep conflicts but add objectclass ldapsubentry to make them invisible by default - extend urp handling to handle all conflict casees and have a consistent database on all servers https://pagure.io/389-ds-base/issue/49043 Reviewed by: Mark, Thierry, William --- diff --git a/Makefile.am b/Makefile.am index 3c71ce7..4863d41 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1197,6 +1197,7 @@ libslapd_la_SOURCES = ldap/servers/slapd/add.c \ ldap/servers/slapd/pblock.c \ ldap/servers/slapd/plugin.c \ ldap/servers/slapd/plugin_acl.c \ + ldap/servers/slapd/plugin_mmr.c \ ldap/servers/slapd/plugin_internal_op.c \ ldap/servers/slapd/plugin_mr.c \ ldap/servers/slapd/plugin_role.c \ diff --git a/ldap/schema/02common.ldif b/ldap/schema/02common.ldif index dda75e7..70d64c0 100644 --- a/ldap/schema/02common.ldif +++ b/ldap/schema/02common.ldif @@ -129,6 +129,7 @@ attributeTypes: ( 2.16.840.1.113730.3.1.3023 NAME 'nsViewFilter' DESC 'Netscape attributeTypes: ( 2.16.840.1.113730.3.1.2063 NAME 'nsEncryptionAlgorithm' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' ) attributeTypes: ( 2.16.840.1.113730.3.1.2093 NAME 'nsslapd-changelogsuffix' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'Netscape' ) attributeTypes: ( 2.16.840.1.113730.3.1.2094 NAME 'nsslapd-parent-suffix' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'Netscape' ) +attributeTypes: ( 2.16.840.1.113730.3.1.2401 NAME 'ConflictCSN' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE directoryOperation X-ORIGIN 'Netscape Directory Server' ) # # objectclasses: # diff --git a/ldap/servers/plugins/replication/repl5.h b/ldap/servers/plugins/replication/repl5.h index 2711c89..3bd878d 100644 --- a/ldap/servers/plugins/replication/repl5.h +++ b/ldap/servers/plugins/replication/repl5.h @@ -205,6 +205,8 @@ int multimaster_preop_modrdn(Slapi_PBlock *pb); int multimaster_preop_search(Slapi_PBlock *pb); int multimaster_preop_compare(Slapi_PBlock *pb); int multimaster_ruv_search(Slapi_PBlock *pb); +int multimaster_mmr_preop (Slapi_PBlock *pb, int flags); +int multimaster_mmr_postop (Slapi_PBlock *pb, int flags); int multimaster_bepreop_add(Slapi_PBlock *pb); int multimaster_bepreop_delete(Slapi_PBlock *pb); int multimaster_bepreop_modify(Slapi_PBlock *pb); diff --git a/ldap/servers/plugins/replication/repl5_init.c b/ldap/servers/plugins/replication/repl5_init.c index 100fc23..5badd7c 100644 --- a/ldap/servers/plugins/replication/repl5_init.c +++ b/ldap/servers/plugins/replication/repl5_init.c @@ -109,6 +109,7 @@ static Slapi_PluginDesc multimasterpostopdesc = {"replication-multimaster-postop static Slapi_PluginDesc multimasterinternalpreopdesc = {"replication-multimaster-internalpreop", VENDOR, DS_PACKAGE_VERSION, "Multi-master replication internal pre-operation plugin"}; static Slapi_PluginDesc multimasterinternalpostopdesc = {"replication-multimaster-internalpostop", VENDOR, DS_PACKAGE_VERSION, "Multimaster replication internal post-operation plugin"}; static Slapi_PluginDesc multimasterbepreopdesc = {"replication-multimaster-bepreop", VENDOR, DS_PACKAGE_VERSION, "Multimaster replication bepre-operation plugin"}; +static Slapi_PluginDesc multimasterbemmrdesc = {"replication-multimaster-bepreop", VENDOR, DS_PACKAGE_VERSION, "Multimaster replication be plugin"}; static Slapi_PluginDesc multimasterbepostopdesc = {"replication-multimaster-bepostop", VENDOR, DS_PACKAGE_VERSION, "Multimaster replication bepost-operation plugin"}; static Slapi_PluginDesc multimasterbetxnpostopdesc = {"replication-multimaster-betxnpostop", VENDOR, DS_PACKAGE_VERSION, "Multimaster replication be transaction post-operation plugin"}; static Slapi_PluginDesc multimasterextopdesc = {"replication-multimaster-extop", VENDOR, DS_PACKAGE_VERSION, "Multimaster replication extended-operation plugin"}; @@ -337,10 +338,6 @@ multimaster_bepreop_init(Slapi_PBlock *pb) if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterbepreopdesc) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_BE_PRE_ADD_FN, (void *)multimaster_bepreop_add) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_BE_PRE_DELETE_FN, (void *)multimaster_bepreop_delete) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN, (void *)multimaster_bepreop_modify) != 0 || - slapi_pblock_set(pb, SLAPI_PLUGIN_BE_PRE_MODRDN_FN, (void *)multimaster_bepreop_modrdn) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_BE_PRE_CLOSE_FN, (void *)cl5Close) != 0 || slapi_pblock_set(pb, SLAPI_PLUGIN_BE_PRE_BACKUP_FN, (void *)cl5WriteRUV) != 0) { slapi_log_err(SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_bepreop_init - Failed\n"); @@ -350,6 +347,21 @@ multimaster_bepreop_init(Slapi_PBlock *pb) return rc; } +int +multimaster_mmr_init( Slapi_PBlock *pb ) +{ + int rc= 0; /* OK */ + + if(slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterbemmrdesc ) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_MMR_BETXN_PREOP, (void *) multimaster_mmr_preop ) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_MMR_BETXN_POSTOP, (void *) multimaster_mmr_postop ) != 0) { + slapi_log_err(SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_mmr_init - Failed\n" ); + rc= -1; +} + +return rc; +} /* * betxnpreop: if betxn is on, call preop urp at betxnpreop. */ @@ -415,27 +427,35 @@ multimaster_betxnpostop_init(Slapi_PBlock *pb) void *mdn_fn; if (repl5_is_betxn) { - add_fn = multimaster_be_betxnpostop_add; + /* add_fn = multimaster_be_betxnpostop_add; del_fn = multimaster_be_betxnpostop_delete; mod_fn = multimaster_be_betxnpostop_modify; mdn_fn = multimaster_be_betxnpostop_modrdn; + */ + if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01) || + slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, + (void *)&multimasterbetxnpostopdesc)) { + slapi_log_err(SLAPI_LOG_PLUGIN, repl_plugin_name, + "multimaster_betxnpostop_init - Failed\n"); + rc = -1; + } } else { add_fn = multimaster_betxnpostop_add; del_fn = multimaster_betxnpostop_delete; mod_fn = multimaster_betxnpostop_modify; mdn_fn = multimaster_betxnpostop_modrdn; - } - if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01) || - slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, - (void *)&multimasterbetxnpostopdesc) || - slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_ADD_FN, add_fn) || - slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN, del_fn) || - slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN, mdn_fn) || - slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN, mod_fn)) { - slapi_log_err(SLAPI_LOG_PLUGIN, repl_plugin_name, - "multimaster_betxnpostop_init - Failed\n"); - rc = -1; + if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01) || + slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, + (void *)&multimasterbetxnpostopdesc) || + slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_ADD_FN, add_fn) || + slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN, del_fn) || + slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN, mdn_fn) || + slapi_pblock_set(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN, mod_fn)) { + slapi_log_err(SLAPI_LOG_PLUGIN, repl_plugin_name, + "multimaster_betxnpostop_init - Failed\n"); + rc = -1; + } } return rc; @@ -887,6 +907,12 @@ replication_multimaster_plugin_init(Slapi_PBlock *pb) multimaster_preop_init, "Multimaster replication preoperation plugin", NULL, identity); + /* Register the main mmr backend plugins */ + rc = slapi_register_plugin("mmr", 1 /* Enabled */, + "multimaster_mmr_init", + multimaster_mmr_init, + "Multimaster replication be operation plugin", + NULL, identity); /* bepreop: setting SLAPI_TXN_RUV_MODS_FN and cleanup old stateinfo * -- should be done before transaction */ /* if betxn is off, urp is called at bepreop. */ diff --git a/ldap/servers/plugins/replication/repl5_plugins.c b/ldap/servers/plugins/replication/repl5_plugins.c index 7c98734..0aee882 100644 --- a/ldap/servers/plugins/replication/repl5_plugins.c +++ b/ldap/servers/plugins/replication/repl5_plugins.c @@ -581,6 +581,54 @@ purge_entry_state_information(Slapi_PBlock *pb) object_release(repl_obj); } } +int + +multimaster_mmr_preop (Slapi_PBlock *pb, int flags) +{ + int rc= SLAPI_PLUGIN_SUCCESS; + + switch (flags) + { + case SLAPI_PLUGIN_BE_PRE_ADD_FN: + rc = multimaster_bepreop_add(pb); + break; + case SLAPI_PLUGIN_BE_PRE_MODIFY_FN: + rc = multimaster_bepreop_modify(pb); + break; + case SLAPI_PLUGIN_BE_PRE_MODRDN_FN: + rc = multimaster_bepreop_modrdn(pb); + break; + case SLAPI_PLUGIN_BE_PRE_DELETE_FN: + rc = multimaster_bepreop_delete(pb); + break; + } + return rc; +} + +int +multimaster_mmr_postop (Slapi_PBlock *pb, int flags) +{ + int rc= SLAPI_PLUGIN_SUCCESS; + + switch (flags) + { + case SLAPI_PLUGIN_BE_TXN_POST_ADD_FN: + rc = multimaster_be_betxnpostop_add(pb); + break; + case SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN: + rc = multimaster_be_betxnpostop_delete(pb); + break; + case SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN: + rc = multimaster_be_betxnpostop_modify(pb); + break; + case SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN: + 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; +} /* pure bepreop's -- should be done before transaction starts */ int @@ -686,6 +734,18 @@ multimaster_bepreop_modrdn(Slapi_PBlock *pb) } int +multimaster_bepostop_add(Slapi_PBlock *pb) +{ + Slapi_Operation *op; + + slapi_pblock_get(pb, SLAPI_OPERATION, &op); + if ( ! operation_is_flag_set (op, OP_FLAG_REPL_FIXUP) ) { + urp_post_add_operation (pb); + } + return SLAPI_PLUGIN_SUCCESS; +} + +int multimaster_bepostop_modrdn(Slapi_PBlock *pb) { Slapi_Operation *op; @@ -793,6 +853,7 @@ multimaster_be_betxnpostop_add(Slapi_PBlock *pb) int rc = 0; /* original betxnpost */ rc = write_changelog_and_ruv(pb); + rc |= multimaster_bepostop_add(pb); return rc; } @@ -1003,16 +1064,23 @@ write_changelog_and_ruv(Slapi_PBlock *pb) if (op_params->operation_type != SLAPI_OPERATION_MODIFY || 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, "write_changelog_and_ruv - Skipped due to DISKFULL\n"); 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 ea351e8..40dc092 100644 --- a/ldap/servers/plugins/replication/repl5_ruv.c +++ b/ldap/servers/plugins/replication/repl5_ruv.c @@ -1510,7 +1510,7 @@ ruv_add_csn_inprogress(void *repl, RUV *ruv, const CSN *csn) if (ruv_covers_csn_internal(ruv, csn, PR_FALSE)) { 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; @@ -1531,7 +1531,7 @@ ruv_add_csn_inprogress(void *repl, 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, NULL); diff --git a/ldap/servers/plugins/replication/urp.c b/ldap/servers/plugins/replication/urp.c index 0e76699..3d63c64 100644 --- a/ldap/servers/plugins/replication/urp.c +++ b/ldap/servers/plugins/replication/urp.c @@ -23,12 +23,25 @@ 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_annotate_dn(char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const char *optype); +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 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_deleted_at_csn(const Slapi_Entry *entry, CSN *opcsn); +static char * urp_get_valid_parent_nsuniqueid(Slapi_DN *parentdn); +static int urp_rename_conflict_children(const char *old_parent, const Slapi_DN *new_parent); +static Slapi_Entry *urp_get_min_naming_conflict_entry(Slapi_PBlock *pb, const char *collision_dn, char *sessionid, CSN *opcsn); /* * Return 0 for OK, -1 for Error. @@ -84,14 +97,12 @@ urp_add_operation(Slapi_PBlock *pb) Slapi_Entry *existing_uniqueid_entry; Slapi_Entry *existing_dn_entry; Slapi_Entry *addentry; - const char *adduniqueid; CSN *opcsn; const char *basedn; char sessionid[REPL_SESSION_ID_SIZE]; int r; int op_result = 0; int rc = 0; /* OK */ - Slapi_DN *sdn = NULL; if (slapi_op_abandoned(pb)) { return rc; @@ -124,6 +135,36 @@ urp_add_operation(Slapi_PBlock *pb) slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &addentry); slapi_pblock_get(pb, SLAPI_ADD_EXISTING_DN_ENTRY, &existing_dn_entry); + + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_add_operation - handling add of (%s).\n", + slapi_entry_get_dn_const(addentry)); + /* first check if there was an existing entry at the time of this add + * in that case the new entry will become a conflict entry independent + * of the case if the other entry still exists + */ + 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_REPL, sessionid, + "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_REPL, sessionid, + "urp_add_operation - new entry to tombstone (%s).\n", + slapi_entry_get_dn_const(addentry)); + goto bailout; + } + if (existing_dn_entry == NULL) /* The target DN does not exist */ { /* Check for parent entry... this could be an orphan. */ @@ -141,80 +182,26 @@ urp_add_operation(Slapi_PBlock *pb) * its RDN to uniqueid+baserdn, and adds operational attribute * ATTR_NSDS5_REPLCONFLIC. */ - basedn = slapi_entry_get_ndn(addentry); - adduniqueid = slapi_entry_get_uniqueid(addentry); - r = csn_compare(entry_get_dncsn(existing_dn_entry), opcsn); + basedn = slapi_entry_get_ndn (addentry); + r = csn_compare (entry_get_dncsn(existing_dn_entry), opcsn); if (r < 0) { /* Entry to be added is a loser */ - char *newdn = get_dn_plus_uniqueid(sessionid, (const Slapi_DN *)addentry, adduniqueid); - if (newdn == NULL) { - op_result = LDAP_OPERATIONS_ERROR; - slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); - rc = SLAPI_PLUGIN_NOOP; /* Abort this Operation */ - slapi_log_err(slapi_log_urp, sessionid, - "urp_add_operation - %s - Add conflict; Unique ID (%s) already in RDN\n", - basedn, adduniqueid); - PROFILE_POINT; /* Add Conflict; Entry Exists; Unique ID already in RDN - Abort this update. */ - } else { - /* Add the nsds5ReplConflict attribute in the mods */ - Slapi_Attr *attr = NULL; - Slapi_Value **vals = NULL; - Slapi_RDN *rdn; - char buf[BUFSIZ]; - -#ifdef DEBUG - PR_snprintf(buf, BUFSIZ, "%s (add) %s", REASON_ANNOTATE_DN, basedn); -#else - PR_snprintf(buf, BUFSIZ, "%s %s", REASON_ANNOTATE_DN, basedn); -#endif - if (slapi_entry_attr_find(addentry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0) { - /* ATTR_NSDS5_REPLCONFLICT exists */ - slapi_log_err(SLAPI_LOG_ERR, sessionid, - "urp_add_operation - New entry has nsds5ReplConflict already\n"); - vals = attr_get_present_values(attr); /* this returns a pointer to the contents */ - } - if (vals == NULL || *vals == NULL) { - /* Add new attribute */ - slapi_entry_add_string(addentry, ATTR_NSDS5_REPLCONFLICT, buf); - } else { - /* - * Replace old attribute. We don't worry about the index - * change here since the entry is yet to be added. - */ - slapi_value_set_string(*vals, buf); - } - /* slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn); */ - slapi_pblock_get(pb, SLAPI_ADD_TARGET_SDN, &sdn); - slapi_sdn_free(&sdn); - - slapi_entry_set_normdn(addentry, newdn); /* dn: passin */ - - sdn = slapi_sdn_dup(slapi_entry_get_sdn_const(addentry)); - slapi_pblock_set(pb, SLAPI_ADD_TARGET_SDN, sdn); - - rdn = slapi_rdn_new_sdn(slapi_entry_get_sdn_const(addentry)); - slapi_log_err(slapi_log_urp, sessionid, - "urp_add_operation - Naming conflict ADD. Add %s instead\n", - slapi_rdn_get_rdn(rdn)); - slapi_rdn_free(&rdn); - - rc = slapi_setbit_int(rc, SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY); - PROFILE_POINT; /* Add Conflict; Entry Exists; Rename Operation Entry */ - } + rc = urp_add_new_entry_to_conflict(pb, sessionid, addentry, opcsn); } else if (r > 0) { + char *conflict_dn = NULL; /* Existing entry is a loser */ - if (!urp_annotate_dn(sessionid, existing_dn_entry, opcsn, "ADD")) { - op_result = LDAP_OPERATIONS_ERROR; + if (!urp_annotate_dn(sessionid, existing_dn_entry, opcsn, "ADD", &conflict_dn)) { + op_result= LDAP_OPERATIONS_ERROR; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); slapi_log_err(slapi_log_urp, sessionid, - "urp_add_operation - %s - Entry to be added is a loser; " - "urp_annotate_dn failed.\n", - basedn); + "urp_add_operation - %s - Entry to be added is a loser; " + "urp_annotate_dn failed.\n", basedn); rc = SLAPI_PLUGIN_NOOP; /* Ignore this Operation */ } else { /* The backend add code should now search for the existing entry again. */ - rc = slapi_setbit_int(rc, SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY); - rc = slapi_setbit_int(rc, SLAPI_RTN_BIT_FETCH_PARENT_ENTRY); + rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY); + rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_PARENT_ENTRY); + slapi_pblock_set(pb, SLAPI_URP_NAMING_COLLISION_DN, conflict_dn); } PROFILE_POINT; /* Add Conflict; Entry Exists; Rename Existing Entry */ } else /* r==0 */ @@ -258,7 +245,7 @@ urp_modrdn_operation(Slapi_PBlock *pb) Slapi_Entry *new_parent_entry; Slapi_DN *newsuperior = NULL; Slapi_DN *parentdn = NULL; - const Slapi_Entry *target_entry; + Slapi_Entry *target_entry; Slapi_Entry *existing_entry; const CSN *target_entry_dncsn; CSN *opcsn = NULL; @@ -320,6 +307,12 @@ urp_modrdn_operation(Slapi_PBlock *pb) slapi_pblock_get(pb, SLAPI_MODRDN_NEWPARENT_ENTRY, &new_parent_entry); slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR_SDN, &newsuperior); + if (is_conflict_entry (target_entry)) { + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_modrdn_operation - Target_entry %s is a conflict; what to do ?\n", + slapi_entry_get_dn((Slapi_Entry *)target_entry)); + } + if (is_tombstone_entry(target_entry)) { /* * It is a non-trivial task to rename a tombstone. @@ -329,21 +322,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, - "urp_modrdn_operation - Target_entry %s is a tombstone; returning LDAP_NO_SUCH_OBJECT.\n", - slapi_entry_get_dn((Slapi_Entry *)target_entry)); + /* 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; } else { @@ -362,9 +370,15 @@ urp_modrdn_operation(Slapi_PBlock *pb) * An entry with the target DN already exists. * The smaller dncsn wins. The loser changes its RDN to * uniqueid+baserdn, and adds operational attribute - * ATTR_NSDS5_REPLCONFLIC + * ATTR_NSDS5_REPLCONFLICT */ + if (is_conflict_entry (existing_entry)) { + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_modrdn_operation - Existing_entry %s is a conflict; what to do ?\n", + slapi_entry_get_dn((Slapi_Entry *)existing_entry)); + } + existing_uniqueid = slapi_entry_get_uniqueid(existing_entry); existing_sdn = slapi_entry_get_sdn_const(existing_entry); @@ -413,6 +427,7 @@ urp_modrdn_operation(Slapi_PBlock *pb) goto bailout; } mod_namingconflict_attr(op_uniqueid, target_sdn, existing_sdn, opcsn, "MODRDN"); + mod_objectclass_attr(op_uniqueid, target_sdn, target_sdn, opcsn, "MODRDN"); slapi_pblock_set(pb, SLAPI_MODRDN_NEWRDN, newrdn_with_uniqueid); slapi_log_err(slapi_log_urp, sessionid, "urp_modrdn_operation - Naming conflict MODRDN. Rename target entry from %s to %s\n", @@ -425,7 +440,7 @@ urp_modrdn_operation(Slapi_PBlock *pb) if (r > 0) { /* The existing entry is a loser */ - int resolve = urp_annotate_dn(sessionid, existing_entry, opcsn, "MODRDN"); + int resolve = urp_annotate_dn(sessionid, existing_entry, opcsn, "MODRDN", NULL); if (!resolve) { op_result = LDAP_OPERATIONS_ERROR; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); @@ -541,7 +556,7 @@ bailout: int urp_delete_operation(Slapi_PBlock *pb) { - const Slapi_Entry *deleteentry; + Slapi_Entry *deleteentry; CSN *opcsn = NULL; char sessionid[REPL_SESSION_ID_SIZE]; int op_result = 0; @@ -564,13 +579,42 @@ urp_delete_operation(Slapi_PBlock *pb) slapi_entry_get_dn((Slapi_Entry *)deleteentry)); PROFILE_POINT; /* Delete Operation; Entry not exist. */ } 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", - slapi_entry_get_dn_const(deleteentry)); + /* 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 */ { @@ -579,26 +623,128 @@ urp_delete_operation(Slapi_PBlock *pb) /* Check if the entry has children. */ if (!slapi_entry_has_children(deleteentry)) { /* Remove possible conflict attributes */ - del_replconflict_attr(deleteentry, opcsn, 0); rc = SLAPI_PLUGIN_SUCCESS; /* OK, to delete the entry */ - PROFILE_POINT; /* Delete Operation; OK. */ + if (is_conflict_entry(deleteentry)) { + 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. */ } else { - /* Turn this entry into a glue_absent_parent entry */ - entry_to_glue(sessionid, deleteentry, REASON_RESURRECT_ENTRY, opcsn); + /* the entry has children, so it is either the orphan-glue + * scenario or the more complex situatin when there is a conflict entry + * for the entry to be deleted which would be restored in the post_delete + * phase. We would just have to swap the entries and be done, + * but unfortunately as a plugin this is not possible. + * So the approach is: + * - move the children to th conflict entry + * - let the delete happen + * - let the post_del rename the conflict entry + */ + Slapi_Entry *conflict_entry = urp_get_min_naming_conflict_entry (pb, slapi_entry_get_dn_const(deleteentry), sessionid, opcsn); + if (conflict_entry) { + urp_rename_conflict_children(slapi_entry_get_dn_const(deleteentry), + slapi_entry_get_sdn_const(conflict_entry)); + slapi_entry_free(conflict_entry); + rc = SLAPI_PLUGIN_SUCCESS; /* OK, to delete the entry */ + } else { + /* Turn this entry into a glue_absent_parent entry */ + rc = entry_to_glue(sessionid, deleteentry, REASON_RESURRECT_ENTRY, opcsn); - /* Turn the Delete into a No-Op */ - op_result = LDAP_SUCCESS; - 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 - Turn entry \"%s\" into a glue_absent_parent entry.\n", - slapi_entry_get_dn_const(deleteentry)); - PROFILE_POINT; /* Delete Operation; Entry has children. */ + /* Turn the Delete into a No-Op */ + op_result= LDAP_SUCCESS; + slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); + if (rc == 0) { + /* Don't apply the Delete, but commit the glue change */ + rc = SLAPI_PLUGIN_NOOP_COMMIT; + } else { + rc = SLAPI_PLUGIN_NOOP; /* Don't apply the Delete */ + } + slapi_log_err(slapi_log_urp, sessionid, + "urp_delete_operation - Turn entry \"%s\" into a glue_absent_parent entry.\n", + slapi_entry_get_dn_const(deleteentry)); + PROFILE_POINT; /* Delete Operation; Entry has children. */ + } } } return rc; } + +/* + * Return 0 for OK, -1 for Error + */ +int +urp_post_add_operation(Slapi_PBlock *pb) +{ + char sessionid[REPL_SESSION_ID_SIZE]; + Slapi_Operation *op; + CSN *opcsn; + 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) { + /* 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_REPL, 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_REPL, sessionid, + "urp_post_add_operation - Entry %s is valid entry, check for children\n", + valid_dn); + 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 ); + + 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); + char *parent_dn = slapi_dn_parent(conflict_dn); + slapi_pblock_get( pb, SLAPI_OPERATION, &op); + get_repl_session_id (pb, sessionid, &opcsn); + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_post_add_operation - Entry %s is conflict from tombstone, check parent\n", + conflict_dn); + rc = tombstone_to_conflict_check_parent(sessionid, parent_dn, NULL, NULL, opcsn, conflict_sdn); + slapi_sdn_free(&conflict_sdn); + slapi_ch_free_string(&parent_dn); + } + return rc; +} + int urp_post_modrdn_operation(Slapi_PBlock *pb) { @@ -607,6 +753,12 @@ urp_post_modrdn_operation(Slapi_PBlock *pb) char *tombstone_uniqueid; Slapi_Entry *postentry; Slapi_Operation *op; + int rc = SLAPI_PLUGIN_SUCCESS; /* OK */ + int oprc = 0; + + /* only execute if operation was successful */ + slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc); + if (oprc) return rc; /* * Do not abandon the post op - the processed CSN needs to be @@ -626,6 +778,10 @@ urp_post_modrdn_operation(Slapi_PBlock *pb) if (!operation_is_flag_set(op, OP_FLAG_REPL_FIXUP)) { get_repl_session_id(pb, sessionid, &opcsn); urp_naming_conflict_removal(pb, sessionid, opcsn, "MODRDN"); + /* keep track of the entry's lifetime befor renaming + * by creating a cenotaph + */ + urp_fixup_add_cenotaph(pb, sessionid, opcsn); } } else { /* @@ -642,7 +798,7 @@ urp_post_modrdn_operation(Slapi_PBlock *pb) slapi_pblock_set(pb, SLAPI_URP_TOMBSTONE_UNIQUEID, NULL); } - return 0; + return rc; } /* @@ -656,6 +812,11 @@ urp_post_delete_operation(Slapi_PBlock *pb) CSN *opcsn; char sessionid[REPL_SESSION_ID_SIZE]; int op_result; + int oprc = 0; + + /* only execute if operation was successful */ + slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc); + if (oprc) return 0; /* * Do not abandon the post op - the processed CSN needs to be @@ -692,6 +853,74 @@ urp_post_delete_operation(Slapi_PBlock *pb) return 0; } +static int +urp_fixup_add_cenotaph (Slapi_PBlock *pb, char *sessionid, CSN *opcsn) +{ + Slapi_PBlock *add_pb = slapi_pblock_new(); + Slapi_Entry *cenotaph = NULL; + Slapi_Entry *pre_entry = NULL; + int ret = 0; + Slapi_DN *pre_sdn = NULL; + Slapi_RDN *rdn = slapi_rdn_new(); + char *parentdn = NULL; + char *newdn; + const char *entrydn; + const char *uniqueid = NULL; + CSN *dncsn = NULL; + char csnstr[CSN_STRSIZE+1]; + + slapi_pblock_get (pb, SLAPI_ENTRY_PRE_OP, &pre_entry); + if (pre_entry == NULL) { + slapi_log_err(SLAPI_LOG_ERR, sessionid, + "urp_fixup_add_cenotaph - failed to get preop entry\n"); + return -1; + } + + 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); + 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); + /* slapi_sdn_free(&pre_sdn); */ + + cenotaph = slapi_entry_alloc(); + slapi_entry_init(cenotaph, newdn, NULL); + + dncsn = (CSN *)entry_get_dncsn (pre_entry); + slapi_entry_add_string(cenotaph, SLAPI_ATTR_OBJECTCLASS, "extensibleobject"); + slapi_entry_add_string(cenotaph, SLAPI_ATTR_OBJECTCLASS, "nstombstone"); + slapi_entry_add_string(cenotaph,"cenotaphfrom", csn_as_string(dncsn, PR_FALSE, csnstr)); + slapi_entry_add_string(cenotaph,"cenotaphto", csn_as_string(opcsn, PR_FALSE, csnstr)); + slapi_entry_add_string(cenotaph,"nstombstonecsn", csn_as_string(opcsn, PR_FALSE, csnstr)); + slapi_entry_add_string(cenotaph, SLAPI_ATTR_NSCP_ENTRYDN, entrydn); + + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_fixup_add_cenotaph - addinng cenotaph: %s \n", newdn); + slapi_pblock_init(add_pb); + + slapi_add_entry_internal_set_pb(add_pb, + cenotaph, + NULL, + repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION), + OP_FLAG_REPL_FIXUP|OP_FLAG_NOOP); + slapi_add_internal_pb(add_pb); + slapi_pblock_get(add_pb, SLAPI_PLUGIN_INTOP_RESULT, &ret); + + if (ret != LDAP_SUCCESS) { + slapi_log_err(SLAPI_LOG_ERR, sessionid, + "urp_fixup_add_cenotaph - failed to add cenotaph, err= %d\n", ret); + } + slapi_pblock_destroy(add_pb); + + return ret; +} + int urp_fixup_add_entry(Slapi_Entry *e, const char *target_uniqueid, const char *parentuniqueid, CSN *opcsn, int opflags) { @@ -730,6 +959,50 @@ urp_fixup_add_entry(Slapi_Entry *e, const char *target_uniqueid, const char *par } 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_REPL, 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_REPL, 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; @@ -771,6 +1044,43 @@ urp_fixup_rename_entry(const Slapi_Entry *entry, const char *newrdn, const char } int +urp_fixup_move_entry (const Slapi_Entry *entry, const Slapi_DN *newsuperior, int opflags) +{ + Slapi_PBlock *newpb; + int op_result; + + newpb = slapi_pblock_new(); + + /* + * Must mark this operation as replicated, + * so that the frontend doesn't add extra attributes. + */ + slapi_rename_internal_set_pb_ext(newpb, + slapi_entry_get_sdn_const (entry), + slapi_entry_get_rdn_const(entry), /*NewRDN*/ + newsuperior, /*NewSuperior*/ + 0, /* !Delete Old RDNS */ + NULL, /*Controls*/ + slapi_entry_get_uniqueid (entry), /*uniqueid*/ + repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION), + OP_FLAG_REPLICATED | OP_FLAG_REPL_FIXUP | opflags); + + /* set operation csn to the entry's dncsn */ + /* TBD check which csn to use + Slapi_Operation *op; + CSN *opcsn; + opcsn = (CSN *)entry_get_dncsn (entry); + slapi_pblock_get (newpb, SLAPI_OPERATION, &op); + operation_set_csn (op, opcsn); +TBD */ + slapi_modrdn_internal_pb(newpb); + slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result); + + slapi_pblock_destroy(newpb); + return op_result; +} + +int urp_fixup_delete_entry(const char *uniqueid, const char *dn, CSN *opcsn, int opflags) { Slapi_PBlock *newpb; @@ -891,6 +1201,46 @@ urp_add_resolve_parententry(Slapi_PBlock *pb, char *sessionid, Slapi_Entry *entr goto bailout; } + if(is_conflict_entry(parententry)) { + /* The parent is a conflict entry + * Check if a valid entry exists and make this the new parent + * + * or if no valid entry exists + * - check if also a tombstone for the parentdn exists + * - compare csns of tombstone and conflict generation + * - turn the latest into a valid glue + */ + char *valid_nsuniqueid = urp_get_valid_parent_nsuniqueid(parentdn); + if (valid_nsuniqueid) { + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_resolve parent entry: found valid parent %s\n", slapi_sdn_get_dn(parentdn)); + struct slapi_operation_parameters *op_params; + slapi_pblock_get( pb, SLAPI_OPERATION_PARAMETERS, &op_params ); + /* free it ? */ + op_params->p.p_add.parentuniqueid = valid_nsuniqueid; + } else { + int op_result; + char *tombstone_nsuniqueid = urp_find_tombstone_for_glue(pb, sessionid, parententry, parentdn, opcsn); + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &op_result); + if (tombstone_nsuniqueid) { + struct slapi_operation_parameters *op_params; + slapi_pblock_get( pb, SLAPI_OPERATION_PARAMETERS, &op_params ); + op_params->p.p_add.parentuniqueid = tombstone_nsuniqueid; + rc = LDAP_SUCCESS; + } else if (0 == op_result) { + rc = urp_conflict_to_glue(sessionid, parententry, parentdn, opcsn); + if (rc == 0) { + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_resolve parent entry: created valid parent from conflict %s\n", slapi_sdn_get_dn(parentdn)); + } else { + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_resolve parent entry: cannot resolve parent %s\n", slapi_sdn_get_dn(parentdn)); + } + } + } + goto bailout; + } + /* The parent is healthy */ /* Now we need to check that the parent has the correct DN */ if (slapi_sdn_isparent(slapi_entry_get_sdn(parententry), slapi_entry_get_sdn(entry))) { @@ -928,8 +1278,394 @@ bailout: slapi_rdn_free(&add_rdn); return rc; } +static int +urp_add_new_entry_to_conflict (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *addentry, CSN *opcsn) +{ + int rc = 0; + int op_result; -/* + /* Entry to be added is a loser */ + char *basedn = slapi_entry_get_ndn(addentry); + const char *adduniqueid = slapi_entry_get_uniqueid(addentry); + char *newdn= get_dn_plus_uniqueid(sessionid, (const Slapi_DN *)addentry, adduniqueid); + if(newdn == NULL) { + op_result= LDAP_OPERATIONS_ERROR; + slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); + rc = SLAPI_PLUGIN_NOOP; /* Abort this Operation */ + slapi_log_err(slapi_log_urp, sessionid, + "urp_add_operation - %s - Add conflict; Unique ID (%s) already in RDN\n", + basedn, adduniqueid); + } else { + /* Add the nsds5ReplConflict attribute in the mods */ + Slapi_Attr *attr = NULL; + Slapi_Value **vals = NULL; + Slapi_Value **csn_vals = NULL; + Slapi_DN *sdn = NULL; + Slapi_RDN *rdn; + char buf[BUFSIZ]; + char csnstr[CSN_STRSIZE+1]; + + PR_snprintf(buf, BUFSIZ, "%s (ADD) %s", REASON_ANNOTATE_DN, basedn); + if (slapi_entry_attr_find(addentry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0) { + /* ATTR_NSDS5_REPLCONFLICT exists */ + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_add_operation - New entry has nsds5ReplConflict already\n"); + vals = attr_get_present_values (attr); /* this returns a pointer to the contents */ + } + if ( vals == NULL || *vals == NULL ) { + /* Add new attribute */ + slapi_entry_add_string(addentry, ATTR_NSDS5_REPLCONFLICT, buf); + } else { + /* + * Replace old attribute. We don't worry about the index + * change here since the entry is yet to be added. + */ + slapi_value_set_string(*vals, buf); + } + + /* add the ldapsubentry objectclass if not present */ + slapi_entry_attr_find(addentry, "objectclass", &attr); + if (attr != NULL) { + struct berval bv; + bv.bv_val = "ldapsubentry"; + bv.bv_len = strlen(bv.bv_val); + if (slapi_attr_value_find(attr, &bv) != 0) { + Slapi_Value *new_v = slapi_value_new(); + slapi_value_init_berval(new_v, &bv); + slapi_attr_add_value(attr, new_v); + slapi_value_free(&new_v); + slapi_entry_set_flag(addentry, SLAPI_ENTRY_LDAPSUBENTRY); + } + } + /* add or replace the conflict csn */ + if (slapi_entry_attr_find (addentry, "conflictcsn", &attr) == 0) { + csn_vals = attr_get_present_values (attr); + } + if (csn_vals == NULL || *csn_vals == NULL ) { + slapi_entry_add_string(addentry,"conflictcsn", csn_as_string(opcsn, PR_FALSE, csnstr)); + } else { + + slapi_value_set_string (*csn_vals, csn_as_string(opcsn, PR_FALSE, csnstr)); + } + + /* slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn); */ + slapi_pblock_get(pb, SLAPI_ADD_TARGET_SDN, &sdn); + slapi_sdn_free(&sdn); + + slapi_entry_set_normdn(addentry, newdn); /* dn: passin */ + + sdn = slapi_sdn_dup(slapi_entry_get_sdn_const(addentry)); + slapi_pblock_set(pb, SLAPI_ADD_TARGET_SDN, sdn); + + rdn = slapi_rdn_new_sdn ( slapi_entry_get_sdn_const(addentry) ); + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_add_operation - Naming conflict ADD. Add %s instead\n", + slapi_rdn_get_rdn(rdn)); + slapi_rdn_free(&rdn); + rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY); + + } + return rc; +} + +static int +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(); + slapi_search_internal_set_pb(newpb, + slapi_sdn_get_dn(suffix), /* Base 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; + } + 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); + } + if (csn_compare(from_csn, opcsn) < 0 && + csn_compare(to_csn, opcsn) > 0 ) { + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "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 *parentdn = slapi_dn_parent_ext(slapi_entry_get_dn_const(entries[i]),1); + char *conflict_dn = slapi_ch_smprintf("%s,%s",newrdn, parentdn); + slapi_log_err(SLAPI_LOG_REPL, 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); + slapi_ch_free_string(&newrdn); + slapi_ch_free_string(&parentdn); + slapi_sdn_free(&conflict_sdn); + rc = 2; + } + slapi_ch_free_string(&to_value); + csn_free(&from_csn); + csn_free(&to_csn); + + if (rc) break; + } + +done: + slapi_free_search_results_internal(newpb); + slapi_pblock_destroy(newpb); + if (filter) { + PR_smprintf_free(filter); + } + 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; + Slapi_Entry *conflict_e = 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) || entries[0] == NULL) { + /* Log a message */ + goto done; + } + + conflict_e = slapi_entry_dup(entries[0]); + conflict_to_tombstone(sessionid, conflict_e, opcsn); + slapi_entry_free(conflict_e); + 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_REPL, sessionid, + "urp_find_valid_entry_to_delete - found (%s) (%s).\n", + delete_nsuniqueid, slapi_entry_get_dn_const(entries[0])); + } + +done: + slapi_free_search_results_internal(newpb); + slapi_pblock_destroy(newpb); + return delete_nsuniqueid; +} + +static char* +urp_find_tombstone_for_glue (Slapi_PBlock *pb, char *sessionid, const Slapi_Entry *entry, Slapi_DN *parentdn, CSN *opcsn) +{ + char *tombstone_nsuniqueid = NULL; + int op_result; + int rc = LDAP_SUCCESS;; + Slapi_Entry **entries = NULL; + Slapi_PBlock *newpb; + const char *basedn = slapi_sdn_get_dn(parentdn); + char *conflict_csnstr = slapi_entry_attr_get_charptr(entry, "conflictcsn"); + CSN *conflict_csn = csn_new_by_string(conflict_csnstr); + slapi_ch_free_string(&conflict_csnstr); + CSN *tombstone_csn = NULL; + + 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 */ + 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; + } + for (int i = 0; entries && (entries[i] != NULL); i++) { + char *tombstone_csn_value = slapi_entry_attr_get_charptr(entries[i], "nstombstonecsn"); + if (tombstone_csn_value) { + tombstone_csn = csn_new_by_string(tombstone_csn_value); + slapi_ch_free_string(&tombstone_csn_value); + if( csn_compare(tombstone_csn, conflict_csn) > 0 ) { + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_find_tombstone_for_glue - found tombstone newer than conflict (%s).\n", + slapi_entry_get_dn_const(entries[i])); + tombstone_nsuniqueid = slapi_entry_attr_get_charptr(entries[i], "nsuniqueid"); + rc = tombstone_to_glue (pb, sessionid, entries[i], parentdn, REASON_RESURRECT_ENTRY, opcsn, NULL); + if (rc) { + slapi_log_err(SLAPI_LOG_ERR, sessionid, + "urp_resolve parent entry: failed to create glue from tombstone %s\n", slapi_sdn_get_dn(parentdn)); + slapi_ch_free_string(&tombstone_nsuniqueid); + tombstone_nsuniqueid = NULL; + } + break; + } + } + } + csn_free(&tombstone_csn); + +done: + slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc); + csn_free(&conflict_csn); + slapi_ch_free_string(&parent_dn); + slapi_free_search_results_internal(newpb); + slapi_pblock_destroy(newpb); + if (filter) { + PR_smprintf_free(filter); + } + return tombstone_nsuniqueid; +} + +static int +urp_conflict_to_glue (char *sessionid, const Slapi_Entry *entry, Slapi_DN *parentdn, CSN *opcsn) +{ + int rc = 0; + int op_result; + const char *newrdn; + Slapi_RDN *parentrdn = slapi_rdn_new(); + const char *basedn; + + basedn = slapi_entry_get_dn_const (entry); + slapi_sdn_get_rdn(parentdn, parentrdn); + newrdn = slapi_rdn_get_rdn(parentrdn); + if(newrdn) { + del_replconflict_attr(entry, opcsn, 0); + slapi_log_err(slapi_log_urp, sessionid, + "urp_conflict_to_glue - %s --> %s\n", basedn, newrdn); + op_result = urp_fixup_rename_entry ( entry, newrdn, NULL, 0 ); + switch(op_result) { + case LDAP_SUCCESS: + break; + case LDAP_NO_SUCH_OBJECT: + slapi_log_err(slapi_log_urp, sessionid, + "urp_conflict_to_glue failed(%d) - %s --> %s\n", op_result, basedn, newrdn); + rc = LDAP_NO_SUCH_OBJECT; + break; + default: + slapi_log_err(slapi_log_urp, sessionid, + "urp_conflict_to_glue failed(%d) - %s --> %s\n", op_result, basedn, newrdn); + rc = 1; + } + slapi_ch_free ( (void**)&newrdn ); + } + return rc; +} +/* * urp_annotate_dn: * Returns 0 on failure * Returns > 0 on success (1 on general conflict resolution success, LDAP_NO_SUCH_OBJECT on no-conflict success) @@ -938,7 +1674,7 @@ bailout: * a new entry (the operation entry) see urp_add_operation. */ static int -urp_annotate_dn(char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const char *optype) +urp_annotate_dn(char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const char *optype, char **conflict_dn) { int rc = 0; /* Fail */ int op_result; @@ -951,8 +1687,12 @@ urp_annotate_dn(char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const cha basesdn = slapi_entry_get_sdn_const(entry); basedn = slapi_entry_get_dn_const(entry); newrdn = get_rdn_plus_uniqueid(sessionid, basedn, uniqueid); + if (conflict_dn) { + *conflict_dn = NULL; + } if (newrdn) { mod_namingconflict_attr(uniqueid, basesdn, basesdn, opcsn, optype); + mod_objectclass_attr(uniqueid, basesdn, basesdn, opcsn, optype); slapi_log_err(slapi_log_urp, sessionid, "urp_annotate_dn - %s --> %s\n", basedn, newrdn); op_result = urp_fixup_rename_entry(entry, newrdn, NULL, 0); @@ -961,6 +1701,9 @@ urp_annotate_dn(char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const cha slapi_log_err(slapi_log_urp, sessionid, "urp_annotate_dn - Naming conflict %s. Renamed existing entry to %s\n", optype, newrdn); + if (conflict_dn) { + *conflict_dn = slapi_ch_smprintf("%s,%s",newrdn,slapi_dn_find_parent(basedn)); + } rc = 1; break; case LDAP_NO_SUCH_OBJECT: @@ -999,6 +1742,85 @@ urp_annotate_dn(char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const cha return rc; } +static char * +urp_get_valid_parent_nsuniqueid (Slapi_DN *parentdn) +{ + Slapi_PBlock *newpb = NULL; + Slapi_Entry **entries = NULL; + int op_result = LDAP_SUCCESS; + char *nsuid = NULL; + + newpb = slapi_pblock_new(); + slapi_search_internal_set_pb(newpb, + slapi_sdn_get_dn(parentdn), + 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) ) { + /* Log a message */ + goto done; + } + + nsuid = slapi_entry_attr_get_charptr(entries[0], "nsuniqueid"); +done: + slapi_free_search_results_internal(newpb); + slapi_pblock_destroy(newpb); + newpb = NULL; + + return nsuid; +} + +static int +urp_rename_conflict_children(const char *old_parent, const Slapi_DN *new_parent) +{ + Slapi_PBlock *newpb = NULL; + Slapi_Entry **entries = NULL; + int op_result = LDAP_SUCCESS; + int i; + + newpb = slapi_pblock_new(); + slapi_search_internal_set_pb(newpb, + old_parent, + LDAP_SCOPE_ONELEVEL, + "(|(objectclass=*)(objectclass=ldapsubentry))", + 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; + } + for (i = 0; NULL != entries[i]; i++) { + op_result = urp_fixup_move_entry(entries[i],new_parent,0); + slapi_log_err(SLAPI_LOG_REPL, "session test", + "urp_rename_conflict children - Renaming: %s, Result: %d\n", + slapi_entry_get_dn_const(entries[i]), op_result); + } + +done: + slapi_free_search_results_internal(newpb); + slapi_pblock_destroy(newpb); + newpb = NULL; + + return op_result; +} + /* * An URP Naming Collision helper function. Retrieves a list of entries * that have the given dn excluding the unique id of the entry. Any @@ -1008,7 +1830,7 @@ urp_annotate_dn(char *sessionid, const Slapi_Entry *entry, CSN *opcsn, const cha * to its RDN. */ static Slapi_Entry * -urp_get_min_naming_conflict_entry(Slapi_PBlock *pb, char *sessionid, CSN *opcsn) +urp_get_min_naming_conflict_entry(Slapi_PBlock *pb, const char *collisiondn, char *sessionid, CSN *opcsn) { Slapi_PBlock *newpb = NULL; LDAPControl **server_ctrls = NULL; @@ -1017,42 +1839,46 @@ urp_get_min_naming_conflict_entry(Slapi_PBlock *pb, char *sessionid, CSN *opcsn) const CSN *min_csn = NULL; char *filter = NULL; char *parent_dn = NULL; - char *basedn; + const char *basedn; int i = 0; int min_i = -1; int op_result = LDAP_SUCCESS; - slapi_pblock_get(pb, SLAPI_URP_NAMING_COLLISION_DN, &basedn); - if (NULL == basedn || strncmp(basedn, SLAPI_ATTR_UNIQUEID, strlen(SLAPI_ATTR_UNIQUEID)) == 0) + if (collisiondn) { + basedn = collisiondn; + } else { + slapi_pblock_get (pb, SLAPI_URP_NAMING_COLLISION_DN, &basedn); + } + if (NULL == basedn || strncmp (basedn, SLAPI_ATTR_UNIQUEID, strlen(SLAPI_ATTR_UNIQUEID)) == 0) return NULL; slapi_log_err(SLAPI_LOG_REPL, sessionid, "urp_get_min_naming_conflict_entry - %s\n", basedn); - filter = slapi_filter_sprintf("(%s=%s %s%s)", ATTR_NSDS5_REPLCONFLICT, REASON_ANNOTATE_DN, + filter = slapi_filter_sprintf("(&(objectclass=ldapsubentry)(%s=%s (ADD) %s%s))", ATTR_NSDS5_REPLCONFLICT, REASON_ANNOTATE_DN, ESC_NEXT_VAL, basedn); /* server_ctrls will be freed when newpb is destroyed */ - server_ctrls = (LDAPControl **)slapi_ch_calloc(2, sizeof(LDAPControl *)); + server_ctrls = (LDAPControl **)slapi_ch_calloc (2, sizeof (LDAPControl *)); server_ctrls[0] = create_managedsait_control(); server_ctrls[1] = NULL; - + newpb = slapi_pblock_new(); - parent_dn = slapi_dn_parent(basedn); + parent_dn = slapi_dn_parent (basedn); slapi_search_internal_set_pb(newpb, parent_dn, /* Base DN */ LDAP_SCOPE_ONELEVEL, filter, - NULL, /* Attrs */ - 0, /* AttrOnly */ + NULL, /* Attrs */ + 0, /* AttrOnly */ server_ctrls, /* Controls */ - NULL, /* UniqueID */ + 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)) { + if ( (op_result != LDAP_SUCCESS) || (entries == NULL) ) { /* Log a message */ goto done; } @@ -1060,9 +1886,9 @@ urp_get_min_naming_conflict_entry(Slapi_PBlock *pb, char *sessionid, CSN *opcsn) for (i = 0; NULL != entries[i]; i++) { const CSN *dncsn; dncsn = entry_get_dncsn(entries[i]); - if ((dncsn != opcsn) && + if ((dncsn != opcsn) && (csn_compare(dncsn, opcsn) > 0 ) && ((min_csn == NULL) || (csn_compare(dncsn, min_csn) < 0)) && - !is_tombstone_entry(entries[i])) { + !is_tombstone_entry (entries[i])) { min_csn = dncsn; min_i = i; } @@ -1077,7 +1903,7 @@ urp_get_min_naming_conflict_entry(Slapi_PBlock *pb, char *sessionid, CSN *opcsn) break; } } - + if (min_csn != NULL) { /* Found one entry */ min_naming_conflict_entry = slapi_entry_dup(entries[min_i]); @@ -1114,20 +1940,36 @@ urp_naming_conflict_removal(Slapi_PBlock *pb, char *sessionid, CSN *opcsn, const /* * Backend op has set SLAPI_URP_NAMING_COLLISION_DN to the basedn. */ - min_naming_conflict_entry = urp_get_min_naming_conflict_entry(pb, sessionid, opcsn); + min_naming_conflict_entry = urp_get_min_naming_conflict_entry(pb, NULL, sessionid, opcsn); if (min_naming_conflict_entry == NULL) { return 0; } + /* EXPERIMENT - do step 2 before 1 */ + + /* Step2: Remove ATTR_NSDS5_REPLCONFLICT from the winning entry */ + /* + * A fixup op will not invoke urp_modrdn_operation(). Even it does, + * urp_modrdn_operation() will do nothing because of the same CSN. + */ + op_result = del_replconflict_attr(min_naming_conflict_entry, opcsn, OP_FLAG_ACTION_INVOKE_FOR_REPLOP); + if (op_result != LDAP_SUCCESS) { + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_naming_conflict_removal - Failed to remove nsds5ReplConflict for %s, err=%d\n", + slapi_entry_get_ndn(min_naming_conflict_entry), op_result); + } + /* end Step 2 */ /* Step 1: Restore the entry's original DN */ oldrdn = slapi_rdn_new_sdn(slapi_entry_get_sdn_const(min_naming_conflict_entry)); oldrdnstr = slapi_rdn_get_rdn(oldrdn); + slapi_log_err(SLAPI_LOG_REPL, sessionid, + "urp_naming_conflict_removal - Found %s\n", slapi_entry_get_ndn(min_naming_conflict_entry)); /* newrdnstr is the old rdn of the entry minus the nsuniqueid part */ - newrdn = slapi_rdn_new_rdn(oldrdn); - slapi_rdn_remove_attr(newrdn, SLAPI_ATTR_UNIQUEID); - newrdnstr = slapi_rdn_get_rdn(newrdn); + newrdn = slapi_rdn_new_rdn ( oldrdn ); + slapi_rdn_remove_attr (newrdn, SLAPI_ATTR_UNIQUEID ); + newrdnstr = slapi_rdn_get_rdn(newrdn); /* * Set OP_FLAG_ACTION_INVOKE_FOR_REPLOP since this operation @@ -1135,25 +1977,15 @@ urp_naming_conflict_removal(Slapi_PBlock *pb, char *sessionid, CSN *opcsn, const * will acquire the DB lock if it sees this flag. */ op_result = urp_fixup_rename_entry((const Slapi_Entry *)min_naming_conflict_entry, newrdnstr, NULL, OP_FLAG_ACTION_INVOKE_FOR_REPLOP); - if (op_result != LDAP_SUCCESS) { + if ( op_result != LDAP_SUCCESS ) { slapi_log_err(slapi_log_urp, sessionid, - "urp_naming_conflict_removal - Failed to restore RDN of %s, err=%d\n", oldrdnstr, op_result); + "urp_naming_conflict_removal - Failed to restore RDN of %s, err=%d\n", oldrdnstr, op_result); goto bailout; } slapi_log_err(slapi_log_urp, sessionid, "urp_naming_conflict_removal - Naming conflict removed by %s. RDN of %s was restored\n", optype, oldrdnstr); - - /* Step2: Remove ATTR_NSDS5_REPLCONFLICT from the winning entry */ - /* - * A fixup op will not invoke urp_modrdn_operation(). Even it does, - * urp_modrdn_operation() will do nothing because of the same CSN. - */ - op_result = del_replconflict_attr(min_naming_conflict_entry, opcsn, OP_FLAG_ACTION_INVOKE_FOR_REPLOP); - if (op_result != LDAP_SUCCESS) { - slapi_log_err(SLAPI_LOG_REPL, sessionid, - "urp_naming_conflict_removal - Failed to remove nsds5ReplConflict for %s, err=%d\n", - newrdnstr, op_result); - } + + /* end Step 1 */ bailout: slapi_entry_free(min_naming_conflict_entry); @@ -1207,17 +2039,16 @@ char * get_rdn_plus_uniqueid(char *sessionid, const char *olddn, const char *uniqueid) { char *newrdn = NULL; + Slapi_RDN *rdn = NULL; /* Check if the RDN already contains the Unique ID */ - Slapi_DN *sdn = slapi_sdn_new_dn_byval(olddn); - Slapi_RDN *rdn = slapi_rdn_new(); - int rc = slapi_rdn_init_all_sdn_ext(rdn, sdn, 1); - if (rc) { + rdn = slapi_rdn_new_dn(olddn); + if (rdn == NULL) { slapi_log_err(SLAPI_LOG_ERR, sessionid, "Failed to convert %s to RDN\n", olddn); goto bail; } - PR_ASSERT(uniqueid != NULL); + PR_ASSERT(uniqueid!=NULL); if (slapi_rdn_is_conflict(rdn)) { /* The Unique ID is already in the RDN. * This is a highly improbable collision. @@ -1228,16 +2059,55 @@ get_rdn_plus_uniqueid(char *sessionid, const char *olddn, const char *uniqueid) slapi_log_err(SLAPI_LOG_WARNING, sessionid, "get_rdn_plus_uniqueid - Annotated RDN %s has naming conflict\n", olddn); } else { - slapi_rdn_add(rdn, SLAPI_ATTR_UNIQUEID, uniqueid); + slapi_rdn_add(rdn,SLAPI_ATTR_UNIQUEID,uniqueid); newrdn = slapi_ch_strdup(slapi_rdn_get_rdn(rdn)); } bail: - slapi_sdn_free(&sdn); slapi_rdn_free(&rdn); return newrdn; } 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; + char *replconflict = slapi_entry_attr_get_charptr(entry,ATTR_NSDS5_REPLCONFLICT ); + if (replconflict) { + is_conflict = 1; + slapi_ch_free_string(&replconflict); + } + + 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) { return is_suffix_dn(pb, slapi_entry_get_sdn(entry), parentdn); @@ -1272,16 +2142,11 @@ mod_namingconflict_attr(const char *uniqueid, const Slapi_DN *entrysdn, const Sl char buf[BUFSIZ]; int op_result; -#ifdef DEBUG PR_snprintf(buf, sizeof(buf), "%s (%s) %s", - REASON_ANNOTATE_DN, optype, slapi_sdn_get_dn(conflictsdn)); -#else - PR_snprintf(buf, sizeof(buf), "%s %s", - REASON_ANNOTATE_DN, slapi_sdn_get_dn(conflictsdn)); -#endif + REASON_ANNOTATE_DN, optype, slapi_sdn_get_dn(conflictsdn)); slapi_mods_init(&smods, 2); - if (strncmp(slapi_sdn_get_dn(entrysdn), SLAPI_ATTR_UNIQUEID, - strlen(SLAPI_ATTR_UNIQUEID)) != 0) { + if ( strncmp(slapi_sdn_get_dn(entrysdn), SLAPI_ATTR_UNIQUEID, + strlen(SLAPI_ATTR_UNIQUEID)) != 0 ) { slapi_mods_add(&smods, LDAP_MOD_ADD, ATTR_NSDS5_REPLCONFLICT, strlen(buf), buf); } else { /* @@ -1297,6 +2162,25 @@ mod_namingconflict_attr(const char *uniqueid, const Slapi_DN *entrysdn, const Sl } static int +mod_objectclass_attr(const char *uniqueid, const Slapi_DN *entrysdn, const Slapi_DN *conflictsdn, CSN *opcsn, const char *optype) +{ + Slapi_Mods smods; + int op_result; + char csnstr[CSN_STRSIZE+1]; + + slapi_mods_init(&smods, 3); + slapi_mods_add(&smods, LDAP_MOD_ADD, "objectclass", strlen("ldapsubentry"),"ldapsubentry"); + slapi_mods_add(&smods, LDAP_MOD_REPLACE, "conflictcsn", strlen(csnstr),csn_as_string(opcsn, PR_FALSE, csnstr)); + op_result = urp_fixup_modify_entry(uniqueid, entrysdn, opcsn, &smods, 0); + slapi_mods_done(&smods); + if (op_result == LDAP_TYPE_OR_VALUE_EXISTS) { + /* the objectclass was already present */ + op_result = LDAP_SUCCESS; + } + return op_result; +} + +static int del_replconflict_attr(const Slapi_Entry *entry, CSN *opcsn, int opflags) { Slapi_Attr *attr; @@ -1309,8 +2193,9 @@ del_replconflict_attr(const Slapi_Entry *entry, CSN *opcsn, int opflags) uniqueid = slapi_entry_get_uniqueid(entry); entrysdn = slapi_entry_get_sdn_const(entry); - slapi_mods_init(&smods, 2); + slapi_mods_init(&smods, 3); slapi_mods_add(&smods, LDAP_MOD_DELETE, ATTR_NSDS5_REPLCONFLICT, 0, NULL); + slapi_mods_add(&smods, LDAP_MOD_DELETE, "objectclass", strlen("ldapsubentry"),"ldapsubentry"); op_result = urp_fixup_modify_entry(uniqueid, entrysdn, opcsn, &smods, opflags); slapi_mods_done(&smods); } diff --git a/ldap/servers/plugins/replication/urp.h b/ldap/servers/plugins/replication/urp.h index c641ab9..d88dfbc 100644 --- a/ldap/servers/plugins/replication/urp.h +++ b/ldap/servers/plugins/replication/urp.h @@ -23,6 +23,7 @@ int urp_modify_operation(Slapi_PBlock *pb); int urp_add_operation(Slapi_PBlock *pb); int urp_delete_operation(Slapi_PBlock *pb); +int urp_post_add_operation(Slapi_PBlock *pb); int urp_post_delete_operation(Slapi_PBlock *pb); int urp_modrdn_operation(Slapi_PBlock *pb); int urp_post_modrdn_operation(Slapi_PBlock *pb); @@ -33,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); @@ -41,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); @@ -51,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_glue.c b/ldap/servers/plugins/replication/urp_glue.c index 80ece48..262a93f 100644 --- a/ldap/servers/plugins/replication/urp_glue.c +++ b/ldap/servers/plugins/replication/urp_glue.c @@ -63,7 +63,7 @@ get_glue_csn(const Slapi_Entry *entry, const CSN **gluecsn) int entry_to_glue(char *sessionid, const Slapi_Entry *entry, const char *reason, CSN *opcsn) { - int op_result = 0; + int op_result = -1; const char *dn; const Slapi_DN *sdn; slapi_mods smods; diff --git a/ldap/servers/plugins/replication/urp_tombstone.c b/ldap/servers/plugins/replication/urp_tombstone.c index 67bfefc..9f1f060 100644 --- a/ldap/servers/plugins/replication/urp_tombstone.c +++ b/ldap/servers/plugins/replication/urp_tombstone.c @@ -51,6 +51,99 @@ 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); + } + slapi_ch_free_string(&replconflict); + } + 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); + const 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); + slapi_sdn_free(&newsuperior); + } + } + } + break; + default: + break; + } + + slapi_free_search_results_internal (newpb); + slapi_pblock_destroy(newpb); + return rc; +} + static int tombstone_to_glue_resolve_parent( Slapi_PBlock *pb, @@ -103,6 +196,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); + } + } + + if (op_result) goto done; + + slapi_log_err(SLAPI_LOG_REPL, 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); + csn_as_string(dncsn, PR_FALSE, csnstr); + + slapi_log_err(SLAPI_LOG_REPL, 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), 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/plugins/retrocl/retrocl_po.c b/ldap/servers/plugins/retrocl/retrocl_po.c index eb46b95..d2af79b 100644 --- a/ldap/servers/plugins/retrocl/retrocl_po.c +++ b/ldap/servers/plugins/retrocl/retrocl_po.c @@ -638,7 +638,8 @@ retrocl_postob(Slapi_PBlock *pb, int optype) return SLAPI_PLUGIN_SUCCESS; } - if (operation_is_flag_set(op, OP_FLAG_TOMBSTONE_ENTRY)) { + if (operation_is_flag_set(op, OP_FLAG_TOMBSTONE_ENTRY) || + operation_is_flag_set(op, OP_FLAG_NOOP)) { slapi_log_err(SLAPI_LOG_TRACE, RETROCL_PLUGIN_NAME, "retrocl_postob - Not applying change for nsTombstone entries\n"); return SLAPI_PLUGIN_SUCCESS; } diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c index 555b15d..b7e17ad 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) { @@ -321,11 +325,20 @@ ldbm_back_add(Slapi_PBlock *pb) /* Call the Backend Pre Add plugins */ ldap_result_code = LDAP_SUCCESS; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); - rc = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_ADD_FN); - if (rc < 0) { + rc = plugin_call_mmr_plugin_preop(pb, NULL,SLAPI_PLUGIN_BE_PRE_ADD_FN); + if (rc == 0 && op_plugin_call) { + rc = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_ADD_FN); + } + 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; } /* @@ -593,6 +606,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 ", @@ -630,27 +644,47 @@ 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_DEBUG, + "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_DEBUG, + "ldbm_back_add", "(tombstone_operation for %s): calculated tombstone_rdn " + "is (%s) \n", entryuniqueid, tombstone_rdn); + /* e_srdn has "uniaqueid=..., " */ + slapi_rdn_replace_rdn(srdn, tombstone_rdn); + slapi_ch_free_string(&tombstone_rdn); } } slapi_entry_set_dn(addingentry->ep_entry, tombstoned_dn); @@ -682,11 +716,21 @@ ldbm_back_add(Slapi_PBlock *pb) } } + 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) { slapi_entry_add_string(addingentry->ep_entry, SLAPI_ATTR_VALUE_PARENT_UNIQUEID, operation->o_params.p.p_add.parentuniqueid); } + } else { + /* if an entry is explicitely added as tombstone the entry flag has to be set */ + if (slapi_entry_attr_hasvalue(addingentry->ep_entry, + SLAPI_ATTR_OBJECTCLASS, SLAPI_ATTR_VALUE_TOMBSTONE)) { + slapi_entry_set_flag(addingentry->ep_entry, SLAPI_ENTRY_FLAG_TOMBSTONE); + } } } @@ -719,8 +763,12 @@ 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), - slapi_entry_get_sdn_const(addingentry->ep_entry))) { + /* TBD better handle tombstone parents, + * we have the entry dn as nsuniqueid=nnnn,,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", slapi_entry_get_rdn_const(addingentry->ep_entry), @@ -748,12 +796,20 @@ ldbm_back_add(Slapi_PBlock *pb) } else if (is_tombstone_operation) { /* 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 { /* * add the parentid, entryid and entrydn operational attributes */ add_update_entry_operational_attributes(addingentry, pid); } + if (is_resurect_operation && tombstoneentry && cache_is_in_cache(&inst->inst_cache, tombstoneentry)) { + /* we need to remove the tombstone from the cacehr otherwise we have two dns with the same id */ + cache_unlock_entry(&inst->inst_cache, tombstoneentry); + CACHE_RETURN(&inst->inst_cache, &tombstoneentry); + } /* Tentatively add the entry to the cache. We do this after adding any * operational attributes to ensure that the cache is sized correctly. */ @@ -805,31 +861,40 @@ 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) { - int opreturn = 0; - if (SLAPI_PLUGIN_NOOP == retval) { - not_an_error = 1; - rc = retval = LDAP_SUCCESS; - } - slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_add", "SLAPI_PLUGIN_BE_TXN_PRE_ADD_FN plugin " - "returned error code %d\n", - retval); - 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); + 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; + rc = retval = LDAP_SUCCESS; + } + slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_add", "SLAPI_PLUGIN_BE_TXN_PRE_ADD_FN plugin " + "returned error code %d\n", + retval); + 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); + 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; } - slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); - 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_DEBUG, "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); @@ -1113,7 +1178,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 " @@ -1134,6 +1199,12 @@ 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); /* after commit - txn is no longer valid - replace SLAPI_TXN with parent */ @@ -1206,7 +1277,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 " @@ -1222,6 +1294,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); @@ -1396,3 +1470,23 @@ delete_update_entrydn_operational_attributes(struct backentry *ep) /* entrydn */ 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 7559f14..db463c1 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c @@ -249,9 +249,8 @@ ldbm_back_delete(Slapi_PBlock *pb) retval = dblayer_txn_begin_ext(li, parent_txn, &txn, PR_FALSE); } if (0 != retval) { - if (LDBM_OS_ERR_IS_DISKFULL(retval)) - disk_full = 1; - ldap_result_code = LDAP_OPERATIONS_ERROR; + if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; + ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } @@ -260,16 +259,18 @@ ldbm_back_delete(Slapi_PBlock *pb) if (0 == retry_count) { /* just once */ /* find and lock the entry we are about to modify */ - /* - * A corner case: - * If a conflict occurred in a MMR topology, a replicated delete + /* + * A corner case: + * If a conflict occurred in a MMR topology, a replicated delete * op from another master could target a conflict entry; while the - * corresponding entry on this server could have been already + * corresponding entry on this server could have been already * deleted. That is, the entry 'e' found with "addr" is a tomb- * stone. If it is the case, we need to back off. */ - if ((e = find_entry2modify(pb, be, addr, &txn, &result_sent)) == NULL) { - ldap_result_code = LDAP_NO_SUCH_OBJECT; +replace_entry: + if ((e = find_entry2modify(pb, be, addr, &txn, &result_sent)) == NULL) + { + ldap_result_code= LDAP_NO_SUCH_OBJECT; retval = -1; slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_delete", "Deleting entry is already deleted.\n"); goto error_return; /* error result sent by find_entry2modify() */ @@ -279,56 +280,92 @@ ldbm_back_delete(Slapi_PBlock *pb) /* JCMACL - Shouldn't the access check be before the has children check... * otherwise we're revealing the fact that an entry exists and has children */ /* Before has children to mask the presence of children disclosure. */ - ldap_result_code = plugin_call_acl_plugin(pb, e->ep_entry, NULL, NULL, SLAPI_ACL_DELETE, - ACLPLUGIN_ACCESS_DEFAULT, &errbuf); - if (ldap_result_code != LDAP_SUCCESS) { - ldap_result_message = errbuf; + ldap_result_code = plugin_call_acl_plugin (pb, e->ep_entry, NULL, NULL, SLAPI_ACL_DELETE, + ACLPLUGIN_ACCESS_DEFAULT, &errbuf ); + if ( ldap_result_code != LDAP_SUCCESS ) { + ldap_result_message= errbuf; retval = -1; goto error_return; } - + /* this has to be handled by urp for replicated operations */ retval = slapi_entry_has_children(e->ep_entry); - if (retval) { - ldap_result_code = LDAP_NOT_ALLOWED_ON_NONLEAF; - slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_delete", - "conn=%lu op=%d Deleting entry %s has %d children.\n", - conn_id, op_id, slapi_entry_get_dn(e->ep_entry), retval); + if (retval && !is_replicated_operation) { + ldap_result_code= LDAP_NOT_ALLOWED_ON_NONLEAF; + slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_delete", + "conn=%lu op=%d Deleting entry %s has %d children.\n", + conn_id, op_id, slapi_entry_get_dn(e->ep_entry), retval); retval = -1; goto error_return; } /* Don't call pre-op for Tombstone entries */ if (!delete_tombstone_entry) { - /* + /* * Some present state information is passed through the PBlock to the * backend pre-op plugin. To ensure a consistent snapshot of this state * we wrap the reading of the entry with the dblock. */ - ldap_result_code = get_copy_of_entry(pb, addr, &txn, - SLAPI_DELETE_EXISTING_ENTRY, !is_replicated_operation); + ldap_result_code= get_copy_of_entry(pb, addr, &txn, + SLAPI_DELETE_EXISTING_ENTRY, !is_replicated_operation); free_delete_existing_entry = 1; - if (ldap_result_code == LDAP_OPERATIONS_ERROR || - ldap_result_code == LDAP_INVALID_DN_SYNTAX) { + if(ldap_result_code==LDAP_OPERATIONS_ERROR || + ldap_result_code==LDAP_INVALID_DN_SYNTAX) { /* restore original entry so the front-end delete code can free it */ retval = -1; goto error_return; } slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); - retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_DELETE_FN); - if (retval) { - if (SLAPI_PLUGIN_NOOP == retval) { - not_an_error = 1; - rc = LDAP_SUCCESS; + retval = plugin_call_mmr_plugin_preop(pb, NULL,SLAPI_PLUGIN_BE_PRE_DELETE_FN); + if (SLAPI_PLUGIN_NOOP == retval) { + not_an_error = 1; + rc = LDAP_SUCCESS; + } else if (SLAPI_PLUGIN_NOOP_COMMIT == retval) { + not_an_error = 1; + 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_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); + slapi_entry_free( e->ep_entry ); + e->ep_entry = slapi_entry_dup( e_to_delete ); + retval = 0; + } + if (retval == 0) { + retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_DELETE_FN); + } + if (retval) + { + /* * Plugin indicated some kind of failure, * or that this Operation became a No-Op. */ slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code); if (!ldap_result_code) { if (LDAP_ALREADY_EXISTS == ldap_result_code) { - /* + /* * The target entry is already a tombstone. * We need to treat this as a success, * but we need to remove the entry e from the entry cache. @@ -339,9 +376,9 @@ ldbm_back_delete(Slapi_PBlock *pb) slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code); } /* restore original entry so the front-end delete code can free it */ - slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); + slapi_pblock_get( pb, SLAPI_PLUGIN_OPRETURN, &opreturn ); if (!opreturn) { - slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &rc); + slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &rc ); } slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; @@ -350,7 +387,7 @@ ldbm_back_delete(Slapi_PBlock *pb) delete_tombstone_entry = operation_is_flag_set(operation, OP_FLAG_TOMBSTONE_ENTRY); } - /* call the transaction pre delete plugins just after the + /* call the transaction pre delete plugins just after the * to-be-deleted entry is prepared. */ /* these should not need to modify the entry to be deleted - if for some reason they ever do, do not use e->ep_entry since @@ -360,18 +397,18 @@ ldbm_back_delete(Slapi_PBlock *pb) retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_PRE_DELETE_FN); if (retval) { slapi_log_err(SLAPI_LOG_TRACE, - "ldbm_back_delete", "SLAPI_PLUGIN_BE_TXN_PRE_DELETE_FN plugin " - "returned error code %d\n", - retval); + "ldbm_back_delete", "SLAPI_PLUGIN_BE_TXN_PRE_DELETE_FN plugin " + "returned error code %d\n", retval ); if (!ldap_result_code) { slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code); } if (!opreturn) { - slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); + 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_set( pb, SLAPI_PLUGIN_OPRETURN, + ldap_result_code ? + &ldap_result_code : &retval ); } slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; @@ -386,13 +423,13 @@ ldbm_back_delete(Slapi_PBlock *pb) if (delete_tombstone_entry) { if (!is_tombstone_entry) { slapi_log_err(SLAPI_LOG_WARNING, "ldbm_back_delete", - "Attempt to delete a non-tombstone entry %s\n", dn); + "Attempt to delete a non-tombstone entry %s\n", dn); delete_tombstone_entry = 0; } } else { - if (is_tombstone_entry) { + if (is_tombstone_entry) { slapi_log_err(SLAPI_LOG_WARNING, "ldbm_back_delete", - "Attempt to Tombstone again a tombstone entry %s\n", dn); + "Attempt to Tombstone again a tombstone entry %s\n", dn); delete_tombstone_entry = 1; } } @@ -401,7 +438,7 @@ ldbm_back_delete(Slapi_PBlock *pb) * If a CSN is set, we need to tombstone the entry, * rather than deleting it outright. */ - opcsn = operation_get_csn(operation); + opcsn = operation_get_csn (operation); if (!delete_tombstone_entry) { if ((opcsn == NULL) && !is_fixup_operation && operation->o_csngen_handler) { /* @@ -409,36 +446,35 @@ ldbm_back_delete(Slapi_PBlock *pb) * by entry_assign_operation_csn() if the dn is in an * updatable replica. */ - opcsn = entry_assign_operation_csn(pb, e->ep_entry, NULL); + opcsn = entry_assign_operation_csn ( pb, e->ep_entry, NULL ); } if (opcsn != NULL) { if (!is_fixup_operation) { - entry_set_maxcsn(e->ep_entry, opcsn); + entry_set_maxcsn (e->ep_entry, opcsn); } } /* * We are dealing with replication and if we haven't been called to * remove a tombstone, then it's because we want to create a new one. */ - if (slapi_operation_get_replica_attr(pb, operation, "nsds5ReplicaTombstonePurgeInterval", - &create_tombstone_entry) == 0) { + if ( slapi_operation_get_replica_attr (pb, operation, "nsds5ReplicaTombstonePurgeInterval", + &create_tombstone_entry) == 0 ) { create_tombstone_entry = (create_tombstone_entry < 0) ? 0 : 1; } } if (create_tombstone_entry && is_tombstone_entry) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_delete", - "Attempt to convert a tombstone entry %s to tombstone\n", dn); + "Attempt to convert a tombstone entry %s to tombstone\n", dn); retval = -1; ldap_result_code = LDAP_UNWILLING_TO_PERFORM; goto error_return; } - + #ifdef DEBUG slapi_log_err(SLAPI_LOG_REPL, "ldbm_back_delete", - "entry: %s - flags: delete %d is_tombstone_entry %d create %d \n", - dn, delete_tombstone_entry, is_tombstone_entry, create_tombstone_entry); + "entry: %s - flags: delete %d is_tombstone_entry %d create %d \n", + dn, delete_tombstone_entry, is_tombstone_entry, create_tombstone_entry); #endif - /* Save away a copy of the entry, before modifications */ slapi_pblock_set(pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup(e->ep_entry)); @@ -1193,6 +1229,15 @@ ldbm_back_delete(Slapi_PBlock *pb) } } + if (rc == 0 && opcsn && !is_fixup_operation && !delete_tombstone_entry) { + /* URP Naming Collision + * When an entry is deleted by a replicated delete operation + * we must check for entries that have had a naming collision + * with this entry. Now that this name has been given up, one + * of those entries can take over the name. + */ + slapi_pblock_set(pb, SLAPI_URP_NAMING_COLLISION_DN, slapi_ch_strdup(dn)); + } /* call the transaction post delete plugins just before the commit */ if (!delete_tombstone_entry && plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN)) { @@ -1219,7 +1264,20 @@ 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: /* Release SERIAL LOCK */ retval = dblayer_txn_commit(be, &txn); /* after commit - txn is no longer valid - replace SLAPI_TXN with parent */ @@ -1282,17 +1340,6 @@ ldbm_back_delete(Slapi_PBlock *pb) } } - 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; @@ -1361,6 +1408,7 @@ error_return: } slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); } + retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN); /* Release SERIAL LOCK */ dblayer_txn_abort(be, &txn); /* abort crashes in case disk full */ @@ -1378,6 +1426,7 @@ error_return: "conn=%lu op=%d modify_unswitch_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); } + common_return: if (orig_entry) { /* NOTE: #define SLAPI_DELETE_BEPREOP_ENTRY SLAPI_ENTRY_PRE_OP */ @@ -1457,15 +1506,6 @@ diskfull_return: conn_id, op_id, parent_modify_c.old_entry, parent_modify_c.new_entry, cache_is_in_cache(&inst->inst_cache, parent_modify_c.new_entry)); myrc = modify_term(&parent_modify_c, be); - if (rc == 0 && opcsn && !is_fixup_operation && !delete_tombstone_entry) { - /* URP Naming Collision - * When an entry is deleted by a replicated delete operation - * we must check for entries that have had a naming collision - * with this entry. Now that this name has been given up, one - * of those entries can take over the name. - */ - slapi_pblock_set(pb, SLAPI_URP_NAMING_COLLISION_DN, slapi_ch_strdup(dn)); - } if (free_delete_existing_entry) { done_with_pblock_entry(pb, SLAPI_DELETE_EXISTING_ENTRY); } else { /* owned by someone else */ diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c index aee39e6..7ee796f 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c @@ -408,6 +408,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; @@ -622,7 +623,10 @@ ldbm_back_modify(Slapi_PBlock *pb) slapi_pblock_set(pb, SLAPI_MODIFY_EXISTING_ENTRY, ec->ep_entry); slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); - opreturn = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN); + opreturn = plugin_call_mmr_plugin_preop(pb, NULL,SLAPI_PLUGIN_BE_PRE_MODIFY_FN); + if (opreturn == 0) { + opreturn = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN); + } if (opreturn || (slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code) && ldap_result_code) || (slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn) && opreturn)) { @@ -636,6 +640,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; @@ -861,6 +866,7 @@ ldbm_back_modify(Slapi_PBlock *pb) slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; } + retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN); /* Release SERIAL LOCK */ retval = dblayer_txn_commit(be, &txn); @@ -911,7 +917,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); @@ -921,6 +927,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/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c index 6bb81fa..2c0cb07 100644 --- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c +++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c @@ -428,7 +428,10 @@ ldbm_back_modrdn(Slapi_PBlock *pb) } /* Call the Backend Pre ModRDN plugins */ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); - rc = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_MODRDN_FN); + rc = plugin_call_mmr_plugin_preop(pb, NULL,SLAPI_PLUGIN_BE_PRE_MODRDN_FN); + if (rc == 0) { + rc= plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_MODRDN_FN); + } if (rc < 0) { if (SLAPI_PLUGIN_NOOP == rc) { not_an_error = 1; @@ -1179,6 +1182,10 @@ ldbm_back_modrdn(Slapi_PBlock *pb) conn_id, op_id, parent_modify_context.old_entry, parent_modify_context.new_entry, myrc); } + if (retval == 0 && opcsn != NULL && !is_fixup_operation) { + slapi_pblock_set(pb, SLAPI_URP_NAMING_COLLISION_DN, + slapi_ch_strdup(slapi_sdn_get_dn(sdn))); + } slapi_pblock_set(pb, SLAPI_ENTRY_POST_OP, postentry); /* call the transaction post modrdn plugins just before the commit */ if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN))) { @@ -1203,6 +1210,7 @@ ldbm_back_modrdn(Slapi_PBlock *pb) slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; } + retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN); /* Release SERIAL LOCK */ retval = dblayer_txn_commit(be, &txn); @@ -1342,6 +1350,7 @@ error_return: } slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); } + retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN); /* Release SERIAL LOCK */ dblayer_txn_abort(be, &txn); /* abort crashes in case disk full */ @@ -1464,11 +1473,7 @@ common_return: backentry_free(&original_entry); backentry_free(&tmpentry); slapi_entry_free(original_targetentry); - slapi_ch_free((void **)&errbuf); - if (retval == 0 && opcsn != NULL && !is_fixup_operation) { - slapi_pblock_set(pb, SLAPI_URP_NAMING_COLLISION_DN, - slapi_ch_strdup(slapi_sdn_get_dn(sdn))); - } + slapi_ch_free((void**)&errbuf); if (pb_conn) { slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modrdn", "leave conn=%" PRIu64 " op=%d\n", diff --git a/ldap/servers/slapd/libmakefile b/ldap/servers/slapd/libmakefile index 11a70e7..f874876 100644 --- a/ldap/servers/slapd/libmakefile +++ b/ldap/servers/slapd/libmakefile @@ -53,7 +53,7 @@ LIBSLAPD_OBJS=plugin_role.o getfilelist.o libglobs.o log.o ch_malloc.o entry.o p lenstr.o eventq.o uuid.o uniqueid.o uniqueidgen.o \ csngen.o utf8compare.o entrywsi.o valueset.o \ attrsyntax.o opshared.o add.o modify.o modrdn.o delete.o dl.o\ - plugin_acl.o counters.o subentry.o object.o objset.o apibroker.o \ + plugin_acl.o plugin_mmr.o counters.o subentry.o object.o objset.o apibroker.o \ csn.o csnset.o slapd_plhash.o attrlist.o vattr.o bitset.o rdn.o \ mapping_tree.o backend_manager.o task.o resourcelimit.o \ bulk_import.o security_wrappers.o index_subsystem.o sasl_map.o diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c index 9a9f84e..077684d 100644 --- a/ldap/servers/slapd/pblock.c +++ b/ldap/servers/slapd/pblock.c @@ -2078,6 +2078,14 @@ slapi_pblock_get(Slapi_PBlock *pblock, int arg, void *value) case SLAPI_PLUGIN_ACL_MODS_UPDATE: (*(IFP *)value) = pblock->pb_plugin->plg_acl_mods_update; break; + /* MMR Plugin */ + case SLAPI_PLUGIN_MMR_BETXN_PREOP: + (*(IFP *)value) = pblock->pb_plugin->plg_mmr_betxn_preop; + break; + case SLAPI_PLUGIN_MMR_BETXN_POSTOP: + (*(IFP *)value) = pblock->pb_plugin->plg_mmr_betxn_postop; + break; + case SLAPI_REQUESTOR_DN: /* NOTE: It's not a copy of the DN */ if (pblock->pb_op != NULL) { @@ -2257,6 +2265,12 @@ 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; @@ -3806,6 +3820,14 @@ slapi_pblock_set(Slapi_PBlock *pblock, int arg, void *value) case SLAPI_PLUGIN_ACL_MODS_UPDATE: pblock->pb_plugin->plg_acl_mods_update = (IFP)value; break; + /* MMR Plugin */ + case SLAPI_PLUGIN_MMR_BETXN_PREOP: + pblock->pb_plugin->plg_mmr_betxn_preop = (IFP) value; + break; + case SLAPI_PLUGIN_MMR_BETXN_POSTOP: + pblock->pb_plugin->plg_mmr_betxn_postop = (IFP) value; + break; + case SLAPI_CLIENT_DNS: if (pblock->pb_conn == NULL) { slapi_log_err(SLAPI_LOG_ERR, @@ -3919,6 +3941,10 @@ 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 5ccfd6d..7ec2f37 100644 --- a/ldap/servers/slapd/pblock_v3.h +++ b/ldap/servers/slapd/pblock_v3.h @@ -141,6 +141,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/plugin.c b/ldap/servers/slapd/plugin.c index d181059..f47ff9b 100644 --- a/ldap/servers/slapd/plugin.c +++ b/ldap/servers/slapd/plugin.c @@ -2261,6 +2261,9 @@ plugin_get_type_and_list( } else if (strcasecmp(plugintype, "accesscontrol") == 0) { *type = SLAPI_PLUGIN_ACL; plugin_list_index = PLUGIN_LIST_ACL; + } else if ( strcasecmp( plugintype, "mmr" ) == 0 ) { + *type = SLAPI_PLUGIN_MMR; + plugin_list_index = PLUGIN_LIST_MMR; } else if (strcasecmp(plugintype, "bepreoperation") == 0) { *type = SLAPI_PLUGIN_BEPREOPERATION; plugin_list_index = PLUGIN_LIST_BEPREOPERATION; @@ -2337,6 +2340,8 @@ plugin_get_type_str(int type) return "syntax"; } else if (type == SLAPI_PLUGIN_ACL) { return "accesscontrol"; + } else if (type == SLAPI_PLUGIN_MMR){ + return "mmr"; } else if (type == SLAPI_PLUGIN_BEPREOPERATION) { return "bepreoperation"; } else if (type == SLAPI_PLUGIN_BEPOSTOPERATION) { diff --git a/ldap/servers/slapd/plugin_mmr.c b/ldap/servers/slapd/plugin_mmr.c new file mode 100644 index 0000000..f581205 --- /dev/null +++ b/ldap/servers/slapd/plugin_mmr.c @@ -0,0 +1,71 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission. + * Copyright (C) 2005 Red Hat, Inc. + * All rights reserved. + * + * License: GPL (version 3 or any later version). + * See LICENSE for details. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include +#endif + + +/* + * plugin_mmr.c - routines for calling mmr pre and postop plugins + */ + +#include "slap.h" + +int +plugin_call_mmr_plugin_preop ( Slapi_PBlock *pb, Slapi_Entry *e, int flags) +{ + struct slapdplugin *p; + int rc = LDAP_INSUFFICIENT_ACCESS; + Operation *operation; + + slapi_pblock_get (pb, SLAPI_OPERATION, &operation); + + /* we don't perform acl check for internal operations and if the plugin has set it not to be checked + if (operation_is_flag_set(operation, SLAPI_OP_FLAG_NO_ACCESS_CHECK|OP_FLAG_INTERNAL|OP_FLAG_REPLICATED|OP_FLAG_LEGACY_REPLICATION_DN)) + return LDAP_SUCCESS; + */ + + /* call the global plugins first and then the backend specific */ + for ( p = get_plugin_list(PLUGIN_LIST_MMR); p != NULL; p = p->plg_next ) { + if (plugin_invoke_plugin_sdn (p, SLAPI_PLUGIN_MMR_BETXN_PREOP, pb, + (Slapi_DN*)slapi_entry_get_sdn_const (e))){ + rc = (*p->plg_mmr_betxn_preop)(pb, flags); + if ( rc != LDAP_SUCCESS ) break; + } + } + + return rc; +} + +int +plugin_call_mmr_plugin_postop ( Slapi_PBlock *pb, Slapi_Entry *e, int flags) +{ + struct slapdplugin *p; + int rc = LDAP_INSUFFICIENT_ACCESS; + Operation *operation; + + slapi_pblock_get (pb, SLAPI_OPERATION, &operation); + + /* we don't perform acl check for internal operations and if the plugin has set it not to be checked + if (operation_is_flag_set(operation, SLAPI_OP_FLAG_NO_ACCESS_CHECK|OP_FLAG_INTERNAL|OP_FLAG_REPLICATED|OP_FLAG_LEGACY_REPLICATION_DN)) + return LDAP_SUCCESS; + */ + + /* call the global plugins first and then the backend specific */ + for ( p = get_plugin_list(PLUGIN_LIST_MMR); p != NULL; p = p->plg_next ) { + if (plugin_invoke_plugin_sdn (p, SLAPI_PLUGIN_MMR_BETXN_POSTOP, pb, + (Slapi_DN*)slapi_entry_get_sdn_const (e))){ + rc = (*p->plg_mmr_betxn_postop)(pb, flags); + if ( rc != LDAP_SUCCESS ) break; + } + } + + return rc; +} diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h index 2661ca8..cff7b43 100644 --- a/ldap/servers/slapd/proto-slap.h +++ b/ldap/servers/slapd/proto-slap.h @@ -941,6 +941,12 @@ int plugin_call_acl_mods_update(Slapi_PBlock *pb, int optype); int plugin_call_acl_verify_syntax(Slapi_PBlock *pb, Slapi_Entry *e, char **errbuf); /* + * plugin_mmr.c + */ +int plugin_call_mmr_plugin_preop ( Slapi_PBlock *pb, Slapi_Entry *e, int flags); +int plugin_call_mmr_plugin_postop ( Slapi_PBlock *pb, Slapi_Entry *e, int flags); + +/* * pw_mgmt.c */ void pw_init(void); diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h index 17245ec..4463258 100644 --- a/ldap/servers/slapd/slap.h +++ b/ldap/servers/slapd/slap.h @@ -860,7 +860,8 @@ struct matchingRuleList #define PLUGIN_LIST_INDEX 19 #define PLUGIN_LIST_BETXNPREOPERATION 20 #define PLUGIN_LIST_BETXNPOSTOPERATION 21 -#define PLUGIN_LIST_GLOBAL_MAX 22 +#define PLUGIN_LIST_MMR 22 +#define PLUGIN_LIST_GLOBAL_MAX 23 /* plugin configuration attributes */ #define ATTR_PLUGIN_PATH "nsslapd-pluginPath" @@ -1282,6 +1283,14 @@ struct slapdplugin #define plg_acl_mods_allowed plg_un.plg_un_acl.plg_un_acl_mods_allowed #define plg_acl_mods_update plg_un.plg_un_acl.plg_un_acl_mods_update + struct plg_un_mmr_struct + { + IFP plg_un_mmr_betxn_preop; + IFP plg_un_mmr_betxn_postop; + } plg_un_mmr; +#define plg_mmr_betxn_preop plg_un.plg_un_mmr.plg_un_mmr_betxn_preop +#define plg_mmr_betxn_postop plg_un.plg_un_mmr.plg_un_mmr_betxn_postop + /* password storage scheme (kexcoff) */ struct plg_un_pwd_storage_scheme_struct { diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index 6da0618..3397c63 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -6902,6 +6902,7 @@ slapi_timer_result slapi_timespec_expire_check(struct timespec *expire); #define SLAPI_PLUGIN_BETXNEXTENDEDOP 21 #define SLAPI_PLUGIN_PREEXTOPERATION 22 #define SLAPI_PLUGIN_POSTEXTOPERATION 23 +#define SLAPI_PLUGIN_MMR 24 /* * special return values for extended operation plugins (zero or positive @@ -6920,6 +6921,13 @@ slapi_timer_result slapi_timespec_expire_check(struct timespec *expire); * Treat as SUCCESS, but skip the backend op. * Also, return SUCCESS to the client/supplier. * Necessary for the replication conflicts. */ +#define SLAPI_PLUGIN_NOOP_COMMIT -3 /* Special in be_pre_op/be_txn_pre_op. + * 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 @@ -6993,6 +7001,7 @@ slapi_timer_result slapi_timespec_expire_check(struct timespec *expire); /* 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 @@ -7192,6 +7201,10 @@ typedef struct slapi_plugindesc #define ACLPLUGIN_ACCESS_MODRDN 4 #define ACLPLUGIN_ACCESS_GET_EFFECTIVE_RIGHTS 5 +/* MMR plugin functions and arguments */ +#define SLAPI_PLUGIN_MMR_BETXN_PREOP 761 +#define SLAPI_PLUGIN_MMR_BETXN_POSTOP 762 + /* Authorization types */ #define SLAPI_BE_MAXNESTLEVEL 742 #define SLAPI_CLIENT_DNS 745 diff --git a/ldap/servers/slapd/slapi-private.h b/ldap/servers/slapd/slapi-private.h index 58f7a86..548d5ca 100644 --- a/ldap/servers/slapd/slapi-private.h +++ b/ldap/servers/slapd/slapi-private.h @@ -423,6 +423,8 @@ char *slapi_filter_to_string_internal(const struct slapi_filter *f, char *buf, s #define OP_FLAG_NEVER_CACHE 0x200000 /* never keep the entry in cache */ #define OP_FLAG_TOMBSTONE_FIXUP 0x400000 /* operation is tombstone fixup op */ #define OP_FLAG_BULK_IMPORT 0x800000 /* operation is bulk import */ +#define OP_FLAG_NOOP 0x01000000 /* operation results from urp and + * should be ignored */ /* reverse search states */ #define REV_STARTED 1