]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/syncrepl.c
delta-mmr conflict resolution
[openldap] / servers / slapd / syncrepl.c
index 22496b66b3825f246c2f5f7a5f75bb097aafa2ae..e69959fd168b4625aae057b86784e476046c2fd4 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2003-2009 The OpenLDAP Foundation.
+ * Copyright 2003-2011 The OpenLDAP Foundation.
  * Portions Copyright 2003 by IBM Corporation.
  * Portions Copyright 2003-2008 by Howard Chu, Symas Corporation.
  * All rights reserved.
 
 #include "ldap_rq.h"
 
+#ifdef ENABLE_REWRITE
+#include "rewrite.h"
+#define SUFFIXM_CTX    "<suffix massage>"
+#endif
+
 struct nonpresent_entry {
        struct berval *npe_name;
        struct berval *npe_nname;
@@ -44,8 +49,14 @@ typedef struct cookie_state {
        int cs_ref;
        struct berval *cs_vals;
        int *cs_sids;
-} cookie_state;
        
+       /* pending changes, not yet committed */
+       ldap_pvt_thread_mutex_t cs_pmutex;
+       int     cs_pnum;
+       struct berval *cs_pvals;
+       int *cs_psids;
+} cookie_state;
+
 #define        SYNCDATA_DEFAULT        0       /* entries are plain LDAP entries */
 #define        SYNCDATA_ACCESSLOG      1       /* entries are accesslog format */
 #define        SYNCDATA_CHANGELOG      2       /* entries are changelog format */
