]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/syncrepl.c
use macros for fake connid
[openldap] / servers / slapd / syncrepl.c
index 627e43a3d2e5458d9f46fea7121e4e10ce2937de..a9b3d7150bf5231a4ea035c4845da4cadfe1395c 100644 (file)
@@ -44,8 +44,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 */
@@ -70,6 +76,7 @@ typedef struct syncinfo_s {
        struct berval           si_logbase;
        struct berval           si_filterstr;
        struct berval           si_logfilterstr;
+       struct berval           si_contextdn;
        int                     si_scope;
        int                     si_attrsonly;
        char                    *si_anfile;
@@ -119,7 +126,7 @@ 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 * );
@@ -458,8 +465,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 +585,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;
@@ -627,7 +635,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,6 +671,11 @@ 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 );
+               slap_compose_sync_cookie( NULL, &si->si_syncCookie.octet_str,
+                       si->si_syncCookie.ctxcsn, si->si_syncCookie.rid,
+                       si->si_syncCookie.sid );
                /* Look for contextCSN from syncprov overlay. */
                check_syncprov( op, si );
        }
@@ -753,10 +766,9 @@ do_syncrep2(
                        err = LDAP_SUCCESS;
        ber_len_t       len;
 
-       struct berval   *psub;
        Modifications   *modlist = NULL;
 
-       int                             match, m;
+       int                             match, m, punlock = 0;
 
        struct timeval *tout_p = NULL;
        struct timeval tout = { 0, 0 };
@@ -775,8 +787,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 ) {
@@ -841,8 +851,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 );
@@ -853,16 +864,51 @@ 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 ) {
+                                                                       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;
+                                                                       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 ) {
+                                                                       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;
+                                                                       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;
                                                }
+                                               punlock = 1;
                                        }
                                        op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
                                }
@@ -873,7 +919,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:
@@ -893,9 +939,11 @@ 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 );
+                               ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_pmutex );
                        ldap_controls_free( rctrls );
                        if ( modlist ) {
                                slap_mods_free( modlist, 1 );
@@ -961,8 +1009,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,7 +1062,7 @@ 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 ) {
@@ -1071,8 +1120,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 +1157,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 );
@@ -1174,7 +1225,7 @@ do_syncrep2(
 
                                        if ( syncCookie.ctxcsn )
                                        {
-                                               rc = syncrepl_updateCookie( si, op, psub, &syncCookie);
+                                               rc = syncrepl_updateCookie( si, op, &syncCookie);
                                        }
                                } 
 
@@ -1302,6 +1353,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 +1377,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;
@@ -1620,6 +1679,13 @@ 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] ) ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "syncrepl_message_to_op: %s got empty dn",
+                       si->si_ridtxt, 0, 0 );
+               return LDAP_OTHER;
+       }
+
        while (( rc = ldap_get_attribute_ber( si->si_ld, msg, ber, &bv, &bvals ) )
                == LDAP_SUCCESS ) {
                if ( bv.bv_val == NULL )
@@ -1857,6 +1923,13 @@ syncrepl_message_to_entry(
                return rc;
        }
 
+       if ( BER_BVISEMPTY( &bdn ) && !BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] ) ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "syncrepl_message_to_entry: %s got empty dn",
+                       si->si_ridtxt, 0, 0 );
+               return LDAP_OTHER;
+       }
+
        if ( syncstate == LDAP_SYNC_PRESENT || syncstate == LDAP_SYNC_DELETE ) {
                /* NOTE: this could be done even before decoding the DN,
                 * although encoding errors wouldn't be detected */
@@ -2943,7 +3016,6 @@ static int
 syncrepl_updateCookie(
        syncinfo_t *si,
        Operation *op,
-       struct berval *pdn,
        struct sync_cookie *syncCookie )
 {
        Backend *be = op->o_bd;
@@ -2953,7 +3025,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 };
@@ -2995,6 +3067,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];
 
@@ -3017,10 +3090,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;
@@ -3034,8 +3108,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;
@@ -3043,6 +3117,20 @@ 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 );
+               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;
 
@@ -3088,6 +3176,10 @@ syncrepl_updateCookie(
        return rc;
 }
 
+/* Compare the attribute from the old entry to the one in the new
+ * entry. The Modifications from the new entry will either be left
+ * in place, or changed to an Add or Delete as needed.
+ */
 static void
 attr_cmp( Operation *op, Attribute *old, Attribute *new,
        Modifications ***mret, Modifications ***mcur )
@@ -3231,6 +3323,86 @@ attr_cmp( Operation *op, Attribute *old, Attribute *new,
        *mret = modtail;
 }
 
