+ struct berval newdn;
+ int freefdn = 0;
+ BackendDB *b0 = op->o_bd, db;
+
+ fc.fdn = &op->o_req_ndn;
+ /* compute new DN */
+ if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
+ struct berval pdn;
+ if ( op->orr_nnewSup ) pdn = *op->orr_nnewSup;
+ else dnParent( fc.fdn, &pdn );
+ build_new_dn( &newdn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
+ fc.fdn = &newdn;
+ freefdn = 1;
+ }
+ if ( op->o_tag != LDAP_REQ_ADD ) {
+ if ( !SLAP_ISOVERLAY( op->o_bd )) {
+ db = *op->o_bd;
+ op->o_bd = &db;
+ }
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, fc.fdn, NULL, NULL, 0, &e );
+ /* If we're sending responses now, make a copy and unlock the DB */
+ if ( e && !saveit ) {
+ Entry *e2 = entry_dup( e );
+ be_entry_release_rw( op, e, 0 );
+ e = e2;
+ }
+ op->o_bd->bd_info = (BackendInfo *)on;
+ if ( rc ) {
+ op->o_bd = b0;
+ return;
+ }
+ } else {
+ e = op->ora_e;
+ }
+
+ if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
+ ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
+ ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
+ opc->sreference = is_entry_referral( e );
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a )
+ ber_dupbv_x( &opc->suuid, &a->a_nvals[0], op->o_tmpmemctx );
+ } else if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
+ op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
+ ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
+ ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
+ }
+
+ ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
+ for (ss = si->si_ops, sprev = (syncops *)&si->si_ops; ss;
+ sprev = ss, ss=snext)
+ {
+ syncmatches *sm;
+ int found = 0;
+
+ snext = ss->s_next;
+ /* validate base */
+ fc.fss = ss;
+ fc.fbase = 0;
+ fc.fscope = 0;
+
+ /* If the base of the search is missing, signal a refresh */
+ rc = syncprov_findbase( op, &fc );
+ if ( rc != LDAP_SUCCESS ) {
+ SlapReply rs = {REP_RESULT};
+ send_ldap_error( ss->s_op, &rs, LDAP_SYNC_REFRESH_REQUIRED,
+ "search base has changed" );
+ sprev->s_next = snext;
+ syncprov_drop_psearch( ss, 1 );
+ ss = sprev;
+ continue;
+ }
+
+
+ /* If we're sending results now, look for this op in old matches */
+ if ( !saveit ) {
+ syncmatches *old;
+
+ /* Did we modify the search base? */
+ if ( dn_match( &op->o_req_ndn, &ss->s_base )) {
+ ldap_pvt_thread_mutex_lock( &ss->s_mutex );
+ ss->s_flags |= PS_WROTE_BASE;
+ ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
+ }
+
+ for ( sm=opc->smatches, old=(syncmatches *)&opc->smatches; sm;
+ old=sm, sm=sm->sm_next ) {
+ if ( sm->sm_op == ss ) {
+ found = 1;
+ old->sm_next = sm->sm_next;
+ op->o_tmpfree( sm, op->o_tmpmemctx );
+ break;
+ }
+ }
+ }
+
+ /* check if current o_req_dn is in scope and matches filter */
+ if ( fc.fscope && test_filter( op, e, ss->s_op->ors_filter ) ==
+ LDAP_COMPARE_TRUE ) {
+ if ( saveit ) {
+ sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx );
+ sm->sm_next = opc->smatches;
+ sm->sm_op = ss;
+ ldap_pvt_thread_mutex_lock( &ss->s_mutex );
+ ++ss->s_inuse;
+ ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
+ opc->smatches = sm;
+ } else {
+ /* if found send UPDATE else send ADD */
+ syncprov_qresp( opc, ss,
+ found ? LDAP_SYNC_MODIFY : LDAP_SYNC_ADD );
+ }
+ } else if ( !saveit && found ) {
+ /* send DELETE */
+ syncprov_qresp( opc, ss, LDAP_SYNC_DELETE );
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+
+ if ( op->o_tag != LDAP_REQ_ADD && e ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ be_entry_release_rw( op, e, 0 );
+ op->o_bd->bd_info = (BackendInfo *)on;
+ }
+ if ( freefdn ) {
+ op->o_tmpfree( fc.fdn->bv_val, op->o_tmpmemctx );
+ }
+ op->o_bd = b0;
+}
+
+static int
+syncprov_op_cleanup( Operation *op, SlapReply *rs )
+{
+ slap_callback *cb = op->o_callback;
+ opcookie *opc = cb->sc_private;
+ slap_overinst *on = opc->son;
+ syncprov_info_t *si = on->on_bi.bi_private;
+ syncmatches *sm, *snext;
+ modtarget *mt, mtdummy;
+
+ for (sm = opc->smatches; sm; sm=snext) {
+ snext = sm->sm_next;
+ syncprov_free_syncop( sm->sm_op );
+ op->o_tmpfree( sm, op->o_tmpmemctx );
+ }
+
+ /* Remove op from lock table */
+ mtdummy.mt_op = op;
+ ldap_pvt_thread_mutex_lock( &si->si_mods_mutex );
+ mt = avl_find( si->si_mods, &mtdummy, sp_avl_cmp );
+ if ( mt ) {
+ modinst *mi = mt->mt_mods;
+
+ /* If there are more, promote the next one */
+ ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
+ if ( mi->mi_next ) {
+ mt->mt_mods = mi->mi_next;
+ mt->mt_op = mt->mt_mods->mi_op;
+ ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
+ } else {
+ avl_delete( &si->si_mods, mt, sp_avl_cmp );
+ ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
+ ldap_pvt_thread_mutex_destroy( &mt->mt_mutex );
+ ch_free( mt );
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
+ if ( !BER_BVISNULL( &opc->suuid ))
+ op->o_tmpfree( opc->suuid.bv_val, op->o_tmpmemctx );
+ if ( !BER_BVISNULL( &opc->sndn ))
+ op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
+ if ( !BER_BVISNULL( &opc->sdn ))
+ op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
+ op->o_callback = cb->sc_next;
+ op->o_tmpfree(cb, op->o_tmpmemctx);
+
+ return 0;
+}
+
+static void
+syncprov_checkpoint( Operation *op, SlapReply *rs, slap_overinst *on )
+{
+ syncprov_info_t *si = on->on_bi.bi_private;
+ Modifications mod;
+ Operation opm;
+ SlapReply rsm = { 0 };
+ struct berval bv[2];
+ slap_callback cb = {0};
+
+ /* If ctxcsn is empty, delete it */
+ if ( BER_BVISEMPTY( &si->si_ctxcsn )) {
+ mod.sml_values = NULL;
+ } else {
+ mod.sml_values = bv;
+ bv[1].bv_val = NULL;
+ bv[0] = si->si_ctxcsn;
+ }
+ mod.sml_nvalues = NULL;
+ mod.sml_desc = slap_schema.si_ad_contextCSN;
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_flags = 0;
+ mod.sml_next = NULL;
+
+ cb.sc_response = slap_null_cb;
+ opm = *op;
+ opm.o_tag = LDAP_REQ_MODIFY;
+ opm.o_callback = &cb;
+ opm.orm_modlist = &mod;
+ opm.o_req_dn = op->o_bd->be_suffix[0];
+ opm.o_req_ndn = op->o_bd->be_nsuffix[0];
+ opm.o_bd->bd_info = on->on_info->oi_orig;
+ 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.
+ */
+ for ( se=sl->sl_head; se; se=se->se_next ) {
+ if ( ber_bvcmp( &se->se_csn, &srs->sr_state.ctxcsn ) <= 0 ) continue;
+ if ( ber_bvcmp( &se->se_csn, ctxcsn ) > 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;