From 2770e8df679491d2adcfcc41b34d4d2f74311cbf Mon Sep 17 00:00:00 2001
From: Howard Chu <hyc@openldap.org>
Date: Fri, 21 Mar 2008 02:06:42 +0000
Subject: [PATCH] Sync with HEAD: ITS#5401, #5405, #5407, #5413, #5418, #5426,
 #5430, #5432, #5433, #5434

---
 servers/slapd/overlays/syncprov.c | 111 ++++++++++------
 servers/slapd/syncrepl.c          | 211 ++++++++++++++++--------------
 2 files changed, 185 insertions(+), 137 deletions(-)

diff --git a/servers/slapd/overlays/syncprov.c b/servers/slapd/overlays/syncprov.c
index ad8b9bb329..74d841b8bf 100644
--- a/servers/slapd/overlays/syncprov.c
+++ b/servers/slapd/overlays/syncprov.c
@@ -696,7 +696,10 @@ again:
 		break;
 	}
 
-	fop.o_bd->bd_info = on->on_info->oi_orig;
+	if ( on->on_next )
+		fop.o_bd->bd_info = (BackendInfo *)on->on_next;
+	else
+		fop.o_bd->bd_info = on->on_info->oi_orig;
 	fop.o_bd->be_search( &fop, &frs );
 	fop.o_bd->bd_info = (BackendInfo *)on;
 
@@ -837,8 +840,10 @@ syncprov_sendresp( Operation *op, opcookie *opc, syncops *so,
 
 /* Play back queued responses */
 static int
-syncprov_qplay( Operation *op, slap_overinst *on, syncops *so )
+syncprov_qplay( Operation *op, struct re_s *rtask )
 {
+	syncops *so = rtask->arg;
+	slap_overinst *on = LDAP_SLIST_FIRST(&so->s_op->o_extra)->oe_key;
 	syncres *sr;
 	Entry *e;
 	opcookie opc;
@@ -853,10 +858,10 @@ syncprov_qplay( Operation *op, slap_overinst *on, syncops *so )
 			so->s_res = sr->s_next;
 		if ( !so->s_res )
 			so->s_restail = NULL;
-		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
-
+		/* Exit loop with mutex held */
 		if ( !sr || so->s_op->o_abandon )
 			break;
+		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
 
 		opc.sdn = sr->s_dn;
 		opc.sndn = sr->s_ndn;
@@ -883,9 +888,24 @@ syncprov_qplay( Operation *op, slap_overinst *on, syncops *so )
 
 		ch_free( sr );
 
-		if ( rc )
+		if ( rc ) {
+			/* Exit loop with mutex held */
+			ldap_pvt_thread_mutex_lock( &so->s_mutex );
 			break;
+		}
+	}
+
+	/* wait until we get explicitly scheduled again */
+	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+	ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+	if ( rc == 0 ) {
+		ldap_pvt_runqueue_resched( &slapd_rq, rtask, 1 );
+	} else {
+		/* bail out on any error */
+		ldap_pvt_runqueue_remove( &slapd_rq, rtask );
 	}
+	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
 	return rc;
 }
 
@@ -895,7 +915,6 @@ syncprov_qtask( void *ctx, void *arg )
 {
 	struct re_s *rtask = arg;
 	syncops *so = rtask->arg;
-	slap_overinst *on = so->s_op->o_private;
 	OperationBuffer opbuf;
 	Operation *op;
 	BackendDB be;
@@ -917,25 +936,14 @@ syncprov_qtask( void *ctx, void *arg )
 	be = *so->s_op->o_bd;
 	be.be_flags |= SLAP_DBFLAG_OVERLAY;
 	op->o_bd = &be;
-	op->o_private = NULL;
+	LDAP_SLIST_FIRST(&op->o_extra) = NULL;
 	op->o_callback = NULL;
 
-	rc = syncprov_qplay( op, on, so );
+	rc = syncprov_qplay( op, rtask );
 
 	/* decrement use count... */
 	syncprov_free_syncop( so );
 
-	/* wait until we get explicitly scheduled again */
-	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
-	ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
-	if ( rc == 0 ) {
-		ldap_pvt_runqueue_resched( &slapd_rq, rtask, 1 );
-	} else {
-		/* bail out on any error */
-		ldap_pvt_runqueue_remove( &slapd_rq, rtask );
-	}
-	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
-
 #if 0	/* FIXME: connection_close isn't exported from slapd.
 		 * should it be?
 		 */
@@ -1209,7 +1217,7 @@ syncprov_matchops( Operation *op, opcookie *opc, int saveit )
 		}
 
 		/* check if current o_req_dn is in scope and matches filter */
