static int syncuuid_cmp( const void *, const void * );
static int avl_presentlist_insert( syncinfo_t* si, struct berval *syncUUID );
-static void syncrepl_del_nonpresent( Operation *, syncinfo_t *, BerVarray, struct berval * );
+static void syncrepl_del_nonpresent( Operation *, syncinfo_t *, BerVarray, struct sync_cookie *, int );
static int syncrepl_message_to_op(
syncinfo_t *, Operation *, LDAPMessage * );
static int syncrepl_message_to_entry(
op->o_tls_ssf = ldap_pvt_tls_get_strength( ssl );
}
#endif /* HAVE_TLS */
- ldap_get_option( si->si_ld, LDAP_OPT_X_SASL_SSF, &op->o_sasl_ssf );
+ {
+ 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;
+ }
op->o_ssf = ( op->o_sasl_ssf > op->o_tls_ssf )
? op->o_sasl_ssf : op->o_tls_ssf;
ldap_set_option( si->si_ld, LDAP_OPT_TIMELIMIT, &si->si_tlimit );
+ rc = LDAP_DEREF_NEVER; /* actually could allow DEREF_FINDING */
+ ldap_set_option( si->si_ld, LDAP_OPT_DEREF, &rc );
+
si->si_syncCookie.rid = si->si_rid;
- si->si_syncCookie.sid = SLAP_SINGLE_SHADOW( si->si_be ) ? -1 :
- slap_serverID;
+
+ /* whenever there are multiple data sources possible, advertise sid */
+ si->si_syncCookie.sid = ( SLAP_MULTIMASTER( si->si_be ) || si->si_be != si->si_wbe ) ?
+ slap_serverID : -1;
/* We've just started up, or the remote server hasn't sent us
* any meaningful state.
return -1;
}
- for (i=0; i<sc1->numcsns; i++) {
- for (j=0; i<sc2->numcsns; j++) {
+ for (j=0; j<sc2->numcsns; j++) {
+ for (i=0; i<sc1->numcsns; i++) {
if ( sc1->sids[i] != sc2->sids[j] )
continue;
value_match( &match, slap_schema.si_ad_entryCSN,
}
break;
}
+ if ( i == sc1->numcsns ) {
+ /* sc2 has a sid sc1 lacks */
+ *which = j;
+ return -1;
+ }
}
return match;
}
rc = err;
goto done;
}
+ if ( err ) {
+ Debug( LDAP_DEBUG_ANY,
+ "do_syncrep2: %s LDAP_RES_SEARCH_RESULT (%d) %s\n",
+ si->si_ridtxt, err, ldap_err2string( err ) );
+ }
if ( rctrls ) {
rctrlp = *rctrls;
ber_init2( ber, &rctrlp->ldctl_value, LBER_USE_DER );
syncCookie_req.numcsns == syncCookie.numcsns )
{
syncrepl_del_nonpresent( op, si, NULL,
- &syncCookie.ctxcsn[m] );
+ &syncCookie, m );
} else {
avl_free( si->si_presentlist, ch_free );
si->si_presentlist = NULL;
ber_scanf( ber, /*"{"*/ "}" );
if ( refreshDeletes ) {
syncrepl_del_nonpresent( op, si, syncUUIDs,
- &syncCookie.ctxcsn[m] );
+ &syncCookie, m );
ber_bvarray_free_x( syncUUIDs, op->o_tmpmemctx );
} else {
int i;
if ( si->si_refreshPresent == 1 &&
syncCookie_req.numcsns == syncCookie.numcsns ) {
syncrepl_del_nonpresent( op, si, NULL,
- &syncCookie.ctxcsn[m] );
+ &syncCookie, m );
}
if ( syncCookie.ctxcsn )
*
* Typically there is a single syncprov mastering the entire
* glued tree. In that case, our contextCSN updates should
- * go to the master DB.
+ * go to the master DB. But if there is no syncprov on the
+ * master DB, then nothing special is needed here.
*
* Alternatively, there may be individual syncprov overlays
* on each glued branch. In that case, each syncprov only
*/
if ( !si->si_wbe ) {
if ( SLAP_GLUE_SUBORDINATE( be ) && !overlay_is_inst( be, "syncprov" )) {
- si->si_wbe = select_backend( &be->be_nsuffix[0], 1 );
+ 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 );
+ else
+ si->si_wbe = be;
} else {
si->si_wbe = be;
}
Operation *op,
syncinfo_t *si,
BerVarray uuids,
- struct berval *cookiecsn )
+ struct sync_cookie *sc,
+ int m )
{
Backend* be = op->o_bd;
slap_callback cb = { NULL };
}
si->si_refreshDelete ^= NP_DELETE_ONE;
} else {
+ Filter *cf, *of;
+
memset( &an[0], 0, 2 * sizeof( AttributeName ) );
an[0].an_name = slap_schema.si_ad_entryUUID->ad_cname;
an[0].an_desc = slap_schema.si_ad_entryUUID;
op->ors_slimit = SLAP_NO_LIMIT;
op->ors_attrsonly = 0;
op->ors_filter = str2filter_x( op, si->si_filterstr.bv_val );
- op->ors_filterstr = si->si_filterstr;
+ /* In multimaster, updates can continue to arrive while
+ * we're searching. Limit the search result to entries
+ * older than all of our cookie CSNs.
+ */
+ if ( SLAP_MULTIMASTER( op->o_bd )) {
+ Filter *f;
+ int i;
+ cf = op->o_tmpalloc( (sc->numcsns+1) * sizeof(Filter) +
+ sc->numcsns * sizeof(AttributeAssertion), op->o_tmpmemctx );
+ f = cf;
+ f->f_choice = LDAP_FILTER_AND;
+ f->f_next = NULL;
+ f->f_and = f+1;
+ of = f->f_and;
+ for ( i=0; i<sc->numcsns; i++ ) {
+ f = of;
+ f->f_choice = LDAP_FILTER_LE;
+ f->f_ava = (AttributeAssertion *)(f+1);
+ f->f_av_desc = slap_schema.si_ad_entryCSN;
+ f->f_av_value = sc->ctxcsn[i];
+ f->f_next = (Filter *)(f->f_ava+1);
+ of = f->f_next;
+ }
+ f->f_next = op->ors_filter;
+ of = op->ors_filter;
+ op->ors_filter = cf;
+ filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
+ } else {
+ cf = NULL;
+ op->ors_filterstr = si->si_filterstr;
+ }
op->o_nocaching = 1;
if ( limits_check( op, &rs_search ) == 0 ) {
rc = be->be_search( op, &rs_search );
}
+ if ( SLAP_MULTIMASTER( op->o_bd )) {
+ op->o_tmpfree( cf, op->o_tmpmemctx );
+ op->ors_filter = of;
+ }
if ( op->ors_filter ) filter_free_x( op, op->ors_filter );
+
}
op->o_nocaching = 0;
if ( !LDAP_LIST_EMPTY( &si->si_nonpresentlist ) ) {
- if ( cookiecsn && !BER_BVISNULL( cookiecsn ) ) {
- csn = *cookiecsn;
+ if ( sc->ctxcsn && !BER_BVISNULL( &sc->ctxcsn[m] ) ) {
+ csn = sc->ctxcsn[m];
} else {
csn = si->si_syncCookie.ctxcsn[0];
}
mod.sml_op = LDAP_MOD_REPLACE;
mod.sml_desc = slap_schema.si_ad_contextCSN;
mod.sml_type = mod.sml_desc->ad_cname;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
mod.sml_nvalues = NULL;
mod.sml_next = NULL;
}
}
+ /* Don't delete/add an objectClass, always use the replace op.
+ * Modify would fail if provider has replaced entry with a new,
+ * and the new explicitly includes a superior of a class that was
+ * only included implicitly in the old entry. Ref ITS#5517.
+ */
+ if ( nn && no < o && old->a_desc == slap_schema.si_ad_objectClass )
+ no = o;
+
i = j;
/* all old values were deleted, just use the replace op */
if ( no == o ) {