+/* Generate a set of modifications to change the old entry into the
+ * new one. On input ml is a list of modifications equivalent to
+ * the new entry. It will be massaged and the result will be stored
+ * in mods.
+ */
+void syncrepl_diff_entry( Operation *op, Attribute *old, Attribute *new,
+       Modifications **mods, Modifications **ml, int is_ctx)
+{
+       Modifications **modtail = mods;
+
+       /* We assume that attributes are saved in the same order
+        * in the remote and local databases. So if we walk through
+        * the attributeDescriptions one by one they should match in
+        * lock step. If not, look for an add or delete.
+        */
+       while ( old && new )
+       {
+               /* If we've seen this before, use its mod now */
+               if ( new->a_flags & SLAP_ATTR_IXADD ) {
+                       attr_cmp( op, NULL, new, &modtail, &ml );
+                       new = new->a_next;
+                       continue;
+               }
+               /* Skip contextCSN */
+               if ( is_ctx && old->a_desc ==
+                       slap_schema.si_ad_contextCSN ) {
+                       old = old->a_next;
+                       continue;
+               }
+
+               if ( old->a_desc != new->a_desc ) {
+                       Modifications *mod;
+                       Attribute *tmp;
+
+                       /* If it's just been re-added later,
+                        * remember that we've seen it.
+                        */
+                       tmp = attr_find( new, old->a_desc );
+                       if ( tmp ) {
+                               tmp->a_flags |= SLAP_ATTR_IXADD;
+                       } else {
+                               /* If it's a new attribute, pull it in.
+                                */
+                               tmp = attr_find( old, new->a_desc );
+                               if ( !tmp ) {
+                                       attr_cmp( op, NULL, new, &modtail, &ml );
+                                       new = new->a_next;
+                                       continue;
+                               }
+                               /* Delete old attr */
+                               mod = ch_malloc( sizeof( Modifications ) );
+                               mod->sml_op = LDAP_MOD_DELETE;
+                               mod->sml_flags = 0;
+                               mod->sml_desc = old->a_desc;
+                               mod->sml_type = mod->sml_desc->ad_cname;
+                               mod->sml_numvals = 0;
+                               mod->sml_values = NULL;
+                               mod->sml_nvalues = NULL;
+                               *modtail = mod;
+                               modtail = &mod->sml_next;
+                       }
+                       old = old->a_next;
+                       continue;
+               }
+               /* kludge - always update modifiersName so that it
+                * stays co-located with the other mod opattrs. But only
+                * if we know there are other valid mods.
+                */
+               if ( *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 );
+               new = new->a_next;
+               old = old->a_next;
+       }
+       *modtail = *ml;
+       *ml = NULL;
+}
+
 static int
 dn_callback(
        Operation*      op,
@@ -3351,78 +3523,9 @@ dn_callback(
                                         */
                                }
 
-                               modtail = &dni->mods;
-                               ml = dni->modlist;
-
-                               /* We assume that attributes are saved in the same order
-                                * in the remote and local databases. So if we walk through
-                                * the attributeDescriptions one by one they should match in
-                                * lock step. If not, look for an add or delete.
-                                */
-                               for ( old = rs->sr_entry->e_attrs, new = dni->new_entry->e_attrs;
-                                               old && new; )
-                               {
-                                       /* If we've seen this before, use its mod now */
-                                       if ( new->a_flags & SLAP_ATTR_IXADD ) {
-                                               attr_cmp( op, NULL, new, &modtail, &ml );
-                                               new = new->a_next;
-                                               continue;
-                                       }
-                                       /* Skip contextCSN */
-                                       if ( is_ctx && old->a_desc ==
-                                               slap_schema.si_ad_contextCSN ) {
-                                               old = old->a_next;
-                                               continue;
-                                       }
-
-                                       if ( old->a_desc != new->a_desc ) {
-                                               Modifications *mod;
-                                               Attribute *tmp;
-
-                                               /* If it's just been re-added later,
-                                                * remember that we've seen it.
-                                                */
-                                               tmp = attr_find( new, old->a_desc );
-                                               if ( tmp ) {
-                                                       tmp->a_flags |= SLAP_ATTR_IXADD;
-                                               } else {
-                                                       /* If it's a new attribute, pull it in.
-                                                        */
-                                                       tmp = attr_find( old, new->a_desc );
-                                                       if ( !tmp ) {
-                                                               attr_cmp( op, NULL, new, &modtail, &ml );
-                                                               new = new->a_next;
-                                                               continue;
-                                                       }
-                                                       /* Delete old attr */
-                                                       mod = ch_malloc( sizeof( Modifications ) );
-                                                       mod->sml_op = LDAP_MOD_DELETE;
-                                                       mod->sml_flags = 0;
-                                                       mod->sml_desc = old->a_desc;
-                                                       mod->sml_type = mod->sml_desc->ad_cname;
-                                                       mod->sml_numvals = 0;
-                                                       mod->sml_values = NULL;
-                                                       mod->sml_nvalues = NULL;
-                                                       *modtail = mod;
-                                                       modtail = &mod->sml_next;
-                                               }
-                                               old = old->a_next;
-                                               continue;
-                                       }
-                                       /* kludge - always update modifiersName so that it
-                                        * stays co-located with the other mod opattrs. But only
-                                        * if we know there are other valid 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 );
-                                       new = new->a_next;
-                                       old = old->a_next;
-                               }
-                               *modtail = *ml;
-                               *ml = NULL;
+                               syncrepl_diff_entry( op, rs->sr_entry->e_attrs,
+                                       dni->new_entry->e_attrs, &dni->mods, dni->modlist,
+                                       is_ctx );
                        }
                }
        } else if ( rs->sr_type == REP_RESULT ) {
@@ -3666,6 +3769,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 ) {
@@ -3729,12 +3835,17 @@ syncinfo_free( syncinfo_t *sie, int free_all )
                        }
                        ch_free( npe );
                }
-               sie->si_cookieState->cs_ref--;
-               if ( !sie->si_cookieState->cs_ref ) {
-                       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 );
+               if ( sie->si_cookieState ) {
+                       sie->si_cookieState->cs_ref--;
+                       if ( !sie->si_cookieState->cs_ref ) {
+                               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 );
+                       }
                }
                ch_free( sie );
                sie = si_next;
@@ -3791,21 +3902,6 @@ enum {
        GOT_REQUIRED            = (GOT_RID|GOT_PROVIDER|GOT_SEARCHBASE)
 };
 
-static struct {
-       struct berval key;
-       int val;
-} scopes[] = {
-       { BER_BVC("base"), LDAP_SCOPE_BASE },
-       { BER_BVC("one"), LDAP_SCOPE_ONELEVEL },
-       { BER_BVC("onelevel"), LDAP_SCOPE_ONELEVEL },   /* OpenLDAP extension */
-       { BER_BVC("children"), LDAP_SCOPE_SUBORDINATE },
-       { BER_BVC("subord"), LDAP_SCOPE_SUBORDINATE },
-       { BER_BVC("subordinate"), LDAP_SCOPE_SUBORDINATE },
-       { BER_BVC("sub"), LDAP_SCOPE_SUBTREE },
-       { BER_BVC("subtree"), LDAP_SCOPE_SUBTREE },     /* OpenLDAP extension */
-       { BER_BVNULL, 0 }
-};
-
 static slap_verbmasks datamodes[] = {
        { BER_BVC("default"), SYNCDATA_DEFAULT },
        { BER_BVC("accesslog"), SYNCDATA_ACCESSLOG },
@@ -3888,6 +3984,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;
@@ -4023,19 +4125,15 @@ parse_syncrepl_line(
                {
                        int j;
                        val = c->argv[ i ] + STRLENOF( SCOPESTR "=" );
-                       for ( j = 0; !BER_BVISNULL(&scopes[j].key); j++ ) {
-                               if (!strcasecmp( val, scopes[j].key.bv_val ) ) {
-                                       si->si_scope = scopes[j].val;
-                                       break;
-                               }
-                       }
-                       if ( BER_BVISNULL(&scopes[j].key) ) {
+                       j = ldap_pvt_str2scope( val );
+                       if ( j < 0 ) {
                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                        "Error: parse_syncrepl_line: "
                                        "unknown scope \"%s\"", val);
                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
                                return -1;
                        }
+                       si->si_scope = j;
                        si->si_got |= GOT_SCOPE;
                } else if ( !strncasecmp( c->argv[ i ], ATTRSONLYSTR,
                                        STRLENOF( ATTRSONLYSTR ) ) )
@@ -4425,6 +4523,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;
                }
@@ -4439,7 +4538,7 @@ add_syncrepl(
 static void
 syncrepl_unparse( syncinfo_t *si, struct berval *bv )
 {
-       struct berval bc, uri;
+       struct berval bc, uri, bs;
        char buf[BUFSIZ*2], *ptr;
        ber_len_t len;
        int i;
@@ -4493,13 +4592,10 @@ syncrepl_unparse( syncinfo_t *si, struct berval *bv )
                ptr = lutil_strcopy( ptr, si->si_logbase.bv_val );
                *ptr++ = '"';
        }
-       for (i=0; !BER_BVISNULL(&scopes[i].key);i++) {
-               if ( si->si_scope == scopes[i].val ) {
-                       if ( WHATSLEFT <= STRLENOF( " " SCOPESTR "=" ) + scopes[i].key.bv_len ) return;
-                       ptr = lutil_strcopy( ptr, " " SCOPESTR "=" );
-                       ptr = lutil_strcopy( ptr, scopes[i].key.bv_val );
-                       break;
-               }
+       if ( ldap_pvt_scope2bv( si->si_scope, &bs ) == LDAP_SUCCESS ) {
+               if ( WHATSLEFT <= STRLENOF( " " SCOPESTR "=" ) + bs.bv_len ) return;
+               ptr = lutil_strcopy( ptr, " " SCOPESTR "=" );
+               ptr = lutil_strcopy( ptr, bs.bv_val );
        }
        if ( si->si_attrsonly ) {
                if ( WHATSLEFT <= STRLENOF( " " ATTRSONLYSTR "=\"" "\"" ) ) return;