-		if ( fc.fscope && test_filter( op, e, ss->s_op->ors_filter ) ==
+		if ( fc.fscope && test_filter( ss->s_op, e, ss->s_op->ors_filter ) ==
 			LDAP_COMPARE_TRUE ) {
 			if ( saveit ) {
 				sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx );
@@ -1301,6 +1309,7 @@ syncprov_checkpoint( Operation *op, SlapReply *rs, slap_overinst *on )
 	Operation opm;
 	SlapReply rsm = { 0 };
 	slap_callback cb = {0};
+	BackendDB be;
 
 	mod.sml_numvals = si->si_numcsns;
 	mod.sml_values = si->si_ctxcsn;
@@ -1316,8 +1325,12 @@ syncprov_checkpoint( Operation *op, SlapReply *rs, slap_overinst *on )
 	opm.o_callback = &cb;
 	opm.orm_modlist = &mod;
 	opm.orm_no_opattrs = 1;
-	opm.o_req_dn = op->o_bd->be_suffix[0];
-	opm.o_req_ndn = op->o_bd->be_nsuffix[0];
+	if ( SLAP_GLUE_SUBORDINATE( op->o_bd )) {
+		be = *on->on_info->oi_origdb;
+		opm.o_bd = &be;
+	}
+	opm.o_req_dn = opm.o_bd->be_suffix[0];
+	opm.o_req_ndn = opm.o_bd->be_nsuffix[0];
 	opm.o_bd->bd_info = on->on_info->oi_orig;
 	opm.o_managedsait = SLAP_CONTROL_NONCRITICAL;
 	opm.o_no_schema_check = 1;
@@ -1325,7 +1338,6 @@ syncprov_checkpoint( Operation *op, SlapReply *rs, slap_overinst *on )
 	if ( mod.sml_next != NULL ) {
 		slap_mods_free( mod.sml_next, 1 );
 	}
-	opm.orm_no_opattrs = 0;
 }
 
 static void
@@ -1519,7 +1531,10 @@ syncprov_playlog( Operation *op, SlapReply *rs, sessionlog *sl,
 		fop.ors_filter = &af;
 
 		cb.sc_response = playlog_cb;
-		fop.o_bd->bd_info = on->on_info->oi_orig;
+		if ( on->on_next )
+			fop.o_bd->bd_info = (BackendInfo *)on->on_next;
+		else
+			fop.o_bd->bd_info = on->on_info->oi_orig;
 
 		for ( i=ndel; i<num; i++ ) {
 			if ( uuids[i].bv_len == 0 ) continue;
@@ -1540,13 +1555,16 @@ syncprov_playlog( Operation *op, SlapReply *rs, sessionlog *sl,
 	if ( ndel ) {
 		struct berval cookie;
 
-		slap_compose_sync_cookie( op, &cookie, delcsn, srs->sr_state.rid,
-			srs->sr_state.sid );
+		if ( delcsn[0].bv_len ) {
+			slap_compose_sync_cookie( op, &cookie, delcsn, srs->sr_state.rid,
+				srs->sr_state.sid );
+		}
 
 		Debug( LDAP_DEBUG_SYNC, "syncprov_playlog: cookie=%s\n", cookie.bv_val, 0, 0 );
 
 		uuids[ndel].bv_val = NULL;
-		syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, &cookie, 0, uuids, 1 );
+		syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET,
+			delcsn[0].bv_len ? &cookie : NULL, 0, uuids, 1 );
 		op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
 	}
 	op->o_tmpfree( uuids, op->o_tmpmemctx );
@@ -1775,7 +1793,13 @@ syncprov_op_mod( Operation *op, SlapReply *rs )
 			/* wait for this op to get to head of list */
 			while ( mt->mt_mods != mi ) {
 				ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
-				ldap_pvt_thread_yield();
+				/* FIXME: if dynamic config can delete overlays or
+				 * databases we'll have to check for cleanup here.
+				 * Currently it's not an issue because there are
+				 * no dynamic config deletes...
+				 */
+				if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
+					ldap_pvt_thread_yield();
 				ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
 
 				/* clean up if the caller is giving up */
@@ -1843,6 +1867,7 @@ syncprov_search_cleanup( Operation *op, SlapReply *rs )
 typedef struct SyncOperationBuffer {
 	Operation		sob_op;
 	Opheader		sob_hdr;
+	OpExtra			sob_oe;
 	AttributeName	sob_extra;	/* not always present */
 	/* Further data allocated here */
 } SyncOperationBuffer;
@@ -1871,6 +1896,7 @@ syncprov_detach_op( Operation *op, syncops *so, slap_overinst *on )
 	sopbuf2 = ch_calloc( 1, size );
 	op2 = &sopbuf2->sob_op;
 	op2->o_hdr = &sopbuf2->sob_hdr;
+	LDAP_SLIST_FIRST(&op2->o_extra) = &sopbuf2->sob_oe;
 
 	/* Copy the fields we care about explicitly, leave the rest alone */
 	*op2->o_hdr = *op->o_hdr;
@@ -1878,7 +1904,8 @@ syncprov_detach_op( Operation *op, syncops *so, slap_overinst *on )
 	op2->o_time = op->o_time;
 	op2->o_bd = on->on_info->oi_origdb;
 	op2->o_request = op->o_request;
-	op2->o_private = on;
+	LDAP_SLIST_FIRST(&op2->o_extra)->oe_key = on;
+	LDAP_SLIST_NEXT(LDAP_SLIST_FIRST(&op2->o_extra), oe_next) = NULL;
 
 	ptr = (char *) sopbuf2 + offsetof( SyncOperationBuffer, sob_extra );
 	if ( i ) {
@@ -1929,12 +1956,10 @@ syncprov_detach_op( Operation *op, syncops *so, slap_overinst *on )
 	op2->o_do_not_cache = 1;
 
 	/* Add op2 to conn so abandon will find us */
-	ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
 	op->o_conn->c_n_ops_executing++;
 	op->o_conn->c_n_ops_completed--;
 	LDAP_STAILQ_INSERT_TAIL( &op->o_conn->c_ops, op2, o_next );
 	so->s_flags |= PS_IS_DETACHED;
-	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
 
 	/* Prevent anyone else from trying to send a result for this op */
 	op->o_abandon = 1;
@@ -2044,15 +2069,27 @@ syncprov_search_response( Operation *op, SlapReply *rs )
 
 			/* Detach this Op from frontend control */
 			ldap_pvt_thread_mutex_lock( &ss->ss_so->s_mutex );
+			ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
 
-			/* Turn off the refreshing flag */
-			ss->ss_so->s_flags ^= PS_IS_REFRESHING;
+			/* But not if this connection was closed along the way */
+			if ( op->o_abandon ) {
+				ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+				ldap_pvt_thread_mutex_unlock( &ss->ss_so->s_mutex );
+				syncprov_free_syncop( ss->ss_so );
+				return SLAPD_ABANDON;
 
-			syncprov_detach_op( op, ss->ss_so, on );
+			} else {
+				/* Turn off the refreshing flag */
+				ss->ss_so->s_flags ^= PS_IS_REFRESHING;
+
+				syncprov_detach_op( op, ss->ss_so, on );
 
-			/* If there are queued responses, fire them off */
-			if ( ss->ss_so->s_res )
-				syncprov_qstart( ss->ss_so );
+				ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+				/* If there are queued responses, fire them off */
+				if ( ss->ss_so->s_res )
+					syncprov_qstart( ss->ss_so );
+			}
 			ldap_pvt_thread_mutex_unlock( &ss->ss_so->s_mutex );
 
 			return LDAP_SUCCESS;
diff --git a/servers/slapd/syncrepl.c b/servers/slapd/syncrepl.c
index f862d8e3e8..e8ddee979a 100644
--- a/servers/slapd/syncrepl.c
+++ b/servers/slapd/syncrepl.c
@@ -468,16 +468,16 @@ do_syncrep1(
 
 	ldap_set_option( si->si_ld, LDAP_OPT_TIMELIMIT, &si->si_tlimit );
 
+	si->si_syncCookie.rid = si->si_rid;
+	si->si_syncCookie.sid = SLAP_SINGLE_SHADOW( si->si_be ) ? -1 :
+		slap_serverID;
+
 	/* We've just started up, or the remote server hasn't sent us
 	 * any meaningful state.
 	 */
 	if ( BER_BVISNULL( &si->si_syncCookie.octet_str ) ) {
 		int i;
 
-		si->si_syncCookie.rid = si->si_rid;
-		si->si_syncCookie.sid = SLAP_SINGLE_SHADOW( si->si_be ) ? -1 :
-			slap_serverID;
-
 		LDAP_STAILQ_FOREACH( sc, &slap_sync_cookie, sc_next ) {
 			if ( si->si_rid == sc->rid ) {
 				cmdline_cookie_found = 1;
@@ -552,26 +552,27 @@ do_syncrep1(
 		 */
 		a.a_desc = slap_schema.si_ad_contextCSN;
 		e.e_attrs = &a;
-		e.e_name = si->si_wbe->be_suffix[0];
-		e.e_nname = si->si_wbe->be_nsuffix[0];
-		rs.sr_entry = &e;
-		rs.sr_flags = REP_ENTRY_MODIFIABLE;
+		e.e_name = op->o_bd->be_suffix[0];
+		e.e_nname = op->o_bd->be_nsuffix[0];
 		at[0].an_name = a.a_desc->ad_cname;
 		at[0].an_desc = a.a_desc;
 		BER_BVZERO( &at[1].an_name );
+		rs.sr_entry = &e;
+		rs.sr_flags = REP_ENTRY_MODIFIABLE;
+		rs.sr_attrs = at;
 		op->o_req_dn = e.e_name;
 		op->o_req_ndn = e.e_nname;
 
 		ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
 		rc = backend_operational( op, &rs );
-		if ( rc == LDAP_SUCCESS && a.a_vals ) {
+		if ( rc == LDAP_SUCCESS && a.a_nvals ) {
 			int num = a.a_numvals;
 			/* check for differences */
 			if ( num != si->si_cookieState->cs_num ) {
 				changed = 1;
 			} else {
 				for ( i=0; i<num; i++ ) {
-					if ( ber_bvcmp( &a.a_vals[i],
+					if ( ber_bvcmp( &a.a_nvals[i],
 						&si->si_cookieState->cs_vals[i] )) {
 						changed =1;
 						break;
@@ -582,50 +583,57 @@ do_syncrep1(
 				ber_bvarray_free( si->si_cookieState->cs_vals );
 				ch_free( si->si_cookieState->cs_sids );
 				si->si_cookieState->cs_num = num;
-				si->si_cookieState->cs_vals = a.a_vals;
-				si->si_cookieState->cs_sids = slap_parse_csn_sids( a.a_vals,
+				si->si_cookieState->cs_vals = a.a_nvals;
+				si->si_cookieState->cs_sids = slap_parse_csn_sids( a.a_nvals,
 					num, NULL );
 				si->si_cookieState->cs_age++;
 			} else {
-				ber_bvarray_free( a.a_vals );
+				ber_bvarray_free( a.a_nvals );
 			}
-			changed = 0;
+			ber_bvarray_free( a.a_vals );
 		}
 		/* See if the cookieState has changed due to anything outside
 		 * this particular consumer. That includes other consumers in
 		 * the same context, or local changes detected above.
 		 */
-		if ( si->si_cookieState->cs_num > 1 && si->si_cookieAge !=
+		if ( si->si_cookieState->cs_num > 0 && si->si_cookieAge !=
 			si->si_cookieState->cs_age ) {
-
-			for (i=0; !BER_BVISNULL( &si->si_syncCookie.ctxcsn[i] ); i++) {
-				/* bogus, just dup everything */
-				if ( si->si_syncCookie.sids[i] == -1 ) {
-					ber_bvarray_free( si->si_syncCookie.ctxcsn );
-					ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn,
-						si->si_cookieState->cs_vals, NULL );
-					changed = 1;
-					break;
-				}
-				for (j=0; j<si->si_cookieState->cs_num; j++) {
-					if ( si->si_syncCookie.sids[i] !=
-						si->si_cookieState->cs_sids[j] )
-						continue;
-					if ( bvmatch( &si->si_syncCookie.ctxcsn[i],
-						&si->si_cookieState->cs_vals[j] ))
+			if ( !si->si_syncCookie.numcsns ) {
+				ber_bvarray_free( si->si_syncCookie.ctxcsn );
+				ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn,
+					si->si_cookieState->cs_vals, NULL );
+				changed = 1;
+			} else {
+				for (i=0; !BER_BVISNULL( &si->si_syncCookie.ctxcsn[i] ); i++) {
+					/* bogus, just dup everything */
+					if ( si->si_syncCookie.sids[i] == -1 ) {
+						ber_bvarray_free( si->si_syncCookie.ctxcsn );
+						ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn,
+							si->si_cookieState->cs_vals, NULL );
+						changed = 1;
 						break;
-					ber_bvreplace( &si->si_syncCookie.ctxcsn[i],
-						&si->si_cookieState->cs_vals[j] );
-					changed = 1;
-					break;
+					}
+					for (j=0; j<si->si_cookieState->cs_num; j++) {
+						if ( si->si_syncCookie.sids[i] !=
+							si->si_cookieState->cs_sids[j] )
+							continue;
+						if ( bvmatch( &si->si_syncCookie.ctxcsn[i],
+							&si->si_cookieState->cs_vals[j] ))
+							break;
+						ber_bvreplace( &si->si_syncCookie.ctxcsn[i],
+							&si->si_cookieState->cs_vals[j] );
+						changed = 1;
+						break;
+					}
 				}
 			}
-			if ( changed ) {
-				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,
-					SLAP_SINGLE_SHADOW( si->si_be ) ? -1 : slap_serverID );
-			}
+		}
+		if ( changed ) {
+			si->si_cookieAge = si->si_cookieState->cs_age;
+			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 );
 		}
 		ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
 	}
@@ -664,14 +672,14 @@ compare_csns( struct sync_cookie *sc1, struct sync_cookie *sc2, int *which )
 		return -1;
 	}
 
-	for (i=0; !BER_BVISNULL( &sc1->ctxcsn[i] ); i++) {
-		for (j=0; !BER_BVISNULL( &sc2->ctxcsn[j] ); j++) {
+	for (i=0; i<sc1->numcsns; i++) {
+		for (j=0; i<sc2->numcsns; j++) {
 			if ( sc1->sids[i] != sc2->sids[j] )
 				continue;
 			value_match( &match, slap_schema.si_ad_entryCSN,
 				slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
 				SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
-				&sc1->ctxcsn[i], &sc2->ctxcsn[i], &text );
+				&sc1->ctxcsn[i], &sc2->ctxcsn[j], &text );
 			if ( match < 0 ) {
 				*which = j;
 				return match;
@@ -1193,25 +1201,40 @@ do_syncrepl(
 	op->o_tmpmfuncs = &ch_mfuncs;
 
 	op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
-	op->o_bd = be = si->si_be;
-	op->o_dn = op->o_bd->be_rootdn;
-	op->o_ndn = op->o_bd->be_rootndn;
-	if ( !si->si_schemachecking )
-		op->o_no_schema_check = 1;
-
-	/* If we're glued, send writes through the glue parent */
+	be = si->si_be;
+
+	/* Coordinate contextCSN updates with any syncprov overlays
+	 * in use. This may be complicated by the use of the glue
+	 * overlay.
+	 *
+	 * Typically there is a single syncprov mastering the entire
+	 * glued tree. In that case, our contextCSN updates should
+	 * go to the master DB.
+	 *
+	 * Alternatively, there may be individual syncprov overlays
+	 * on each glued branch. In that case, each syncprov only
+	 * knows about changes within its own branch. And so our
+	 * contextCSN updates should only go to the local DB.
+	 */
 	if ( !si->si_wbe ) {
-		if ( SLAP_GLUE_SUBORDINATE( be )) {
+		if ( SLAP_GLUE_SUBORDINATE( be ) && !overlay_is_inst( be, "syncprov" )) {
 			si->si_wbe = select_backend( &be->be_nsuffix[0], 1 );
 		} else {
 			si->si_wbe = be;
 		}
 	}
+	if ( !si->si_schemachecking )
+		op->o_no_schema_check = 1;
 
 	/* Establish session, do search */
 	if ( !si->si_ld ) {
 		si->si_refreshDelete = 0;
 		si->si_refreshPresent = 0;
+
+		/* use main DB when retrieving contextCSN */
+		op->o_bd = si->si_wbe;
+		op->o_dn = op->o_bd->be_rootdn;
+		op->o_ndn = op->o_bd->be_rootndn;
 		rc = do_syncrep1( op, si );
 	}
 
@@ -1220,6 +1243,10 @@ reload:
 	if ( rc == LDAP_SUCCESS ) {
 		ldap_get_option( si->si_ld, LDAP_OPT_DESC, &s );
 
+		/* use current DB */
+		op->o_bd = be;
+		op->o_dn = op->o_bd->be_rootdn;
+		op->o_ndn = op->o_bd->be_rootndn;
 		rc = do_syncrep2( op, si );
 		if ( rc == LDAP_SYNC_REFRESH_REQUIRED )	{
 			rc = ldap_sync_search( si, op->o_tmpmemctx );
@@ -2613,7 +2640,7 @@ syncrepl_updateCookie(
 	struct sync_cookie *syncCookie )
 {
 	Backend *be = op->o_bd;
-	Modifications mod[2];
+	Modifications mod;
 	struct berval first = BER_BVNULL;
 
 	int rc, i, j, len;
@@ -2621,24 +2648,22 @@ syncrepl_updateCookie(
 	slap_callback cb = { NULL };
 	SlapReply	rs_modify = {REP_RESULT};
 
-	mod[0].sml_op = LDAP_MOD_DELETE;
-	mod[0].sml_desc = slap_schema.si_ad_contextCSN;
-	mod[0].sml_type = mod[0].sml_desc->ad_cname;
-	mod[0].sml_values = NULL;
-	mod[0].sml_nvalues = NULL;
-	mod[0].sml_numvals = 0;
-	mod[0].sml_next = &mod[1];
-
-	mod[1].sml_op = LDAP_MOD_ADD;
-	mod[1].sml_desc = slap_schema.si_ad_contextCSN;
-	mod[1].sml_type = mod[0].sml_desc->ad_cname;
-	mod[1].sml_values = NULL;
-	mod[1].sml_nvalues = NULL;
-	mod[1].sml_numvals = 0;
-	mod[1].sml_next = NULL;
+	mod.sml_op = LDAP_MOD_REPLACE;
+	mod.sml_desc = slap_schema.si_ad_contextCSN;
+	mod.sml_type = mod.sml_desc->ad_cname;
+	mod.sml_nvalues = NULL;
+	mod.sml_next = NULL;
 
 	ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
 
+	/* clone the cookieState CSNs so we can Replace the whole thing */
+	mod.sml_numvals = si->si_cookieState->cs_num;
+	mod.sml_values = op->o_tmpalloc(( mod.sml_numvals+1 )*sizeof(struct berval), op->o_tmpmemctx );
+	for ( i=0; i<mod.sml_numvals; i++ )
+		mod.sml_values[i] = si->si_cookieState->cs_vals[i];
+	BER_BVZERO( &mod.sml_values[i] );
+
+	/* find any CSNs in the syncCookie that are newer than the cookieState */
 	for ( i=0; i<syncCookie->numcsns; i++ ) {
 		for ( j=0; j<si->si_cookieState->cs_num; j++ ) {
 			if ( syncCookie->sids[i] != si->si_cookieState->cs_sids[j] )
@@ -2648,12 +2673,7 @@ syncrepl_updateCookie(
 				len = si->si_cookieState->cs_vals[j].bv_len;
 			if ( memcmp( syncCookie->ctxcsn[i].bv_val,
 				si->si_cookieState->cs_vals[j].bv_val, len ) > 0 ) {
-				ber_bvarray_add_x( &mod[0].sml_values,
-					&si->si_cookieState->cs_vals[j], op->o_tmpmemctx );
-				mod[0].sml_numvals++;
-				ber_bvarray_add_x( &mod[1].sml_values,
-					&syncCookie->ctxcsn[i], op->o_tmpmemctx );
-				mod[1].sml_numvals++;
+				mod.sml_values[j] = syncCookie->ctxcsn[i];
 				if ( BER_BVISNULL( &first ))
 					first = syncCookie->ctxcsn[i];
 			}
@@ -2661,9 +2681,10 @@ syncrepl_updateCookie(
 		}
 		/* there was no match for this SID, it's a new CSN */
 		if ( j == si->si_cookieState->cs_num ) {
-			ber_bvarray_add_x( &mod[1].sml_values,
-				&syncCookie->ctxcsn[i], op->o_tmpmemctx );
-			mod[1].sml_numvals++;
+			mod.sml_values = op->o_tmprealloc( mod.sml_values,
+				( mod.sml_numvals+2 )*sizeof(struct berval), op->o_tmpmemctx );
+			mod.sml_values[mod.sml_numvals++] = syncCookie->ctxcsn[i];
+			BER_BVZERO( &mod.sml_values[mod.sml_numvals] );
 			if ( BER_BVISNULL( &first ))
 				first = syncCookie->ctxcsn[i];
 		}
@@ -2671,6 +2692,7 @@ syncrepl_updateCookie(
 	/* Should never happen, ITS#5065 */
 	if ( BER_BVISNULL( &first )) {
 		ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+		op->o_tmpfree( mod.sml_values, op->o_tmpmemctx );
 		return 0;
 	}
 	op->o_bd = si->si_wbe;
@@ -2688,11 +2710,7 @@ syncrepl_updateCookie(
 	/* update contextCSN */
 	op->o_msgid = SLAP_SYNC_UPDATE_MSGID;
 
-	if ( mod[0].sml_values )
-		op->orm_modlist = mod;
-	else
-		op->orm_modlist = &mod[1];
-
+	op->orm_modlist = &mod;
 	op->orm_no_opattrs = 1;
 	rc = op->o_bd->be_modify( op, &rs_modify );
 	op->orm_no_opattrs = 0;
@@ -2702,21 +2720,15 @@ syncrepl_updateCookie(
 		slap_sync_cookie_free( &si->si_syncCookie, 0 );
 		slap_dup_sync_cookie( &si->si_syncCookie, syncCookie );
 		/* If we replaced any old values */
-		if ( mod[0].sml_values ) {
-			for ( i=0; !BER_BVISNULL( &mod[0].sml_values[i] ); i++ ) {
-				for ( j=0; j<si->si_cookieState->cs_num; j++ ) {
-					if ( mod[0].sml_values[i].bv_val !=
-						si->si_cookieState->cs_vals[j].bv_val )
-						continue;
-					ber_bvreplace( &si->si_cookieState->cs_vals[j],
-						&mod[1].sml_values[i] );
-					break;
-				}
-			}
-		} else {
-			/* Else we just added */
-			si->si_cookieState->cs_num += syncCookie->numcsns;
-			value_add( &si->si_cookieState->cs_vals, syncCookie->ctxcsn );
+		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 )
+					ber_bvreplace( &si->si_cookieState->cs_vals[i],
+						&mod.sml_values[i] );
+		}
+		/* Handle any added values */
+		if ( i < mod.sml_numvals ) {
+			si->si_cookieState->cs_num = mod.sml_numvals;
+			value_add( &si->si_cookieState->cs_vals, &mod.sml_values[i] );
 			free( si->si_cookieState->cs_sids );
 			si->si_cookieState->cs_sids = slap_parse_csn_sids(
 				si->si_cookieState->cs_vals, si->si_cookieState->cs_num, NULL );
@@ -2734,9 +2746,8 @@ syncrepl_updateCookie(
 	op->o_bd = be;
 	op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx );
 	BER_BVZERO( &op->o_csn );
-	if ( mod[1].sml_next ) slap_mods_free( mod[1].sml_next, 1 );
-	op->o_tmpfree( mod[1].sml_values, op->o_tmpmemctx );
-	op->o_tmpfree( mod[0].sml_values, op->o_tmpmemctx );
+	if ( mod.sml_next ) slap_mods_free( mod.sml_next, 1 );
+	op->o_tmpfree( mod.sml_values, op->o_tmpmemctx );
 
 	return rc;
 }
-- 
GitLab