X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Fsyncrepl.c;h=09de210905503392446f70826ed8a43ac07d87a9;hb=5fcc9285fb8c549a5264921a2b61cfc40803d720;hp=29efd16cbe05ca7ff290c6931ffb4bff9950cf63;hpb=9e00b6cc6ce2857490b33218bdaf1339319c5f60;p=openldap diff --git a/servers/slapd/syncrepl.c b/servers/slapd/syncrepl.c index 29efd16cbe..09de210905 100644 --- a/servers/slapd/syncrepl.c +++ b/servers/slapd/syncrepl.c @@ -2,7 +2,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 2003-2011 The OpenLDAP Foundation. + * Copyright 2003-2012 The OpenLDAP Foundation. * Portions Copyright 2003 by IBM Corporation. * Portions Copyright 2003-2008 by Howard Chu, Symas Corporation. * All rights reserved. @@ -44,17 +44,17 @@ struct nonpresent_entry { typedef struct cookie_state { ldap_pvt_thread_mutex_t cs_mutex; + struct berval *cs_vals; + int *cs_sids; int cs_num; int cs_age; int cs_ref; - struct berval *cs_vals; - int *cs_sids; - + /* pending changes, not yet committed */ ldap_pvt_thread_mutex_t cs_pmutex; - int cs_pnum; struct berval *cs_pvals; int *cs_psids; + int cs_pnum; } cookie_state; #define SYNCDATA_DEFAULT 0 /* entries are plain LDAP entries */ @@ -131,7 +131,7 @@ static int syncrepl_message_to_op( syncinfo_t *, Operation *, LDAPMessage * ); static int syncrepl_message_to_entry( syncinfo_t *, Operation *, LDAPMessage *, - Modifications **, Entry **, int ); + Modifications **, Entry **, int, struct berval* ); static int syncrepl_entry( syncinfo_t *, Operation*, Entry*, Modifications**,int, struct berval*, @@ -144,6 +144,9 @@ static struct berval * slap_uuidstr_from_normalized( static int syncrepl_add_glue_ancestors( Operation* op, Entry *e ); +/* delta-mmr overlay handler */ +static int syncrepl_op_modify( Operation *op, SlapReply *rs ); + /* callback functions */ static int dn_callback( Operation *, SlapReply * ); static int nonpresent_callback( Operation *, SlapReply * ); @@ -151,6 +154,36 @@ static int null_callback( Operation *, SlapReply * ); static AttributeDescription *sync_descs[4]; +/* delta-mmr */ +static AttributeDescription *ad_reqMod, *ad_reqDN; + +typedef struct logschema { + struct berval ls_dn; + struct berval ls_req; + struct berval ls_mod; + struct berval ls_newRdn; + struct berval ls_delRdn; + struct berval ls_newSup; +} logschema; + +static logschema changelog_sc = { + BER_BVC("targetDN"), + BER_BVC("changeType"), + BER_BVC("changes"), + BER_BVC("newRDN"), + BER_BVC("deleteOldRDN"), + BER_BVC("newSuperior") +}; + +static logschema accesslog_sc = { + BER_BVC("reqDN"), + BER_BVC("reqType"), + BER_BVC("reqMod"), + BER_BVC("reqNewRDN"), + BER_BVC("reqDeleteOldRDN"), + BER_BVC("reqNewSuperior") +}; + static const char * syncrepl_state2str( int state ) { @@ -171,12 +204,35 @@ syncrepl_state2str( int state ) return "UNKNOWN"; } +static slap_overinst syncrepl_ov; + static void init_syncrepl(syncinfo_t *si) { int i, j, k, l, n; char **attrs, **exattrs; + if ( !syncrepl_ov.on_bi.bi_type ) { + syncrepl_ov.on_bi.bi_type = "syncrepl"; + syncrepl_ov.on_bi.bi_op_modify = syncrepl_op_modify; + overlay_register( &syncrepl_ov ); + } + + /* delta-MMR needs the overlay, nothing else does. + * This must happen before accesslog overlay is configured. + */ + if ( si->si_syncdata && + !overlay_is_inst( si->si_be, syncrepl_ov.on_bi.bi_type )) { + overlay_config( si->si_be, syncrepl_ov.on_bi.bi_type, -1, NULL, NULL ); + if ( !ad_reqMod ) { + const char *text; + logschema *ls = &accesslog_sc; + + slap_bv2ad( &ls->ls_mod, &ad_reqMod, &text ); + slap_bv2ad( &ls->ls_dn, &ad_reqDN, &text ); + } + } + if ( !sync_descs[0] ) { sync_descs[0] = slap_schema.si_ad_objectClass; sync_descs[1] = slap_schema.si_ad_structuralObjectClass; @@ -330,33 +386,6 @@ init_syncrepl(syncinfo_t *si) si->si_exattrs = exattrs; } -typedef struct logschema { - struct berval ls_dn; - struct berval ls_req; - struct berval ls_mod; - struct berval ls_newRdn; - struct berval ls_delRdn; - struct berval ls_newSup; -} logschema; - -static logschema changelog_sc = { - BER_BVC("targetDN"), - BER_BVC("changeType"), - BER_BVC("changes"), - BER_BVC("newRDN"), - BER_BVC("deleteOldRDN"), - BER_BVC("newSuperior") -}; - -static logschema accesslog_sc = { - BER_BVC("reqDN"), - BER_BVC("reqType"), - BER_BVC("reqMod"), - BER_BVC("reqNewRDN"), - BER_BVC("reqDeleteOldRDN"), - BER_BVC("reqNewSuperior") -}; - static int ldap_sync_search( syncinfo_t *si, @@ -560,6 +589,7 @@ check_syncprov( slap_compose_sync_cookie( NULL, &si->si_syncCookie.octet_str, si->si_syncCookie.ctxcsn, si->si_syncCookie.rid, si->si_syncCookie.sid ); + slap_parse_sync_cookie( &si->si_syncCookie, NULL ); } ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex ); return changed; @@ -661,6 +691,7 @@ do_syncrep1( for (i=0; !BER_BVISNULL( &csn[i] ); i++); si->si_cookieState->cs_num = i; si->si_cookieState->cs_sids = slap_parse_csn_sids( csn, i, NULL ); + slap_sort_csn_sids( csn, si->si_cookieState->cs_sids, i, NULL ); } } if ( si->si_cookieState->cs_num ) { @@ -792,7 +823,7 @@ do_syncrep2( slap_dup_sync_cookie( &syncCookie_req, &si->si_syncCookie ); - if ( abs(si->si_type) == LDAP_SYNC_REFRESH_AND_PERSIST ) { + if ( abs(si->si_type) == LDAP_SYNC_REFRESH_AND_PERSIST && si->si_refreshDone ) { tout_p = &tout; } else { tout_p = NULL; @@ -802,7 +833,7 @@ do_syncrep2( tout_p, &msg ) ) > 0 ) { int match, punlock, syncstate; - struct berval *retdata, syncUUID, cookie = BER_BVNULL; + struct berval *retdata, syncUUID[2], cookie = BER_BVNULL; char *retoid; LDAPControl **rctrls = NULL, *rctrlp = NULL; BerVarray syncUUIDs; @@ -854,7 +885,7 @@ do_syncrep2( goto done; } ber_init2( ber, &rctrlp->ldctl_value, LBER_USE_DER ); - if ( ber_scanf( ber, "{em" /*"}"*/, &syncstate, &syncUUID ) + if ( ber_scanf( ber, "{em" /*"}"*/, &syncstate, &syncUUID[0] ) == LBER_ERROR ) { bdn.bv_val[bdn.bv_len] = '\0'; Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s malformed message (%s)\n", @@ -865,7 +896,7 @@ do_syncrep2( } /* FIXME: what if syncUUID is NULL or empty? * (happens with back-sql...) */ - if ( BER_BVISEMPTY( &syncUUID ) ) { + if ( BER_BVISEMPTY( &syncUUID[0] ) ) { bdn.bv_val[bdn.bv_len] = '\0'; Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s " "got empty syncUUID with LDAP_SYNC_%s (%s)\n", @@ -894,6 +925,9 @@ do_syncrep2( int i, sid = slap_parse_csn_sid( syncCookie.ctxcsn ); check_syncprov( op, si ); for ( i =0; isi_cookieState->cs_num; i++ ) { + /* new SID */ + if ( sid < si->si_cookieState->cs_sids[i] ) + break; if ( si->si_cookieState->cs_sids[i] == sid ) { if ( ber_bvcmp( syncCookie.ctxcsn, &si->si_cookieState->cs_vals[i] ) <= 0 ) { bdn.bv_val[bdn.bv_len] = '\0'; @@ -916,6 +950,8 @@ do_syncrep2( ldap_pvt_thread_yield(); } for ( i =0; isi_cookieState->cs_pnum; i++ ) { + if ( sid < si->si_cookieState->cs_psids[i] ) + break; if ( si->si_cookieState->cs_psids[i] == sid ) { if ( ber_bvcmp( syncCookie.ctxcsn, &si->si_cookieState->cs_pvals[i] ) <= 0 ) { bdn.bv_val[bdn.bv_len] = '\0'; @@ -932,11 +968,11 @@ do_syncrep2( } } /* new SID, add it */ - if ( i == si->si_cookieState->cs_pnum ) { - value_add( &si->si_cookieState->cs_pvals, syncCookie.ctxcsn ); - si->si_cookieState->cs_pnum++; - si->si_cookieState->cs_psids = ch_realloc( si->si_cookieState->cs_psids, si->si_cookieState->cs_pnum * sizeof(int)); - si->si_cookieState->cs_psids[i] = sid; + if ( i == si->si_cookieState->cs_pnum || + sid != si->si_cookieState->cs_psids[i] ) { + slap_insert_csn_sids( + (struct sync_cookie *)&si->si_cookieState->cs_pvals, + i, sid, syncCookie.ctxcsn ); } assert( punlock < 0 ); punlock = i; @@ -956,6 +992,7 @@ do_syncrep2( case LDAP_NO_SUCH_OBJECT: case LDAP_NO_SUCH_ATTRIBUTE: case LDAP_TYPE_OR_VALUE_EXISTS: + case LDAP_NOT_ALLOWED_ON_NONLEAF: rc = LDAP_SYNC_REFRESH_REQUIRED; si->si_logstate = SYNCLOG_FALLBACK; ldap_abandon_ext( si->si_ld, si->si_msgid, NULL, NULL ); @@ -971,10 +1008,10 @@ do_syncrep2( break; } } else if ( ( rc = syncrepl_message_to_entry( si, op, msg, - &modlist, &entry, syncstate ) ) == LDAP_SUCCESS ) + &modlist, &entry, syncstate, syncUUID ) ) == LDAP_SUCCESS ) { if ( ( rc = syncrepl_entry( si, op, entry, &modlist, - syncstate, &syncUUID, syncCookie.ctxcsn ) ) == LDAP_SUCCESS && + syncstate, syncUUID, syncCookie.ctxcsn ) ) == LDAP_SUCCESS && syncCookie.ctxcsn ) { rc = syncrepl_updateCookie( si, op, &syncCookie ); @@ -1205,6 +1242,9 @@ do_syncrep2( si->si_refreshDone = 1; } ber_scanf( ber, /*"{"*/ "}" ); + if ( abs(si->si_type) == LDAP_SYNC_REFRESH_AND_PERSIST && + si->si_refreshDone ) + tout_p = &tout; break; case LDAP_TAG_SYNC_ID_SET: Debug( LDAP_DEBUG_SYNC, @@ -1745,6 +1785,367 @@ syncrepl_changelog_mods( return -1; /* FIXME */ } +typedef struct OpExtraSync { + OpExtra oe; + syncinfo_t *oe_si; +} OpExtraSync; + +/* Copy the original modlist, split Replace ops into Delete/Add, + * and drop mod opattrs since this modification is in the past. + */ +static Modifications *mods_dup( Operation *op, Modifications *modlist, int match ) +{ + Modifications *mod, *modnew = NULL, *modtail = NULL; + int size; + for ( ; modlist; modlist = modlist->sml_next ) { + /* older ops */ + if ( match < 0 ) { + if ( modlist->sml_desc == slap_schema.si_ad_modifiersName || + modlist->sml_desc == slap_schema.si_ad_modifyTimestamp || + modlist->sml_desc == slap_schema.si_ad_entryCSN ) + continue; + if ( modlist->sml_op == LDAP_MOD_REPLACE ) { + mod = op->o_tmpalloc( sizeof(Modifications), op->o_tmpmemctx ); + mod->sml_desc = modlist->sml_desc; + mod->sml_values = NULL; + mod->sml_nvalues = NULL; + mod->sml_op = LDAP_MOD_DELETE; + mod->sml_numvals = 0; + mod->sml_flags = 0; + if ( !modnew ) + modnew = mod; + if ( modtail ) + modtail->sml_next = mod; + modtail = mod; + } + } + if ( modlist->sml_numvals ) { + size = (modlist->sml_numvals+1) * sizeof(struct berval); + if ( modlist->sml_nvalues ) size *= 2; + } else { + size = 0; + } + size += sizeof(Modifications); + mod = op->o_tmpalloc( size, op->o_tmpmemctx ); + if ( !modnew ) + modnew = mod; + if ( modtail ) + modtail->sml_next = mod; + modtail = mod; + mod->sml_desc = modlist->sml_desc; + mod->sml_numvals = modlist->sml_numvals; + mod->sml_flags = 0; + if ( modlist->sml_numvals ) { + int i; + mod->sml_values = (BerVarray)(mod+1); + for (i=0; isml_numvals; i++) + mod->sml_values[i] = modlist->sml_values[i]; + BER_BVZERO(&mod->sml_values[i]); + if ( modlist->sml_nvalues ) { + mod->sml_nvalues = mod->sml_values + mod->sml_numvals + 1; + for (i=0; isml_numvals; i++) + mod->sml_nvalues[i] = modlist->sml_nvalues[i]; + BER_BVZERO(&mod->sml_nvalues[i]); + } else { + mod->sml_nvalues = NULL; + } + } else { + mod->sml_values = NULL; + mod->sml_nvalues = NULL; + } + if ( match < 0 && modlist->sml_op == LDAP_MOD_REPLACE ) + mod->sml_op = LDAP_MOD_ADD; + else + mod->sml_op = modlist->sml_op; + mod->sml_next = NULL; + } + return modnew; +} + +typedef struct resolve_ctxt { + syncinfo_t *rx_si; + Modifications *rx_mods; +} resolve_ctxt; + +static void +compare_vals( Modifications *m1, Modifications *m2 ) +{ + int i, j; + struct berval *bv1, *bv2; + + if ( m2->sml_nvalues ) { + bv2 = m2->sml_nvalues; + bv1 = m1->sml_nvalues; + } else { + bv2 = m2->sml_values; + bv1 = m1->sml_values; + } + for ( j=0; jsml_numvals; j++ ) { + for ( i=0; isml_numvals; i++ ) { + if ( !ber_bvcmp( &bv1[i], &bv2[j] )) { + int k; + for ( k=i; ksml_numvals-1; k++ ) { + m1->sml_values[k] = m1->sml_values[k+1]; + if ( m1->sml_nvalues ) + m1->sml_nvalues[k] = m1->sml_nvalues[k+1]; + } + BER_BVZERO(&m1->sml_values[k]); + if ( m1->sml_nvalues ) { + BER_BVZERO(&m1->sml_nvalues[k]); + } + m1->sml_numvals--; + i--; + } + } + } +} + +static int +syncrepl_resolve_cb( Operation *op, SlapReply *rs ) +{ + if ( rs->sr_type == REP_SEARCH ) { + resolve_ctxt *rx = op->o_callback->sc_private; + Attribute *a = attr_find( rs->sr_entry->e_attrs, ad_reqMod ); + if ( a ) { + Modifications *oldmods, *newmods, *m1, *m2, **prev; + oldmods = rx->rx_mods; + syncrepl_accesslog_mods( rx->rx_si, a->a_vals, &newmods ); + for ( m2 = newmods; m2; m2=m2->sml_next ) { + for ( prev = &oldmods, m1 = *prev; m1; m1 = *prev ) { + if ( m1->sml_desc != m2->sml_desc ) { + prev = &m1->sml_next; + continue; + } + if ( m2->sml_op == LDAP_MOD_DELETE || + m2->sml_op == LDAP_MOD_REPLACE ) { + int numvals = m2->sml_numvals; + if ( m2->sml_op == LDAP_MOD_REPLACE ) + numvals = 0; + /* New delete All cancels everything */ + if ( numvals == 0 ) { +drop: + *prev = m1->sml_next; + op->o_tmpfree( m1, op->o_tmpmemctx ); + continue; + } + if ( m1->sml_op == LDAP_MOD_DELETE ) { + if ( m1->sml_numvals == 0 ) { + /* turn this to SOFTDEL later */ + m1->sml_flags = SLAP_MOD_INTERNAL; + } else { + compare_vals( m1, m2 ); + if ( !m1->sml_numvals ) + goto drop; + } + } else if ( m1->sml_op == LDAP_MOD_ADD ) { + compare_vals( m1, m2 ); + if ( !m1->sml_numvals ) + goto drop; + } + } + + if ( m2->sml_op == LDAP_MOD_ADD || + m2->sml_op == LDAP_MOD_REPLACE ) { + if ( m1->sml_op == LDAP_MOD_DELETE ) { + if ( !m1->sml_numvals ) goto drop; + compare_vals( m1, m2 ); + if ( !m1->sml_numvals ) + goto drop; + } + if ( m2->sml_desc->ad_type->sat_atype.at_single_value ) + goto drop; + compare_vals( m1, m2 ); + if ( !m1->sml_numvals ) + goto drop; + } + prev = &m1->sml_next; + } + } + slap_mods_free( newmods, 1 ); + } + } + return LDAP_SUCCESS; +} + +typedef struct modify_ctxt { + Modifications *mx_orig; + Modifications *mx_free; +} modify_ctxt; + +static int +syncrepl_modify_cb( Operation *op, SlapReply *rs ) +{ + slap_callback *sc = op->o_callback; + modify_ctxt *mx = sc->sc_private; + Modifications *ml; + + op->orm_no_opattrs = 0; + op->orm_modlist = mx->mx_orig; + for ( ml = mx->mx_free; ml; ml = mx->mx_free ) { + mx->mx_free = ml->sml_next; + op->o_tmpfree( ml, op->o_tmpmemctx ); + } + op->o_callback = sc->sc_next; + op->o_tmpfree( sc, op->o_tmpmemctx ); + return SLAP_CB_CONTINUE; +} + +static int +syncrepl_op_modify( Operation *op, SlapReply *rs ) +{ + slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; + OpExtra *oex; + syncinfo_t *si; + Entry *e; + int rc, match = 0; + Modifications *mod, *newlist; + + LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) { + if ( oex->oe_key == (void *)syncrepl_message_to_op ) + break; + } + if ( !oex ) + return SLAP_CB_CONTINUE; + + si = ((OpExtraSync *)oex)->oe_si; + + /* Check if entryCSN in modlist is newer than entryCSN in entry. + * We do it here because the op has been serialized by accesslog + * by the time we get here. If the CSN is new enough, just do the + * mod. If not, we need to resolve conflicts. + */ + + for ( mod = op->orm_modlist; mod; mod=mod->sml_next ) { + if ( mod->sml_desc == slap_schema.si_ad_entryCSN ) break; + } + /* FIXME: what should we do if entryCSN is missing from the mod? */ + if ( !mod ) + return SLAP_CB_CONTINUE; + + rc = overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ); + if ( rc == 0 ) { + Attribute *a; + const char *text; + a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN ); + value_match( &match, slap_schema.si_ad_entryCSN, + slap_schema.si_ad_entryCSN->ad_type->sat_ordering, + SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX, + &mod->sml_nvalues[0], &a->a_nvals[0], &text ); + overlay_entry_release_ov( op, e, 0, on ); + } + /* equal? Should never happen */ + if ( match == 0 ) + return LDAP_SUCCESS; + + /* mod is older: resolve conflicts... + * 1. Save/copy original modlist. Split Replace to Del/Add. + * 2. Find all mods to this reqDN newer than the mod stamp. + * 3. Resolve any mods in this request that affect attributes + * touched by newer mods. + * old new + * delete all delete all drop + * delete all delete X SOFTDEL + * delete X delete all drop + * delete X delete X drop + * delete X delete Y OK + * delete all add X drop + * delete X add X drop + * delete X add Y OK + * add X delete all drop + * add X delete X drop + * add X add X drop + * add X add Y if SV, drop else OK + * + * 4. Swap original modlist back in response callback so + * that accesslog logs the original mod. + * + * Even if the mod is newer, other out-of-order changes may + * have been committed, forcing us to tweak the modlist: + * 1. Save/copy original modlist. + * 2. Change deletes to soft deletes. + * 3. Change Adds of single-valued attrs to Replace. + */ + + newlist = mods_dup( op, op->orm_modlist, match ); + + /* mod is older */ + if ( match < 0 ) { + Operation op2 = *op; + AttributeName an[2]; + const char *text; + struct berval bv; + char *ptr; + Modifications *ml; + int size, rc; + SlapReply rs1 = {0}; + resolve_ctxt rx; + slap_callback cb = { NULL, syncrepl_resolve_cb, NULL, NULL }; + + rx.rx_si = si; + rx.rx_mods = newlist; + cb.sc_private = ℞ + + op2.o_tag = LDAP_REQ_SEARCH; + op2.ors_scope = LDAP_SCOPE_SUBTREE; + op2.ors_deref = LDAP_DEREF_NEVER; + op2.o_req_dn = si->si_logbase; + op2.o_req_ndn = si->si_logbase; + op2.ors_tlimit = SLAP_NO_LIMIT; + op2.ors_slimit = SLAP_NO_LIMIT; + op2.ors_limit = NULL; + memset( an, 0, sizeof(an)); + an[0].an_desc = ad_reqMod; + an[0].an_name = ad_reqMod->ad_cname; + op2.ors_attrs = an; + op2.ors_attrsonly = 0; + + bv = mod->sml_nvalues[0]; + + size = sizeof("(&(entryCSN>=)(reqDN=))"); + size += bv.bv_len + op->o_req_ndn.bv_len + si->si_logfilterstr.bv_len; + op2.ors_filterstr.bv_val = op->o_tmpalloc( size, op->o_tmpmemctx ); + op2.ors_filterstr.bv_len = sprintf(op2.ors_filterstr.bv_val, + "(&(entryCSN>=%s)(reqDN=%s)%s)", + bv.bv_val, op->o_req_ndn.bv_val, si->si_logfilterstr.bv_val ); + op2.ors_filter = str2filter_x( op, op2.ors_filterstr.bv_val ); + + op2.o_callback = &cb; + op2.o_bd = select_backend( &op2.o_req_ndn, 1 ); + op2.o_bd->be_search( &op2, &rs1 ); + newlist = rx.rx_mods; + } + + { + slap_callback *sc = op->o_tmpalloc( sizeof(slap_callback) + + sizeof(modify_ctxt), op->o_tmpmemctx ); + modify_ctxt *mx = (modify_ctxt *)(sc+1); + Modifications *ml; + + sc->sc_response = syncrepl_modify_cb; + sc->sc_private = mx; + sc->sc_next = op->o_callback; + sc->sc_cleanup = NULL; + op->o_callback = sc; + op->orm_no_opattrs = 1; + mx->mx_orig = op->orm_modlist; + mx->mx_free = newlist; + for ( ml = newlist; ml; ml=ml->sml_next ) { + if ( ml->sml_flags == SLAP_MOD_INTERNAL ) { + ml->sml_flags = 0; + ml->sml_op = SLAP_MOD_SOFTDEL; + } + else if ( ml->sml_op == LDAP_MOD_DELETE ) + ml->sml_op = SLAP_MOD_SOFTDEL; + else if ( ml->sml_op == LDAP_MOD_ADD && + ml->sml_desc->ad_type->sat_atype.at_single_value ) + ml->sml_op = LDAP_MOD_REPLACE; + } + op->orm_modlist = newlist; + op->o_csn = mod->sml_nvalues[0]; + } + return SLAP_CB_CONTINUE; +} + static int syncrepl_message_to_op( syncinfo_t *si, @@ -1904,9 +2305,20 @@ syncrepl_message_to_op( if ( e == op->ora_e ) be_entry_release_w( op, op->ora_e ); } else { + OpExtraSync oes; op->orm_modlist = modlist; op->o_bd = si->si_wbe; + /* delta-mmr needs additional checks in syncrepl_op_modify */ + if ( SLAP_MULTIMASTER( op->o_bd )) { + oes.oe.oe_key = (void *)syncrepl_message_to_op; + oes.oe_si = si; + LDAP_SLIST_INSERT_HEAD( &op->o_extra, &oes.oe, oe_next ); + } rc = op->o_bd->be_modify( op, &rs ); + if ( SLAP_MULTIMASTER( op->o_bd )) { + LDAP_SLIST_REMOVE( &op->o_extra, &oes.oe, OpExtra, oe_next ); + BER_BVZERO( &op->o_csn ); + } modlist = op->orm_modlist; Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC, "syncrepl_message_to_op: %s be_modify %s (%d)\n", @@ -2004,7 +2416,8 @@ syncrepl_message_to_entry( LDAPMessage *msg, Modifications **modlist, Entry **entry, - int syncstate + int syncstate, + struct berval *syncUUID ) { Entry *e = NULL; @@ -2046,6 +2459,14 @@ syncrepl_message_to_entry( return LDAP_OTHER; } + /* syncUUID[0] is normalized UUID received over the wire + * syncUUID[1] is denormalized UUID, generated here + */ + (void)slap_uuidstr_from_normalized( &syncUUID[1], &syncUUID[0], op->o_tmpmemctx ); + Debug( LDAP_DEBUG_SYNC, + "syncrepl_message_to_entry: %s DN: %s, UUID: %s\n", + si->si_ridtxt, bdn.bv_val, syncUUID[1].bv_val ); + if ( syncstate == LDAP_SYNC_PRESENT || syncstate == LDAP_SYNC_DELETE ) { /* NOTE: this could be done even before decoding the DN, * although encoding errors wouldn't be detected */ @@ -2225,7 +2646,7 @@ typedef struct dninfo { int delOldRDN; /* Was old RDN deleted? */ Modifications **modlist; /* the modlist we received */ Modifications *mods; /* the modlist we compared */ - Attribute *oldNattr; /* old naming attr */ + int oldNcount; /* #values of old naming attr */ AttributeDescription *oldDesc; /* for renames */ AttributeDescription *newDesc; /* for renames */ } dninfo; @@ -2266,7 +2687,6 @@ syncrepl_entry( Backend *be = op->o_bd; slap_callback cb = { NULL, NULL, NULL, NULL }; int syncuuid_inserted = 0; - struct berval syncUUID_strrep = BER_BVNULL; SlapReply rs_search = {REP_RESULT}; Filter f = {0}; @@ -2296,14 +2716,13 @@ syncrepl_entry( } } - (void)slap_uuidstr_from_normalized( &syncUUID_strrep, syncUUID, op->o_tmpmemctx ); if ( syncstate != LDAP_SYNC_DELETE ) { Attribute *a = attr_find( entry->e_attrs, slap_schema.si_ad_entryUUID ); if ( a == NULL ) { /* add if missing */ attr_merge_one( entry, slap_schema.si_ad_entryUUID, - &syncUUID_strrep, syncUUID ); + &syncUUID[1], syncUUID ); } else if ( !bvmatch( &a->a_nvals[0], syncUUID ) ) { /* replace only if necessary */ @@ -2312,7 +2731,7 @@ syncrepl_entry( ber_dupbv( &a->a_nvals[0], syncUUID ); } ber_memfree( a->a_vals[0].bv_val ); - ber_dupbv( &a->a_vals[0], &syncUUID_strrep ); + ber_dupbv( &a->a_vals[0], &syncUUID[1] ); } } @@ -2323,16 +2742,16 @@ syncrepl_entry( if ( syncuuid_inserted ) { Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: %s inserted UUID %s\n", - si->si_ridtxt, syncUUID_strrep.bv_val, 0 ); + si->si_ridtxt, syncUUID[1].bv_val, 0 ); } op->ors_filter = &f; - op->ors_filterstr.bv_len = STRLENOF( "(entryUUID=)" ) + syncUUID_strrep.bv_len; + op->ors_filterstr.bv_len = STRLENOF( "(entryUUID=)" ) + syncUUID[1].bv_len; op->ors_filterstr.bv_val = (char *) slap_sl_malloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx ); AC_MEMCPY( op->ors_filterstr.bv_val, "(entryUUID=", STRLENOF( "(entryUUID=" ) ); AC_MEMCPY( &op->ors_filterstr.bv_val[STRLENOF( "(entryUUID=" )], - syncUUID_strrep.bv_val, syncUUID_strrep.bv_len ); + syncUUID[1].bv_val, syncUUID[1].bv_len ); op->ors_filterstr.bv_val[op->ors_filterstr.bv_len - 1] = ')'; op->ors_filterstr.bv_val[op->ors_filterstr.bv_len] = '\0'; @@ -2406,9 +2825,7 @@ syncrepl_entry( /* FIXME: op->o_csn is assumed to be * on the thread's slab; this needs * to be cleared ASAP. - * What happens if already present? */ - assert( BER_BVISNULL( &op->o_csn ) ); op->o_csn = a->a_vals[0]; freecsn = 0; } @@ -2437,8 +2854,24 @@ retry_add:; case LDAP_REFERRAL: /* we assume that LDAP_NO_SUCH_OBJECT is returned - * only if the suffix entry is not present */ + * only if the suffix entry is not present. + * This should not happen during Persist phase. + */ case LDAP_NO_SUCH_OBJECT: + if ( abs(si->si_type) == LDAP_SYNC_REFRESH_AND_PERSIST && + si->si_refreshDone ) { + /* Something's wrong, start over */ + ber_bvarray_free( si->si_syncCookie.ctxcsn ); + si->si_syncCookie.ctxcsn = NULL; + ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex ); + ber_bvarray_free( si->si_cookieState->cs_vals ); + ch_free( si->si_cookieState->cs_sids ); + si->si_cookieState->cs_vals = NULL; + si->si_cookieState->cs_sids = 0; + si->si_cookieState->cs_num = 0; + ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex ); + return LDAP_NO_SUCH_OBJECT; + } rc = syncrepl_add_glue( op, entry ); entry = NULL; break; @@ -2529,10 +2962,10 @@ retry_add:; * If delOldRDN is TRUE then we should see a delete modop * for oldDesc. We might see a replace instead. * delete with no values: therefore newDesc != oldDesc. - * if oldNattr had only one value, then Drop this op. + * if oldNcount == 1, then Drop this op. * delete with 1 value: can only be the oldRDN value. Drop op. * delete with N values: Drop oldRDN value, keep remainder. - * replace with 1 value: if oldNattr had only one value and + * replace with 1 value: if oldNcount == 1 and * newDesc == oldDesc, Drop this op. * Any other cases must be left intact. * @@ -2553,7 +2986,7 @@ retry_add:; continue; } if ( mod->sml_numvals <= 1 && - dni.oldNattr->a_numvals == 1 && + dni.oldNcount == 1 && ( mod->sml_op == LDAP_MOD_DELETE || mod->sml_op == LDAP_MOD_REPLACE )) { if ( mod->sml_op == LDAP_MOD_REPLACE ) @@ -2686,7 +3119,7 @@ retry_modrdn:; /* NOTE: noSuchObject should result because the new superior * has not been added yet (ITS#6472) */ - if ( rc == LDAP_NO_SUCH_OBJECT && !BER_BVISNULL( op->orr_nnewSup )) { + if ( rc == LDAP_NO_SUCH_OBJECT && op->orr_nnewSup != NULL ) { Operation op2 = *op; rc = syncrepl_add_glue_ancestors( &op2, entry ); if ( rc == LDAP_SUCCESS ) { @@ -2749,10 +3182,15 @@ retry_modrdn:; op->o_req_ndn = dni.ndn; op->o_tag = LDAP_REQ_DELETE; op->o_bd = si->si_wbe; + if ( !syncCSN ) { + slap_queue_csn( op, si->si_syncCookie.ctxcsn ); + } rc = op->o_bd->be_delete( op, &rs_delete ); Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: %s be_delete %s (%d)\n", si->si_ridtxt, op->o_req_dn.bv_val, rc ); + if ( rc == LDAP_NO_SUCH_OBJECT ) + rc = LDAP_SUCCESS; while ( rs_delete.sr_err == LDAP_SUCCESS && op->o_delete_glue_parent ) { @@ -2782,10 +3220,8 @@ retry_modrdn:; } done: - if ( !BER_BVISNULL( &syncUUID_strrep ) ) { - slap_sl_free( syncUUID_strrep.bv_val, op->o_tmpmemctx ); - BER_BVZERO( &syncUUID_strrep ); - } + slap_sl_free( syncUUID[1].bv_val, op->o_tmpmemctx ); + BER_BVZERO( &syncUUID[1] ); if ( !BER_BVISNULL( &dni.ndn ) ) { op->o_tmpfree( dni.ndn.bv_val, op->o_tmpmemctx ); } @@ -3087,8 +3523,8 @@ syncrepl_add_glue_ancestors( } if ( !BER_BVISEMPTY( &ptr ) ) { - dn.bv_len -= ptr.bv_len + 1; - dn.bv_val += ptr.bv_len + 1; + dn.bv_len -= ptr.bv_len + ( suffrdns != 0 ); + dn.bv_val += ptr.bv_len + ( suffrdns != 0 ); } /* the normalizedDNs are always the same length, no counting @@ -3222,6 +3658,7 @@ syncrepl_updateCookie( Backend *be = op->o_bd; Modifications mod; struct berval first = BER_BVNULL; + struct sync_cookie sc; #ifdef CHECK_CSN Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax; #endif @@ -3251,44 +3688,45 @@ syncrepl_updateCookie( #endif /* clone the cookieState CSNs so we can Replace the whole thing */ - mod.sml_numvals = si->si_cookieState->cs_num; - mod.sml_values = op->o_tmpalloc(( mod.sml_numvals+1 )*sizeof(struct berval), op->o_tmpmemctx ); - for ( i=0; isi_cookieState->cs_vals[i]; - BER_BVZERO( &mod.sml_values[i] ); + sc.numcsns = si->si_cookieState->cs_num; + if ( sc.numcsns ) { + ber_bvarray_dup_x( &sc.ctxcsn, si->si_cookieState->cs_vals, NULL ); + sc.sids = ch_malloc( sc.numcsns * sizeof(int)); + for ( i=0; isi_cookieState->cs_sids[i]; + } else { + sc.ctxcsn = NULL; + sc.sids = NULL; + } /* find any CSNs in the syncCookie that are newer than the cookieState */ for ( i=0; inumcsns; i++ ) { - for ( j=0; jsi_cookieState->cs_num; j++ ) { - if ( syncCookie->sids[i] != si->si_cookieState->cs_sids[j] ) + for ( j=0; jsids[i] < sc.sids[j] ) + break; + if ( syncCookie->sids[i] != sc.sids[j] ) continue; len = syncCookie->ctxcsn[i].bv_len; - if ( len > si->si_cookieState->cs_vals[j].bv_len ) - len = si->si_cookieState->cs_vals[j].bv_len; + if ( len > sc.ctxcsn[j].bv_len ) + len = sc.ctxcsn[j].bv_len; if ( memcmp( syncCookie->ctxcsn[i].bv_val, - si->si_cookieState->cs_vals[j].bv_val, len ) > 0 ) { - mod.sml_values[j] = syncCookie->ctxcsn[i]; + sc.ctxcsn[j].bv_val, len ) > 0 ) { + ber_bvreplace( &sc.ctxcsn[j], &syncCookie->ctxcsn[i] ); changed = 1; - if ( BER_BVISNULL( &first ) ) { - first = syncCookie->ctxcsn[i]; - - } else if ( memcmp( syncCookie->ctxcsn[i].bv_val, first.bv_val, first.bv_len ) > 0 ) - { + if ( BER_BVISNULL( &first ) || + memcmp( syncCookie->ctxcsn[i].bv_val, first.bv_val, first.bv_len ) > 0 ) { first = syncCookie->ctxcsn[i]; } } break; } /* there was no match for this SID, it's a new CSN */ - if ( j == si->si_cookieState->cs_num ) { - mod.sml_values = op->o_tmprealloc( mod.sml_values, - ( mod.sml_numvals+2 )*sizeof(struct berval), op->o_tmpmemctx ); - mod.sml_values[mod.sml_numvals++] = syncCookie->ctxcsn[i]; - BER_BVZERO( &mod.sml_values[mod.sml_numvals] ); - if ( BER_BVISNULL( &first ) ) { - first = syncCookie->ctxcsn[i]; - } else if ( memcmp( syncCookie->ctxcsn[i].bv_val, first.bv_val, first.bv_len ) > 0 ) - { + if ( j == sc.numcsns || + syncCookie->sids[i] != sc.sids[j] ) { + slap_insert_csn_sids( &sc, j, syncCookie->sids[i], + &syncCookie->ctxcsn[i] ); + if ( BER_BVISNULL( &first ) || + memcmp( syncCookie->ctxcsn[i].bv_val, first.bv_val, first.bv_len ) > 0 ) { first = syncCookie->ctxcsn[i]; } changed = 1; @@ -3297,7 +3735,8 @@ syncrepl_updateCookie( /* Should never happen, ITS#5065 */ if ( BER_BVISNULL( &first ) || !changed ) { ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex ); - op->o_tmpfree( mod.sml_values, op->o_tmpmemctx ); + ber_bvarray_free( sc.ctxcsn ); + ch_free( sc.sids ); return 0; } op->o_bd = si->si_wbe; @@ -3315,6 +3754,9 @@ syncrepl_updateCookie( /* update contextCSN */ op->o_dont_replicate = 1; + mod.sml_numvals = sc.numcsns; + mod.sml_values = sc.ctxcsn; + op->orm_modlist = &mod; op->orm_no_opattrs = 1; rc = op->o_bd->be_modify( op, &rs_modify ); @@ -3338,20 +3780,11 @@ syncrepl_updateCookie( if ( rs_modify.sr_err == LDAP_SUCCESS ) { slap_sync_cookie_free( &si->si_syncCookie, 0 ); - /* If we replaced any old values */ - for ( i=0; isi_cookieState->cs_num; i++ ) { - if ( mod.sml_values[i].bv_val != si->si_cookieState->cs_vals[i].bv_val ) - ber_bvreplace( &si->si_cookieState->cs_vals[i], - &mod.sml_values[i] ); - } - /* Handle any added values */ - if ( i < mod.sml_numvals ) { - si->si_cookieState->cs_num = mod.sml_numvals; - value_add( &si->si_cookieState->cs_vals, &mod.sml_values[i] ); - free( si->si_cookieState->cs_sids ); - si->si_cookieState->cs_sids = slap_parse_csn_sids( - si->si_cookieState->cs_vals, si->si_cookieState->cs_num, NULL ); - } + ber_bvarray_free( si->si_cookieState->cs_vals ); + ch_free( si->si_cookieState->cs_sids ); + si->si_cookieState->cs_vals = sc.ctxcsn; + si->si_cookieState->cs_sids = sc.sids; + si->si_cookieState->cs_num = sc.numcsns; /* Don't just dup the provider's cookie, recreate it */ si->si_syncCookie.numcsns = si->si_cookieState->cs_num; @@ -3366,6 +3799,8 @@ syncrepl_updateCookie( Debug( LDAP_DEBUG_ANY, "syncrepl_updateCookie: %s be_modify failed (%d)\n", si->si_ridtxt, rs_modify.sr_err, 0 ); + ch_free( sc.sids ); + ber_bvarray_free( sc.ctxcsn ); } ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex ); @@ -3373,7 +3808,6 @@ syncrepl_updateCookie( op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx ); BER_BVZERO( &op->o_csn ); if ( mod.sml_next ) slap_mods_free( mod.sml_next, 1 ); - op->o_tmpfree( mod.sml_values, op->o_tmpmemctx ); #ifdef CHECK_CSN for ( i=0; isi_cookieState->cs_num; i++ ) { @@ -3707,7 +4141,10 @@ dn_callback( dni->oldDesc = ad; for ( oldpos=0, a=rs->sr_entry->e_attrs; a && a->a_desc != ad; oldpos++, a=a->a_next ); - dni->oldNattr = a; + /* a should not be NULL but apparently it happens. + * ITS#7144 + */ + dni->oldNcount = a ? a->a_numvals : 0; for ( newpos=0, a=dni->new_entry->e_attrs; a && a->a_desc != ad; newpos++, a=a->a_next ); if ( !a || oldpos != newpos || attr_valfind( a, @@ -5086,13 +5523,13 @@ syncrepl_config( ConfigArgs *c ) ldap_pvt_runqueue_stoptask( &slapd_rq, re ); isrunning = 1; } - ldap_pvt_runqueue_remove( &slapd_rq, re ); - ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); - if ( ldap_pvt_thread_pool_retract( &connection_pool, re->routine, re ) > 0 ) isrunning = 0; + ldap_pvt_runqueue_remove( &slapd_rq, re ); + ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); + ldap_pvt_thread_mutex_unlock( &si->si_mutex ); } }