Slapi_Entry * sync_deleted_entry_from_changelog(Slapi_Entry *cl_entry) { Slapi_Entry *db_entry = NULL; char *entrydn = NULL; char *uniqueid = NULL; entrydn = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_ENTRYDN); uniqueid = sync_get_attr_value_from_entry (cl_entry, CL_ATTR_UNIQUEID); /* when the Retro CL can provide the deleted entry * the entry will be taken from th RCL. * For now. just create an entry to holde the nsuniqueid */ db_entry = slapi_entry_alloc(); slapi_entry_init(db_entry, entrydn, NULL); slapi_entry_add_string(db_entry, "nsuniqueid", uniqueid); slapi_ch_free((void**)&uniqueid); return(db_entry); }
/* * Return 0 for OK, * -1 for Ignore or Error depending on SLAPI_RESULT_CODE, * >0 for action code * Action Code Bit 0: Fetch existing entry. * Action Code Bit 1: Fetch parent entry. * The function is called as a be pre-op on consumers. */ int 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; } get_repl_session_id (pb, sessionid, &opcsn); slapi_pblock_get( pb, SLAPI_ADD_EXISTING_UNIQUEID_ENTRY, &existing_uniqueid_entry ); if (existing_uniqueid_entry!=NULL) { /* * An entry with this uniqueid already exists. * - It could be a replay of the same Add, or * - It could be a UUID generation collision, or */ /* * This operation won't be replayed. That is, this CSN won't update * the max csn in RUV. The CSN is left uncommitted in RUV unless an * error is set to op_result. Just to get rid of this CSN from RUV, * setting an error to op_result */ /* op_result = LDAP_SUCCESS; */ slapi_log_error(slapi_log_urp, sessionid, "urp_add (%s): an entry with this uniqueid already exists.\n", slapi_entry_get_dn_const(existing_uniqueid_entry)); op_result= LDAP_UNWILLING_TO_PERFORM; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = SLAPI_PLUGIN_NOOP; /* Ignore this Operation */ PROFILE_POINT; /* Add Conflict; UniqueID Exists; Ignore */ goto bailout; } slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &addentry ); slapi_pblock_get( pb, SLAPI_ADD_EXISTING_DN_ENTRY, &existing_dn_entry ); if (existing_dn_entry==NULL) /* The target DN does not exist */ { /* Check for parent entry... this could be an orphan. */ Slapi_Entry *parententry; slapi_pblock_get( pb, SLAPI_ADD_PARENT_ENTRY, &parententry ); rc = urp_add_resolve_parententry (pb, sessionid, addentry, parententry, opcsn); PROFILE_POINT; /* Add Entry */ goto bailout; } /* * Naming conflict: an entry with the target DN already exists. * Compare the DistinguishedNameCSN of the existing entry * and the OperationCSN. The smaller CSN wins. The loser changes * 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); if (r<0) { /* Entry to be added is a loser */ char *newdn= get_dn_plus_uniqueid (sessionid, basedn, 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_error(slapi_log_urp, sessionid, "urp_add (%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]; PR_snprintf(buf, BUFSIZ, "%s %s", REASON_ANNOTATE_DN, basedn); if (slapi_entry_attr_find (addentry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0) { /* ATTR_NSDS5_REPLCONFLICT exists */ slapi_log_error(SLAPI_LOG_FATAL, sessionid, "urp_add: 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_error (slapi_log_urp, sessionid, "urp_add: 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 */ } } else if(r>0) { /* Existing entry is a loser */ if (!urp_annotate_dn(sessionid, existing_dn_entry, opcsn, "ADD")) { op_result= LDAP_OPERATIONS_ERROR; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); slapi_log_error(slapi_log_urp, sessionid, "urp_add (%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); } PROFILE_POINT; /* Add Conflict; Entry Exists; Rename Existing Entry */ } else /* r==0 */ { /* The CSN of the Operation and the Entry DN are the same. * This could only happen if: * a) There are two replicas with the same ReplicaID. * b) We've seen the Operation before. * Let's go with (b) and ignore the little bastard. */ /* * This operation won't be replayed. That is, this CSN won't update * the max csn in RUV. The CSN is left uncommitted in RUV unless an * error is set to op_result. Just to get rid of this CSN from RUV, * setting an error to op_result */ /* op_result = LDAP_SUCCESS; */ slapi_log_error(slapi_log_urp, sessionid, "urp_add (%s): The CSN of the Operation and the Entry DN are the same.", slapi_entry_get_dn_const(existing_dn_entry)); op_result= LDAP_UNWILLING_TO_PERFORM; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result); rc = SLAPI_PLUGIN_NOOP; /* Ignore this Operation */ PROFILE_POINT; /* Add Conflict; Entry Exists; Same CSN */ } bailout: return rc; }
/** Apply the mods to the ec entry. Check for syntax, schema problems. Check for abandon. Return code: -1 - error - result code and message are set appropriately 0 - successfully applied and checked 1 - not an error - no mods to apply or op abandoned */ static int modify_apply_check_expand( Slapi_PBlock *pb, Slapi_Operation *operation, LDAPMod **mods, /* list of mods to apply */ struct backentry *e, /* original "before" entry */ struct backentry *ec, /* "after" entry with mods applied */ Slapi_Entry **postentry, int *ldap_result_code, char **ldap_result_message ) { int rc = 0; int i; int repl_op; int change_entry = 0; Slapi_Mods smods = {0}; CSN *csn = operation_get_csn(operation); slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op); slapi_mods_init_byref( &smods, mods ); if ( (change_entry = mods_have_effect (ec->ep_entry, &smods)) ) { *ldap_result_code = entry_apply_mods_wsi(ec->ep_entry, &smods, csn, operation_is_flag_set(operation, OP_FLAG_REPLICATED)); /* * XXXmcs: it would be nice to get back an error message from * the above call so we could pass it along to the client, e.g., * "duplicate value for attribute givenName." */ } else { Slapi_Entry *epostop = NULL; /* If the entry was not actually changed, we still need to * set the SLAPI_ENTRY_POST_OP field in the pblock (post-op * plugins expect that field to be present for all modify * operations that return LDAP_SUCCESS). */ slapi_pblock_get ( pb, SLAPI_ENTRY_POST_OP, &epostop ); slapi_entry_free ( epostop ); /* free existing one, if any */ slapi_pblock_set ( pb, SLAPI_ENTRY_POST_OP, slapi_entry_dup( e->ep_entry ) ); *postentry = NULL; /* to avoid free in main error cleanup code */ } if ( !change_entry || *ldap_result_code != 0 ) { /* change_entry == 0 is not an error just a no-op */ rc = change_entry ? -1 : 1; goto done; } /* * If the objectClass attribute type was modified in any way, expand * the objectClass values to reflect the inheritance hierarchy. */ for ( i = 0; mods && mods[i]; ++i ) { if ( 0 == strcasecmp( SLAPI_ATTR_OBJECTCLASS, mods[i]->mod_type )) { slapi_schema_expand_objectclasses( ec->ep_entry ); break; } } /* * We are about to pass the last abandon test, so from now on we are * committed to finish this operation. Set status to "will complete" * before we make our last abandon check to avoid race conditions in * the code that processes abandon operations. */ operation->o_status = SLAPI_OP_STATUS_WILL_COMPLETE; if ( slapi_op_abandoned( pb ) ) { rc = 1; goto done; } /* multimaster replication can result in a schema violation, * although the individual operations on each master were valid * It is too late to resolve this. But we can check schema and * add a replication conflict attribute. */ /* check that the entry still obeys the schema */ if ((operation_is_flag_set(operation,OP_FLAG_ACTION_SCHEMA_CHECK)) && slapi_entry_schema_check_ext( pb, ec->ep_entry, 1 ) != 0 ) { if(repl_op){ Slapi_Attr *attr; Slapi_Mods smods; LDAPMod **lmods; if (slapi_entry_attr_find (ec->ep_entry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0) { /* add value */ Slapi_Value *val = slapi_value_new_string("Schema violation"); slapi_attr_add_value(attr,val); slapi_value_free(&val); } else { /* Add new attribute */ slapi_entry_add_string (ec->ep_entry, ATTR_NSDS5_REPLCONFLICT, "Schema violation"); } /* the replconflict attribute is indexed and the index is built from the mods, * so we need to extend the mods */ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &lmods); slapi_mods_init_passin(&smods, lmods); slapi_mods_add_string (&smods, LDAP_MOD_ADD, ATTR_NSDS5_REPLCONFLICT, "Schema violation"); lmods = slapi_mods_get_ldapmods_passout(&smods); slapi_pblock_set(pb, SLAPI_MODIFY_MODS, lmods); slapi_mods_done(&smods); } else { *ldap_result_code = LDAP_OBJECT_CLASS_VIOLATION; slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, ldap_result_message); rc = -1; goto done; } } if(!repl_op){ /* check attribute syntax for the new values */ if (slapi_mods_syntax_check(pb, mods, 0) != 0) { *ldap_result_code = LDAP_INVALID_SYNTAX; slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, ldap_result_message); rc = -1; goto done; } /* * make sure the entry contains all values in the RDN. * if not, the modification must have removed them. */ if ( ! slapi_entry_rdn_values_present( ec->ep_entry ) ) { *ldap_result_code= LDAP_NOT_ALLOWED_ON_RDN; rc = -1; goto done; } } done: slapi_mods_done( &smods ); return rc; }