+ opm.o_managedsait = SLAP_CONTROL_NONCRITICAL;
+ SLAP_DBFLAGS( opm.o_bd ) |= SLAP_DBFLAG_NOLASTMOD;
+ opm.o_bd->be_modify( &opm, &rsm );
+ SLAP_DBFLAGS( opm.o_bd ) ^= SLAP_DBFLAG_NOLASTMOD;
+ if ( mod.sml_next != NULL ) {
+ slap_mods_free( mod.sml_next, 1 );
+ }
+}
+
+static void
+syncprov_add_slog( Operation *op )
+{
+ opcookie *opc = op->o_callback->sc_private;
+ slap_overinst *on = opc->son;
+ syncprov_info_t *si = on->on_bi.bi_private;
+ sessionlog *sl;
+ slog_entry *se;
+
+ sl = si->si_logs;
+ {
+ /* Allocate a record. UUIDs are not NUL-terminated. */
+ se = ch_malloc( sizeof( slog_entry ) + opc->suuid.bv_len +
+ op->o_csn.bv_len + 1 );
+ se->se_next = NULL;
+ se->se_tag = op->o_tag;
+
+ se->se_uuid.bv_val = (char *)(&se[1]);
+ AC_MEMCPY( se->se_uuid.bv_val, opc->suuid.bv_val, opc->suuid.bv_len );
+ se->se_uuid.bv_len = opc->suuid.bv_len;
+
+ se->se_csn.bv_val = se->se_uuid.bv_val + opc->suuid.bv_len;
+ AC_MEMCPY( se->se_csn.bv_val, op->o_csn.bv_val, op->o_csn.bv_len );
+ se->se_csn.bv_val[op->o_csn.bv_len] = '\0';
+ se->se_csn.bv_len = op->o_csn.bv_len;
+
+ ldap_pvt_thread_mutex_lock( &sl->sl_mutex );
+ if ( sl->sl_head ) {
+ sl->sl_tail->se_next = se;
+ } else {
+ sl->sl_head = se;
+ }
+ sl->sl_tail = se;
+ sl->sl_num++;
+ while ( sl->sl_num > sl->sl_size ) {
+ se = sl->sl_head;
+ sl->sl_head = se->se_next;
+ strcpy( sl->sl_mincsn.bv_val, se->se_csn.bv_val );
+ sl->sl_mincsn.bv_len = se->se_csn.bv_len;
+ ch_free( se );
+ sl->sl_num--;
+ }
+ ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
+ }
+}
+
+/* Just set a flag if we found the matching entry */
+static int
+playlog_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ op->o_callback->sc_private = (void *)1;
+ }
+ return rs->sr_err;
+}
+
+/* enter with sl->sl_mutex locked, release before returning */
+static void
+syncprov_playlog( Operation *op, SlapReply *rs, sessionlog *sl,
+ sync_control *srs, struct berval *ctxcsn )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ slog_entry *se;
+ int i, j, ndel, num, nmods, mmods;
+ char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
+ BerVarray uuids;
+ struct berval delcsn;
+
+ if ( !sl->sl_num ) {
+ ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
+ return;
+ }
+
+ num = sl->sl_num;
+ i = 0;
+ nmods = 0;
+
+ uuids = op->o_tmpalloc( (num+1) * sizeof( struct berval ) +
+ num * UUID_LEN, op->o_tmpmemctx );
+ uuids[0].bv_val = (char *)(uuids + num + 1);
+
+ delcsn.bv_len = 0;
+ delcsn.bv_val = cbuf;
+
+ /* Make a copy of the relevant UUIDs. Put the Deletes up front
+ * and everything else at the end. Do this first so we can
+ * unlock the list mutex.
+ */
+ Debug( LDAP_DEBUG_SYNC, "srs csn %s\n", srs->sr_state.ctxcsn.bv_val, 0, 0 );
+ for ( se=sl->sl_head; se; se=se->se_next ) {
+ Debug( LDAP_DEBUG_SYNC, "log csn %s\n", se->se_csn.bv_val, 0, 0 );
+ ndel = ber_bvcmp( &se->se_csn, &srs->sr_state.ctxcsn );
+ if ( ndel <= 0 ) {
+ Debug( LDAP_DEBUG_SYNC, "cmp %d, too old\n", ndel, 0, 0 );
+ continue;
+ }
+ ndel = ber_bvcmp( &se->se_csn, ctxcsn );
+ if ( ndel > 0 ) {
+ Debug( LDAP_DEBUG_SYNC, "cmp %d, too new\n", ndel, 0, 0 );
+ break;
+ }
+ if ( se->se_tag == LDAP_REQ_DELETE ) {
+ j = i;
+ i++;
+ AC_MEMCPY( cbuf, se->se_csn.bv_val, se->se_csn.bv_len );
+ delcsn.bv_len = se->se_csn.bv_len;
+ delcsn.bv_val[delcsn.bv_len] = '\0';
+ } else {
+ nmods++;
+ j = num - nmods;
+ }
+ uuids[j].bv_val = uuids[0].bv_val + (j * UUID_LEN);
+ AC_MEMCPY(uuids[j].bv_val, se->se_uuid.bv_val, UUID_LEN);
+ uuids[j].bv_len = UUID_LEN;
+ }
+ ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
+
+ ndel = i;
+
+ /* Zero out unused slots */
+ for ( i=ndel; i < num - nmods; i++ )
+ uuids[i].bv_len = 0;
+
+ /* Mods must be validated to see if they belong in this delete set.
+ */
+
+ mmods = nmods;
+ /* Strip any duplicates */
+ for ( i=0; i<nmods; i++ ) {
+ for ( j=0; j<ndel; j++ ) {
+ if ( bvmatch( &uuids[j], &uuids[num - 1 - i] )) {
+ uuids[num - 1 - i].bv_len = 0;
+ mmods --;
+ break;
+ }
+ }
+ if ( uuids[num - 1 - i].bv_len == 0 ) continue;
+ for ( j=0; j<i; j++ ) {
+ if ( bvmatch( &uuids[num - 1 - j], &uuids[num - 1 - i] )) {
+ uuids[num - 1 - i].bv_len = 0;
+ mmods --;
+ break;
+ }
+ }
+ }
+
+ if ( mmods ) {
+ Operation fop;
+ SlapReply frs = { REP_RESULT };
+ int rc;
+ Filter mf, af;
+#ifdef LDAP_COMP_MATCH
+ AttributeAssertion eq = { NULL, BER_BVNULL, NULL };
+#else
+ AttributeAssertion eq;
+#endif
+ slap_callback cb = {0};
+
+ fop = *op;
+
+ fop.o_sync_mode = 0;
+ fop.o_callback = &cb;
+ fop.ors_limit = NULL;
+ fop.ors_tlimit = SLAP_NO_LIMIT;
+ fop.ors_attrs = slap_anlist_all_attributes;
+ fop.ors_attrsonly = 0;
+ fop.o_managedsait = SLAP_CONTROL_CRITICAL;
+
+ af.f_choice = LDAP_FILTER_AND;
+ af.f_next = NULL;
+ af.f_and = &mf;
+ mf.f_choice = LDAP_FILTER_EQUALITY;
+ mf.f_ava = &eq;
+ mf.f_av_desc = slap_schema.si_ad_entryUUID;
+ mf.f_next = fop.ors_filter;
+
+ fop.ors_filter = ⁡
+
+ cb.sc_response = playlog_cb;
+ fop.o_bd->bd_info = on->on_info->oi_orig;
+
+ for ( i=ndel; i<num; i++ ) {
+ if ( uuids[i].bv_len == 0 ) continue;
+
+ mf.f_av_value = uuids[i];
+ cb.sc_private = NULL;
+ fop.ors_slimit = 1;
+ frs.sr_nentries = 0;
+ rc = fop.o_bd->be_search( &fop, &frs );
+
+ /* If entry was not found, add to delete list */
+ if ( !cb.sc_private ) {
+ uuids[ndel++] = uuids[i];
+ }
+ }
+ fop.o_bd->bd_info = (BackendInfo *)on;
+ }
+ if ( ndel ) {
+ struct berval cookie;
+
+ slap_compose_sync_cookie( op, &cookie, &delcsn, srs->sr_state.rid );
+ uuids[ndel].bv_val = NULL;
+ syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, &cookie, 0, uuids, 1 );
+ op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
+ }
+ op->o_tmpfree( uuids, op->o_tmpmemctx );