@@ -69,7 +80,9 @@ typedef struct syncinfo_s {
        struct berval           si_base;
        struct berval           si_logbase;
        struct berval           si_filterstr;
+       Filter                  *si_filter;
        struct berval           si_logfilterstr;
+       struct berval           si_contextdn;
        int                     si_scope;
        int                     si_attrsonly;
        char                    *si_anfile;
@@ -98,11 +111,16 @@ typedef struct syncinfo_s {
        int                     si_syncdata;
        int                     si_logstate;
        int                     si_got;
+       int                     si_strict_refresh;      /* stop listening during fallback refresh */
        ber_int_t       si_msgid;
        Avlnode                 *si_presentlist;
        LDAP                    *si_ld;
        Connection              *si_conn;
        LDAP_LIST_HEAD(np, nonpresent_entry)    si_nonpresentlist;
+#ifdef ENABLE_REWRITE
+       struct rewrite_info *si_rewrite;
+       struct berval   si_suffixm;
+#endif
        ldap_pvt_thread_mutex_t si_mutex;
 } syncinfo_t;
 
@@ -119,10 +137,15 @@ static int syncrepl_entry(
                                        Modifications**,int, struct berval*,
                                        struct berval *cookieCSN );
 static int syncrepl_updateCookie(
-                                       syncinfo_t *, Operation *, struct berval *,
+                                       syncinfo_t *, Operation *,
                                        struct sync_cookie * );
 static struct berval * slap_uuidstr_from_normalized(
                                        struct berval *, struct berval *, void * );
+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 * );
@@ -131,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 )
 {
@@ -151,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;
@@ -310,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,
@@ -447,7 +496,7 @@ check_syncprov(
        AttributeName at[2];
        Attribute a = {0};
        Entry e = {0};
-       SlapReply rs = {0};
+       SlapReply rs = {REP_SEARCH};
        int i, j, changed = 0;
 
        /* Look for contextCSN from syncprov overlay. If
@@ -458,8 +507,8 @@ check_syncprov(
         */
        a.a_desc = slap_schema.si_ad_contextCSN;
        e.e_attrs = &a;
-       e.e_name = op->o_bd->be_suffix[0];
-       e.e_nname = op->o_bd->be_nsuffix[0];
+       e.e_name = si->si_contextdn;
+       e.e_nname = si->si_contextdn;
        at[0].an_name = a.a_desc->ad_cname;
        at[0].an_desc = a.a_desc;
        BER_BVZERO( &at[1].an_name );
@@ -578,8 +627,9 @@ do_syncrep1(
        {
                ber_len_t ssf; /* ITS#5403, 3864 LDAP_OPT_X_SASL_SSF probably ought
                                                  to use sasl_ssf_t but currently uses ber_len_t */
-               ldap_get_option( si->si_ld, LDAP_OPT_X_SASL_SSF, &ssf );
-               op->o_sasl_ssf = ssf;
+               if ( ldap_get_option( si->si_ld, LDAP_OPT_X_SASL_SSF, &ssf )
+                       == LDAP_SUCCESS )
+                       op->o_sasl_ssf = ssf;
        }
        op->o_ssf = ( op->o_sasl_ssf > op->o_tls_ssf )
                ?  op->o_sasl_ssf : op->o_tls_ssf;
@@ -600,7 +650,7 @@ do_syncrep1(
        /* We've just started up, or the remote server hasn't sent us
         * any meaningful state.
         */
-       if ( BER_BVISNULL( &si->si_syncCookie.octet_str ) ) {
+       if ( !si->si_syncCookie.ctxcsn ) {
                int i;
 
                LDAP_STAILQ_FOREACH( sc, &slap_sync_cookie, sc_next ) {
@@ -627,7 +677,7 @@ do_syncrep1(
                                BerVarray csn = NULL;
                                void *ctx = op->o_tmpmemctx;
 
-                               op->o_req_ndn = op->o_bd->be_nsuffix[0];
+                               op->o_req_ndn = si->si_contextdn;
                                op->o_req_dn = op->o_req_ndn;
 
                                /* try to read stored contextCSN */
@@ -663,8 +713,15 @@ do_syncrep1(
                        si->si_syncCookie.ctxcsn, si->si_syncCookie.rid,
                        si->si_syncCookie.sid );
        } else {
+               /* ITS#6367: recreate the cookie so it has our SID, not our peer's */
+               ch_free( si->si_syncCookie.octet_str.bv_val );
+               BER_BVZERO( &si->si_syncCookie.octet_str );
                /* Look for contextCSN from syncprov overlay. */
                check_syncprov( op, si );
+               if ( BER_BVISNULL( &si->si_syncCookie.octet_str ))
+                       slap_compose_sync_cookie( NULL, &si->si_syncCookie.octet_str,
+                               si->si_syncCookie.ctxcsn, si->si_syncCookie.rid,
+                               si->si_syncCookie.sid );
        }
 
        si->si_refreshDone = 0;
@@ -731,39 +788,26 @@ do_syncrep2(
        Operation *op,
        syncinfo_t *si )
 {
-       LDAPControl     **rctrls = NULL;
-
        BerElementBuffer berbuf;
        BerElement      *ber = (BerElement *)&berbuf;
 
        LDAPMessage     *msg = NULL;
 
-       char            *retoid = NULL;
-       struct berval   *retdata = NULL;
-
-       Entry           *entry = NULL;
-
-       int             syncstate;
-       struct berval   syncUUID = BER_BVNULL;
        struct sync_cookie      syncCookie = { NULL };
        struct sync_cookie      syncCookie_req = { NULL };
-       struct berval           cookie = BER_BVNULL;
 
        int             rc,
                        err = LDAP_SUCCESS;
-       ber_len_t       len;
 
-       struct berval   *psub;
        Modifications   *modlist = NULL;
 
-       int                             match, m;
+       int                             m;
 
        struct timeval *tout_p = NULL;
        struct timeval tout = { 0, 0 };
 
        int             refreshDeletes = 0;
-       BerVarray syncUUIDs = NULL;
-       ber_tag_t si_tag;
+       char empty[6] = "empty";
 
        if ( slapd_shutdown ) {
                rc = -2;
@@ -775,8 +819,6 @@ do_syncrep2(
 
        Debug( LDAP_DEBUG_TRACE, "=>do_syncrep2 %s\n", si->si_ridtxt, 0, 0 );
 
-       psub = &si->si_be->be_nsuffix[0];
-
        slap_dup_sync_cookie( &syncCookie_req, &si->si_syncCookie );
 
        if ( abs(si->si_type) == LDAP_SYNC_REFRESH_AND_PERSIST ) {
@@ -788,7 +830,15 @@ do_syncrep2(
        while ( ( rc = ldap_result( si->si_ld, si->si_msgid, LDAP_MSG_ONE,
                tout_p, &msg ) ) > 0 )
        {
-               LDAPControl     *rctrlp = NULL;
+               int                             match, punlock, syncstate;
+               struct berval   *retdata, syncUUID, cookie = BER_BVNULL;
+               char                    *retoid;
+               LDAPControl             **rctrls = NULL, *rctrlp = NULL;
+               BerVarray               syncUUIDs;
+               ber_len_t               len;
+               ber_tag_t               si_tag;
+               Entry                   *entry;
+               struct berval   bdn;
 
                if ( slapd_shutdown ) {
                        rc = -2;
@@ -797,9 +847,14 @@ do_syncrep2(
                switch( ldap_msgtype( msg ) ) {
                case LDAP_RES_SEARCH_ENTRY:
                        ldap_get_entry_controls( si->si_ld, msg, &rctrls );
+                       ldap_get_dn_ber( si->si_ld, msg, NULL, &bdn );
+                       if (!bdn.bv_len) {
+                               bdn.bv_val = empty;
+                               bdn.bv_len = sizeof(empty)-1;
+                       }
                        /* we can't work without the control */
                        if ( rctrls ) {
-                               LDAPControl **next;
+                               LDAPControl **next = NULL;
                                /* NOTE: make sure we use the right one;
                                 * a better approach would be to run thru
                                 * the whole list and take care of all */
@@ -810,39 +865,52 @@ do_syncrep2(
                                rctrlp = ldap_control_find( LDAP_CONTROL_SYNC_STATE, rctrls, &next );
                                if ( next && ldap_control_find( LDAP_CONTROL_SYNC_STATE, next, NULL ) )
                                {
+                                       bdn.bv_val[bdn.bv_len] = '\0';
                                        Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
                                                "got search entry with multiple "
-                                               "Sync State control\n", si->si_ridtxt, 0, 0 );
+                                               "Sync State control (%s)\n", si->si_ridtxt, bdn.bv_val, 0 );
                                        ldap_controls_free( rctrls );
                                        rc = -1;
                                        goto done;
                                }
                        }
                        if ( rctrlp == NULL ) {
+                               bdn.bv_val[bdn.bv_len] = '\0';
                                Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
                                        "got search entry without "
-                                       "Sync State control\n", si->si_ridtxt, 0, 0 );
+                                       "Sync State control (%s)\n", si->si_ridtxt, bdn.bv_val, 0 );
                                rc = -1;
                                goto done;
                        }
                        ber_init2( ber, &rctrlp->ldctl_value, LBER_USE_DER );
-                       ber_scanf( ber, "{em" /*"}"*/, &syncstate, &syncUUID );
+                       if ( ber_scanf( ber, "{em" /*"}"*/, &syncstate, &syncUUID )
+                                       == LBER_ERROR ) {
+                               bdn.bv_val[bdn.bv_len] = '\0';
+                               Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s malformed message (%s)\n",
+                                       si->si_ridtxt, bdn.bv_val, 0 );
+                               ldap_controls_free( rctrls );
+                               rc = -1;
+                               goto done;
+                       }
                        /* FIXME: what if syncUUID is NULL or empty?
                         * (happens with back-sql...) */
                        if ( BER_BVISEMPTY( &syncUUID ) ) {
+                               bdn.bv_val[bdn.bv_len] = '\0';
                                Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
-                                       "got empty syncUUID with LDAP_SYNC_%s\n",
+                                       "got empty syncUUID with LDAP_SYNC_%s (%s)\n",
                                        si->si_ridtxt,
-                                       syncrepl_state2str( syncstate ), 0 );
+                                       syncrepl_state2str( syncstate ), bdn.bv_val );
                                ldap_controls_free( rctrls );
                                rc = -1;
                                goto done;
                        }
+                       punlock = -1;
                        if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
                                ber_scanf( ber, /*"{"*/ "m}", &cookie );
 
-                               Debug( LDAP_DEBUG_SYNC, "do_syncrep2: cookie=%s\n",
-                                       BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0, 0 );
+                               Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+                                       si->si_ridtxt,
+                                       BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0 );
 
                                if ( !BER_BVISNULL( &cookie ) ) {
                                        ch_free( syncCookie.octet_str.bv_val );
@@ -853,16 +921,54 @@ do_syncrep2(
                                        slap_parse_sync_cookie( &syncCookie, NULL );
                                        if ( syncCookie.ctxcsn ) {
                                                int i, sid = slap_parse_csn_sid( syncCookie.ctxcsn );
+                                               check_syncprov( op, si );
                                                for ( i =0; i<si->si_cookieState->cs_num; i++ ) {
-                                                       if ( si->si_cookieState->cs_sids[i] == sid && 
-                                                               ber_bvcmp( syncCookie.ctxcsn, &si->si_cookieState->cs_vals[i] ) <= 0 ) {
-                                                               Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s CSN too old, ignoring %s\n",
-                                                                       si->si_ridtxt, syncCookie.ctxcsn->bv_val, 0 );
-                                                               ldap_controls_free( rctrls );
-                                                               rc = 0;
+                                                       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';
+                                                                       Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s CSN too old, ignoring %s (%s)\n",
+                                                                               si->si_ridtxt, syncCookie.ctxcsn->bv_val, bdn.bv_val );
+                                                                       ldap_controls_free( rctrls );
+                                                                       rc = 0;
+                                                                       goto done;
+                                                               }
+                                                               break;
+                                                       }
+                                               }
+                                               /* check pending CSNs too */
+                                               while ( ldap_pvt_thread_mutex_trylock( &si->si_cookieState->cs_pmutex )) {
+                                                       if ( slapd_shutdown ) {
+                                                               rc = -2;
                                                                goto done;
                                                        }
+                                                       if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
+                                                               ldap_pvt_thread_yield();
                                                }
+                                               for ( i =0; i<si->si_cookieState->cs_pnum; i++ ) {
+                                                       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';
+                                                                       Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s CSN pending, ignoring %s (%s)\n",
+                                                                               si->si_ridtxt, syncCookie.ctxcsn->bv_val, bdn.bv_val );
+                                                                       ldap_controls_free( rctrls );
+                                                                       rc = 0;
+                                                                       ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_pmutex );
+                                                                       goto done;
+                                                               }
+                                                               ber_bvreplace( &si->si_cookieState->cs_pvals[i],
+                                                                       syncCookie.ctxcsn );
+                                                               break;
+                                                       }
+                                               }
+                                               /* 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;
+                                               }
+                                               assert( punlock < 0 );
+                                               punlock = i;
                                        }
                                        op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
                                }
@@ -873,7 +979,7 @@ do_syncrep2(
                                if ( ( rc = syncrepl_message_to_op( si, op, msg ) ) == LDAP_SUCCESS &&
                                        syncCookie.ctxcsn )
                                {
-                                       rc = syncrepl_updateCookie( si, op, psub, &syncCookie );
+                                       rc = syncrepl_updateCookie( si, op, &syncCookie );
                                } else switch ( rc ) {
                                        case LDAP_ALREADY_EXISTS:
                                        case LDAP_NO_SUCH_OBJECT:
@@ -882,6 +988,13 @@ do_syncrep2(
                                                rc = LDAP_SYNC_REFRESH_REQUIRED;
                                                si->si_logstate = SYNCLOG_FALLBACK;
                                                ldap_abandon_ext( si->si_ld, si->si_msgid, NULL, NULL );
+                                               bdn.bv_val[bdn.bv_len] = '\0';
+                                               Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s delta-sync lost sync on (%s), switching to REFRESH\n",
+                                                       si->si_ridtxt, bdn.bv_val, 0 );
+                                               if (si->si_strict_refresh) {
+                                                       slap_suspend_listeners();
+                                                       connections_drop();
+                                               }
                                                break;
                                        default:
                                                break;
@@ -893,9 +1006,25 @@ do_syncrep2(
                                        syncstate, &syncUUID, syncCookie.ctxcsn ) ) == LDAP_SUCCESS &&
                                        syncCookie.ctxcsn )
                                {
-                                       rc = syncrepl_updateCookie( si, op, psub, &syncCookie );
+                                       rc = syncrepl_updateCookie( si, op, &syncCookie );
                                }
                        }
+                       if ( punlock >= 0 ) {
+                               /* on failure, revert pending CSN */
+                               if ( rc != LDAP_SUCCESS ) {
+                                       int i;
+                                       for ( i = 0; i<si->si_cookieState->cs_num; i++ ) {
+                                               if ( si->si_cookieState->cs_sids[i] == si->si_cookieState->cs_psids[punlock] ) {
+                                                       ber_bvreplace( &si->si_cookieState->cs_pvals[punlock],
+                                                               &si->si_cookieState->cs_vals[i] );
+                                                       break;
+                                               }
+                                       }
+                                       if ( i == si->si_cookieState->cs_num )
+                                               si->si_cookieState->cs_pvals[punlock].bv_val[0] = '\0';
+                               }
+                               ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_pmutex );
+                       }
                        ldap_controls_free( rctrls );
                        if ( modlist ) {
                                slap_mods_free( modlist, 1 );
@@ -914,6 +1043,7 @@ do_syncrep2(
                        Debug( LDAP_DEBUG_SYNC,
                                "do_syncrep2: %s LDAP_RES_SEARCH_RESULT\n",
                                si->si_ridtxt, 0, 0 );
+                       err = LDAP_OTHER; /* FIXME check parse result properly */
                        ldap_parse_result( si->si_ld, msg, &err, NULL, NULL, NULL,
                                &rctrls, 0 );
 #ifdef LDAP_X_SYNC_REFRESH_REQUIRED
@@ -925,6 +1055,12 @@ do_syncrep2(
                        if ( err == LDAP_SYNC_REFRESH_REQUIRED ) {
                                if ( si->si_logstate == SYNCLOG_LOGGING ) {
                                        si->si_logstate = SYNCLOG_FALLBACK;
+                                       Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s delta-sync lost sync, switching to REFRESH\n",
+                                               si->si_ridtxt, 0, 0 );
+                                       if (si->si_strict_refresh) {
+                                               slap_suspend_listeners();
+                                               connections_drop();
+                                       }
                                }
                                rc = err;
                                goto done;
@@ -935,7 +1071,7 @@ do_syncrep2(
                                        si->si_ridtxt, err, ldap_err2string( err ) );
                        }
                        if ( rctrls ) {
-                               LDAPControl **next;
+                               LDAPControl **next = NULL;
                                /* NOTE: make sure we use the right one;
                                 * a better approach would be to run thru
                                 * the whole list and take care of all */
@@ -961,8 +1097,9 @@ do_syncrep2(
                                if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
                                        ber_scanf( ber, "m", &cookie );
 
-                                       Debug( LDAP_DEBUG_SYNC, "do_syncrep2: cookie=%s\n",
-                                               BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0, 0 );
+                                       Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+                                               si->si_ridtxt, 
+                                               BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0 );
 
                                        if ( !BER_BVISNULL( &cookie ) ) {
                                                ch_free( syncCookie.octet_str.bv_val );
@@ -1013,19 +1150,21 @@ do_syncrep2(
                        }
                        if ( syncCookie.ctxcsn && match < 0 && err == LDAP_SUCCESS )
                        {
-                               rc = syncrepl_updateCookie( si, op, psub, &syncCookie );
+                               rc = syncrepl_updateCookie( si, op, &syncCookie );
                        }
                        if ( err == LDAP_SUCCESS
                                && si->si_logstate == SYNCLOG_FALLBACK ) {
                                si->si_logstate = SYNCLOG_LOGGING;
                                rc = LDAP_SYNC_REFRESH_REQUIRED;
+                               slap_resume_listeners();
                        } else {
                                rc = -2;
                        }
                        goto done;
-                       break;
 
                case LDAP_RES_INTERMEDIATE:
+                       retoid = NULL;
+                       retdata = NULL;
                        rc = ldap_parse_intermediate( si->si_ld, msg,
                                &retoid, &retdata, NULL, 0 );
                        if ( !rc && !strcmp( retoid, LDAP_SYNC_INFO ) ) {
@@ -1071,8 +1210,9 @@ do_syncrep2(
                                        {
                                                ber_scanf( ber, "m", &cookie );
 
-                                               Debug( LDAP_DEBUG_SYNC, "do_syncrep2: cookie=%s\n",
-                                                       BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0, 0 );
+                                               Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+                                                       si->si_ridtxt, 
+                                                       BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0 );
 
                                                if ( !BER_BVISNULL( &cookie ) ) {
                                                        ch_free( syncCookie.octet_str.bv_val );
@@ -1107,8 +1247,9 @@ do_syncrep2(
                                        {
                                                ber_scanf( ber, "m", &cookie );
 
-                                               Debug( LDAP_DEBUG_SYNC, "do_syncrep2: cookie=%s\n",
-                                                       BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0, 0 );
+                                               Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+                                                       si->si_ridtxt,
+                                                       BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0 );
 
                                                if ( !BER_BVISNULL( &cookie ) ) {
                                                        ch_free( syncCookie.octet_str.bv_val );
@@ -1126,6 +1267,7 @@ do_syncrep2(
                                        {
                                                ber_scanf( ber, "b", &refreshDeletes );
                                        }
+                                       syncUUIDs = NULL;
                                        ber_scanf( ber, "[W]", &syncUUIDs );
                                        ber_scanf( ber, /*"{"*/ "}" );
                                        if ( refreshDeletes ) {
@@ -1174,13 +1316,12 @@ do_syncrep2(
 
                                        if ( syncCookie.ctxcsn )
                                        {
-                                               rc = syncrepl_updateCookie( si, op, psub, &syncCookie);
+                                               rc = syncrepl_updateCookie( si, op, &syncCookie);
                                        }
                                } 
 
                                ldap_memfree( retoid );
                                ber_bvfree( retdata );
-                               break;
 
                        } else {
                                Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
@@ -1188,7 +1329,6 @@ do_syncrep2(
                                        si->si_ridtxt, rc, 0 );
                                ldap_memfree( retoid );
                                ber_bvfree( retdata );
-                               break;
                        }
                        break;
 
@@ -1215,6 +1355,7 @@ do_syncrep2(
        }
 
        if ( rc == -1 ) {
+               rc = LDAP_OTHER;
                ldap_get_option( si->si_ld, LDAP_OPT_ERROR_NUMBER, &rc );
                err = rc;
        }
@@ -1302,6 +1443,8 @@ do_syncrepl(
 
        connection_fake_init( &conn, &opbuf, ctx );
        op = &opbuf.ob_op;
+       /* o_connids must be unique for slap_graduate_commit_csn */
+       op->o_connid = SLAPD_SYNC_RID2SYNCCONN(si->si_rid);
 
        op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
        be = si->si_be;
@@ -1324,12 +1467,18 @@ do_syncrepl(
                if ( SLAP_GLUE_SUBORDINATE( be ) && !overlay_is_inst( be, "syncprov" )) {
                        BackendDB * top_be = select_backend( &be->be_nsuffix[0], 1 );
                        if ( overlay_is_inst( top_be, "syncprov" ))
-                               si->si_wbe = select_backend( &be->be_nsuffix[0], 1 );
+                               si->si_wbe = top_be;
                        else
                                si->si_wbe = be;
                } else {
                        si->si_wbe = be;
                }
+               if ( SLAP_SYNC_SUBENTRY( si->si_wbe )) {
+                       build_new_dn( &si->si_contextdn, &si->si_wbe->be_nsuffix[0],
+                               (struct berval *)&slap_ldapsync_cn_bv, NULL );
+               } else {
+                       si->si_contextdn = si->si_wbe->be_nsuffix[0];
+               }
        }
        if ( !si->si_schemachecking )
                op->o_no_schema_check = 1;
@@ -1339,6 +1488,11 @@ do_syncrepl(
                si->si_refreshDelete = 0;
                si->si_refreshPresent = 0;
 
+               if ( si->si_presentlist ) {
+                   avl_free( si->si_presentlist, ch_free );
+                   si->si_presentlist = NULL;
+               }
+
                /* use main DB when retrieving contextCSN */
                op->o_bd = si->si_wbe;
                op->o_dn = op->o_bd->be_rootdn;
@@ -1477,6 +1631,46 @@ deleted:
        return NULL;
 }
 
+#ifdef ENABLE_REWRITE
+static int
+syncrepl_rewrite_dn(
+       syncinfo_t *si,
+       struct berval *dn,
+       struct berval *sdn )
+{
+       char nul;
+       int rc;
+
+       nul = dn->bv_val[dn->bv_len];
+       dn->bv_val[dn->bv_len] = 0;
+       rc = rewrite( si->si_rewrite, SUFFIXM_CTX, dn->bv_val, &sdn->bv_val );
+       dn->bv_val[dn->bv_len] = nul;
+
+       if ( sdn->bv_val == dn->bv_val )
+               sdn->bv_val = NULL;
+       else if ( rc == REWRITE_REGEXEC_OK && sdn->bv_val )
+               sdn->bv_len = strlen( sdn->bv_val );
+       return rc;
+}
+#define        REWRITE_VAL(si, ad, bv, bv2)    \
+       BER_BVZERO( &bv2 );     \
+       if ( si->si_rewrite && ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName) \
+               syncrepl_rewrite_dn( si, &bv, &bv2); \
+       if ( BER_BVISNULL( &bv2 ))  \
+               ber_dupbv( &bv2, &bv )
+#define REWRITE_DN(si, bv, bv2, dn, ndn) \
+       BER_BVZERO( &bv2 );     \
+       if (si->si_rewrite) \
+               syncrepl_rewrite_dn(si, &bv, &bv2); \
+       rc = dnPrettyNormal( NULL, bv2.bv_val ? &bv2 : &bv, &dn, &ndn, op->o_tmpmemctx ); \
+       ch_free(bv2.bv_val)
+#else
+#define REWRITE_VAL(si, ad, bv, bv2)   ber_dupbv(&bv2, &bv)
+#define REWRITE_DN(si, bv, bv2, dn, ndn) \
+       rc = dnPrettyNormal( NULL, &bv, &dn, &ndn, op->o_tmpmemctx )
+#endif
+
+
 static slap_verbmasks modops[] = {
        { BER_BVC("add"), LDAP_REQ_ADD },
        { BER_BVC("delete"), LDAP_REQ_DELETE },
@@ -1485,10 +1679,11 @@ static slap_verbmasks modops[] = {
        { BER_BVNULL, 0 }
 };
 
-static Modifications *
+static int
 syncrepl_accesslog_mods(
        syncinfo_t *si,
-       struct berval *vals
+       struct berval *vals,
+       struct Modifications **modres
 )
 {
        char *colon;
@@ -1497,7 +1692,7 @@ syncrepl_accesslog_mods(
        struct berval bv, bv2;
        short op;
        Modifications *mod = NULL, *modlist = NULL, **modtail;
-       int i;
+       int i, rc = 0;
 
        modtail = &modlist;
 
@@ -1514,7 +1709,13 @@ syncrepl_accesslog_mods(
                bv.bv_len = colon - bv.bv_val;
                if ( slap_bv2ad( &bv, &ad, &text ) ) {
                        /* Invalid */
-                       continue;
+                       Debug( LDAP_DEBUG_ANY, "syncrepl_accesslog_mods: %s "
+                               "Invalid attribute %s, %s\n",
+                               si->si_ridtxt, bv.bv_val, text );
+                       slap_mods_free( modlist, 1 );
+                       modlist = NULL;
+                       rc = -1;
+                       break;
                }
 
                /* Ignore dynamically generated attrs */
@@ -1554,21 +1755,359 @@ syncrepl_accesslog_mods(
                if ( colon[2] == ' ' ) {
                        bv.bv_val = colon + 3;
                        bv.bv_len = vals[i].bv_len - ( bv.bv_val - vals[i].bv_val );
-                       ber_dupbv( &bv2, &bv );
+                       REWRITE_VAL( si, ad, bv, bv2 );
                        ber_bvarray_add( &mod->sml_values, &bv2 );
                        mod->sml_numvals++;
                }
        }
-       return modlist;
+       *modres = modlist;
+       return rc;
 }
 
-static Modifications *
+static int
 syncrepl_changelog_mods(
        syncinfo_t *si,
-       struct berval *vals
+       struct berval *vals,
+       struct Modifications **modres
 )
 {
-       return NULL;    /* FIXME */
+       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 )
+{
+       Modifications *mod, *modnew = NULL, *modtail = NULL;
+       int size;
+       for ( ; modlist; modlist = modlist->sml_next ) {
+               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; i<mod->sml_numvals; i++)
+                               mod->sml_values[i] = modlist->sml_values[i];
+                       if ( modlist->sml_nvalues ) {
+                               mod->sml_nvalues = mod->sml_values + mod->sml_numvals + 1;
+                               for (i=0; i<mod->sml_numvals; i++)
+                                       mod->sml_nvalues[i] = modlist->sml_nvalues[i];
+                       }
+               }
+               if ( 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; j<m2->sml_numvals; j++ ) {
+               for ( i=0; i<m1->sml_numvals; i++ ) {
+                       if ( !ber_bvcmp( &bv1[i], &bv2[j] )) {
+                               int k;
+                               for ( k=i; k<m1->sml_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];
+                               }
+                               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->o_callback = sc->sc_next;
+       op->o_tmpfree( sc, op->o_tmpmemctx );
+
+       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 );
+       }
+       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 );
+       }
+       /* mod is newer, let it go */
+       if ( match > 0 )
+               return SLAP_CB_CONTINUE;
+       /* 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.
+        */
+
+       newlist = mods_dup( op, op->orm_modlist );
+
+       {
+               Operation op2 = *op;
+               AttributeName an[2];
+               const char *text;
+               slap_callback cb;
+               struct berval bv;
+               char *ptr;
+               int size, rc;
+               SlapReply rs1 = {0};
+               resolve_ctxt rx = { si, newlist };
+
+               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];
+               ptr = strchr(bv.bv_val, '.');
+               bv.bv_len = ptr - bv.bv_val;
+
+               size = sizeof("(&(reqStart>=)(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,
+                       "(&(reqStart>=%.*s)(reqDN=%s)%s)",
+                       (int)bv.bv_len, 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;
+               cb.sc_response = syncrepl_resolve_cb;
+               cb.sc_private = &rx;
+               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;
+               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;
+                       }
+               }
+               op->orm_modlist = newlist;
+       }
+       return SLAP_CB_CONTINUE;
 }
 
 static int
@@ -1589,7 +2128,7 @@ syncrepl_message_to_op(
        size_t textlen = sizeof txtbuf;
 
        struct berval   bdn, dn = BER_BVNULL, ndn;
-       struct berval   bv, *bvals = NULL;
+       struct berval   bv, bv2, *bvals = NULL;
        struct berval   rdn = BER_BVNULL, sup = BER_BVNULL,
                prdn = BER_BVNULL, nrdn = BER_BVNULL,
                psup = BER_BVNULL, nsup = BER_BVNULL;
@@ -1620,7 +2159,7 @@ syncrepl_message_to_op(
        op->o_tag = LBER_DEFAULT;
        op->o_bd = si->si_wbe;
 
-       if ( BER_BVISEMPTY( &bdn ) && !BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] ) ) {
+       if ( BER_BVISEMPTY( &bdn )) {
                Debug( LDAP_DEBUG_ANY,
                        "syncrepl_message_to_op: %s got empty dn",
                        si->si_ridtxt, 0, 0 );
@@ -1634,7 +2173,7 @@ syncrepl_message_to_op(
 
                if ( !ber_bvstrcasecmp( &bv, &ls->ls_dn ) ) {
                        bdn = bvals[0];
-                       rc = dnPrettyNormal( NULL, &bdn, &dn, &ndn, op->o_tmpmemctx );
+                       REWRITE_DN( si, bdn, bv2, dn, ndn );
                        if ( rc != LDAP_SUCCESS ) {
                                Debug( LDAP_DEBUG_ANY,
                                        "syncrepl_message_to_op: %s "
@@ -1663,10 +2202,11 @@ syncrepl_message_to_op(
                } else if ( !ber_bvstrcasecmp( &bv, &ls->ls_mod ) ) {
                        /* Parse attribute into modlist */
                        if ( si->si_syncdata == SYNCDATA_ACCESSLOG ) {
-                               modlist = syncrepl_accesslog_mods( si, bvals );
+                               rc = syncrepl_accesslog_mods( si, bvals, &modlist );
                        } else {
-                               modlist = syncrepl_changelog_mods( si, bvals );
+                               rc = syncrepl_changelog_mods( si, bvals, &modlist );
                        }
+                       if ( rc ) goto done;
                } else if ( !ber_bvstrcasecmp( &bv, &ls->ls_newRdn ) ) {
                        rdn = bvals[0];
                } else if ( !ber_bvstrcasecmp( &bv, &ls->ls_delRdn ) ) {
@@ -1729,9 +2269,19 @@ 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 );
+                       }
                        modlist = op->orm_modlist;
                        Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC,
                                "syncrepl_message_to_op: %s be_modify %s (%d)\n", 
@@ -1750,9 +2300,9 @@ syncrepl_message_to_op(
                        goto done;
                }
                if ( !BER_BVISNULL( &sup ) ) {
-                       if ( dnPrettyNormal( NULL, &sup, &psup, &nsup, NULL ) ) {
+                       REWRITE_DN( si, sup, bv2, psup, nsup );
+                       if ( rc )
                                goto done;
-                       }
                        op->orr_newSup = &psup;
                        op->orr_nnewSup = &nsup;
                } else {
@@ -1842,7 +2392,7 @@ syncrepl_message_to_entry(
        char txtbuf[SLAP_TEXT_BUFLEN];
        size_t textlen = sizeof txtbuf;
 
-       struct berval   bdn = BER_BVNULL, dn, ndn;
+       struct berval   bdn = BER_BVNULL, dn, ndn, bv2;
        int             rc, is_ctx;
 
        *modlist = NULL;
@@ -1882,7 +2432,7 @@ syncrepl_message_to_entry(
                return -1;
        }
 
-       rc = dnPrettyNormal( NULL, &bdn, &dn, &ndn, op->o_tmpmemctx );
+       REWRITE_DN( si, bdn, bv2, dn, ndn );
        if ( rc != LDAP_SUCCESS ) {
                /* One of the things that could happen is that the schema
                 * is not lined-up; this could result in unknown attributes.
@@ -1935,6 +2485,26 @@ syncrepl_message_to_entry(
                mod->sml_nvalues = NULL;
                mod->sml_numvals = 0;   /* slap_mods_check will set this */
 
+#ifdef ENABLE_REWRITE
+               if (si->si_rewrite) {
+                       AttributeDescription *ad = NULL;
+                       slap_bv2ad( &tmp.sml_type, &ad, &text );
+                       if ( ad ) {
+                               mod->sml_desc = ad;
+                               mod->sml_type = ad->ad_cname;
+                               if ( ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) {
+                                       int i;
+                                       for ( i = 0; tmp.sml_values[i].bv_val; i++ ) {
+                                               syncrepl_rewrite_dn( si, &tmp.sml_values[i], &bv2);
+                                               if ( !BER_BVISNULL( &bv2 )) {
+                                                       ber_memfree( tmp.sml_values[i].bv_val );
+                                                       tmp.sml_values[i] = bv2;
+                                               }
+                                       }
+                               }
+                       }
+               }
+#endif
                *modtail = mod;
                modtail = &mod->sml_next;
        }
@@ -2074,9 +2644,6 @@ syncrepl_entry(
        struct berval   syncUUID_strrep = BER_BVNULL;
 
        SlapReply       rs_search = {REP_RESULT};
-       SlapReply       rs_delete = {REP_RESULT};
-       SlapReply       rs_add = {REP_RESULT};
-       SlapReply       rs_modify = {REP_RESULT};
        Filter f = {0};
        AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
        int rc = LDAP_SUCCESS;
@@ -2149,8 +2716,16 @@ syncrepl_entry(
        op->ors_deref = LDAP_DEREF_NEVER;
 
        /* get the entry for this UUID */
-       op->o_req_dn = si->si_base;
-       op->o_req_ndn = si->si_base;
+#ifdef ENABLE_REWRITE
+       if ( si->si_rewrite ) {
+               op->o_req_dn = si->si_suffixm;
+               op->o_req_ndn = si->si_suffixm;
+       } else
+#endif
+       {
+               op->o_req_dn = si->si_base;
+               op->o_req_ndn = si->si_base;
+       }
 
        op->o_time = slap_get_time();
        op->ors_tlimit = SLAP_NO_LIMIT;
@@ -2215,6 +2790,7 @@ syncrepl_entry(
                }
 retry_add:;
                if ( BER_BVISNULL( &dni.dn ) ) {
+                       SlapReply       rs_add = {REP_RESULT};
 
                        op->o_req_dn = entry->e_name;
                        op->o_req_ndn = entry->e_nname;
@@ -2252,7 +2828,7 @@ retry_add:;
                        case LDAP_ALREADY_EXISTS:
                                if ( retry ) {
                                        Operation       op2 = *op;
-                                       SlapReply       rs2 = { 0 };
+                                       SlapReply       rs2 = { REP_RESULT };
                                        slap_callback   cb2 = { 0 };
 
                                        op2.o_bd = be;
@@ -2302,6 +2878,7 @@ retry_add:;
                        struct berval noldp, newp;
                        Modifications *mod, **modtail, **ml, *m2;
                        int i, got_replace = 0, just_rename = 0;
+                       SlapReply rs_modify = {REP_RESULT};
 
                        op->o_tag = LDAP_REQ_MODRDN;
                        dnRdn( &entry->e_name, &op->orr_newrdn );
@@ -2478,7 +3055,20 @@ retry_add:;
                                mod->sml_next = m2;
                        }
                        op->o_bd = si->si_wbe;
+retry_modrdn:;
+                       rs_reinit( &rs_modify, REP_RESULT );
                        rc = op->o_bd->be_modrdn( op, &rs_modify );
+
+                       /* 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 )) {
+                               Operation op2 = *op;
+                               rc = syncrepl_add_glue_ancestors( &op2, entry );
+                               if ( rc == LDAP_SUCCESS ) {
+                                       goto retry_modrdn;
+                               }
+                       }
+               
                        op->o_tmpfree( op->orr_nnewrdn.bv_val, op->o_tmpmemctx );
                        op->o_tmpfree( op->orr_newrdn.bv_val, op->o_tmpmemctx );
 
@@ -2497,6 +3087,8 @@ retry_add:;
                                slap_queue_csn( op, syncCSN );
                }
                if ( dni.mods ) {
+                       SlapReply rs_modify = {REP_RESULT};
+
                        op->o_tag = LDAP_REQ_MODIFY;
                        op->orm_modlist = dni.mods;
                        op->orm_no_opattrs = 1;
@@ -2527,6 +3119,7 @@ retry_add:;
                goto done;
        case LDAP_SYNC_DELETE :
                if ( !BER_BVISNULL( &dni.dn ) ) {
+                       SlapReply       rs_delete = {REP_RESULT};
                        op->o_req_dn = dni.dn;
                        op->o_req_ndn = dni.ndn;
                        op->o_tag = LDAP_REQ_DELETE;
@@ -2546,6 +3139,7 @@ retry_add:;
                                        op->o_req_dn = pdn;
                                        op->o_req_ndn = pdn;
                                        op->o_callback = &cb;
+                                       rs_reinit( &rs_delete, REP_RESULT );
                                        op->o_bd->be_delete( op, &rs_delete );
                                } else {
                                        break;
@@ -2604,9 +3198,6 @@ syncrepl_del_nonpresent(
 {
        Backend* be = op->o_bd;
        slap_callback   cb = { NULL };
-       SlapReply       rs_search = {REP_RESULT};
-       SlapReply       rs_delete = {REP_RESULT};
-       SlapReply       rs_modify = {REP_RESULT};
        struct nonpresent_entry *np_list, *np_prev;
        int rc;
        AttributeName   an[2];
@@ -2614,8 +3205,16 @@ syncrepl_del_nonpresent(
        struct berval pdn = BER_BVNULL;
        struct berval csn;
 
-       op->o_req_dn = si->si_base;
-       op->o_req_ndn = si->si_base;
+#ifdef ENABLE_REWRITE
+       if ( si->si_rewrite ) {
+               op->o_req_dn = si->si_suffixm;
+               op->o_req_ndn = si->si_suffixm;
+       } else
+#endif
+       {
+               op->o_req_dn = si->si_base;
+               op->o_req_ndn = si->si_base;
+       }
 
        cb.sc_response = nonpresent_callback;
        cb.sc_private = si;
@@ -2645,6 +3244,8 @@ syncrepl_del_nonpresent(
                si->si_refreshDelete |= NP_DELETE_ONE;
 
                for (i=0; uuids[i].bv_val; i++) {
+                       SlapReply rs_search = {REP_RESULT};
+
                        op->ors_slimit = 1;
                        uf.f_av_value = uuids[i];
                        filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
@@ -2656,6 +3257,7 @@ syncrepl_del_nonpresent(
                Filter *cf, *of;
                Filter mmf[2];
                AttributeAssertion mmaa;
+               SlapReply rs_search = {REP_RESULT};
 
                memset( &an[0], 0, 2 * sizeof( AttributeName ) );
                an[0].an_name = slap_schema.si_ad_entryUUID->ad_cname;
@@ -2665,7 +3267,7 @@ syncrepl_del_nonpresent(
                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 );
+               op->ors_filter = filter_dup( si->si_filter, op->o_tmpmemctx );
                /* In multimaster, updates can continue to arrive while
                 * we're searching. Limit the search result to entries
                 * older than our newest cookie CSN.
@@ -2725,6 +3327,8 @@ syncrepl_del_nonpresent(
 
                np_list = LDAP_LIST_FIRST( &si->si_nonpresentlist );
                while ( np_list != NULL ) {
+                       SlapReply rs_delete = {REP_RESULT};
+
                        LDAP_LIST_REMOVE( np_list, npe_link );
                        np_prev = np_list;
                        np_list = LDAP_LIST_NEXT( np_list, npe_link );
@@ -2740,6 +3344,7 @@ syncrepl_del_nonpresent(
                                si->si_ridtxt, op->o_req_dn.bv_val, rc );
 
                        if ( rs_delete.sr_err == LDAP_NOT_ALLOWED_ON_NONLEAF ) {
+                               SlapReply rs_modify = {REP_RESULT};
                                Modifications mod1, mod2;
                                mod1.sml_op = LDAP_MOD_REPLACE;
                                mod1.sml_flags = 0;
@@ -2776,6 +3381,7 @@ syncrepl_del_nonpresent(
                                        op->o_req_dn = pdn;
                                        op->o_req_ndn = pdn;
                                        op->o_callback = &cb;
+                                       rs_reinit( &rs_delete, REP_RESULT );
                                        /* give it a root privil ? */
                                        op->o_bd->be_delete( op, &rs_delete );
                                } else {
@@ -2804,21 +3410,20 @@ syncrepl_del_nonpresent(
        return;
 }
 
-int
-syncrepl_add_glue(
+static int
+syncrepl_add_glue_ancestors(
        Operation* op,
        Entry *e )
 {
        Backend *be = op->o_bd;
        slap_callback cb = { NULL };
        Attribute       *a;
-       int     rc;
+       int     rc = LDAP_SUCCESS;
        int suffrdns;
        int i;
        struct berval dn = BER_BVNULL;
        struct berval ndn = BER_BVNULL;
        Entry   *glue;
-       SlapReply       rs_add = {REP_RESULT};
        struct berval   ptr, nptr;
        char            *comma;
 
@@ -2876,6 +3481,8 @@ syncrepl_add_glue(
        }
 
        while ( ndn.bv_val > e->e_nname.bv_val ) {
+               SlapReply       rs_add = {REP_RESULT};
+
                glue = entry_alloc();
                ber_dupbv( &glue->e_name, &dn );
                ber_dupbv( &glue->e_nname, &ndn );
@@ -2939,6 +3546,34 @@ syncrepl_add_glue(
                ndn.bv_len = e->e_nname.bv_len - (ndn.bv_val - e->e_nname.bv_val);
        }
 
+       return rc;
+}
+
+int
+syncrepl_add_glue(
+       Operation* op,
+       Entry *e )
+{
+       slap_callback cb = { NULL };
+       int     rc;
+       Backend *be = op->o_bd;
+       SlapReply       rs_add = {REP_RESULT};
+
+       rc = syncrepl_add_glue_ancestors( op, e );
+       switch ( rc ) {
+       case LDAP_SUCCESS:
+       case LDAP_ALREADY_EXISTS:
+               break;
+
+       default:
+               return rc;
+       }
+
+       op->o_tag = LDAP_REQ_ADD;
+       op->o_callback = &cb;
+       cb.sc_response = null_callback;
+       cb.sc_private = NULL;
+
        op->o_req_dn = e->e_name;
        op->o_req_ndn = e->e_nname;
        op->ora_e = e;
@@ -2957,7 +3592,6 @@ static int
 syncrepl_updateCookie(
        syncinfo_t *si,
        Operation *op,
-       struct berval *pdn,
        struct sync_cookie *syncCookie )
 {
        Backend *be = op->o_bd;
@@ -2967,7 +3601,7 @@ syncrepl_updateCookie(
        Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
 #endif
 
-       int rc, i, j;
+       int rc, i, j, changed = 0;
        ber_len_t len;
 
        slap_callback cb = { NULL };
@@ -3009,6 +3643,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];
+                               changed = 1;
                                if ( BER_BVISNULL( &first ) ) {
                                        first = syncCookie->ctxcsn[i];
 
@@ -3031,10 +3666,11 @@ syncrepl_updateCookie(
                        {
                                first = syncCookie->ctxcsn[i];
                        }
+                       changed = 1;
                }
        }
        /* Should never happen, ITS#5065 */
-       if ( BER_BVISNULL( &first )) {
+       if ( BER_BVISNULL( &first ) || !changed ) {
                ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
                op->o_tmpfree( mod.sml_values, op->o_tmpmemctx );
                return 0;
@@ -3048,8 +3684,8 @@ syncrepl_updateCookie(
        cb.sc_private = si;
 
        op->o_callback = &cb;
-       op->o_req_dn = op->o_bd->be_suffix[0];
-       op->o_req_ndn = op->o_bd->be_nsuffix[0];
+       op->o_req_dn = si->si_contextdn;
+       op->o_req_ndn = si->si_contextdn;
 
        /* update contextCSN */
        op->o_dont_replicate = 1;
@@ -3057,12 +3693,26 @@ syncrepl_updateCookie(
        op->orm_modlist = &mod;
        op->orm_no_opattrs = 1;
        rc = op->o_bd->be_modify( op, &rs_modify );
+
+       if ( rs_modify.sr_err == LDAP_NO_SUCH_OBJECT &&
+               SLAP_SYNC_SUBENTRY( op->o_bd )) {
+               const char      *text;
+               char txtbuf[SLAP_TEXT_BUFLEN];
+               size_t textlen = sizeof txtbuf;
+               Entry *e = slap_create_context_csn_entry( op->o_bd, NULL );
+               rs_reinit( &rs_modify, REP_RESULT );
+               rc = slap_mods2entry( &mod, &e, 0, 1, &text, txtbuf, textlen);
+               op->ora_e = e;
+               rc = op->o_bd->be_add( op, &rs_modify );
+               if ( e == op->ora_e )
+                       be_entry_release_w( op, op->ora_e );
+       }
+
        op->orm_no_opattrs = 0;
        op->o_dont_replicate = 0;
 
        if ( rs_modify.sr_err == LDAP_SUCCESS ) {
                slap_sync_cookie_free( &si->si_syncCookie, 0 );
-               slap_dup_sync_cookie( &si->si_syncCookie, syncCookie );
                /* If we replaced any old values */
                for ( i=0; i<si->si_cookieState->cs_num; i++ ) {
                        if ( mod.sml_values[i].bv_val != si->si_cookieState->cs_vals[i].bv_val )
@@ -3078,6 +3728,13 @@ syncrepl_updateCookie(
                                si->si_cookieState->cs_vals, si->si_cookieState->cs_num, NULL );
                }
 
+               /* Don't just dup the provider's cookie, recreate it */
+               si->si_syncCookie.numcsns = si->si_cookieState->cs_num;
+               ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn, si->si_cookieState->cs_vals, NULL );
+               si->si_syncCookie.sids = ch_malloc( si->si_cookieState->cs_num * sizeof(int) );
+               for ( i=0; i<si->si_cookieState->cs_num; i++ )
+                       si->si_syncCookie.sids[i] = si->si_cookieState->cs_sids[i];
+
                si->si_cookieState->cs_age++;
                si->si_cookieAge = si->si_cookieState->cs_age;
        } else {
@@ -3157,10 +3814,12 @@ attr_cmp( Operation *op, Attribute *old, Attribute *new,
                 * Also use replace op if attr has no equality matching rule.
                 * (ITS#5781)
                 */
-               if ( nn && no < o &&
+               if ( ( nn || ( no > 0 && no < o ) ) &&
                        ( old->a_desc == slap_schema.si_ad_objectClass ||
-                        !old->a_desc->ad_type->sat_equality ))
+                        !old->a_desc->ad_type->sat_equality ) )
+               {
                        no = o;
+               }
 
                i = j;
                /* all old values were deleted, just use the replace op */
@@ -3349,7 +4008,6 @@ dn_callback(
                         * in the provider are always propagated.
                         */
                        if ( dni->new_entry ) {
-                               Modifications **modtail, **ml;
                                Attribute *old, *new;
                                struct berval old_rdn, new_rdn;
                                struct berval old_p, new_p;
@@ -3686,6 +4344,9 @@ syncinfo_free( syncinfo_t *sie, int free_all )
                if ( sie->si_filterstr.bv_val ) {
                        ch_free( sie->si_filterstr.bv_val );
                }
+               if ( sie->si_filter ) {
+                       filter_free( sie->si_filter );
+               }
                if ( sie->si_logfilterstr.bv_val ) {
                        ch_free( sie->si_logfilterstr.bv_val );
                }
@@ -3695,6 +4356,9 @@ syncinfo_free( syncinfo_t *sie, int free_all )
                if ( sie->si_logbase.bv_val ) {
                        ch_free( sie->si_logbase.bv_val );
                }
+               if ( sie->si_be && SLAP_SYNC_SUBENTRY( sie->si_be )) {
+                       ch_free( sie->si_contextdn.bv_val );
+               }
                if ( sie->si_attrs ) {
                        int i = 0;
                        while ( sie->si_attrs[i] != NULL ) {
@@ -3764,15 +4428,61 @@ syncinfo_free( syncinfo_t *sie, int free_all )
                                ch_free( sie->si_cookieState->cs_sids );
                                ber_bvarray_free( sie->si_cookieState->cs_vals );
                                ldap_pvt_thread_mutex_destroy( &sie->si_cookieState->cs_mutex );
+                               ch_free( sie->si_cookieState->cs_psids );
+                               ber_bvarray_free( sie->si_cookieState->cs_pvals );
+                               ldap_pvt_thread_mutex_destroy( &sie->si_cookieState->cs_pmutex );
                                ch_free( sie->si_cookieState );
                        }
                }
+#ifdef ENABLE_REWRITE
+               if ( sie->si_rewrite )
+                       rewrite_info_delete( &sie->si_rewrite );
+               if ( sie->si_suffixm.bv_val )
+                       ch_free( sie->si_suffixm.bv_val );
+#endif
                ch_free( sie );
                sie = si_next;
        } while ( free_all && si_next );
 }
 
+#ifdef ENABLE_REWRITE
+static int
+config_suffixm( ConfigArgs *c, syncinfo_t *si )
+{
+       char *argvEngine[] = { "rewriteEngine", "on", NULL };
+       char *argvContext[] = { "rewriteContext", SUFFIXM_CTX, NULL };
+       char *argvRule[] = { "rewriteRule", NULL, NULL, ":", NULL };
+       char *vnc, *rnc;
+       int rc;
+
+       if ( si->si_rewrite )
+               rewrite_info_delete( &si->si_rewrite );
+       si->si_rewrite = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
+
+       rc = rewrite_parse( si->si_rewrite, c->fname, c->lineno, 2, argvEngine );
+       if ( rc != LDAP_SUCCESS )
+               return rc;
+
+       rc = rewrite_parse( si->si_rewrite, c->fname, c->lineno, 2, argvContext );
+       if ( rc != LDAP_SUCCESS )
+               return rc;
 
+       vnc = ch_malloc( si->si_base.bv_len + 6 );
+       strcpy( vnc, "(.*)" );
+       lutil_strcopy( lutil_strcopy( vnc+4, si->si_base.bv_val ), "$" );
+       argvRule[1] = vnc;
+
+       rnc = ch_malloc( si->si_suffixm.bv_len + 3 );
+       strcpy( rnc, "%1" );
+       strcpy( rnc+2, si->si_suffixm.bv_val );
+       argvRule[2] = rnc;
+
+       rc = rewrite_parse( si->si_rewrite, c->fname, c->lineno, 4, argvRule );
+       ch_free( vnc );
+       ch_free( rnc );
+       return rc;
+}
+#endif
 
 /* NOTE: used & documented in slapd.conf(5) */
 #define IDSTR                  "rid"
@@ -3791,6 +4501,8 @@ syncinfo_free( syncinfo_t *sie, int free_all )
 #define SYNCDATASTR            "syncdata"
 #define LOGBASESTR             "logbase"
 #define LOGFILTERSTR   "logfilter"
+#define SUFFIXMSTR             "suffixmassage"
+#define        STRICT_REFRESH  "strictrefresh"
 
 /* FIXME: undocumented */
 #define EXATTRSSTR             "exattrs"
@@ -3817,6 +4529,7 @@ enum {
        GOT_EXATTRS             = 0x00010000U,
        GOT_MANAGEDSAIT         = 0x00020000U,
        GOT_BINDCONF            = 0x00040000U,
+       GOT_SUFFIXM             = 0x00080000U,
 
 /* check */
        GOT_REQUIRED            = (GOT_RID|GOT_PROVIDER|GOT_SEARCHBASE)
@@ -3904,6 +4617,12 @@ parse_syncrepl_retry(
                        }
                }
        }
+       if ( j < 1 || si->si_retrynum_init[j-1] != RETRYNUM_FOREVER ) {
+               Debug( LDAP_DEBUG_CONFIG,
+                       "%s: syncrepl will eventually stop retrying; the \"retry\" parameter should end with a '+'.\n",
+                       c->log, 0, 0 );
+       }
+
        si->si_retrynum_init[j] = RETRYNUM_TAIL;
        si->si_retrynum[j] = RETRYNUM_TAIL;
        si->si_retryinterval[j] = 0;
@@ -3941,10 +4660,10 @@ parse_syncrepl_line(
                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
                                return -1;
                        }
-                       if ( tmp > SLAP_SYNC_SID_MAX || tmp < 0 ) {
+                       if ( tmp > SLAP_SYNC_RID_MAX || tmp < 0 ) {
                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                        "Error: parse_syncrepl_line: "
-                                       "syncrepl id %d is out of range [0..4095]", tmp );
+                                       "syncrepl id %d is out of range [0..%d]", tmp, SLAP_SYNC_RID_MAX );
                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
                                return -1;
                        }
@@ -3956,6 +4675,10 @@ parse_syncrepl_line(
                {
                        val = c->argv[ i ] + STRLENOF( PROVIDERSTR "=" );
                        ber_str2bv( val, 0, 1, &si->si_bindconf.sb_uri );
+#ifdef HAVE_TLS
+                       if ( ldap_is_ldaps_url( val ))
+                               si->si_bindconf.sb_tls_do_init = 1;
+#endif
                        si->si_got |= GOT_PROVIDER;
                } else if ( !strncasecmp( c->argv[ i ], SCHEMASTR "=",
                                        STRLENOF( SCHEMASTR "=" ) ) )
@@ -4004,16 +4727,38 @@ parse_syncrepl_line(
                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
                                return -1;
                        }
-                       if ( !be_issubordinate( c->be, &si->si_base ) ) {
-                               ch_free( si->si_base.bv_val );
-                               BER_BVZERO( &si->si_base );
+                       si->si_got |= GOT_SEARCHBASE;
+#ifdef ENABLE_REWRITE
+               } else if ( !strncasecmp( c->argv[ i ], SUFFIXMSTR "=",
+                                       STRLENOF( SUFFIXMSTR "=" ) ) )
+               {
+                       struct berval   bv;
+                       int             rc;
+
+                       val = c->argv[ i ] + STRLENOF( SUFFIXMSTR "=" );
+                       if ( si->si_suffixm.bv_val ) {
+                               ch_free( si->si_suffixm.bv_val );
+                       }
+                       ber_str2bv( val, 0, 0, &bv );
+                       rc = dnNormalize( 0, NULL, NULL, &bv, &si->si_suffixm, NULL );
+                       if ( rc != LDAP_SUCCESS ) {
                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                       "Base DN \"%s\" is not within the database naming context",
+                                       "Invalid massage DN \"%s\": %d (%s)",
+                                       val, rc, ldap_err2string( rc ) );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+                               return -1;
+                       }
+                       if ( !be_issubordinate( c->be, &si->si_suffixm )) {
+                               ch_free( si->si_suffixm.bv_val );
+                               BER_BVZERO( &si->si_suffixm );
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                       "Massage DN \"%s\" is not within the database naming context",
                                        val );
                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
                                return -1;
                        }
-                       si->si_got |= GOT_SEARCHBASE;
+                       si->si_got |= GOT_SUFFIXM;
+#endif
                } else if ( !strncasecmp( c->argv[ i ], LOGBASESTR "=",
                                        STRLENOF( LOGBASESTR "=" ) ) )
                {
@@ -4256,6 +5001,10 @@ parse_syncrepl_line(
                        val = c->argv[ i ] + STRLENOF( SYNCDATASTR "=" );
                        si->si_syncdata = verb_to_mask( val, datamodes );
                        si->si_got |= GOT_SYNCDATA;
+               } else if ( !strncasecmp( c->argv[ i ], STRICT_REFRESH,
+                                       STRLENOF( STRICT_REFRESH ) ) )
+               {
+                       si->si_strict_refresh = 1;
                } else if ( bindconf_parse( c->argv[i], &si->si_bindconf ) ) {
                        snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                "Error: parse_syncrepl_line: "
@@ -4276,6 +5025,29 @@ parse_syncrepl_line(
                return -1;
        }
 
+       if ( !be_issubordinate( c->be, &si->si_base ) && !( si->si_got & GOT_SUFFIXM )) {
+               ch_free( si->si_base.bv_val );
+               BER_BVZERO( &si->si_base );
+               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                       "Base DN \"%s\" is not within the database naming context",
+                       val );
+               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+               return -1;
+       }
+
+#ifdef ENABLE_REWRITE
+       if ( si->si_got & GOT_SUFFIXM ) {
+               if (config_suffixm( c, si )) {
+                       ch_free( si->si_suffixm.bv_val );
+                       BER_BVZERO( &si->si_suffixm );
+                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                               "Error configuring rewrite engine" );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+                       return -1;
+               }
+       }
+#endif
+
        if ( !( si->si_got & GOT_RETRY ) ) {
                Debug( LDAP_DEBUG_ANY, "syncrepl %s " SEARCHBASESTR "=\"%s\": no retry defined, using default\n", 
                        si->si_ridtxt, c->be->be_suffix ? c->be->be_suffix[ 0 ].bv_val : "(null)", 0 );
@@ -4286,6 +5058,13 @@ parse_syncrepl_line(
                }
        }
 
+       si->si_filter = str2filter( si->si_filterstr.bv_val );
+       if ( si->si_filter == NULL ) {
+               Debug( LDAP_DEBUG_ANY, "syncrepl %s " SEARCHBASESTR "=\"%s\": unable to parse filter=\"%s\"\n", 
+                       si->si_ridtxt, c->be->be_suffix ? c->be->be_suffix[ 0 ].bv_val : "(null)", si->si_filterstr.bv_val );
+               return 1;
+       }
+
        return 0;
 }
 
@@ -4423,9 +5202,10 @@ add_syncrepl(
                return 1;
        } else {
                Debug( LDAP_DEBUG_CONFIG,
-                       "Config: ** successfully added syncrepl \"%s\"\n",
+                       "Config: ** successfully added syncrepl %s \"%s\"\n",
+                       si->si_ridtxt,
                        BER_BVISNULL( &si->si_bindconf.sb_uri ) ?
-                       "(null)" : si->si_bindconf.sb_uri.bv_val, 0, 0 );
+                       "(null)" : si->si_bindconf.sb_uri.bv_val, 0 );
                if ( c->be->be_syncinfo ) {
                        syncinfo_t *sip;
 
@@ -4437,6 +5217,7 @@ add_syncrepl(
                } else {
                        si->si_cookieState = ch_calloc( 1, sizeof( cookie_state ));
                        ldap_pvt_thread_mutex_init( &si->si_cookieState->cs_mutex );
+                       ldap_pvt_thread_mutex_init( &si->si_cookieState->cs_pmutex );
 
                        c->be->be_syncinfo = si;
                }
@@ -4468,7 +5249,7 @@ syncrepl_unparse( syncinfo_t *si, struct berval *bv )
        si->si_bindconf.sb_version = LDAP_VERSION3;
 
        ptr = buf;
-       assert( si->si_rid >= 0 && si->si_rid <= SLAP_SYNC_SID_MAX );
+       assert( si->si_rid >= 0 && si->si_rid <= SLAP_SYNC_RID_MAX );
        len = snprintf( ptr, WHATSLEFT, IDSTR "=%03d " PROVIDERSTR "=%s",
                si->si_rid, si->si_bindconf.sb_uri.bv_val );
        if ( len >= sizeof( buf ) ) return;
@@ -4493,6 +5274,14 @@ syncrepl_unparse( syncinfo_t *si, struct berval *bv )
                ptr = lutil_strcopy( ptr, si->si_base.bv_val );
                *ptr++ = '"';
        }
+#ifdef ENABLE_REWRITE
+       if ( !BER_BVISNULL( &si->si_suffixm ) ) {
+               if ( WHATSLEFT <= STRLENOF( " " SUFFIXMSTR "=\"" "\"" ) + si->si_suffixm.bv_len ) return;
+               ptr = lutil_strcopy( ptr, " " SUFFIXMSTR "=\"" );
+               ptr = lutil_strcopy( ptr, si->si_suffixm.bv_val );
+               *ptr++ = '"';
+       }
+#endif
        if ( !BER_BVISEMPTY( &si->si_logfilterstr ) ) {
                if ( WHATSLEFT <= STRLENOF( " " LOGFILTERSTR "=\"" "\"" ) + si->si_logfilterstr.bv_len ) return;
                ptr = lutil_strcopy( ptr, " " LOGFILTERSTR "=\"" );
@@ -4645,6 +5434,8 @@ syncrepl_config( ConfigArgs *c )
                                si = *sip;
                                if ( c->valx == -1 || i == c->valx ) {
                                        *sip = si->si_next;
+                                       si->si_ctype = -1;
+                                       si->si_next = NULL;
                                        /* 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.
@@ -4653,22 +5444,34 @@ syncrepl_config( ConfigArgs *c )
                                                if ( ldap_pvt_thread_mutex_trylock( &si->si_mutex )) {
                                                        isrunning = 1;
                                                } else {
+                                                       /* There is no active thread, but we must still
+                                                        * ensure that no thread is (or will be) queued
+                                                        * while we removes the task.
+                                                        */
+                                                       struct re_s *re = si->si_re;
+                                                       si->si_re = NULL;
+
                                                        if ( si->si_conn ) {
-                                                               /* If there's a persistent connection, it may
-                                                                * already have a thread queued. We know it's
-                                                                * not active, so it must be pending and we
-                                                                * can simply cancel it now.
-                                                                */
-                                                               ldap_pvt_thread_pool_retract( &connection_pool,
-                                                                       si->si_re->routine, si->si_re );
+                                                               connection_client_stop( si->si_conn );
+                                                               si->si_conn = NULL;
                                                        }
+
+                                                       ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+                                                       if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) ) {
+                                                               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_thread_mutex_unlock( &si->si_mutex );
                                                }
                                        }
-                                       if ( isrunning ) {
-                                               si->si_ctype = -1;
-                                               si->si_next = NULL;
-                                       } else {
+                                       if ( !isrunning ) {
                                                syncinfo_free( si, 0 );
                                        }
                                        if ( i == c->valx )