]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/syncrepl.c
fix ITS#5959 fix
[openldap] / servers / slapd / syncrepl.c
index a0de80d99ca48a34b138ca172d6ff18803d3cb3d..f10c36b600e72370bfcbf9ce80bfd6d659047fc3 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2003-2008 The OpenLDAP Foundation.
+ * Copyright 2003-2009 The OpenLDAP Foundation.
  * Portions Copyright 2003 by IBM Corporation.
  * Portions Copyright 2003-2008 by Howard Chu, Symas Corporation.
  * All rights reserved.
@@ -343,7 +343,7 @@ ldap_sync_search(
 {
        BerElementBuffer berbuf;
        BerElement *ber = (BerElement *)&berbuf;
-       LDAPControl c[2], *ctrls[3];
+       LDAPControl c[3], *ctrls[4];
        int rc;
        int rhint;
        char *base;
@@ -417,14 +417,19 @@ ldap_sync_search(
        c[0].ldctl_iscritical = si->si_type < 0;
        ctrls[0] = &c[0];
 
+       c[1].ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
+       BER_BVZERO( &c[1].ldctl_value );
+       c[1].ldctl_iscritical = 1;
+       ctrls[1] = &c[1];
+
        if ( !BER_BVISNULL( &si->si_bindconf.sb_authzId ) ) {
-               c[1].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
-               c[1].ldctl_value = si->si_bindconf.sb_authzId;
-               c[1].ldctl_iscritical = 1;
-               ctrls[1] = &c[1];
-               ctrls[2] = NULL;
+               c[2].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
+               c[2].ldctl_value = si->si_bindconf.sb_authzId;
+               c[2].ldctl_iscritical = 1;
+               ctrls[2] = &c[2];
+               ctrls[3] = NULL;
        } else {
-               ctrls[1] = NULL;
+               ctrls[2] = NULL;
        }
 
        rc = ldap_search_ext( si->si_ld, base, scope, filter, attrs, attrsonly,
@@ -583,6 +588,8 @@ do_syncrep1(
        rc = LDAP_DEREF_NEVER;  /* actually could allow DEREF_FINDING */
        ldap_set_option( si->si_ld, LDAP_OPT_DEREF, &rc );
 
+       ldap_set_option( si->si_ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF );
+
        si->si_syncCookie.rid = si->si_rid;
 
        /* whenever there are multiple data sources possible, advertise sid */
@@ -724,7 +731,6 @@ do_syncrep2(
        syncinfo_t *si )
 {
        LDAPControl     **rctrls = NULL;
-       LDAPControl     *rctrlp;
 
        BerElementBuffer berbuf;
        BerElement      *ber = (BerElement *)&berbuf;
@@ -781,6 +787,8 @@ do_syncrep2(
        while ( ( rc = ldap_result( si->si_ld, si->si_msgid, LDAP_MSG_ONE,
                tout_p, &msg ) ) > 0 )
        {
+               LDAPControl     *rctrlp = NULL;
+
                if ( slapd_shutdown ) {
                        rc = -2;
                        goto done;
@@ -789,18 +797,22 @@ do_syncrep2(
                case LDAP_RES_SEARCH_ENTRY:
                        ldap_get_entry_controls( si->si_ld, msg, &rctrls );
                        /* we can't work without the control */
-                       rctrlp = NULL;
                        if ( rctrls ) {
                                LDAPControl **next;
                                /* NOTE: make sure we use the right one;
                                 * a better approach would be to run thru
                                 * the whole list and take care of all */
+                               /* NOTE: since we issue the search request,
+                                * we should know what controls to expect,
+                                * and there should be none apart from the
+                                * sync-related control */
                                rctrlp = ldap_control_find( LDAP_CONTROL_SYNC_STATE, rctrls, &next );
                                if ( next && ldap_control_find( LDAP_CONTROL_SYNC_STATE, next, NULL ) )
                                {
                                        Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
                                                "got search entry with multiple "
                                                "Sync State control\n", si->si_ridtxt, 0, 0 );
+                                       ldap_controls_free( rctrls );
                                        rc = -1;
                                        goto done;
                                }
@@ -921,7 +933,26 @@ do_syncrep2(
                                        si->si_ridtxt, err, ldap_err2string( err ) );
                        }
                        if ( rctrls ) {
-                               rctrlp = *rctrls;
+                               LDAPControl **next;
+                               /* NOTE: make sure we use the right one;
+                                * a better approach would be to run thru
+                                * the whole list and take care of all */
+                               /* NOTE: since we issue the search request,
+                                * we should know what controls to expect,
+                                * and there should be none apart from the
+                                * sync-related control */
+                               rctrlp = ldap_control_find( LDAP_CONTROL_SYNC_DONE, rctrls, &next );
+                               if ( next && ldap_control_find( LDAP_CONTROL_SYNC_DONE, next, NULL ) )
+                               {
+                                       Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
+                                               "got search result with multiple "
+                                               "Sync State control\n", si->si_ridtxt, 0, 0 );
+                                       ldap_controls_free( rctrls );
+                                       rc = -1;
+                                       goto done;
+                               }
+                       }
+                       if ( rctrlp ) {
                                ber_init2( ber, &rctrlp->ldctl_value, LBER_USE_DER );
 
                                ber_scanf( ber, "{" /*"}"*/);
@@ -1006,6 +1037,17 @@ do_syncrep2(
                                                "LDAP_RES_INTERMEDIATE", 
                                                "NEW_COOKIE" );
                                        ber_scanf( ber, "tm", &tag, &cookie );
+                                       Debug( LDAP_DEBUG_SYNC,
+                                               "do_syncrep2: %s NEW_COOKIE: %s\n",
+                                               si->si_ridtxt,
+                                               cookie.bv_val, 0);
+                                       if ( !BER_BVISNULL( &cookie ) ) {
+                                               ch_free( syncCookie.octet_str.bv_val );
+                                               ber_dupbv( &syncCookie.octet_str, &cookie );
+                                       }
+                                       if (!BER_BVISNULL( &syncCookie.octet_str ) ) {
+                                               slap_parse_sync_cookie( &syncCookie, NULL );
+                                       }
                                        break;
                                case LDAP_TAG_SYNC_REFRESH_DELETE:
                                case LDAP_TAG_SYNC_REFRESH_PRESENT:
@@ -1118,6 +1160,7 @@ do_syncrep2(
 
                                if ( match < 0 ) {
                                        if ( si->si_refreshPresent == 1 &&
+                                               si_tag != LDAP_TAG_SYNC_NEW_COOKIE &&
                                                syncCookie_req.numcsns == syncCookie.numcsns ) {
                                                syncrepl_del_nonpresent( op, si, NULL,
                                                        &syncCookie, m );
@@ -1375,7 +1418,10 @@ reload:
 
                if ( !si->si_ctype
                        || !si->si_retrynum || si->si_retrynum[i] == RETRYNUM_TAIL ) {
-                       ldap_pvt_runqueue_remove( &slapd_rq, rtask );
+                       if ( si->si_re ) {
+                               ldap_pvt_runqueue_remove( &slapd_rq, rtask );
+                               si->si_re = NULL;
+                       }
                        fail = RETRYNUM_TAIL;
                } else if ( RETRYNUM_VALID( si->si_retrynum[i] ) ) {
                        if ( si->si_retrynum[i] > 0 )
@@ -1408,22 +1454,9 @@ reload:
 
        /* Do final delete cleanup */
        if ( !si->si_ctype ) {
-               cookie_state *cs = NULL;
-               syncinfo_t **sip;
-
-               cs = be->be_syncinfo->si_cookieState;
-               for ( sip = &be->be_syncinfo; *sip != si; sip = &(*sip)->si_next );
-               *sip = si->si_next;
-               syncinfo_free( si, 0 );
-               if ( !be->be_syncinfo ) {
-                       SLAP_DBFLAGS( be ) &= ~(SLAP_DBFLAG_SHADOW|SLAP_DBFLAG_SYNC_SHADOW);
-                       if ( cs ) {
-                               ch_free( cs->cs_sids );
-                               ber_bvarray_free( cs->cs_vals );
-                               ldap_pvt_thread_mutex_destroy( &cs->cs_mutex );
-                               ch_free( cs );
-                       }
-               }
+               cookie_state *cs = si->si_cookieState;
+               syncinfo_free( si, ( !be->be_syncinfo ||
+                       be->be_syncinfo->si_cookieState != cs ));
        }
        return NULL;
 }
@@ -2062,6 +2095,7 @@ syncrepl_entry(
        op->o_time = slap_get_time();
        op->ors_tlimit = SLAP_NO_LIMIT;
        op->ors_slimit = 1;
+       op->ors_limit = NULL;
 
        op->ors_attrs = slap_anlist_all_attributes;
        op->ors_attrsonly = 0;
@@ -2073,12 +2107,10 @@ syncrepl_entry(
        dni.new_entry = entry;
        dni.modlist = modlist;
 
-       if ( limits_check( op, &rs_search ) == 0 ) {
-               rc = be->be_search( op, &rs_search );
-               Debug( LDAP_DEBUG_SYNC,
-                               "syncrepl_entry: %s be_search (%d)\n", 
-                               si->si_ridtxt, rc, 0 );
-       }
+       rc = be->be_search( op, &rs_search );
+       Debug( LDAP_DEBUG_SYNC,
+                       "syncrepl_entry: %s be_search (%d)\n", 
+                       si->si_ridtxt, rc, 0 );
 
        if ( !BER_BVISNULL( &op->ors_filterstr ) ) {
                slap_sl_free( op->ors_filterstr.bv_val, op->o_tmpmemctx );
@@ -2208,7 +2240,7 @@ retry_add:;
                if ( dni.renamed ) {
                        struct berval noldp, newp;
                        Modifications *mod, **modtail, **ml, *m2;
-                       int i, got_replace = 0;
+                       int i, got_replace = 0, just_rename = 0;
 
                        op->o_tag = LDAP_REQ_MODRDN;
                        dnRdn( &entry->e_name, &op->orr_newrdn );
@@ -2228,23 +2260,18 @@ retry_add:;
                                goto done;
                        }
 
-                       /* Drop the RDN mods from this op:
-                        * If delOldRDN is TRUE then we should see a delete modop
-                        * for oldDesc. The valid cases are:
-                        *  oldNattr had only one value, newDesc == oldDesc:
-                        *    we'll see a replace modop for oldDesc
-                        *  oldNattr had only one value, newDesc != oldDesc:
-                        *    we'll see a delete modop for oldDesc with no values
-                        *  oldNattr had multiple values:
-                        *    we'll see a delete modop for oldDesc / old RDN value
-                        *
-                        * If the modop has only one value, just drop it; it's already
-                        * present in orr_modlist. Otherwise, if we see a delete for
-                        * oldDesc with more values, we must remove the old RDN value
-                        * from that modop since it's already in orr_modlist.
+                       /* Drop the RDN-related mods from this op, because their
+                        * equivalents were just setup by slap_modrdn2mods.
                         *
-                        * Likewise if we see a replace on oldDesc with multiple values,
-                        * we must drop the new RDN value and turn it into an 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.
+                        *  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
+                        *     newDesc == oldDesc, Drop this op.
+                        * Any other cases must be left intact.
                         *
                         * We should also see an add modop for newDesc. (But not if
                         * we got a replace modop due to delOldRDN.) If it has
@@ -2254,7 +2281,6 @@ retry_add:;
                        if ( dni.delOldRDN ) {
                                for ( ml = &dni.mods; *ml; ml = &(*ml)->sml_next ) {
                                        if ( (*ml)->sml_desc == dni.oldDesc ) {
-                                               short sm_op;
                                                mod = *ml;
                                                if ( mod->sml_op == LDAP_MOD_REPLACE &&
                                                        dni.oldDesc != dni.newDesc ) {
@@ -2267,27 +2293,19 @@ retry_add:;
                                                        dni.oldNattr->a_numvals == 1 &&
                                                        ( mod->sml_op == LDAP_MOD_DELETE ||
                                                          mod->sml_op == LDAP_MOD_REPLACE )) {
+                                                       if ( mod->sml_op == LDAP_MOD_REPLACE )
+                                                               got_replace = 1;
                                                        /* Drop this op */
                                                        *ml = mod->sml_next;
                                                        mod->sml_next = NULL;
                                                        slap_mods_free( mod, 1 );
-                                                       if ( mod->sml_op == LDAP_MOD_REPLACE )
-                                                               got_replace = 1;
                                                        break;
                                                }
-                                               if ( mod->sml_op == LDAP_MOD_ADD )
-                                                       continue;
-                                               if ( mod->sml_op == LDAP_MOD_DELETE && mod->sml_numvals == 0 )
+                                               if ( mod->sml_op != LDAP_MOD_DELETE || mod->sml_numvals == 0 )
                                                        continue;
-                                               if ( mod->sml_op == LDAP_MOD_REPLACE ) {
-                                                       got_replace = 1;
-                                                       sm_op = SLAP_MOD_SOFTADD;
-                                               } else {
-                                                       sm_op = LDAP_MOD_DELETE;
-                                               }
                                                for ( m2 = op->orr_modlist; m2; m2=m2->sml_next ) {
                                                        if ( m2->sml_desc == dni.oldDesc &&
-                                                               m2->sml_op == sm_op ) break;
+                                                               m2->sml_op == LDAP_MOD_DELETE ) break;
                                                }
                                                for ( i=0; i<mod->sml_numvals; i++ ) {
                                                        if ( bvmatch( &mod->sml_values[i], &m2->sml_values[0] )) {
@@ -2382,8 +2400,12 @@ retry_add:;
                                 */
                                if ( dni.mods ) {
                                        mod = dni.mods;
+                                       /* don't set a CSN for the rename op */
+                                       if ( syncCSN )
+                                               slap_graduate_commit_csn( op );
                                } else {
                                        mod = op->orr_modlist;
+                                       just_rename = 1;
                                }
                                for ( ; mod->sml_next; mod=mod->sml_next );
                                mod->sml_next = m2;
@@ -2401,6 +2423,9 @@ retry_add:;
                        /* Renamed entries may still have other mods so just fallthru */
                        op->o_req_dn = entry->e_name;
                        op->o_req_ndn = entry->e_nname;
+                       /* Use CSN on the modify */
+                       if ( syncCSN && !just_rename )
+                               slap_queue_csn( op, syncCSN );
                }
                if ( dni.mods ) {
                        op->o_tag = LDAP_REQ_MODIFY;
@@ -2554,40 +2579,43 @@ syncrepl_del_nonpresent(
                si->si_refreshDelete ^= NP_DELETE_ONE;
        } else {
                Filter *cf, *of;
+               Filter mmf[2];
+               AttributeAssertion mmaa;
 
                memset( &an[0], 0, 2 * sizeof( AttributeName ) );
                an[0].an_name = slap_schema.si_ad_entryUUID->ad_cname;
                an[0].an_desc = slap_schema.si_ad_entryUUID;
                op->ors_attrs = an;
                op->ors_slimit = SLAP_NO_LIMIT;
+               op->ors_tlimit = SLAP_NO_LIMIT;
+               op->ors_limit = NULL;
                op->ors_attrsonly = 0;
                op->ors_filter = str2filter_x( op, si->si_filterstr.bv_val );
                /* In multimaster, updates can continue to arrive while
                 * we're searching. Limit the search result to entries
-                * older than all of our cookie CSNs.
+                * older than our newest cookie CSN.
                 */
                if ( SLAP_MULTIMASTER( op->o_bd )) {
                        Filter *f;
                        int i;
-                       cf = op->o_tmpalloc( (sc->numcsns+1) * sizeof(Filter) +
-                               sc->numcsns * sizeof(AttributeAssertion), op->o_tmpmemctx );
-                       f = cf;
+
+                       f = mmf;
                        f->f_choice = LDAP_FILTER_AND;
-                       f->f_next = NULL;
+                       f->f_next = op->ors_filter;
                        f->f_and = f+1;
                        of = f->f_and;
+                       f = of;
+                       f->f_choice = LDAP_FILTER_LE;
+                       f->f_ava = &mmaa;
+                       f->f_av_desc = slap_schema.si_ad_entryCSN;
+                       f->f_next = NULL;
+                       BER_BVZERO( &f->f_av_value );
                        for ( i=0; i<sc->numcsns; i++ ) {
-                               f = of;
-                               f->f_choice = LDAP_FILTER_LE;
-                               f->f_ava = (AttributeAssertion *)(f+1);
-                               f->f_av_desc = slap_schema.si_ad_entryCSN;
-                               f->f_av_value = sc->ctxcsn[i];
-                               f->f_next = (Filter *)(f->f_ava+1);
-                               of = f->f_next;
+                               if ( ber_bvcmp( &sc->ctxcsn[i], &f->f_av_value ) > 0 )
+                                       f->f_av_value = sc->ctxcsn[i];
                        }
-                       f->f_next = op->ors_filter;
                        of = op->ors_filter;
-                       op->ors_filter = cf;
+                       op->ors_filter = mmf;
                        filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
                } else {
                        cf = NULL;
@@ -2595,11 +2623,9 @@ syncrepl_del_nonpresent(
                }
                op->o_nocaching = 1;
 
-               if ( limits_check( op, &rs_search ) == 0 ) {
-                       rc = be->be_search( op, &rs_search );
-               }
+
+               rc = be->be_search( op, &rs_search );
                if ( SLAP_MULTIMASTER( op->o_bd )) {
-                       op->o_tmpfree( cf, op->o_tmpmemctx );
                        op->ors_filter = of;
                }
                if ( op->ors_filter ) filter_free_x( op, op->ors_filter, 1 );
@@ -2861,9 +2887,11 @@ syncrepl_updateCookie(
 {
        Backend *be = op->o_bd;
        Modifications mod;
-       struct berval first = BER_BVNULL;
+#ifdef CHECK_CSN
+       Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
+#endif
 
-       int rc, i, j;
+       int rc, i, j, csn_changed = 0;
        ber_len_t len;
 
        slap_callback cb = { NULL };
@@ -2878,6 +2906,15 @@ syncrepl_updateCookie(
 
        ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
 
+#ifdef CHECK_CSN
+       for ( i=0; i<syncCookie->numcsns; i++ ) {
+               assert( !syn->ssyn_validate( syn, syncCookie->ctxcsn+i ));
+       }
+       for ( i=0; i<si->si_cookieState->cs_num; i++ ) {
+               assert( !syn->ssyn_validate( syn, si->si_cookieState->cs_vals+i ));
+       }
+#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 );
@@ -2896,13 +2933,7 @@ syncrepl_updateCookie(
                        if ( memcmp( syncCookie->ctxcsn[i].bv_val,
                                si->si_cookieState->cs_vals[j].bv_val, len ) > 0 ) {
                                mod.sml_values[j] = syncCookie->ctxcsn[i];
-                               if ( BER_BVISNULL( &first ) ) {
-                                       first = syncCookie->ctxcsn[i];
-
-                               } else if ( memcmp( syncCookie->ctxcsn[i].bv_val, first.bv_val, first.bv_len ) > 0 )
-                               {
-                                       first = syncCookie->ctxcsn[i];
-                               }
+                               csn_changed = 1;
                        }
                        break;
                }
@@ -2912,23 +2943,16 @@ syncrepl_updateCookie(
                                ( 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 )
-                       {
-                               first = syncCookie->ctxcsn[i];
-                       }
+                       csn_changed = 1;
                }
        }
        /* Should never happen, ITS#5065 */
-       if ( BER_BVISNULL( &first )) {
+       if ( !csn_changed ) {
                ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
                op->o_tmpfree( mod.sml_values, op->o_tmpmemctx );
                return 0;
        }
        op->o_bd = si->si_wbe;
-       slap_queue_csn( op, &first );
-
        op->o_tag = LDAP_REQ_MODIFY;
 
        cb.sc_response = null_callback;
@@ -2980,6 +3004,12 @@ syncrepl_updateCookie(
        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; i<si->si_cookieState->cs_num; i++ ) {
+               assert( !syn->ssyn_validate( syn, si->si_cookieState->cs_vals+i ));
+       }
+#endif
+
        return rc;
 }
 
@@ -3152,33 +3182,39 @@ dn_callback(
                                struct berval old_p, new_p;
                                int is_ctx, new_sup = 0;
 
-                               /* Make sure new entry is actually newer than old entry */
-                               old = attr_find( rs->sr_entry->e_attrs,
-                                       slap_schema.si_ad_entryCSN );
-                               new = attr_find( dni->new_entry->e_attrs,
-                                       slap_schema.si_ad_entryCSN );
-                               if ( new && old ) {
-                                       int rc;
-                                       ber_len_t len = old->a_vals[0].bv_len;
-                                       if ( len > new->a_vals[0].bv_len )
-                                               len = new->a_vals[0].bv_len;
-                                       rc = memcmp( old->a_vals[0].bv_val,
-                                               new->a_vals[0].bv_val, len );
-                                       if ( rc > 0 ) {
-                                               Debug( LDAP_DEBUG_SYNC,
-                                                       "dn_callback : new entry is older than ours "
-                                                       "%s ours %s, new %s\n",
-                                                       rs->sr_entry->e_name.bv_val,
-                                                       old->a_vals[0].bv_val,
-                                                       new->a_vals[0].bv_val );
-                                               return LDAP_SUCCESS;
-                                       } else if ( rc == 0 ) {
-                                               Debug( LDAP_DEBUG_SYNC,
-                                                       "dn_callback : entries have identical CSN "
-                                                       "%s %s\n",
-                                                       rs->sr_entry->e_name.bv_val,
-                                                       old->a_vals[0].bv_val, 0 );
-                                               return LDAP_SUCCESS;
+                               /* If old entry is not a glue entry, make sure new entry
+                                * is actually newer than old entry
+                                */
+                               if ( !is_entry_glue( rs->sr_entry )) {
+                                       old = attr_find( rs->sr_entry->e_attrs,
+                                               slap_schema.si_ad_objectClass );
+                                       old = attr_find( rs->sr_entry->e_attrs,
+                                               slap_schema.si_ad_entryCSN );
+                                       new = attr_find( dni->new_entry->e_attrs,
+                                               slap_schema.si_ad_entryCSN );
+                                       if ( new && old ) {
+                                               int rc;
+                                               ber_len_t len = old->a_vals[0].bv_len;
+                                               if ( len > new->a_vals[0].bv_len )
+                                                       len = new->a_vals[0].bv_len;
+                                               rc = memcmp( old->a_vals[0].bv_val,
+                                                       new->a_vals[0].bv_val, len );
+                                               if ( rc > 0 ) {
+                                                       Debug( LDAP_DEBUG_SYNC,
+                                                               "dn_callback : new entry is older than ours "
+                                                               "%s ours %s, new %s\n",
+                                                               rs->sr_entry->e_name.bv_val,
+                                                               old->a_vals[0].bv_val,
+                                                               new->a_vals[0].bv_val );
+                                                       return LDAP_SUCCESS;
+                                               } else if ( rc == 0 ) {
+                                                       Debug( LDAP_DEBUG_SYNC,
+                                                               "dn_callback : entries have identical CSN "
+                                                               "%s %s\n",
+                                                               rs->sr_entry->e_name.bv_val,
+                                                               old->a_vals[0].bv_val, 0 );
+                                                       return LDAP_SUCCESS;
+                                               }
                                        }
                                }
 
@@ -3304,8 +3340,8 @@ dn_callback(
                                         * stays co-located with the other mod opattrs. But only
                                         * if we know there are other valid mods.
                                         */
-                                       if ( old->a_desc == slap_schema.si_ad_modifiersName &&
-                                               dni->mods )
+                                       if ( dni->mods && ( old->a_desc == slap_schema.si_ad_modifiersName ||
+                                               old->a_desc == slap_schema.si_ad_modifyTimestamp ))
                                                attr_cmp( op, NULL, new, &modtail, &ml );
                                        else
                                                attr_cmp( op, old, new, &modtail, &ml );
@@ -4287,13 +4323,22 @@ add_syncrepl(
                        BER_BVISNULL( &si->si_bindconf.sb_uri ) ?
                        "(null)" : si->si_bindconf.sb_uri.bv_val, 0, 0 );
                if ( c->be->be_syncinfo ) {
+                       syncinfo_t *sip;
+
                        si->si_cookieState = c->be->be_syncinfo->si_cookieState;
+
+                       // add new syncrepl to end of list (same order as when deleting)
+                       for ( sip = c->be->be_syncinfo; sip->si_next; sip = sip->si_next );
+                       sip->si_next = si;
                } else {
                        si->si_cookieState = ch_calloc( 1, sizeof( cookie_state ));
                        ldap_pvt_thread_mutex_init( &si->si_cookieState->cs_mutex );
+
+                       c->be->be_syncinfo = si;
                }
-               si->si_next = c->be->be_syncinfo;
-               c->be->be_syncinfo = si;
+
+               si->si_next = NULL;
+
                return 0;
        }
 }
@@ -4490,6 +4535,7 @@ syncrepl_config( ConfigArgs *c )
                return 1;
        } else if ( c->op == LDAP_MOD_DELETE ) {
                cookie_state *cs = NULL;
+               int isrunning = 0;
                if ( c->be->be_syncinfo ) {
                        syncinfo_t *si, **sip;
                        int i;
@@ -4498,19 +4544,21 @@ syncrepl_config( ConfigArgs *c )
                        for ( sip = &c->be->be_syncinfo, i=0; *sip; i++ ) {
                                si = *sip;
                                if ( c->valx == -1 || i == c->valx ) {
-                                       int isrunning = 0;
                                        *sip = si->si_next;
                                        /* If the task is currently active, we have to leave
                                         * it running. It will exit on its own. This will only
                                         * happen when running on the cn=config DB.
                                         */
                                        if ( si->si_re ) {
-                                               ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
-                                               isrunning = ldap_pvt_runqueue_isrunning( &slapd_rq, si->si_re );
-                                               ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+                                               if ( ldap_pvt_thread_mutex_trylock( &si->si_mutex )) {
+                                                       isrunning = 1;
+                                               } else {
+                                                       ldap_pvt_thread_mutex_unlock( &si->si_mutex );
+                                               }
                                        }
                                        if ( si->si_re && isrunning ) {
                                                si->si_ctype = 0;
+                                               si->si_next = NULL;
                                        } else {
                                                syncinfo_free( si, 0 );
                                        }
@@ -4522,8 +4570,9 @@ syncrepl_config( ConfigArgs *c )
                        }
                }
                if ( !c->be->be_syncinfo ) {
-                       SLAP_DBFLAGS( c->be ) &= ~(SLAP_DBFLAG_SHADOW|SLAP_DBFLAG_SYNC_SHADOW);
-                       if ( cs ) {
+                       SLAP_DBFLAGS( c->be ) &= ~SLAP_DBFLAG_SHADOW_MASK;
+                       if ( cs && !isrunning ) {
+                               ch_free( cs->cs_sids );
                                ber_bvarray_free( cs->cs_vals );
                                ldap_pvt_thread_mutex_destroy( &cs->cs_mutex );
                                ch_free( cs );