xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/syncprov.c (revision d90047b5d07facf36e6c01dcc0bded8997ce9cc2)
1 /*	$NetBSD: syncprov.c,v 1.1.1.8 2019/08/08 13:31:39 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* syncprov.c - syncrepl provider */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2004-2019 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by Howard Chu for inclusion in
20  * OpenLDAP Software.
21  */
22 
23 #include <sys/cdefs.h>
24 __RCSID("$NetBSD: syncprov.c,v 1.1.1.8 2019/08/08 13:31:39 christos Exp $");
25 
26 #include "portable.h"
27 
28 #ifdef SLAPD_OVER_SYNCPROV
29 
30 #include <ac/string.h>
31 #include "lutil.h"
32 #include "slap.h"
33 #include "config.h"
34 #include "ldap_rq.h"
35 
36 #ifdef LDAP_DEVEL
37 #define	CHECK_CSN	1
38 #endif
39 
40 /* A modify request on a particular entry */
41 typedef struct modinst {
42 	struct modinst *mi_next;
43 	Operation *mi_op;
44 } modinst;
45 
46 typedef struct modtarget {
47 	struct modinst *mt_mods;
48 	struct modinst *mt_tail;
49 	struct berval mt_dn;
50 	ldap_pvt_thread_mutex_t mt_mutex;
51 } modtarget;
52 
53 /* All the info of a psearch result that's shared between
54  * multiple queues
55  */
56 typedef struct resinfo {
57 	struct syncres *ri_list;
58 	Entry *ri_e;
59 	struct berval ri_dn;
60 	struct berval ri_ndn;
61 	struct berval ri_uuid;
62 	struct berval ri_csn;
63 	struct berval ri_cookie;
64 	char ri_isref;
65 	ldap_pvt_thread_mutex_t ri_mutex;
66 } resinfo;
67 
68 /* A queued result of a persistent search */
69 typedef struct syncres {
70 	struct syncres *s_next;	/* list of results on this psearch queue */
71 	struct syncres *s_rilist;	/* list of psearches using this result */
72 	resinfo *s_info;
73 	char s_mode;
74 } syncres;
75 
76 /* Record of a persistent search */
77 typedef struct syncops {
78 	struct syncops *s_next;
79 	struct syncprov_info_t *s_si;
80 	struct berval	s_base;		/* ndn of search base */
81 	ID		s_eid;		/* entryID of search base */
82 	Operation	*s_op;		/* search op */
83 	int		s_rid;
84 	int		s_sid;
85 	struct berval s_filterstr;
86 	int		s_flags;	/* search status */
87 #define	PS_IS_REFRESHING	0x01
88 #define	PS_IS_DETACHED		0x02
89 #define	PS_WROTE_BASE		0x04
90 #define	PS_FIND_BASE		0x08
91 #define	PS_FIX_FILTER		0x10
92 #define	PS_TASK_QUEUED		0x20
93 
94 	int		s_inuse;	/* reference count */
95 	struct syncres *s_res;
96 	struct syncres *s_restail;
97 	ldap_pvt_thread_mutex_t	s_mutex;
98 } syncops;
99 
100 /* A received sync control */
101 typedef struct sync_control {
102 	struct sync_cookie sr_state;
103 	int sr_rhint;
104 } sync_control;
105 
106 #if 0 /* moved back to slap.h */
107 #define	o_sync	o_ctrlflag[slap_cids.sc_LDAPsync]
108 #endif
109 /* o_sync_mode uses data bits of o_sync */
110 #define	o_sync_mode	o_ctrlflag[slap_cids.sc_LDAPsync]
111 
112 #define SLAP_SYNC_NONE					(LDAP_SYNC_NONE<<SLAP_CONTROL_SHIFT)
113 #define SLAP_SYNC_REFRESH				(LDAP_SYNC_REFRESH_ONLY<<SLAP_CONTROL_SHIFT)
114 #define SLAP_SYNC_PERSIST				(LDAP_SYNC_RESERVED<<SLAP_CONTROL_SHIFT)
115 #define SLAP_SYNC_REFRESH_AND_PERSIST	(LDAP_SYNC_REFRESH_AND_PERSIST<<SLAP_CONTROL_SHIFT)
116 
117 /* Record of which searches matched at premodify step */
118 typedef struct syncmatches {
119 	struct syncmatches *sm_next;
120 	syncops *sm_op;
121 } syncmatches;
122 
123 /* Session log data */
124 typedef struct slog_entry {
125 	struct slog_entry *se_next;
126 	struct berval se_uuid;
127 	struct berval se_csn;
128 	int	se_sid;
129 	ber_tag_t	se_tag;
130 } slog_entry;
131 
132 typedef struct sessionlog {
133 	BerVarray	sl_mincsn;
134 	int		*sl_sids;
135 	int		sl_numcsns;
136 	int		sl_num;
137 	int		sl_size;
138 	int		sl_playing;
139 	slog_entry *sl_head;
140 	slog_entry *sl_tail;
141 	ldap_pvt_thread_mutex_t sl_mutex;
142 } sessionlog;
143 
144 /* The main state for this overlay */
145 typedef struct syncprov_info_t {
146 	syncops		*si_ops;
147 	struct berval	si_contextdn;
148 	BerVarray	si_ctxcsn;	/* ldapsync context */
149 	int		*si_sids;
150 	int		si_numcsns;
151 	int		si_chkops;	/* checkpointing info */
152 	int		si_chktime;
153 	int		si_numops;	/* number of ops since last checkpoint */
154 	int		si_nopres;	/* Skip present phase */
155 	int		si_usehint;	/* use reload hint */
156 	int		si_active;	/* True if there are active mods */
157 	int		si_dirty;	/* True if the context is dirty, i.e changes
158 						 * have been made without updating the csn. */
159 	time_t	si_chklast;	/* time of last checkpoint */
160 	Avlnode	*si_mods;	/* entries being modified */
161 	sessionlog	*si_logs;
162 	ldap_pvt_thread_rdwr_t	si_csn_rwlock;
163 	ldap_pvt_thread_mutex_t	si_ops_mutex;
164 	ldap_pvt_thread_mutex_t	si_mods_mutex;
165 	ldap_pvt_thread_mutex_t	si_resp_mutex;
166 } syncprov_info_t;
167 
168 typedef struct opcookie {
169 	slap_overinst *son;
170 	syncmatches *smatches;
171 	modtarget *smt;
172 	Entry *se;
173 	struct berval sdn;	/* DN of entry, for deletes */
174 	struct berval sndn;
175 	struct berval suuid;	/* UUID of entry */
176 	struct berval sctxcsn;
177 	short osid;	/* sid of op csn */
178 	short rsid;	/* sid of relay */
179 	short sreference;	/* Is the entry a reference? */
180 	syncres ssres;
181 } opcookie;
182 
183 typedef struct fbase_cookie {
184 	struct berval *fdn;	/* DN of a modified entry, for scope testing */
185 	syncops *fss;	/* persistent search we're testing against */
186 	int fbase;	/* if TRUE we found the search base and it's still valid */
187 	int fscope;	/* if TRUE then fdn is within the psearch scope */
188 } fbase_cookie;
189 
190 static AttributeName csn_anlist[3];
191 static AttributeName uuid_anlist[2];
192 
193 /* Build a LDAPsync intermediate state control */
194 static int
195 syncprov_state_ctrl(
196 	Operation	*op,
197 	SlapReply	*rs,
198 	Entry		*e,
199 	int		entry_sync_state,
200 	LDAPControl	**ctrls,
201 	int		num_ctrls,
202 	int		send_cookie,
203 	struct berval	*cookie )
204 {
205 	Attribute* a;
206 	int ret;
207 
208 	BerElementBuffer berbuf;
209 	BerElement *ber = (BerElement *)&berbuf;
210 	LDAPControl *cp;
211 	struct berval bv;
212 	struct berval	entryuuid_bv = BER_BVNULL;
213 
214 	ber_init2( ber, 0, LBER_USE_DER );
215 	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
216 
217 	for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
218 		AttributeDescription *desc = a->a_desc;
219 		if ( desc == slap_schema.si_ad_entryUUID ) {
220 			entryuuid_bv = a->a_nvals[0];
221 			break;
222 		}
223 	}
224 
225 	/* FIXME: what if entryuuid is NULL or empty ? */
226 
227 	if ( send_cookie && cookie ) {
228 		ber_printf( ber, "{eOON}",
229 			entry_sync_state, &entryuuid_bv, cookie );
230 	} else {
231 		ber_printf( ber, "{eON}",
232 			entry_sync_state, &entryuuid_bv );
233 	}
234 
235 	ret = ber_flatten2( ber, &bv, 0 );
236 	if ( ret == 0 ) {
237 		cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
238 		cp->ldctl_oid = LDAP_CONTROL_SYNC_STATE;
239 		cp->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
240 		cp->ldctl_value.bv_val = (char *)&cp[1];
241 		cp->ldctl_value.bv_len = bv.bv_len;
242 		AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
243 		ctrls[num_ctrls] = cp;
244 	}
245 	ber_free_buf( ber );
246 
247 	if ( ret < 0 ) {
248 		Debug( LDAP_DEBUG_TRACE,
249 			"slap_build_sync_ctrl: ber_flatten2 failed (%d)\n",
250 			ret, 0, 0 );
251 		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
252 		return LDAP_OTHER;
253 	}
254 
255 	return LDAP_SUCCESS;
256 }
257 
258 /* Build a LDAPsync final state control */
259 static int
260 syncprov_done_ctrl(
261 	Operation	*op,
262 	SlapReply	*rs,
263 	LDAPControl	**ctrls,
264 	int			num_ctrls,
265 	int			send_cookie,
266 	struct berval *cookie,
267 	int			refreshDeletes )
268 {
269 	int ret;
270 	BerElementBuffer berbuf;
271 	BerElement *ber = (BerElement *)&berbuf;
272 	LDAPControl *cp;
273 	struct berval bv;
274 
275 	ber_init2( ber, NULL, LBER_USE_DER );
276 	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
277 
278 	ber_printf( ber, "{" );
279 	if ( send_cookie && cookie ) {
280 		ber_printf( ber, "O", cookie );
281 	}
282 	if ( refreshDeletes == LDAP_SYNC_REFRESH_DELETES ) {
283 		ber_printf( ber, "b", refreshDeletes );
284 	}
285 	ber_printf( ber, "N}" );
286 
287 	ret = ber_flatten2( ber, &bv, 0 );
288 	if ( ret == 0 ) {
289 		cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
290 		cp->ldctl_oid = LDAP_CONTROL_SYNC_DONE;
291 		cp->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
292 		cp->ldctl_value.bv_val = (char *)&cp[1];
293 		cp->ldctl_value.bv_len = bv.bv_len;
294 		AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
295 		ctrls[num_ctrls] = cp;
296 	}
297 
298 	ber_free_buf( ber );
299 
300 	if ( ret < 0 ) {
301 		Debug( LDAP_DEBUG_TRACE,
302 			"syncprov_done_ctrl: ber_flatten2 failed (%d)\n",
303 			ret, 0, 0 );
304 		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
305 		return LDAP_OTHER;
306 	}
307 
308 	return LDAP_SUCCESS;
309 }
310 
311 static int
312 syncprov_sendinfo(
313 	Operation	*op,
314 	SlapReply	*rs,
315 	int			type,
316 	struct berval *cookie,
317 	int			refreshDone,
318 	BerVarray	syncUUIDs,
319 	int			refreshDeletes )
320 {
321 	BerElementBuffer berbuf;
322 	BerElement *ber = (BerElement *)&berbuf;
323 	struct berval rspdata;
324 
325 	int ret;
326 
327 	ber_init2( ber, NULL, LBER_USE_DER );
328 	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
329 
330 	if ( type ) {
331 		switch ( type ) {
332 		case LDAP_TAG_SYNC_NEW_COOKIE:
333 			ber_printf( ber, "tO", type, cookie );
334 			break;
335 		case LDAP_TAG_SYNC_REFRESH_DELETE:
336 		case LDAP_TAG_SYNC_REFRESH_PRESENT:
337 			ber_printf( ber, "t{", type );
338 			if ( cookie ) {
339 				ber_printf( ber, "O", cookie );
340 			}
341 			if ( refreshDone == 0 ) {
342 				ber_printf( ber, "b", refreshDone );
343 			}
344 			ber_printf( ber, "N}" );
345 			break;
346 		case LDAP_TAG_SYNC_ID_SET:
347 			ber_printf( ber, "t{", type );
348 			if ( cookie ) {
349 				ber_printf( ber, "O", cookie );
350 			}
351 			if ( refreshDeletes == 1 ) {
352 				ber_printf( ber, "b", refreshDeletes );
353 			}
354 			ber_printf( ber, "[W]", syncUUIDs );
355 			ber_printf( ber, "N}" );
356 			break;
357 		default:
358 			Debug( LDAP_DEBUG_TRACE,
359 				"syncprov_sendinfo: invalid syncinfo type (%d)\n",
360 				type, 0, 0 );
361 			return LDAP_OTHER;
362 		}
363 	}
364 
365 	ret = ber_flatten2( ber, &rspdata, 0 );
366 
367 	if ( ret < 0 ) {
368 		Debug( LDAP_DEBUG_TRACE,
369 			"syncprov_sendinfo: ber_flatten2 failed (%d)\n",
370 			ret, 0, 0 );
371 		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
372 		return LDAP_OTHER;
373 	}
374 
375 	rs->sr_rspoid = LDAP_SYNC_INFO;
376 	rs->sr_rspdata = &rspdata;
377 	send_ldap_intermediate( op, rs );
378 	rs->sr_rspdata = NULL;
379 	ber_free_buf( ber );
380 
381 	return LDAP_SUCCESS;
382 }
383 
384 /* Find a modtarget in an AVL tree */
385 static int
386 sp_avl_cmp( const void *c1, const void *c2 )
387 {
388 	const modtarget *m1, *m2;
389 	int rc;
390 
391 	m1 = c1; m2 = c2;
392 	rc = m1->mt_dn.bv_len - m2->mt_dn.bv_len;
393 
394 	if ( rc ) return rc;
395 	return ber_bvcmp( &m1->mt_dn, &m2->mt_dn );
396 }
397 
398 /* syncprov_findbase:
399  *   finds the true DN of the base of a search (with alias dereferencing) and
400  * checks to make sure the base entry doesn't get replaced with a different
401  * entry (e.g., swapping trees via ModDN, or retargeting an alias). If a
402  * change is detected, any persistent search on this base must be terminated /
403  * reloaded.
404  *   On the first call, we just save the DN and entryID. On subsequent calls
405  * we compare the DN and entryID with the saved values.
406  */
407 static int
408 findbase_cb( Operation *op, SlapReply *rs )
409 {
410 	slap_callback *sc = op->o_callback;
411 
412 	if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
413 		fbase_cookie *fc = sc->sc_private;
414 
415 		/* If no entryID, we're looking for the first time.
416 		 * Just store whatever we got.
417 		 */
418 		if ( fc->fss->s_eid == NOID ) {
419 			fc->fbase = 2;
420 			fc->fss->s_eid = rs->sr_entry->e_id;
421 			ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
422 
423 		} else if ( rs->sr_entry->e_id == fc->fss->s_eid &&
424 			dn_match( &rs->sr_entry->e_nname, &fc->fss->s_base )) {
425 
426 		/* OK, the DN is the same and the entryID is the same. */
427 			fc->fbase = 1;
428 		}
429 	}
430 	if ( rs->sr_err != LDAP_SUCCESS ) {
431 		Debug( LDAP_DEBUG_ANY, "findbase failed! %d\n", rs->sr_err,0,0 );
432 	}
433 	return LDAP_SUCCESS;
434 }
435 
436 static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL };
437 static struct berval generic_filterstr = BER_BVC("(objectclass=*)");
438 
439 static int
440 syncprov_findbase( Operation *op, fbase_cookie *fc )
441 {
442 	/* Use basic parameters from syncrepl search, but use
443 	 * current op's threadctx / tmpmemctx
444 	 */
445 	ldap_pvt_thread_mutex_lock( &fc->fss->s_mutex );
446 	if ( fc->fss->s_flags & PS_FIND_BASE ) {
447 		slap_callback cb = {0};
448 		Operation fop;
449 		SlapReply frs = { REP_RESULT };
450 		int rc;
451 
452 		fc->fss->s_flags ^= PS_FIND_BASE;
453 		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
454 
455 		fop = *fc->fss->s_op;
456 
457 		fop.o_bd = fop.o_bd->bd_self;
458 		fop.o_hdr = op->o_hdr;
459 		fop.o_time = op->o_time;
460 		fop.o_tincr = op->o_tincr;
461 		fop.o_extra = op->o_extra;
462 
463 		cb.sc_response = findbase_cb;
464 		cb.sc_private = fc;
465 
466 		fop.o_sync_mode = 0;	/* turn off sync mode */
467 		fop.o_managedsait = SLAP_CONTROL_CRITICAL;
468 		fop.o_callback = &cb;
469 		fop.o_tag = LDAP_REQ_SEARCH;
470 		fop.ors_scope = LDAP_SCOPE_BASE;
471 		fop.ors_limit = NULL;
472 		fop.ors_slimit = 1;
473 		fop.ors_tlimit = SLAP_NO_LIMIT;
474 		fop.ors_attrs = slap_anlist_no_attrs;
475 		fop.ors_attrsonly = 1;
476 		fop.ors_filter = &generic_filter;
477 		fop.ors_filterstr = generic_filterstr;
478 
479 		rc = fop.o_bd->be_search( &fop, &frs );
480 	} else {
481 		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
482 		fc->fbase = 1;
483 	}
484 
485 	/* After the first call, see if the fdn resides in the scope */
486 	if ( fc->fbase == 1 ) {
487 		switch ( fc->fss->s_op->ors_scope ) {
488 		case LDAP_SCOPE_BASE:
489 			fc->fscope = dn_match( fc->fdn, &fc->fss->s_base );
490 			break;
491 		case LDAP_SCOPE_ONELEVEL: {
492 			struct berval pdn;
493 			dnParent( fc->fdn, &pdn );
494 			fc->fscope = dn_match( &pdn, &fc->fss->s_base );
495 			break; }
496 		case LDAP_SCOPE_SUBTREE:
497 			fc->fscope = dnIsSuffix( fc->fdn, &fc->fss->s_base );
498 			break;
499 		case LDAP_SCOPE_SUBORDINATE:
500 			fc->fscope = dnIsSuffix( fc->fdn, &fc->fss->s_base ) &&
501 				!dn_match( fc->fdn, &fc->fss->s_base );
502 			break;
503 		}
504 	}
505 
506 	if ( fc->fbase )
507 		return LDAP_SUCCESS;
508 
509 	/* If entryID has changed, then the base of this search has
510 	 * changed. Invalidate the psearch.
511 	 */
512 	return LDAP_NO_SUCH_OBJECT;
513 }
514 
515 /* syncprov_findcsn:
516  *   This function has three different purposes, but they all use a search
517  * that filters on entryCSN so they're combined here.
518  * 1: at startup time, after a contextCSN has been read from the database,
519  * we search for all entries with CSN >= contextCSN in case the contextCSN
520  * was not checkpointed at the previous shutdown.
521  *
522  * 2: when the current contextCSN is known and we have a sync cookie, we search
523  * for one entry with CSN = the cookie CSN. If not found, try <= cookie CSN.
524  * If an entry is found, the cookie CSN is valid, otherwise it is stale.
525  *
526  * 3: during a refresh phase, we search for all entries with CSN <= the cookie
527  * CSN, and generate Present records for them. We always collect this result
528  * in SyncID sets, even if there's only one match.
529  */
530 typedef enum find_csn_t {
531 	FIND_MAXCSN	= 1,
532 	FIND_CSN	= 2,
533 	FIND_PRESENT	= 3
534 } find_csn_t;
535 
536 static int
537 findmax_cb( Operation *op, SlapReply *rs )
538 {
539 	if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
540 		struct berval *maxcsn = op->o_callback->sc_private;
541 		Attribute *a = attr_find( rs->sr_entry->e_attrs,
542 			slap_schema.si_ad_entryCSN );
543 
544 		if ( a && ber_bvcmp( &a->a_vals[0], maxcsn ) > 0 &&
545 			slap_parse_csn_sid( &a->a_vals[0] ) == slap_serverID ) {
546 			maxcsn->bv_len = a->a_vals[0].bv_len;
547 			strcpy( maxcsn->bv_val, a->a_vals[0].bv_val );
548 		}
549 	}
550 	return LDAP_SUCCESS;
551 }
552 
553 static int
554 findcsn_cb( Operation *op, SlapReply *rs )
555 {
556 	slap_callback *sc = op->o_callback;
557 
558 	/* We just want to know that at least one exists, so it's OK if
559 	 * we exceed the unchecked limit.
560 	 */
561 	if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ||
562 		(rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS )) {
563 		sc->sc_private = (void *)1;
564 	}
565 	return LDAP_SUCCESS;
566 }
567 
568 /* Build a list of entryUUIDs for sending in a SyncID set */
569 
570 #define UUID_LEN	16
571 
572 typedef struct fpres_cookie {
573 	int num;
574 	BerVarray uuids;
575 	char *last;
576 } fpres_cookie;
577 
578 static int
579 findpres_cb( Operation *op, SlapReply *rs )
580 {
581 	slap_callback *sc = op->o_callback;
582 	fpres_cookie *pc = sc->sc_private;
583 	Attribute *a;
584 	int ret = SLAP_CB_CONTINUE;
585 
586 	switch ( rs->sr_type ) {
587 	case REP_SEARCH:
588 		a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
589 		if ( a ) {
590 			pc->uuids[pc->num].bv_val = pc->last;
591 			AC_MEMCPY( pc->uuids[pc->num].bv_val, a->a_nvals[0].bv_val,
592 				pc->uuids[pc->num].bv_len );
593 			pc->num++;
594 			pc->last = pc->uuids[pc->num].bv_val;
595 			pc->uuids[pc->num].bv_val = NULL;
596 		}
597 		ret = LDAP_SUCCESS;
598 		if ( pc->num != SLAP_SYNCUUID_SET_SIZE )
599 			break;
600 		/* FALLTHRU */
601 	case REP_RESULT:
602 		ret = rs->sr_err;
603 		if ( pc->num ) {
604 			ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
605 				0, pc->uuids, 0 );
606 			pc->uuids[pc->num].bv_val = pc->last;
607 			pc->num = 0;
608 			pc->last = pc->uuids[0].bv_val;
609 		}
610 		break;
611 	default:
612 		break;
613 	}
614 	return ret;
615 }
616 
617 static int
618 syncprov_findcsn( Operation *op, find_csn_t mode, struct berval *csn )
619 {
620 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
621 	syncprov_info_t		*si = on->on_bi.bi_private;
622 
623 	slap_callback cb = {0};
624 	Operation fop;
625 	SlapReply frs = { REP_RESULT };
626 	char buf[LDAP_PVT_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
627 	char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
628 	struct berval maxcsn;
629 	Filter cf;
630 	AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
631 	fpres_cookie pcookie;
632 	sync_control *srs = NULL;
633 	struct slap_limits_set fc_limits;
634 	int i, rc = LDAP_SUCCESS, findcsn_retry = 1;
635 	int maxid;
636 
637 	if ( mode != FIND_MAXCSN ) {
638 		srs = op->o_controls[slap_cids.sc_LDAPsync];
639 	}
640 
641 	fop = *op;
642 	fop.o_sync_mode &= SLAP_CONTROL_MASK;	/* turn off sync_mode */
643 	/* We want pure entries, not referrals */
644 	fop.o_managedsait = SLAP_CONTROL_CRITICAL;
645 
646 	cf.f_ava = &eq;
647 	cf.f_av_desc = slap_schema.si_ad_entryCSN;
648 	BER_BVZERO( &cf.f_av_value );
649 	cf.f_next = NULL;
650 
651 	fop.o_callback = &cb;
652 	fop.ors_limit = NULL;
653 	fop.ors_tlimit = SLAP_NO_LIMIT;
654 	fop.ors_filter = &cf;
655 	fop.ors_filterstr.bv_val = buf;
656 
657 again:
658 	switch( mode ) {
659 	case FIND_MAXCSN:
660 		cf.f_choice = LDAP_FILTER_GE;
661 		/* If there are multiple CSNs, use the one with our serverID */
662 		for ( i=0; i<si->si_numcsns; i++) {
663 			if ( slap_serverID == si->si_sids[i] ) {
664 				maxid = i;
665 				break;
666 			}
667 		}
668 		if ( i == si->si_numcsns ) {
669 			/* No match: this is multimaster, and none of the content in the DB
670 			 * originated locally. Treat like no CSN.
671 			 */
672 			return LDAP_NO_SUCH_OBJECT;
673 		}
674 		cf.f_av_value = si->si_ctxcsn[maxid];
675 		fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
676 			"(entryCSN>=%s)", cf.f_av_value.bv_val );
677 		if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
678 			return LDAP_OTHER;
679 		}
680 		fop.ors_attrsonly = 0;
681 		fop.ors_attrs = csn_anlist;
682 		fop.ors_slimit = SLAP_NO_LIMIT;
683 		cb.sc_private = &maxcsn;
684 		cb.sc_response = findmax_cb;
685 		strcpy( cbuf, cf.f_av_value.bv_val );
686 		maxcsn.bv_val = cbuf;
687 		maxcsn.bv_len = cf.f_av_value.bv_len;
688 		break;
689 	case FIND_CSN:
690 		if ( BER_BVISEMPTY( &cf.f_av_value )) {
691 			cf.f_av_value = *csn;
692 		}
693 		fop.o_dn = op->o_bd->be_rootdn;
694 		fop.o_ndn = op->o_bd->be_rootndn;
695 		fop.o_req_dn = op->o_bd->be_suffix[0];
696 		fop.o_req_ndn = op->o_bd->be_nsuffix[0];
697 		/* Look for exact match the first time */
698 		if ( findcsn_retry ) {
699 			cf.f_choice = LDAP_FILTER_EQUALITY;
700 			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
701 				"(entryCSN=%s)", cf.f_av_value.bv_val );
702 		/* On retry, look for <= */
703 		} else {
704 			cf.f_choice = LDAP_FILTER_LE;
705 			fop.ors_limit = &fc_limits;
706 			memset( &fc_limits, 0, sizeof( fc_limits ));
707 			fc_limits.lms_s_unchecked = 1;
708 			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
709 				"(entryCSN<=%s)", cf.f_av_value.bv_val );
710 		}
711 		if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
712 			return LDAP_OTHER;
713 		}
714 		fop.ors_attrsonly = 1;
715 		fop.ors_attrs = slap_anlist_no_attrs;
716 		fop.ors_slimit = 1;
717 		cb.sc_private = NULL;
718 		cb.sc_response = findcsn_cb;
719 		break;
720 	case FIND_PRESENT:
721 		fop.ors_filter = op->ors_filter;
722 		fop.ors_filterstr = op->ors_filterstr;
723 		fop.ors_attrsonly = 0;
724 		fop.ors_attrs = uuid_anlist;
725 		fop.ors_slimit = SLAP_NO_LIMIT;
726 		cb.sc_private = &pcookie;
727 		cb.sc_response = findpres_cb;
728 		pcookie.num = 0;
729 
730 		/* preallocate storage for a full set */
731 		pcookie.uuids = op->o_tmpalloc( (SLAP_SYNCUUID_SET_SIZE+1) *
732 			sizeof(struct berval) + SLAP_SYNCUUID_SET_SIZE * UUID_LEN,
733 			op->o_tmpmemctx );
734 		pcookie.last = (char *)(pcookie.uuids + SLAP_SYNCUUID_SET_SIZE+1);
735 		pcookie.uuids[0].bv_val = pcookie.last;
736 		pcookie.uuids[0].bv_len = UUID_LEN;
737 		for (i=1; i<SLAP_SYNCUUID_SET_SIZE; i++) {
738 			pcookie.uuids[i].bv_val = pcookie.uuids[i-1].bv_val + UUID_LEN;
739 			pcookie.uuids[i].bv_len = UUID_LEN;
740 		}
741 		break;
742 	}
743 
744 	fop.o_bd->bd_info = (BackendInfo *)on->on_info;
745 	fop.o_bd->be_search( &fop, &frs );
746 	fop.o_bd->bd_info = (BackendInfo *)on;
747 
748 	switch( mode ) {
749 	case FIND_MAXCSN:
750 		if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
751 #ifdef CHECK_CSN
752 			Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
753 			assert( !syn->ssyn_validate( syn, &maxcsn ));
754 #endif
755 			ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
756 			si->si_numops++;	/* ensure a checkpoint */
757 		}
758 		break;
759 	case FIND_CSN:
760 		/* If matching CSN was not found, invalidate the context. */
761 		if ( !cb.sc_private ) {
762 			/* If we didn't find an exact match, then try for <= */
763 			if ( findcsn_retry ) {
764 				findcsn_retry = 0;
765 				rs_reinit( &frs, REP_RESULT );
766 				goto again;
767 			}
768 			rc = LDAP_NO_SUCH_OBJECT;
769 		}
770 		break;
771 	case FIND_PRESENT:
772 		op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
773 		break;
774 	}
775 
776 	return rc;
777 }
778 
779 static void free_resinfo( syncres *sr )
780 {
781 	syncres **st;
782 	int freeit = 0;
783 	ldap_pvt_thread_mutex_lock( &sr->s_info->ri_mutex );
784 	for (st = &sr->s_info->ri_list; *st; st = &(*st)->s_rilist) {
785 		if (*st == sr) {
786 			*st = sr->s_rilist;
787 			break;
788 		}
789 	}
790 	if ( !sr->s_info->ri_list )
791 		freeit = 1;
792 	ldap_pvt_thread_mutex_unlock( &sr->s_info->ri_mutex );
793 	if ( freeit ) {
794 		ldap_pvt_thread_mutex_destroy( &sr->s_info->ri_mutex );
795 		if ( sr->s_info->ri_e )
796 			entry_free( sr->s_info->ri_e );
797 		if ( !BER_BVISNULL( &sr->s_info->ri_cookie ))
798 			ch_free( sr->s_info->ri_cookie.bv_val );
799 		ch_free( sr->s_info );
800 	}
801 }
802 
803 static int
804 syncprov_free_syncop( syncops *so, int unlink )
805 {
806 	syncres *sr, *srnext;
807 	GroupAssertion *ga, *gnext;
808 
809 	ldap_pvt_thread_mutex_lock( &so->s_mutex );
810 	/* already being freed, or still in use */
811 	if ( !so->s_inuse || --so->s_inuse > 0 ) {
812 		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
813 		return 0;
814 	}
815 	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
816 	if ( unlink ) {
817 		syncops **sop;
818 		ldap_pvt_thread_mutex_lock( &so->s_si->si_ops_mutex );
819 		for ( sop = &so->s_si->si_ops; *sop; sop = &(*sop)->s_next ) {
820 			if ( *sop == so ) {
821 				*sop = so->s_next;
822 				break;
823 			}
824 		}
825 		ldap_pvt_thread_mutex_unlock( &so->s_si->si_ops_mutex );
826 	}
827 	if ( so->s_flags & PS_IS_DETACHED ) {
828 		filter_free( so->s_op->ors_filter );
829 		for ( ga = so->s_op->o_groups; ga; ga=gnext ) {
830 			gnext = ga->ga_next;
831 			ch_free( ga );
832 		}
833 		ch_free( so->s_op );
834 	}
835 	ch_free( so->s_base.bv_val );
836 	for ( sr=so->s_res; sr; sr=srnext ) {
837 		srnext = sr->s_next;
838 		free_resinfo( sr );
839 		ch_free( sr );
840 	}
841 	ldap_pvt_thread_mutex_destroy( &so->s_mutex );
842 	ch_free( so );
843 	return 1;
844 }
845 
846 /* Send a persistent search response */
847 static int
848 syncprov_sendresp( Operation *op, resinfo *ri, syncops *so, int mode )
849 {
850 	SlapReply rs = { REP_SEARCH };
851 	struct berval cookie, csns[2];
852 	Entry e_uuid = {0};
853 	Attribute a_uuid = {0};
854 
855 	if ( so->s_op->o_abandon )
856 		return SLAPD_ABANDON;
857 
858 	rs.sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2, op->o_tmpmemctx );
859 	rs.sr_ctrls[1] = NULL;
860 	rs.sr_flags = REP_CTRLS_MUSTBEFREED;
861 	csns[0] = ri->ri_csn;
862 	BER_BVZERO( &csns[1] );
863 	slap_compose_sync_cookie( op, &cookie, csns, so->s_rid, slap_serverID ? slap_serverID : -1 );
864 
865 #ifdef LDAP_DEBUG
866 	if ( so->s_sid > 0 ) {
867 		Debug( LDAP_DEBUG_SYNC, "syncprov_sendresp: to=%03x, cookie=%s\n",
868 			so->s_sid, cookie.bv_val, 0 );
869 	} else {
870 		Debug( LDAP_DEBUG_SYNC, "syncprov_sendresp: cookie=%s\n",
871 			cookie.bv_val, 0, 0 );
872 	}
873 #endif
874 
875 	e_uuid.e_attrs = &a_uuid;
876 	a_uuid.a_desc = slap_schema.si_ad_entryUUID;
877 	a_uuid.a_nvals = &ri->ri_uuid;
878 	rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
879 		mode, rs.sr_ctrls, 0, 1, &cookie );
880 	op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
881 
882 	rs.sr_entry = &e_uuid;
883 	if ( mode == LDAP_SYNC_ADD || mode == LDAP_SYNC_MODIFY ) {
884 		e_uuid = *ri->ri_e;
885 		e_uuid.e_private = NULL;
886 	}
887 
888 	switch( mode ) {
889 	case LDAP_SYNC_ADD:
890 		if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
891 			rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
892 			rs.sr_err = send_search_reference( op, &rs );
893 			ber_bvarray_free( rs.sr_ref );
894 			break;
895 		}
896 		/* fallthru */
897 	case LDAP_SYNC_MODIFY:
898 		rs.sr_attrs = op->ors_attrs;
899 		rs.sr_err = send_search_entry( op, &rs );
900 		break;
901 	case LDAP_SYNC_DELETE:
902 		e_uuid.e_attrs = NULL;
903 		e_uuid.e_name = ri->ri_dn;
904 		e_uuid.e_nname = ri->ri_ndn;
905 		if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
906 			struct berval bv = BER_BVNULL;
907 			rs.sr_ref = &bv;
908 			rs.sr_err = send_search_reference( op, &rs );
909 		} else {
910 			rs.sr_err = send_search_entry( op, &rs );
911 		}
912 		break;
913 	default:
914 		assert(0);
915 	}
916 	return rs.sr_err;
917 }
918 
919 static void
920 syncprov_qstart( syncops *so );
921 
922 /* Play back queued responses */
923 static int
924 syncprov_qplay( Operation *op, syncops *so )
925 {
926 	syncres *sr;
927 	int rc = 0;
928 
929 	do {
930 		ldap_pvt_thread_mutex_lock( &so->s_mutex );
931 		sr = so->s_res;
932 		/* Exit loop with mutex held */
933 		if ( !sr )
934 			break;
935 		so->s_res = sr->s_next;
936 		if ( !so->s_res )
937 			so->s_restail = NULL;
938 		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
939 
940 		if ( !so->s_op->o_abandon ) {
941 
942 			if ( sr->s_mode == LDAP_SYNC_NEW_COOKIE ) {
943 				SlapReply rs = { REP_INTERMEDIATE };
944 
945 				rc = syncprov_sendinfo( op, &rs, LDAP_TAG_SYNC_NEW_COOKIE,
946 					&sr->s_info->ri_cookie, 0, NULL, 0 );
947 			} else {
948 				rc = syncprov_sendresp( op, sr->s_info, so, sr->s_mode );
949 			}
950 		}
951 
952 		free_resinfo( sr );
953 		ch_free( sr );
954 
955 		if ( so->s_op->o_abandon )
956 			continue;
957 
958 		/* Exit loop with mutex held */
959 		ldap_pvt_thread_mutex_lock( &so->s_mutex );
960 		break;
961 
962 	} while (1);
963 
964 	/* We now only send one change at a time, to prevent one
965 	 * psearch from hogging all the CPU. Resubmit this task if
966 	 * there are more responses queued and no errors occurred.
967 	 */
968 
969 	if ( rc == 0 && so->s_res ) {
970 		syncprov_qstart( so );
971 	} else {
972 		so->s_flags ^= PS_TASK_QUEUED;
973 	}
974 
975 	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
976 	return rc;
977 }
978 
979 /* task for playing back queued responses */
980 static void *
981 syncprov_qtask( void *ctx, void *arg )
982 {
983 	syncops *so = arg;
984 	OperationBuffer opbuf;
985 	Operation *op;
986 	BackendDB be;
987 	int rc;
988 
989 	op = &opbuf.ob_op;
990 	*op = *so->s_op;
991 	op->o_hdr = &opbuf.ob_hdr;
992 	op->o_controls = opbuf.ob_controls;
993 	memset( op->o_controls, 0, sizeof(opbuf.ob_controls) );
994 	op->o_sync = SLAP_CONTROL_IGNORED;
995 
996 	*op->o_hdr = *so->s_op->o_hdr;
997 
998 	op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 1);
999 	op->o_tmpmfuncs = &slap_sl_mfuncs;
1000 	op->o_threadctx = ctx;
1001 
1002 	/* syncprov_qplay expects a fake db */
1003 	be = *so->s_op->o_bd;
1004 	be.be_flags |= SLAP_DBFLAG_OVERLAY;
1005 	op->o_bd = &be;
1006 	LDAP_SLIST_FIRST(&op->o_extra) = NULL;
1007 	op->o_callback = NULL;
1008 
1009 	rc = syncprov_qplay( op, so );
1010 
1011 	/* decrement use count... */
1012 	syncprov_free_syncop( so, 1 );
1013 
1014 	return NULL;
1015 }
1016 
1017 /* Start the task to play back queued psearch responses */
1018 static void
1019 syncprov_qstart( syncops *so )
1020 {
1021 	so->s_flags |= PS_TASK_QUEUED;
1022 	so->s_inuse++;
1023 	ldap_pvt_thread_pool_submit( &connection_pool,
1024 		syncprov_qtask, so );
1025 }
1026 
1027 /* Queue a persistent search response */
1028 static int
1029 syncprov_qresp( opcookie *opc, syncops *so, int mode )
1030 {
1031 	syncres *sr;
1032 	resinfo *ri;
1033 	int srsize;
1034 	struct berval csn = opc->sctxcsn;
1035 
1036 	sr = ch_malloc( sizeof( syncres ));
1037 	sr->s_next = NULL;
1038 	sr->s_mode = mode;
1039 	if ( !opc->ssres.s_info ) {
1040 
1041 		srsize = sizeof( resinfo );
1042 		if ( csn.bv_len )
1043 			srsize += csn.bv_len + 1;
1044 
1045 		if ( opc->se ) {
1046 			Attribute *a;
1047 			ri = ch_malloc( srsize );
1048 			ri->ri_dn = opc->se->e_name;
1049 			ri->ri_ndn = opc->se->e_nname;
1050 			a = attr_find( opc->se->e_attrs, slap_schema.si_ad_entryUUID );
1051 			if ( a )
1052 				ri->ri_uuid = a->a_nvals[0];
1053 			else
1054 				ri->ri_uuid.bv_len = 0;
1055 			if ( csn.bv_len ) {
1056 				ri->ri_csn.bv_val = (char *)(ri + 1);
1057 				ri->ri_csn.bv_len = csn.bv_len;
1058 				memcpy( ri->ri_csn.bv_val, csn.bv_val, csn.bv_len );
1059 				ri->ri_csn.bv_val[csn.bv_len] = '\0';
1060 			} else {
1061 				ri->ri_csn.bv_val = NULL;
1062 			}
1063 		} else {
1064 			srsize += opc->suuid.bv_len +
1065 				opc->sdn.bv_len + 1 + opc->sndn.bv_len + 1;
1066 			ri = ch_malloc( srsize );
1067 			ri->ri_dn.bv_val = (char *)(ri + 1);
1068 			ri->ri_dn.bv_len = opc->sdn.bv_len;
1069 			ri->ri_ndn.bv_val = lutil_strcopy( ri->ri_dn.bv_val,
1070 				opc->sdn.bv_val ) + 1;
1071 			ri->ri_ndn.bv_len = opc->sndn.bv_len;
1072 			ri->ri_uuid.bv_val = lutil_strcopy( ri->ri_ndn.bv_val,
1073 				opc->sndn.bv_val ) + 1;
1074 			ri->ri_uuid.bv_len = opc->suuid.bv_len;
1075 			AC_MEMCPY( ri->ri_uuid.bv_val, opc->suuid.bv_val, opc->suuid.bv_len );
1076 			if ( csn.bv_len ) {
1077 				ri->ri_csn.bv_val = ri->ri_uuid.bv_val + ri->ri_uuid.bv_len;
1078 				memcpy( ri->ri_csn.bv_val, csn.bv_val, csn.bv_len );
1079 				ri->ri_csn.bv_val[csn.bv_len] = '\0';
1080 			} else {
1081 				ri->ri_csn.bv_val = NULL;
1082 			}
1083 		}
1084 		ri->ri_list = &opc->ssres;
1085 		ri->ri_e = opc->se;
1086 		ri->ri_csn.bv_len = csn.bv_len;
1087 		ri->ri_isref = opc->sreference;
1088 		BER_BVZERO( &ri->ri_cookie );
1089 		ldap_pvt_thread_mutex_init( &ri->ri_mutex );
1090 		opc->se = NULL;
1091 		opc->ssres.s_info = ri;
1092 	}
1093 	ri = opc->ssres.s_info;
1094 	sr->s_info = ri;
1095 	ldap_pvt_thread_mutex_lock( &ri->ri_mutex );
1096 	sr->s_rilist = ri->ri_list;
1097 	ri->ri_list = sr;
1098 	if ( mode == LDAP_SYNC_NEW_COOKIE && BER_BVISNULL( &ri->ri_cookie )) {
1099 		syncprov_info_t	*si = opc->son->on_bi.bi_private;
1100 
1101 		slap_compose_sync_cookie( NULL, &ri->ri_cookie, si->si_ctxcsn,
1102 			so->s_rid, slap_serverID ? slap_serverID : -1);
1103 	}
1104 	ldap_pvt_thread_mutex_unlock( &ri->ri_mutex );
1105 
1106 	ldap_pvt_thread_mutex_lock( &so->s_mutex );
1107 	if ( !so->s_res ) {
1108 		so->s_res = sr;
1109 	} else {
1110 		so->s_restail->s_next = sr;
1111 	}
1112 	so->s_restail = sr;
1113 
1114 	/* If the base of the psearch was modified, check it next time round */
1115 	if ( so->s_flags & PS_WROTE_BASE ) {
1116 		so->s_flags ^= PS_WROTE_BASE;
1117 		so->s_flags |= PS_FIND_BASE;
1118 	}
1119 	if (( so->s_flags & (PS_IS_DETACHED|PS_TASK_QUEUED)) == PS_IS_DETACHED ) {
1120 		syncprov_qstart( so );
1121 	}
1122 	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
1123 	return LDAP_SUCCESS;
1124 }
1125 
1126 static int
1127 syncprov_drop_psearch( syncops *so, int lock )
1128 {
1129 	if ( so->s_flags & PS_IS_DETACHED ) {
1130 		if ( lock )
1131 			ldap_pvt_thread_mutex_lock( &so->s_op->o_conn->c_mutex );
1132 		so->s_op->o_conn->c_n_ops_executing--;
1133 		so->s_op->o_conn->c_n_ops_completed++;
1134 		LDAP_STAILQ_REMOVE( &so->s_op->o_conn->c_ops, so->s_op, Operation,
1135 			o_next );
1136 		if ( lock )
1137 			ldap_pvt_thread_mutex_unlock( &so->s_op->o_conn->c_mutex );
1138 	}
1139 	return syncprov_free_syncop( so, 0 );
1140 }
1141 
1142 static int
1143 syncprov_ab_cleanup( Operation *op, SlapReply *rs )
1144 {
1145 	slap_callback *sc = op->o_callback;
1146 	op->o_callback = sc->sc_next;
1147 	syncprov_drop_psearch( sc->sc_private, 0 );
1148 	op->o_tmpfree( sc, op->o_tmpmemctx );
1149 	return 0;
1150 }
1151 
1152 static int
1153 syncprov_op_abandon( Operation *op, SlapReply *rs )
1154 {
1155 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
1156 	syncprov_info_t		*si = on->on_bi.bi_private;
1157 	syncops *so, **sop;
1158 
1159 	ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1160 	for ( sop=&si->si_ops; (so = *sop); sop = &(*sop)->s_next ) {
1161 		if ( so->s_op->o_connid == op->o_connid &&
1162 			so->s_op->o_msgid == op->orn_msgid ) {
1163 				so->s_op->o_abandon = 1;
1164 				*sop = so->s_next;
1165 				break;
1166 		}
1167 	}
1168 	ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1169 	if ( so ) {
1170 		/* Is this really a Cancel exop? */
1171 		if ( op->o_tag != LDAP_REQ_ABANDON ) {
1172 			so->s_op->o_cancel = SLAP_CANCEL_ACK;
1173 			rs->sr_err = LDAP_CANCELLED;
1174 			send_ldap_result( so->s_op, rs );
1175 			if ( so->s_flags & PS_IS_DETACHED ) {
1176 				slap_callback *cb;
1177 				cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
1178 				cb->sc_cleanup = syncprov_ab_cleanup;
1179 				cb->sc_next = op->o_callback;
1180 				cb->sc_private = so;
1181 				op->o_callback = cb;
1182 				return SLAP_CB_CONTINUE;
1183 			}
1184 		}
1185 		syncprov_drop_psearch( so, 0 );
1186 	}
1187 	return SLAP_CB_CONTINUE;
1188 }
1189 
1190 /* Find which persistent searches are affected by this operation */
1191 static void
1192 syncprov_matchops( Operation *op, opcookie *opc, int saveit )
1193 {
1194 	slap_overinst *on = opc->son;
1195 	syncprov_info_t		*si = on->on_bi.bi_private;
1196 
1197 	fbase_cookie fc;
1198 	syncops **pss;
1199 	Entry *e = NULL;
1200 	Attribute *a;
1201 	int rc, gonext;
1202 	struct berval newdn;
1203 	int freefdn = 0;
1204 	BackendDB *b0 = op->o_bd, db;
1205 
1206 	fc.fdn = &op->o_req_ndn;
1207 	/* compute new DN */
1208 	if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
1209 		struct berval pdn;
1210 		if ( op->orr_nnewSup ) pdn = *op->orr_nnewSup;
1211 		else dnParent( fc.fdn, &pdn );
1212 		build_new_dn( &newdn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
1213 		fc.fdn = &newdn;
1214 		freefdn = 1;
1215 	}
1216 	if ( op->o_tag != LDAP_REQ_ADD ) {
1217 		if ( !SLAP_ISOVERLAY( op->o_bd )) {
1218 			db = *op->o_bd;
1219 			op->o_bd = &db;
1220 		}
1221 		rc = overlay_entry_get_ov( op, fc.fdn, NULL, NULL, 0, &e, on );
1222 		/* If we're sending responses now, make a copy and unlock the DB */
1223 		if ( e && !saveit ) {
1224 			if ( !opc->se )
1225 				opc->se = entry_dup( e );
1226 			overlay_entry_release_ov( op, e, 0, on );
1227 			e = opc->se;
1228 		}
1229 		if ( rc ) {
1230 			op->o_bd = b0;
1231 			return;
1232 		}
1233 	} else {
1234 		e = op->ora_e;
1235 		if ( !saveit ) {
1236 			if ( !opc->se )
1237 				opc->se = entry_dup( e );
1238 			e = opc->se;
1239 		}
1240 	}
1241 
1242 	if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
1243 		ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
1244 		ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
1245 		opc->sreference = is_entry_referral( e );
1246 		a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
1247 		if ( a )
1248 			ber_dupbv_x( &opc->suuid, &a->a_nvals[0], op->o_tmpmemctx );
1249 	} else if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
1250 		op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
1251 		op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
1252 		ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
1253 		ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
1254 	}
1255 
1256 	ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1257 	for (pss = &si->si_ops; *pss; pss = gonext ? &(*pss)->s_next : pss)
1258 	{
1259 		Operation op2;
1260 		Opheader oh;
1261 		syncmatches *sm;
1262 		int found = 0;
1263 		syncops *snext, *ss = *pss;
1264 
1265 		gonext = 1;
1266 		if ( ss->s_op->o_abandon )
1267 			continue;
1268 
1269 		/* Don't send ops back to the originator */
1270 		if ( opc->osid > 0 && opc->osid == ss->s_sid ) {
1271 			Debug( LDAP_DEBUG_SYNC, "syncprov_matchops: skipping original sid %03x\n",
1272 				opc->osid, 0, 0 );
1273 			continue;
1274 		}
1275 
1276 		/* Don't send ops back to the messenger */
1277 		if ( opc->rsid > 0 && opc->rsid == ss->s_sid ) {
1278 			Debug( LDAP_DEBUG_SYNC, "syncprov_matchops: skipping relayed sid %03x\n",
1279 				opc->rsid, 0, 0 );
1280 			continue;
1281 		}
1282 
1283 		/* validate base */
1284 		fc.fss = ss;
1285 		fc.fbase = 0;
1286 		fc.fscope = 0;
1287 
1288 		/* If the base of the search is missing, signal a refresh */
1289 		rc = syncprov_findbase( op, &fc );
1290 		if ( rc != LDAP_SUCCESS ) {
1291 			SlapReply rs = {REP_RESULT};
1292 			send_ldap_error( ss->s_op, &rs, LDAP_SYNC_REFRESH_REQUIRED,
1293 				"search base has changed" );
1294 			snext = ss->s_next;
1295 			if ( syncprov_drop_psearch( ss, 1 ) )
1296 				*pss = snext;
1297 			gonext = 0;
1298 			continue;
1299 		}
1300 
1301 		/* If we're sending results now, look for this op in old matches */
1302 		if ( !saveit ) {
1303 			syncmatches *old;
1304 
1305 			/* Did we modify the search base? */
1306 			if ( dn_match( &op->o_req_ndn, &ss->s_base )) {
1307 				ldap_pvt_thread_mutex_lock( &ss->s_mutex );
1308 				ss->s_flags |= PS_WROTE_BASE;
1309 				ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
1310 			}
1311 
1312 			for ( sm=opc->smatches, old=(syncmatches *)&opc->smatches; sm;
1313 				old=sm, sm=sm->sm_next ) {
1314 				if ( sm->sm_op == ss ) {
1315 					found = 1;
1316 					old->sm_next = sm->sm_next;
1317 					op->o_tmpfree( sm, op->o_tmpmemctx );
1318 					break;
1319 				}
1320 			}
1321 		}
1322 
1323 		if ( fc.fscope ) {
1324 			ldap_pvt_thread_mutex_lock( &ss->s_mutex );
1325 			op2 = *ss->s_op;
1326 			oh = *op->o_hdr;
1327 			oh.oh_conn = ss->s_op->o_conn;
1328 			oh.oh_connid = ss->s_op->o_connid;
1329 			op2.o_bd = op->o_bd->bd_self;
1330 			op2.o_hdr = &oh;
1331 			op2.o_extra = op->o_extra;
1332 			op2.o_callback = NULL;
1333 			if (ss->s_flags & PS_FIX_FILTER) {
1334 				/* Skip the AND/GE clause that we stuck on in front. We
1335 				   would lose deletes/mods that happen during the refresh
1336 				   phase otherwise (ITS#6555) */
1337 				op2.ors_filter = ss->s_op->ors_filter->f_and->f_next;
1338 			}
1339 			rc = test_filter( &op2, e, op2.ors_filter );
1340 			ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
1341 		}
1342 
1343 		Debug( LDAP_DEBUG_TRACE, "syncprov_matchops: sid %03x fscope %d rc %d\n",
1344 			ss->s_sid, fc.fscope, rc );
1345 
1346 		/* check if current o_req_dn is in scope and matches filter */
1347 		if ( fc.fscope && rc == LDAP_COMPARE_TRUE ) {
1348 			if ( saveit ) {
1349 				sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx );
1350 				sm->sm_next = opc->smatches;
1351 				sm->sm_op = ss;
1352 				ldap_pvt_thread_mutex_lock( &ss->s_mutex );
1353 				++ss->s_inuse;
1354 				ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
1355 				opc->smatches = sm;
1356 			} else {
1357 				/* if found send UPDATE else send ADD */
1358 				syncprov_qresp( opc, ss,
1359 					found ? LDAP_SYNC_MODIFY : LDAP_SYNC_ADD );
1360 			}
1361 		} else if ( !saveit && found ) {
1362 			/* send DELETE */
1363 			syncprov_qresp( opc, ss, LDAP_SYNC_DELETE );
1364 		} else if ( !saveit ) {
1365 			syncprov_qresp( opc, ss, LDAP_SYNC_NEW_COOKIE );
1366 		}
1367 		if ( !saveit && found ) {
1368 			/* Decrement s_inuse, was incremented when called
1369 			 * with saveit == TRUE
1370 			 */
1371 			snext = ss->s_next;
1372 			if ( syncprov_free_syncop( ss, 0 ) ) {
1373 				*pss = snext;
1374 				gonext = 0;
1375 			}
1376 		}
1377 	}
1378 	ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1379 
1380 	if ( op->o_tag != LDAP_REQ_ADD && e ) {
1381 		if ( !SLAP_ISOVERLAY( op->o_bd )) {
1382 			op->o_bd = &db;
1383 		}
1384 		if ( saveit )
1385 			overlay_entry_release_ov( op, e, 0, on );
1386 		op->o_bd = b0;
1387 	}
1388 	if ( !saveit ) {
1389 		if ( opc->ssres.s_info )
1390 			free_resinfo( &opc->ssres );
1391 		else if ( opc->se )
1392 			entry_free( opc->se );
1393 	}
1394 	if ( freefdn ) {
1395 		op->o_tmpfree( fc.fdn->bv_val, op->o_tmpmemctx );
1396 	}
1397 	op->o_bd = b0;
1398 }
1399 
1400 static int
1401 syncprov_op_cleanup( Operation *op, SlapReply *rs )
1402 {
1403 	slap_callback *cb = op->o_callback;
1404 	opcookie *opc = cb->sc_private;
1405 	slap_overinst *on = opc->son;
1406 	syncprov_info_t		*si = on->on_bi.bi_private;
1407 	syncmatches *sm, *snext;
1408 	modtarget *mt;
1409 
1410 	ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1411 	if ( si->si_active )
1412 		si->si_active--;
1413 	ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1414 
1415 	for (sm = opc->smatches; sm; sm=snext) {
1416 		snext = sm->sm_next;
1417 		syncprov_free_syncop( sm->sm_op, 1 );
1418 		op->o_tmpfree( sm, op->o_tmpmemctx );
1419 	}
1420 
1421 	/* Remove op from lock table */
1422 	mt = opc->smt;
1423 	if ( mt ) {
1424 		modinst *mi = (modinst *)(opc+1), **m2;
1425 		ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
1426 		for (m2 = &mt->mt_mods; ; m2 = &(*m2)->mi_next) {
1427 			if ( *m2 == mi ) {
1428 				*m2 = mi->mi_next;
1429 				if ( mt->mt_tail == mi )
1430 					mt->mt_tail = ( m2 == &mt->mt_mods ) ? NULL : (modinst *)m2;
1431 				break;
1432 			}
1433 		}
1434 		/* If there are more, promote the next one */
1435 		if ( mt->mt_mods ) {
1436 			ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
1437 		} else {
1438 			ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
1439 			ldap_pvt_thread_mutex_lock( &si->si_mods_mutex );
1440 			avl_delete( &si->si_mods, mt, sp_avl_cmp );
1441 			ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
1442 			ldap_pvt_thread_mutex_destroy( &mt->mt_mutex );
1443 			ch_free( mt->mt_dn.bv_val );
1444 			ch_free( mt );
1445 		}
1446 	}
1447 	if ( !BER_BVISNULL( &opc->suuid ))
1448 		op->o_tmpfree( opc->suuid.bv_val, op->o_tmpmemctx );
1449 	if ( !BER_BVISNULL( &opc->sndn ))
1450 		op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
1451 	if ( !BER_BVISNULL( &opc->sdn ))
1452 		op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
1453 	op->o_callback = cb->sc_next;
1454 	op->o_tmpfree(cb, op->o_tmpmemctx);
1455 
1456 	return 0;
1457 }
1458 
1459 static void
1460 syncprov_checkpoint( Operation *op, slap_overinst *on )
1461 {
1462 	syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
1463 	Modifications mod;
1464 	Operation opm;
1465 	SlapReply rsm = {REP_RESULT};
1466 	slap_callback cb = {0};
1467 	BackendDB be;
1468 	BackendInfo *bi;
1469 
1470 #ifdef CHECK_CSN
1471 	Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
1472 
1473 	int i;
1474 	for ( i=0; i<si->si_numcsns; i++ ) {
1475 		assert( !syn->ssyn_validate( syn, si->si_ctxcsn+i ));
1476 	}
1477 #endif
1478 	mod.sml_numvals = si->si_numcsns;
1479 	mod.sml_values = si->si_ctxcsn;
1480 	mod.sml_nvalues = NULL;
1481 	mod.sml_desc = slap_schema.si_ad_contextCSN;
1482 	mod.sml_op = LDAP_MOD_REPLACE;
1483 	mod.sml_flags = SLAP_MOD_INTERNAL;
1484 	mod.sml_next = NULL;
1485 
1486 	cb.sc_response = slap_null_cb;
1487 	opm = *op;
1488 	opm.o_tag = LDAP_REQ_MODIFY;
1489 	opm.o_callback = &cb;
1490 	opm.orm_modlist = &mod;
1491 	opm.orm_no_opattrs = 1;
1492 	if ( SLAP_GLUE_SUBORDINATE( op->o_bd )) {
1493 		be = *on->on_info->oi_origdb;
1494 		opm.o_bd = &be;
1495 	}
1496 	opm.o_req_dn = si->si_contextdn;
1497 	opm.o_req_ndn = si->si_contextdn;
1498 	bi = opm.o_bd->bd_info;
1499 	opm.o_bd->bd_info = on->on_info->oi_orig;
1500 	opm.o_managedsait = SLAP_CONTROL_NONCRITICAL;
1501 	opm.o_no_schema_check = 1;
1502 	opm.o_dont_replicate = 1;
1503 	opm.o_opid = -1;
1504 	opm.o_bd->be_modify( &opm, &rsm );
1505 
1506 	if ( rsm.sr_err == LDAP_NO_SUCH_OBJECT &&
1507 		SLAP_SYNC_SUBENTRY( opm.o_bd )) {
1508 		const char	*text;
1509 		char txtbuf[SLAP_TEXT_BUFLEN];
1510 		size_t textlen = sizeof txtbuf;
1511 		Entry *e = slap_create_context_csn_entry( opm.o_bd, NULL );
1512 		rs_reinit( &rsm, REP_RESULT );
1513 		slap_mods2entry( &mod, &e, 0, 1, &text, txtbuf, textlen);
1514 		opm.ora_e = e;
1515 		opm.o_bd->be_add( &opm, &rsm );
1516 		if ( e == opm.ora_e )
1517 			be_entry_release_w( &opm, opm.ora_e );
1518 	}
1519 	opm.o_bd->bd_info = bi;
1520 
1521 	if ( mod.sml_next != NULL ) {
1522 		slap_mods_free( mod.sml_next, 1 );
1523 	}
1524 #ifdef CHECK_CSN
1525 	for ( i=0; i<si->si_numcsns; i++ ) {
1526 		assert( !syn->ssyn_validate( syn, si->si_ctxcsn+i ));
1527 	}
1528 #endif
1529 }
1530 
1531 static void
1532 syncprov_add_slog( Operation *op )
1533 {
1534 	opcookie *opc = op->o_callback->sc_private;
1535 	slap_overinst *on = opc->son;
1536 	syncprov_info_t		*si = on->on_bi.bi_private;
1537 	sessionlog *sl;
1538 	slog_entry *se;
1539 
1540 	sl = si->si_logs;
1541 	{
1542 		if ( BER_BVISEMPTY( &op->o_csn ) ) {
1543 			/* During the syncrepl refresh phase we can receive operations
1544 			 * without a csn.  We cannot reliably determine the consumers
1545 			 * state with respect to such operations, so we ignore them and
1546 			 * wipe out anything in the log if we see them.
1547 			 */
1548 			ldap_pvt_thread_mutex_lock( &sl->sl_mutex );
1549 			/* can only do this if no one else is reading the log at the moment */
1550 			if (!sl->sl_playing) {
1551 			while ( se = sl->sl_head ) {
1552 				sl->sl_head = se->se_next;
1553 				ch_free( se );
1554 			}
1555 			sl->sl_tail = NULL;
1556 			sl->sl_num = 0;
1557 			}
1558 			ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
1559 			return;
1560 		}
1561 
1562 		/* Allocate a record. UUIDs are not NUL-terminated. */
1563 		se = ch_malloc( sizeof( slog_entry ) + opc->suuid.bv_len +
1564 			op->o_csn.bv_len + 1 );
1565 		se->se_next = NULL;
1566 		se->se_tag = op->o_tag;
1567 
1568 		se->se_uuid.bv_val = (char *)(&se[1]);
1569 		AC_MEMCPY( se->se_uuid.bv_val, opc->suuid.bv_val, opc->suuid.bv_len );
1570 		se->se_uuid.bv_len = opc->suuid.bv_len;
1571 
1572 		se->se_csn.bv_val = se->se_uuid.bv_val + opc->suuid.bv_len;
1573 		AC_MEMCPY( se->se_csn.bv_val, op->o_csn.bv_val, op->o_csn.bv_len );
1574 		se->se_csn.bv_val[op->o_csn.bv_len] = '\0';
1575 		se->se_csn.bv_len = op->o_csn.bv_len;
1576 		se->se_sid = slap_parse_csn_sid( &se->se_csn );
1577 
1578 		ldap_pvt_thread_mutex_lock( &sl->sl_mutex );
1579 		if ( sl->sl_head ) {
1580 			/* Keep the list in csn order. */
1581 			if ( ber_bvcmp( &sl->sl_tail->se_csn, &se->se_csn ) <= 0 ) {
1582 				sl->sl_tail->se_next = se;
1583 				sl->sl_tail = se;
1584 			} else {
1585 				slog_entry **sep;
1586 
1587 				for ( sep = &sl->sl_head; *sep; sep = &(*sep)->se_next ) {
1588 					if ( ber_bvcmp( &se->se_csn, &(*sep)->se_csn ) < 0 ) {
1589 						se->se_next = *sep;
1590 						*sep = se;
1591 						break;
1592 					}
1593 				}
1594 			}
1595 		} else {
1596 			sl->sl_head = se;
1597 			sl->sl_tail = se;
1598 			if ( !sl->sl_mincsn ) {
1599 				sl->sl_numcsns = 1;
1600 				sl->sl_mincsn = ch_malloc( 2*sizeof( struct berval ));
1601 				sl->sl_sids = ch_malloc( sizeof( int ));
1602 				sl->sl_sids[0] = se->se_sid;
1603 				ber_dupbv( sl->sl_mincsn, &se->se_csn );
1604 				BER_BVZERO( &sl->sl_mincsn[1] );
1605 			}
1606 		}
1607 		sl->sl_num++;
1608 		if (!sl->sl_playing) {
1609 		while ( sl->sl_num > sl->sl_size ) {
1610 			int i;
1611 			se = sl->sl_head;
1612 			sl->sl_head = se->se_next;
1613 			for ( i=0; i<sl->sl_numcsns; i++ )
1614 				if ( sl->sl_sids[i] >= se->se_sid )
1615 					break;
1616 			if  ( i == sl->sl_numcsns || sl->sl_sids[i] != se->se_sid ) {
1617 				slap_insert_csn_sids( (struct sync_cookie *)sl,
1618 					i, se->se_sid, &se->se_csn );
1619 			} else {
1620 				ber_bvreplace( &sl->sl_mincsn[i], &se->se_csn );
1621 			}
1622 			ch_free( se );
1623 			sl->sl_num--;
1624 		}
1625 		}
1626 		ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
1627 	}
1628 }
1629 
1630 /* Just set a flag if we found the matching entry */
1631 static int
1632 playlog_cb( Operation *op, SlapReply *rs )
1633 {
1634 	if ( rs->sr_type == REP_SEARCH ) {
1635 		op->o_callback->sc_private = (void *)1;
1636 	}
1637 	return rs->sr_err;
1638 }
1639 
1640 /* enter with sl->sl_mutex locked, release before returning */
1641 static void
1642 syncprov_playlog( Operation *op, SlapReply *rs, sessionlog *sl,
1643 	sync_control *srs, BerVarray ctxcsn, int numcsns, int *sids )
1644 {
1645 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
1646 	slog_entry *se;
1647 	int i, j, ndel, num, nmods, mmods;
1648 	char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
1649 	BerVarray uuids;
1650 	struct berval delcsn[2];
1651 
1652 	if ( !sl->sl_num ) {
1653 		ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
1654 		return;
1655 	}
1656 
1657 	num = sl->sl_num;
1658 	i = 0;
1659 	nmods = 0;
1660 	sl->sl_playing++;
1661 	ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
1662 
1663 	uuids = op->o_tmpalloc( (num+1) * sizeof( struct berval ) +
1664 		num * UUID_LEN, op->o_tmpmemctx );
1665 	uuids[0].bv_val = (char *)(uuids + num + 1);
1666 
1667 	delcsn[0].bv_len = 0;
1668 	delcsn[0].bv_val = cbuf;
1669 	BER_BVZERO(&delcsn[1]);
1670 
1671 	/* Make a copy of the relevant UUIDs. Put the Deletes up front
1672 	 * and everything else at the end. Do this first so we can
1673 	 * unlock the list mutex.
1674 	 */
1675 	Debug( LDAP_DEBUG_SYNC, "srs csn %s\n",
1676 		srs->sr_state.ctxcsn[0].bv_val, 0, 0 );
1677 	for ( se=sl->sl_head; se; se=se->se_next ) {
1678 		int k;
1679 		Debug( LDAP_DEBUG_SYNC, "log csn %s\n", se->se_csn.bv_val, 0, 0 );
1680 		ndel = 1;
1681 		for ( k=0; k<srs->sr_state.numcsns; k++ ) {
1682 			if ( se->se_sid == srs->sr_state.sids[k] ) {
1683 				ndel = ber_bvcmp( &se->se_csn, &srs->sr_state.ctxcsn[k] );
1684 				break;
1685 			}
1686 		}
1687 		if ( ndel <= 0 ) {
1688 			Debug( LDAP_DEBUG_SYNC, "cmp %d, too old\n", ndel, 0, 0 );
1689 			continue;
1690 		}
1691 		ndel = 0;
1692 		for ( k=0; k<numcsns; k++ ) {
1693 			if ( se->se_sid == sids[k] ) {
1694 				ndel = ber_bvcmp( &se->se_csn, &ctxcsn[k] );
1695 				break;
1696 			}
1697 		}
1698 		if ( ndel > 0 ) {
1699 			Debug( LDAP_DEBUG_SYNC, "cmp %d, too new\n", ndel, 0, 0 );
1700 			break;
1701 		}
1702 		if ( se->se_tag == LDAP_REQ_DELETE ) {
1703 			j = i;
1704 			i++;
1705 			AC_MEMCPY( cbuf, se->se_csn.bv_val, se->se_csn.bv_len );
1706 			delcsn[0].bv_len = se->se_csn.bv_len;
1707 			delcsn[0].bv_val[delcsn[0].bv_len] = '\0';
1708 		} else {
1709 			if ( se->se_tag == LDAP_REQ_ADD )
1710 				continue;
1711 			nmods++;
1712 			j = num - nmods;
1713 		}
1714 		uuids[j].bv_val = uuids[0].bv_val + (j * UUID_LEN);
1715 		AC_MEMCPY(uuids[j].bv_val, se->se_uuid.bv_val, UUID_LEN);
1716 		uuids[j].bv_len = UUID_LEN;
1717 	}
1718 	ldap_pvt_thread_mutex_lock( &sl->sl_mutex );
1719 	sl->sl_playing--;
1720 	ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
1721 
1722 	ndel = i;
1723 
1724 	/* Zero out unused slots */
1725 	for ( i=ndel; i < num - nmods; i++ )
1726 		uuids[i].bv_len = 0;
1727 
1728 	/* Mods must be validated to see if they belong in this delete set.
1729 	 */
1730 
1731 	mmods = nmods;
1732 	/* Strip any duplicates */
1733 	for ( i=0; i<nmods; i++ ) {
1734 		for ( j=0; j<ndel; j++ ) {
1735 			if ( bvmatch( &uuids[j], &uuids[num - 1 - i] )) {
1736 				uuids[num - 1 - i].bv_len = 0;
1737 				mmods --;
1738 				break;
1739 			}
1740 		}
1741 		if ( uuids[num - 1 - i].bv_len == 0 ) continue;
1742 		for ( j=0; j<i; j++ ) {
1743 			if ( bvmatch( &uuids[num - 1 - j], &uuids[num - 1 - i] )) {
1744 				uuids[num - 1 - i].bv_len = 0;
1745 				mmods --;
1746 				break;
1747 			}
1748 		}
1749 	}
1750 
1751 	if ( mmods ) {
1752 		Operation fop;
1753 		int rc;
1754 		Filter mf, af;
1755 		AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
1756 		slap_callback cb = {0};
1757 
1758 		fop = *op;
1759 
1760 		fop.o_sync_mode = 0;
1761 		fop.o_callback = &cb;
1762 		fop.ors_limit = NULL;
1763 		fop.ors_tlimit = SLAP_NO_LIMIT;
1764 		fop.ors_attrs = slap_anlist_all_attributes;
1765 		fop.ors_attrsonly = 0;
1766 		fop.o_managedsait = SLAP_CONTROL_CRITICAL;
1767 
1768 		af.f_choice = LDAP_FILTER_AND;
1769 		af.f_next = NULL;
1770 		af.f_and = &mf;
1771 		mf.f_choice = LDAP_FILTER_EQUALITY;
1772 		mf.f_ava = &eq;
1773 		mf.f_av_desc = slap_schema.si_ad_entryUUID;
1774 		mf.f_next = fop.ors_filter;
1775 
1776 		fop.ors_filter = &af;
1777 
1778 		cb.sc_response = playlog_cb;
1779 		fop.o_bd->bd_info = (BackendInfo *)on->on_info;
1780 
1781 		for ( i=ndel; i<num; i++ ) {
1782 		  if ( uuids[i].bv_len != 0 ) {
1783 			SlapReply frs = { REP_RESULT };
1784 
1785 			mf.f_av_value = uuids[i];
1786 			cb.sc_private = NULL;
1787 			fop.ors_slimit = 1;
1788 			rc = fop.o_bd->be_search( &fop, &frs );
1789 
1790 			/* If entry was not found, add to delete list */
1791 			if ( !cb.sc_private ) {
1792 				uuids[ndel++] = uuids[i];
1793 			}
1794 		  }
1795 		}
1796 		fop.o_bd->bd_info = (BackendInfo *)on;
1797 	}
1798 	if ( ndel ) {
1799 		struct berval cookie;
1800 
1801 		if ( delcsn[0].bv_len ) {
1802 			slap_compose_sync_cookie( op, &cookie, delcsn, srs->sr_state.rid,
1803 				slap_serverID ? slap_serverID : -1 );
1804 
1805 			Debug( LDAP_DEBUG_SYNC, "syncprov_playlog: cookie=%s\n", cookie.bv_val, 0, 0 );
1806 		}
1807 
1808 		uuids[ndel].bv_val = NULL;
1809 		syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET,
1810 			delcsn[0].bv_len ? &cookie : NULL, 0, uuids, 1 );
1811 		if ( delcsn[0].bv_len ) {
1812 			op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
1813 		}
1814 	}
1815 	op->o_tmpfree( uuids, op->o_tmpmemctx );
1816 }
1817 
1818 static int
1819 syncprov_new_ctxcsn( opcookie *opc, syncprov_info_t *si, int csn_changed, int numvals, BerVarray vals )
1820 {
1821 	unsigned i;
1822 	int j, sid;
1823 
1824 	for ( i=0; i<numvals; i++ ) {
1825 		sid = slap_parse_csn_sid( &vals[i] );
1826 		for ( j=0; j<si->si_numcsns; j++ ) {
1827 			if ( sid < si->si_sids[j] )
1828 				break;
1829 			if ( sid == si->si_sids[j] ) {
1830 				if ( ber_bvcmp( &vals[i], &si->si_ctxcsn[j] ) > 0 ) {
1831 					ber_bvreplace( &si->si_ctxcsn[j], &vals[i] );
1832 					csn_changed = 1;
1833 				}
1834 				break;
1835 			}
1836 		}
1837 
1838 		if ( j == si->si_numcsns || sid != si->si_sids[j] ) {
1839 			slap_insert_csn_sids( (struct sync_cookie *)&si->si_ctxcsn,
1840 				j, sid, &vals[i] );
1841 			csn_changed = 1;
1842 		}
1843 	}
1844 	if ( csn_changed )
1845 		si->si_dirty = 0;
1846 	ldap_pvt_thread_rdwr_wunlock( &si->si_csn_rwlock );
1847 
1848 	if ( csn_changed ) {
1849 		syncops *ss;
1850 		ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1851 		for ( ss = si->si_ops; ss; ss = ss->s_next ) {
1852 			if ( ss->s_op->o_abandon )
1853 				continue;
1854 			/* Send the updated csn to all syncrepl consumers,
1855 			 * including the server from which it originated.
1856 			 * The syncrepl consumer and syncprov provider on
1857 			 * the originating server may be configured to store
1858 			 * their csn values in different entries.
1859 			 */
1860 			syncprov_qresp( opc, ss, LDAP_SYNC_NEW_COOKIE );
1861 		}
1862 		ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1863 	}
1864 	return csn_changed;
1865 }
1866 
1867 static int
1868 syncprov_op_response( Operation *op, SlapReply *rs )
1869 {
1870 	opcookie *opc = op->o_callback->sc_private;
1871 	slap_overinst *on = opc->son;
1872 	syncprov_info_t		*si = on->on_bi.bi_private;
1873 	syncmatches *sm;
1874 
1875 	if ( rs->sr_err == LDAP_SUCCESS )
1876 	{
1877 		struct berval maxcsn;
1878 		char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
1879 		int do_check = 0, have_psearches, foundit, csn_changed = 0;
1880 
1881 		ldap_pvt_thread_mutex_lock( &si->si_resp_mutex );
1882 
1883 		/* Update our context CSN */
1884 		cbuf[0] = '\0';
1885 		maxcsn.bv_val = cbuf;
1886 		maxcsn.bv_len = sizeof(cbuf);
1887 		ldap_pvt_thread_rdwr_wlock( &si->si_csn_rwlock );
1888 
1889 		slap_get_commit_csn( op, &maxcsn, &foundit );
1890 		if ( BER_BVISEMPTY( &maxcsn ) && SLAP_GLUE_SUBORDINATE( op->o_bd )) {
1891 			/* syncrepl queues the CSN values in the db where
1892 			 * it is configured , not where the changes are made.
1893 			 * So look for a value in the glue db if we didn't
1894 			 * find any in this db.
1895 			 */
1896 			BackendDB *be = op->o_bd;
1897 			op->o_bd = select_backend( &be->be_nsuffix[0], 1);
1898 			maxcsn.bv_val = cbuf;
1899 			maxcsn.bv_len = sizeof(cbuf);
1900 			slap_get_commit_csn( op, &maxcsn, &foundit );
1901 			op->o_bd = be;
1902 		}
1903 		if ( !BER_BVISEMPTY( &maxcsn ) ) {
1904 			int i, sid;
1905 #ifdef CHECK_CSN
1906 			Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
1907 			assert( !syn->ssyn_validate( syn, &maxcsn ));
1908 #endif
1909 			sid = slap_parse_csn_sid( &maxcsn );
1910 			for ( i=0; i<si->si_numcsns; i++ ) {
1911 				if ( sid < si->si_sids[i] )
1912 					break;
1913 				if ( sid == si->si_sids[i] ) {
1914 					if ( ber_bvcmp( &maxcsn, &si->si_ctxcsn[i] ) > 0 ) {
1915 						ber_bvreplace( &si->si_ctxcsn[i], &maxcsn );
1916 						csn_changed = 1;
1917 					}
1918 					break;
1919 				}
1920 			}
1921 			/* It's a new SID for us */
1922 			if ( i == si->si_numcsns || sid != si->si_sids[i] ) {
1923 				slap_insert_csn_sids((struct sync_cookie *)&(si->si_ctxcsn),
1924 					i, sid, &maxcsn );
1925 				csn_changed = 1;
1926 			}
1927 		}
1928 
1929 		/* Don't do any processing for consumer contextCSN updates */
1930 		if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) &&
1931 			op->o_tag == LDAP_REQ_MODIFY &&
1932 			op->orm_modlist &&
1933 			op->orm_modlist->sml_op == LDAP_MOD_REPLACE &&
1934 			op->orm_modlist->sml_desc == slap_schema.si_ad_contextCSN ) {
1935 			/* Catch contextCSN updates from syncrepl. We have to look at
1936 			 * all the attribute values, as there may be more than one csn
1937 			 * that changed, and only one can be passed in the csn queue.
1938 			 */
1939 			csn_changed = syncprov_new_ctxcsn( opc, si, csn_changed,
1940 				op->orm_modlist->sml_numvals, op->orm_modlist->sml_values );
1941 			if ( csn_changed )
1942 				si->si_numops++;
1943 			goto leave;
1944 		}
1945 		if ( op->o_dont_replicate ) {
1946 			if ( csn_changed )
1947 				si->si_numops++;
1948 			ldap_pvt_thread_rdwr_wunlock( &si->si_csn_rwlock );
1949 			goto leave;
1950 		}
1951 
1952 		/* If we're adding the context entry, parse all of its contextCSNs */
1953 		if ( op->o_tag == LDAP_REQ_ADD &&
1954 			dn_match( &op->o_req_ndn, &si->si_contextdn )) {
1955 			Attribute *a = attr_find( op->ora_e->e_attrs, slap_schema.si_ad_contextCSN );
1956 			if ( a ) {
1957 				csn_changed = syncprov_new_ctxcsn( opc, si, csn_changed, a->a_numvals, a->a_vals );
1958 				if ( csn_changed )
1959 					si->si_numops++;
1960 				goto added;
1961 			}
1962 		}
1963 
1964 		if ( csn_changed )
1965 			si->si_numops++;
1966 		if ( si->si_chkops || si->si_chktime ) {
1967 			/* Never checkpoint adding the context entry,
1968 			 * it will deadlock
1969 			 */
1970 			if ( op->o_tag != LDAP_REQ_ADD ||
1971 				!dn_match( &op->o_req_ndn, &si->si_contextdn )) {
1972 				if ( si->si_chkops && si->si_numops >= si->si_chkops ) {
1973 					do_check = 1;
1974 					si->si_numops = 0;
1975 				}
1976 				if ( si->si_chktime &&
1977 					(op->o_time - si->si_chklast >= si->si_chktime )) {
1978 					if ( si->si_chklast ) {
1979 						do_check = 1;
1980 						si->si_chklast = op->o_time;
1981 					} else {
1982 						si->si_chklast = 1;
1983 					}
1984 				}
1985 			}
1986 		}
1987 		si->si_dirty = !csn_changed;
1988 		ldap_pvt_thread_rdwr_wunlock( &si->si_csn_rwlock );
1989 
1990 added:
1991 		if ( do_check ) {
1992 			ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
1993 			syncprov_checkpoint( op, on );
1994 			ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
1995 		}
1996 
1997 		/* only update consumer ctx if this is a newer csn */
1998 		if ( csn_changed ) {
1999 			opc->sctxcsn = maxcsn;
2000 		}
2001 
2002 		/* Handle any persistent searches */
2003 		ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
2004 		have_psearches = ( si->si_ops != NULL );
2005 		ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
2006 		if ( have_psearches ) {
2007 			switch(op->o_tag) {
2008 			case LDAP_REQ_ADD:
2009 			case LDAP_REQ_MODIFY:
2010 			case LDAP_REQ_MODRDN:
2011 			case LDAP_REQ_EXTENDED:
2012 				syncprov_matchops( op, opc, 0 );
2013 				break;
2014 			case LDAP_REQ_DELETE:
2015 				/* for each match in opc->smatches:
2016 				 *   send DELETE msg
2017 				 */
2018 				for ( sm = opc->smatches; sm; sm=sm->sm_next ) {
2019 					if ( sm->sm_op->s_op->o_abandon )
2020 						continue;
2021 					syncprov_qresp( opc, sm->sm_op, LDAP_SYNC_DELETE );
2022 				}
2023 				if ( opc->ssres.s_info )
2024 					free_resinfo( &opc->ssres );
2025 				break;
2026 			}
2027 		}
2028 
2029 		/* Add any log records */
2030 		if ( si->si_logs ) {
2031 			syncprov_add_slog( op );
2032 		}
2033 leave:		ldap_pvt_thread_mutex_unlock( &si->si_resp_mutex );
2034 	}
2035 	return SLAP_CB_CONTINUE;
2036 }
2037 
2038 /* We don't use a subentry to store the context CSN any more.
2039  * We expose the current context CSN as an operational attribute
2040  * of the suffix entry.
2041  */
2042 static int
2043 syncprov_op_compare( Operation *op, SlapReply *rs )
2044 {
2045 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
2046 	syncprov_info_t		*si = on->on_bi.bi_private;
2047 	int rc = SLAP_CB_CONTINUE;
2048 
2049 	if ( dn_match( &op->o_req_ndn, &si->si_contextdn ) &&
2050 		op->oq_compare.rs_ava->aa_desc == slap_schema.si_ad_contextCSN )
2051 	{
2052 		Entry e = {0};
2053 		Attribute a = {0};
2054 
2055 		e.e_name = si->si_contextdn;
2056 		e.e_nname = si->si_contextdn;
2057 		e.e_attrs = &a;
2058 
2059 		a.a_desc = slap_schema.si_ad_contextCSN;
2060 
2061 		ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
2062 
2063 		a.a_vals = si->si_ctxcsn;
2064 		a.a_nvals = a.a_vals;
2065 		a.a_numvals = si->si_numcsns;
2066 
2067 		rs->sr_err = access_allowed( op, &e, op->oq_compare.rs_ava->aa_desc,
2068 			&op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
2069 		if ( ! rs->sr_err ) {
2070 			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2071 			goto return_results;
2072 		}
2073 
2074 		if ( get_assert( op ) &&
2075 			( test_filter( op, &e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) )
2076 		{
2077 			rs->sr_err = LDAP_ASSERTION_FAILED;
2078 			goto return_results;
2079 		}
2080 
2081 
2082 		rs->sr_err = LDAP_COMPARE_FALSE;
2083 
2084 		if ( attr_valfind( &a,
2085 			SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
2086 				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
2087 				&op->oq_compare.rs_ava->aa_value, NULL, op->o_tmpmemctx ) == 0 )
2088 		{
2089 			rs->sr_err = LDAP_COMPARE_TRUE;
2090 		}
2091 
2092 return_results:;
2093 
2094 		ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
2095 
2096 		send_ldap_result( op, rs );
2097 
2098 		if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
2099 			rs->sr_err = LDAP_SUCCESS;
2100 		}
2101 		rc = rs->sr_err;
2102 	}
2103 
2104 	return rc;
2105 }
2106 
2107 static int
2108 syncprov_op_mod( Operation *op, SlapReply *rs )
2109 {
2110 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
2111 	syncprov_info_t		*si = on->on_bi.bi_private;
2112 	slap_callback *cb;
2113 	opcookie *opc;
2114 	int have_psearches, cbsize;
2115 
2116 	ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
2117 	have_psearches = ( si->si_ops != NULL );
2118 	si->si_active++;
2119 	ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
2120 
2121 	cbsize = sizeof(slap_callback) + sizeof(opcookie) +
2122 		(have_psearches ? sizeof(modinst) : 0 );
2123 
2124 	cb = op->o_tmpcalloc(1, cbsize, op->o_tmpmemctx);
2125 	opc = (opcookie *)(cb+1);
2126 	opc->son = on;
2127 	cb->sc_response = syncprov_op_response;
2128 	cb->sc_cleanup = syncprov_op_cleanup;
2129 	cb->sc_private = opc;
2130 	cb->sc_next = op->o_callback;
2131 	op->o_callback = cb;
2132 
2133 	opc->osid = -1;
2134 	opc->rsid = -1;
2135 	if ( op->o_csn.bv_val ) {
2136 		opc->osid = slap_parse_csn_sid( &op->o_csn );
2137 	}
2138 	if ( op->o_controls ) {
2139 		struct sync_cookie *scook =
2140 		op->o_controls[slap_cids.sc_LDAPsync];
2141 		if ( scook )
2142 			opc->rsid = scook->sid;
2143 	}
2144 
2145 	if ( op->o_dont_replicate )
2146 		return SLAP_CB_CONTINUE;
2147 
2148 	/* If there are active persistent searches, lock this operation.
2149 	 * See seqmod.c for the locking logic on its own.
2150 	 */
2151 	if ( have_psearches ) {
2152 		modtarget *mt, mtdummy;
2153 		modinst *mi;
2154 
2155 		mi = (modinst *)(opc+1);
2156 		mi->mi_op = op;
2157 
2158 		/* See if we're already modifying this entry... */
2159 		mtdummy.mt_dn = op->o_req_ndn;
2160 retry:
2161 		ldap_pvt_thread_mutex_lock( &si->si_mods_mutex );
2162 		mt = avl_find( si->si_mods, &mtdummy, sp_avl_cmp );
2163 		if ( mt ) {
2164 			ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
2165 			if ( mt->mt_mods == NULL ) {
2166 				/* Cannot reuse this mt, as another thread is about
2167 				 * to release it in syncprov_op_cleanup. Wait for them
2168 				 * to finish; our own insert is required to succeed.
2169 				 */
2170 				ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
2171 				ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
2172 				ldap_pvt_thread_yield();
2173 				goto retry;
2174 			}
2175 		}
2176 		if ( mt ) {
2177 			mt->mt_tail->mi_next = mi;
2178 			mt->mt_tail = mi;
2179 			ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
2180 			/* wait for this op to get to head of list */
2181 			while ( mt->mt_mods != mi ) {
2182 				modinst *m2;
2183 				/* don't wait on other mods from the same thread */
2184 				for ( m2 = mt->mt_mods; m2; m2 = m2->mi_next ) {
2185 					if ( m2->mi_op->o_threadctx == op->o_threadctx ) {
2186 						break;
2187 					}
2188 				}
2189 				if ( m2 )
2190 					break;
2191 
2192 				ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
2193 				/* FIXME: if dynamic config can delete overlays or
2194 				 * databases we'll have to check for cleanup here.
2195 				 * Currently it's not an issue because there are
2196 				 * no dynamic config deletes...
2197 				 */
2198 				if ( slapd_shutdown )
2199 					return SLAPD_ABANDON;
2200 
2201 				if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
2202 					ldap_pvt_thread_yield();
2203 				ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
2204 
2205 				/* clean up if the caller is giving up */
2206 				if ( op->o_abandon ) {
2207 					modinst **m2;
2208 					slap_callback **sc;
2209 					for (m2 = &mt->mt_mods; ; m2 = &(*m2)->mi_next) {
2210 						if ( *m2 == mi ) {
2211 							*m2 = mi->mi_next;
2212 							if ( mt->mt_tail == mi )
2213 								mt->mt_tail = ( m2 == &mt->mt_mods ) ? NULL : (modinst *)m2;
2214 							break;
2215 						}
2216 					}
2217 					for (sc = &op->o_callback; ; sc = &(*sc)->sc_next) {
2218 						if ( *sc == cb ) {
2219 							*sc = cb->sc_next;
2220 							break;
2221 						}
2222 					}
2223 					op->o_tmpfree( cb, op->o_tmpmemctx );
2224 					ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
2225 					return SLAPD_ABANDON;
2226 				}
2227 			}
2228 			ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
2229 		} else {
2230 			/* Record that we're modifying this entry now */
2231 			mt = ch_malloc( sizeof(modtarget) );
2232 			mt->mt_mods = mi;
2233 			mt->mt_tail = mi;
2234 			ber_dupbv( &mt->mt_dn, &mi->mi_op->o_req_ndn );
2235 			ldap_pvt_thread_mutex_init( &mt->mt_mutex );
2236 			avl_insert( &si->si_mods, mt, sp_avl_cmp, avl_dup_error );
2237 			ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
2238 		}
2239 		opc->smt = mt;
2240 	}
2241 
2242 	if (( have_psearches || si->si_logs ) && op->o_tag != LDAP_REQ_ADD )
2243 		syncprov_matchops( op, opc, 1 );
2244 
2245 	return SLAP_CB_CONTINUE;
2246 }
2247 
2248 static int
2249 syncprov_op_extended( Operation *op, SlapReply *rs )
2250 {
2251 	if ( exop_is_write( op ))
2252 		return syncprov_op_mod( op, rs );
2253 
2254 	return SLAP_CB_CONTINUE;
2255 }
2256 
2257 typedef struct searchstate {
2258 	slap_overinst *ss_on;
2259 	syncops *ss_so;
2260 	BerVarray ss_ctxcsn;
2261 	int *ss_sids;
2262 	int ss_numcsns;
2263 #define	SS_PRESENT	0x01
2264 #define	SS_CHANGED	0x02
2265 	int ss_flags;
2266 } searchstate;
2267 
2268 typedef struct SyncOperationBuffer {
2269 	Operation		sob_op;
2270 	Opheader		sob_hdr;
2271 	OpExtra			sob_oe;
2272 	AttributeName	sob_extra;	/* not always present */
2273 	/* Further data allocated here */
2274 } SyncOperationBuffer;
2275 
2276 static void
2277 syncprov_detach_op( Operation *op, syncops *so, slap_overinst *on )
2278 {
2279 	SyncOperationBuffer *sopbuf2;
2280 	Operation *op2;
2281 	int i, alen = 0;
2282 	size_t size;
2283 	char *ptr;
2284 	GroupAssertion *g1, *g2;
2285 
2286 	/* count the search attrs */
2287 	for (i=0; op->ors_attrs && !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++) {
2288 		alen += op->ors_attrs[i].an_name.bv_len + 1;
2289 	}
2290 	/* Make a new copy of the operation */
2291 	size = offsetof( SyncOperationBuffer, sob_extra ) +
2292 		(i ? ( (i+1) * sizeof(AttributeName) + alen) : 0) +
2293 		op->o_req_dn.bv_len + 1 +
2294 		op->o_req_ndn.bv_len + 1 +
2295 		op->o_ndn.bv_len + 1 +
2296 		so->s_filterstr.bv_len + 1;
2297 	sopbuf2 = ch_calloc( 1, size );
2298 	op2 = &sopbuf2->sob_op;
2299 	op2->o_hdr = &sopbuf2->sob_hdr;
2300 	LDAP_SLIST_FIRST(&op2->o_extra) = &sopbuf2->sob_oe;
2301 
2302 	/* Copy the fields we care about explicitly, leave the rest alone */
2303 	*op2->o_hdr = *op->o_hdr;
2304 	op2->o_tag = op->o_tag;
2305 	op2->o_time = op->o_time;
2306 	op2->o_bd = on->on_info->oi_origdb;
2307 	op2->o_request = op->o_request;
2308 	op2->o_managedsait = op->o_managedsait;
2309 	LDAP_SLIST_FIRST(&op2->o_extra)->oe_key = on;
2310 	LDAP_SLIST_NEXT(LDAP_SLIST_FIRST(&op2->o_extra), oe_next) = NULL;
2311 
2312 	ptr = (char *) sopbuf2 + offsetof( SyncOperationBuffer, sob_extra );
2313 	if ( i ) {
2314 		op2->ors_attrs = (AttributeName *) ptr;
2315 		ptr = (char *) &op2->ors_attrs[i+1];
2316 		for (i=0; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++) {
2317 			op2->ors_attrs[i] = op->ors_attrs[i];
2318 			op2->ors_attrs[i].an_name.bv_val = ptr;
2319 			ptr = lutil_strcopy( ptr, op->ors_attrs[i].an_name.bv_val ) + 1;
2320 		}
2321 		BER_BVZERO( &op2->ors_attrs[i].an_name );
2322 	}
2323 
2324 	op2->o_authz = op->o_authz;
2325 	op2->o_ndn.bv_val = ptr;
2326 	ptr = lutil_strcopy(ptr, op->o_ndn.bv_val) + 1;
2327 	op2->o_dn = op2->o_ndn;
2328 	op2->o_req_dn.bv_len = op->o_req_dn.bv_len;
2329 	op2->o_req_dn.bv_val = ptr;
2330 	ptr = lutil_strcopy(ptr, op->o_req_dn.bv_val) + 1;
2331 	op2->o_req_ndn.bv_len = op->o_req_ndn.bv_len;
2332 	op2->o_req_ndn.bv_val = ptr;
2333 	ptr = lutil_strcopy(ptr, op->o_req_ndn.bv_val) + 1;
2334 	op2->ors_filterstr.bv_val = ptr;
2335 	strcpy( ptr, so->s_filterstr.bv_val );
2336 	op2->ors_filterstr.bv_len = so->s_filterstr.bv_len;
2337 
2338 	/* Skip the AND/GE clause that we stuck on in front */
2339 	if ( so->s_flags & PS_FIX_FILTER ) {
2340 		op2->ors_filter = op->ors_filter->f_and->f_next;
2341 		so->s_flags ^= PS_FIX_FILTER;
2342 	} else {
2343 		op2->ors_filter = op->ors_filter;
2344 	}
2345 	op2->ors_filter = filter_dup( op2->ors_filter, NULL );
2346 	so->s_op = op2;
2347 
2348 	/* Copy any cached group ACLs individually */
2349 	op2->o_groups = NULL;
2350 	for ( g1=op->o_groups; g1; g1=g1->ga_next ) {
2351 		g2 = ch_malloc( sizeof(GroupAssertion) + g1->ga_len );
2352 		*g2 = *g1;
2353 		strcpy( g2->ga_ndn, g1->ga_ndn );
2354 		g2->ga_next = op2->o_groups;
2355 		op2->o_groups = g2;
2356 	}
2357 	/* Don't allow any further group caching */
2358 	op2->o_do_not_cache = 1;
2359 
2360 	/* Add op2 to conn so abandon will find us */
2361 	op->o_conn->c_n_ops_executing++;
2362 	op->o_conn->c_n_ops_completed--;
2363 	LDAP_STAILQ_INSERT_TAIL( &op->o_conn->c_ops, op2, o_next );
2364 	so->s_flags |= PS_IS_DETACHED;
2365 
2366 	/* Prevent anyone else from trying to send a result for this op */
2367 	op->o_abandon = 1;
2368 }
2369 
2370 static int
2371 syncprov_search_response( Operation *op, SlapReply *rs )
2372 {
2373 	searchstate *ss = op->o_callback->sc_private;
2374 	slap_overinst *on = ss->ss_on;
2375 	syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
2376 	sync_control *srs = op->o_controls[slap_cids.sc_LDAPsync];
2377 
2378 	if ( rs->sr_type == REP_SEARCH || rs->sr_type == REP_SEARCHREF ) {
2379 		Attribute *a;
2380 		/* If we got a referral without a referral object, there's
2381 		 * something missing that we cannot replicate. Just ignore it.
2382 		 * The consumer will abort because we didn't send the expected
2383 		 * control.
2384 		 */
2385 		if ( !rs->sr_entry ) {
2386 			assert( rs->sr_entry != NULL );
2387 			Debug( LDAP_DEBUG_ANY, "bogus referral in context\n",0,0,0 );
2388 			return SLAP_CB_CONTINUE;
2389 		}
2390 		a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryCSN );
2391 		if ( a == NULL && rs->sr_operational_attrs != NULL ) {
2392 			a = attr_find( rs->sr_operational_attrs, slap_schema.si_ad_entryCSN );
2393 		}
2394 		if ( a ) {
2395 			int i, sid;
2396 			sid = slap_parse_csn_sid( &a->a_nvals[0] );
2397 
2398 			/* If not a persistent search */
2399 			if ( !ss->ss_so ) {
2400 				/* Make sure entry is less than the snapshot'd contextCSN */
2401 				for ( i=0; i<ss->ss_numcsns; i++ ) {
2402 					if ( sid == ss->ss_sids[i] && ber_bvcmp( &a->a_nvals[0],
2403 						&ss->ss_ctxcsn[i] ) > 0 ) {
2404 						Debug( LDAP_DEBUG_SYNC,
2405 							"Entry %s CSN %s greater than snapshot %s\n",
2406 							rs->sr_entry->e_name.bv_val,
2407 							a->a_nvals[0].bv_val,
2408 							ss->ss_ctxcsn[i].bv_val );
2409 						return LDAP_SUCCESS;
2410 					}
2411 				}
2412 			}
2413 
2414 			/* Don't send old entries twice */
2415 			if ( srs->sr_state.ctxcsn ) {
2416 				for ( i=0; i<srs->sr_state.numcsns; i++ ) {
2417 					if ( sid == srs->sr_state.sids[i] &&
2418 						ber_bvcmp( &a->a_nvals[0],
2419 							&srs->sr_state.ctxcsn[i] )<= 0 ) {
2420 						Debug( LDAP_DEBUG_SYNC,
2421 							"Entry %s CSN %s older or equal to ctx %s\n",
2422 							rs->sr_entry->e_name.bv_val,
2423 							a->a_nvals[0].bv_val,
2424 							srs->sr_state.ctxcsn[i].bv_val );
2425 						return LDAP_SUCCESS;
2426 					}
2427 				}
2428 			}
2429 		}
2430 		rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
2431 			op->o_tmpmemctx );
2432 		rs->sr_ctrls[1] = NULL;
2433 		rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
2434 		/* If we're in delta-sync mode, always send a cookie */
2435 		if ( si->si_nopres && si->si_usehint && a ) {
2436 			struct berval cookie;
2437 			slap_compose_sync_cookie( op, &cookie, a->a_nvals, srs->sr_state.rid, slap_serverID ? slap_serverID : -1 );
2438 			rs->sr_err = syncprov_state_ctrl( op, rs, rs->sr_entry,
2439 				LDAP_SYNC_ADD, rs->sr_ctrls, 0, 1, &cookie );
2440 			op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
2441 		} else {
2442 			rs->sr_err = syncprov_state_ctrl( op, rs, rs->sr_entry,
2443 				LDAP_SYNC_ADD, rs->sr_ctrls, 0, 0, NULL );
2444 		}
2445 	} else if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS ) {
2446 		struct berval cookie = BER_BVNULL;
2447 
2448 		if ( ( ss->ss_flags & SS_CHANGED ) &&
2449 			ss->ss_ctxcsn && !BER_BVISNULL( &ss->ss_ctxcsn[0] )) {
2450 			slap_compose_sync_cookie( op, &cookie, ss->ss_ctxcsn,
2451 				srs->sr_state.rid, slap_serverID ? slap_serverID : -1 );
2452 
2453 			Debug( LDAP_DEBUG_SYNC, "syncprov_search_response: cookie=%s\n", cookie.bv_val, 0, 0 );
2454 		}
2455 
2456 		/* Is this a regular refresh?
2457 		 * Note: refresh never gets here if there were no changes
2458 		 */
2459 		if ( !ss->ss_so ) {
2460 			rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
2461 				op->o_tmpmemctx );
2462 			rs->sr_ctrls[1] = NULL;
2463 			rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
2464 			rs->sr_err = syncprov_done_ctrl( op, rs, rs->sr_ctrls,
2465 				0, 1, &cookie, ( ss->ss_flags & SS_PRESENT ) ?  LDAP_SYNC_REFRESH_PRESENTS :
2466 					LDAP_SYNC_REFRESH_DELETES );
2467 			op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
2468 		} else {
2469 		/* It's RefreshAndPersist, transition to Persist phase */
2470 			syncprov_sendinfo( op, rs, ( ss->ss_flags & SS_PRESENT ) ?
2471 	 			LDAP_TAG_SYNC_REFRESH_PRESENT : LDAP_TAG_SYNC_REFRESH_DELETE,
2472 				( ss->ss_flags & SS_CHANGED ) ? &cookie : NULL,
2473 				1, NULL, 0 );
2474 			if ( !BER_BVISNULL( &cookie ))
2475 				op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
2476 
2477 			/* Detach this Op from frontend control */
2478 			ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
2479 
2480 			/* But not if this connection was closed along the way */
2481 			if ( op->o_abandon ) {
2482 				ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
2483 				/* syncprov_ab_cleanup will free this syncop */
2484 				return SLAPD_ABANDON;
2485 
2486 			} else {
2487 				ldap_pvt_thread_mutex_lock( &ss->ss_so->s_mutex );
2488 				/* Turn off the refreshing flag */
2489 				ss->ss_so->s_flags ^= PS_IS_REFRESHING;
2490 
2491 				syncprov_detach_op( op, ss->ss_so, on );
2492 
2493 				ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
2494 
2495 				/* If there are queued responses, fire them off */
2496 				if ( ss->ss_so->s_res )
2497 					syncprov_qstart( ss->ss_so );
2498 				ldap_pvt_thread_mutex_unlock( &ss->ss_so->s_mutex );
2499 			}
2500 
2501 			return LDAP_SUCCESS;
2502 		}
2503 	}
2504 
2505 	return SLAP_CB_CONTINUE;
2506 }
2507 
2508 static int
2509 syncprov_op_search( Operation *op, SlapReply *rs )
2510 {
2511 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
2512 	syncprov_info_t		*si = (syncprov_info_t *)on->on_bi.bi_private;
2513 	slap_callback	*cb;
2514 	int gotstate = 0, changed = 0, do_present = 0;
2515 	syncops *sop = NULL;
2516 	searchstate *ss;
2517 	sync_control *srs;
2518 	BerVarray ctxcsn;
2519 	int i, *sids, numcsns;
2520 	struct berval mincsn, maxcsn;
2521 	int minsid, maxsid;
2522 	int dirty = 0;
2523 
2524 	if ( !(op->o_sync_mode & SLAP_SYNC_REFRESH) ) return SLAP_CB_CONTINUE;
2525 
2526 	if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
2527 		send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "illegal value for derefAliases" );
2528 		return rs->sr_err;
2529 	}
2530 
2531 	srs = op->o_controls[slap_cids.sc_LDAPsync];
2532 
2533 	/* If this is a persistent search, set it up right away */
2534 	if ( op->o_sync_mode & SLAP_SYNC_PERSIST ) {
2535 		syncops so = {0};
2536 		fbase_cookie fc;
2537 		opcookie opc;
2538 		slap_callback sc = {0};
2539 
2540 		fc.fss = &so;
2541 		fc.fbase = 0;
2542 		so.s_eid = NOID;
2543 		so.s_op = op;
2544 		so.s_flags = PS_IS_REFRESHING | PS_FIND_BASE;
2545 		/* syncprov_findbase expects to be called as a callback... */
2546 		sc.sc_private = &opc;
2547 		opc.son = on;
2548 		ldap_pvt_thread_mutex_init( &so.s_mutex );
2549 		cb = op->o_callback;
2550 		op->o_callback = &sc;
2551 		rs->sr_err = syncprov_findbase( op, &fc );
2552 		op->o_callback = cb;
2553 		ldap_pvt_thread_mutex_destroy( &so.s_mutex );
2554 
2555 		if ( rs->sr_err != LDAP_SUCCESS ) {
2556 			send_ldap_result( op, rs );
2557 			return rs->sr_err;
2558 		}
2559 		sop = ch_malloc( sizeof( syncops ));
2560 		*sop = so;
2561 		sop->s_rid = srs->sr_state.rid;
2562 		sop->s_sid = srs->sr_state.sid;
2563 		/* set refcount=2 to prevent being freed out from under us
2564 		 * by abandons that occur while we're running here
2565 		 */
2566 		sop->s_inuse = 2;
2567 
2568 		ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
2569 		while ( si->si_active ) {
2570 			/* Wait for active mods to finish before proceeding, as they
2571 			 * may already have inspected the si_ops list looking for
2572 			 * consumers to replicate the change to.  Using the log
2573 			 * doesn't help, as we may finish playing it before the
2574 			 * active mods gets added to it.
2575 			 */
2576 			ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
2577 			if ( slapd_shutdown ) {
2578 				ch_free( sop );
2579 				return SLAPD_ABANDON;
2580 			}
2581 			if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
2582 				ldap_pvt_thread_yield();
2583 			ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
2584 		}
2585 		if ( op->o_abandon ) {
2586 			ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
2587 			ch_free( sop );
2588 			return SLAPD_ABANDON;
2589 		}
2590 		ldap_pvt_thread_mutex_init( &sop->s_mutex );
2591 		sop->s_next = si->si_ops;
2592 		sop->s_si = si;
2593 		si->si_ops = sop;
2594 		ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
2595 	}
2596 
2597 	/* snapshot the ctxcsn
2598 	 * Note: this must not be done before the psearch setup. (ITS#8365)
2599 	 */
2600 	ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
2601 	numcsns = si->si_numcsns;
2602 	if ( numcsns ) {
2603 		ber_bvarray_dup_x( &ctxcsn, si->si_ctxcsn, op->o_tmpmemctx );
2604 		sids = op->o_tmpalloc( numcsns * sizeof(int), op->o_tmpmemctx );
2605 		for ( i=0; i<numcsns; i++ )
2606 			sids[i] = si->si_sids[i];
2607 	} else {
2608 		ctxcsn = NULL;
2609 		sids = NULL;
2610 	}
2611 	dirty = si->si_dirty;
2612 	ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
2613 
2614 	/* If we have a cookie, handle the PRESENT lookups */
2615 	if ( srs->sr_state.ctxcsn ) {
2616 		sessionlog *sl;
2617 		int i, j;
2618 
2619 		/* If we don't have any CSN of our own yet, bail out.
2620 		 */
2621 		if ( !numcsns ) {
2622 			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
2623 			rs->sr_text = "consumer has state info but provider doesn't!";
2624 			goto bailout;
2625 		}
2626 
2627 		if ( !si->si_nopres )
2628 			do_present = SS_PRESENT;
2629 
2630 		/* If there are SIDs we don't recognize in the cookie, drop them */
2631 		for (i=0; i<srs->sr_state.numcsns; ) {
2632 			for (j=i; j<numcsns; j++) {
2633 				if ( srs->sr_state.sids[i] <= sids[j] ) {
2634 					break;
2635 				}
2636 			}
2637 			/* not found */
2638 			if ( j == numcsns || srs->sr_state.sids[i] != sids[j] ) {
2639 				char *tmp = srs->sr_state.ctxcsn[i].bv_val;
2640 				srs->sr_state.numcsns--;
2641 				for ( j=i; j<srs->sr_state.numcsns; j++ ) {
2642 					srs->sr_state.ctxcsn[j] = srs->sr_state.ctxcsn[j+1];
2643 					srs->sr_state.sids[j] = srs->sr_state.sids[j+1];
2644 				}
2645 				srs->sr_state.ctxcsn[j].bv_val = tmp;
2646 				srs->sr_state.ctxcsn[j].bv_len = 0;
2647 				continue;
2648 			}
2649 			i++;
2650 		}
2651 
2652 		if (srs->sr_state.numcsns != numcsns) {
2653 			/* consumer doesn't have the right number of CSNs */
2654 			changed = SS_CHANGED;
2655 			if ( srs->sr_state.ctxcsn ) {
2656 				ber_bvarray_free_x( srs->sr_state.ctxcsn, op->o_tmpmemctx );
2657 				srs->sr_state.ctxcsn = NULL;
2658 			}
2659 			if ( srs->sr_state.sids ) {
2660 				slap_sl_free( srs->sr_state.sids, op->o_tmpmemctx );
2661 				srs->sr_state.sids = NULL;
2662 			}
2663 			srs->sr_state.numcsns = 0;
2664 			goto shortcut;
2665 		}
2666 
2667 		/* Find the smallest CSN which differs from contextCSN */
2668 		mincsn.bv_len = 0;
2669 		maxcsn.bv_len = 0;
2670 		for ( i=0,j=0; i<srs->sr_state.numcsns; i++ ) {
2671 			int newer;
2672 			while ( srs->sr_state.sids[i] != sids[j] ) j++;
2673 			if ( BER_BVISEMPTY( &maxcsn ) || ber_bvcmp( &maxcsn,
2674 				&srs->sr_state.ctxcsn[i] ) < 0 ) {
2675 				maxcsn = srs->sr_state.ctxcsn[i];
2676 				maxsid = sids[j];
2677 			}
2678 			newer = ber_bvcmp( &srs->sr_state.ctxcsn[i], &ctxcsn[j] );
2679 			/* If our state is newer, tell consumer about changes */
2680 			if ( newer < 0) {
2681 				changed = SS_CHANGED;
2682 				if ( BER_BVISEMPTY( &mincsn ) || ber_bvcmp( &mincsn,
2683 					&srs->sr_state.ctxcsn[i] ) > 0 ) {
2684 					mincsn = srs->sr_state.ctxcsn[i];
2685 					minsid = sids[j];
2686 				}
2687 			} else if ( newer > 0 && sids[j] == slap_serverID ) {
2688 			/* our state is older, complain to consumer */
2689 				rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
2690 				rs->sr_text = "consumer state is newer than provider!";
2691 bailout:
2692 				if ( sop ) {
2693 					syncops **sp = &si->si_ops;
2694 
2695 					ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
2696 					while ( *sp != sop )
2697 						sp = &(*sp)->s_next;
2698 					*sp = sop->s_next;
2699 					ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
2700 					ch_free( sop );
2701 				}
2702 				rs->sr_ctrls = NULL;
2703 				send_ldap_result( op, rs );
2704 				return rs->sr_err;
2705 			}
2706  		}
2707 		if ( BER_BVISEMPTY( &mincsn )) {
2708 			mincsn = maxcsn;
2709 			minsid = maxsid;
2710 		}
2711 
2712 		/* If nothing has changed, shortcut it */
2713 		if ( !changed && !dirty ) {
2714 			do_present = 0;
2715 no_change:	if ( !(op->o_sync_mode & SLAP_SYNC_PERSIST) ) {
2716 				LDAPControl	*ctrls[2];
2717 
2718 				ctrls[0] = NULL;
2719 				ctrls[1] = NULL;
2720 				syncprov_done_ctrl( op, rs, ctrls, 0, 0,
2721 					NULL, LDAP_SYNC_REFRESH_DELETES );
2722 				rs->sr_ctrls = ctrls;
2723 				rs->sr_err = LDAP_SUCCESS;
2724 				send_ldap_result( op, rs );
2725 				rs->sr_ctrls = NULL;
2726 				return rs->sr_err;
2727 			}
2728 			goto shortcut;
2729 		}
2730 
2731 		/* Do we have a sessionlog for this search? */
2732 		sl=si->si_logs;
2733 		if ( sl ) {
2734 			int do_play = 0;
2735 			ldap_pvt_thread_mutex_lock( &sl->sl_mutex );
2736 			/* Are there any log entries, and is the consumer state
2737 			 * present in the session log?
2738 			 */
2739 			if ( sl->sl_num > 0 ) {
2740 				int i;
2741 				for ( i=0; i<sl->sl_numcsns; i++ ) {
2742 					/* SID not present == new enough */
2743 					if ( minsid < sl->sl_sids[i] ) {
2744 						do_play = 1;
2745 						break;
2746 					}
2747 					/* SID present */
2748 					if ( minsid == sl->sl_sids[i] ) {
2749 						/* new enough? */
2750 						if ( ber_bvcmp( &mincsn, &sl->sl_mincsn[i] ) >= 0 )
2751 							do_play = 1;
2752 						break;
2753 					}
2754 				}
2755 				/* SID not present == new enough */
2756 				if ( i == sl->sl_numcsns )
2757 					do_play = 1;
2758 			}
2759 			if ( do_play ) {
2760 				do_present = 0;
2761 				/* mutex is unlocked in playlog */
2762 				syncprov_playlog( op, rs, sl, srs, ctxcsn, numcsns, sids );
2763 			} else {
2764 				ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
2765 			}
2766 		}
2767 		/* Is the CSN still present in the database? */
2768 		if ( syncprov_findcsn( op, FIND_CSN, &mincsn ) != LDAP_SUCCESS ) {
2769 			/* No, so a reload is required */
2770 			/* the 2.2 consumer doesn't send this hint */
2771 			if ( si->si_usehint && srs->sr_rhint == 0 ) {
2772 				if ( ctxcsn )
2773 					ber_bvarray_free_x( ctxcsn, op->o_tmpmemctx );
2774 				if ( sids )
2775 					op->o_tmpfree( sids, op->o_tmpmemctx );
2776 				rs->sr_err = LDAP_SYNC_REFRESH_REQUIRED;
2777 				rs->sr_text = "sync cookie is stale";
2778 				goto bailout;
2779 			}
2780 			if ( srs->sr_state.ctxcsn ) {
2781 				ber_bvarray_free_x( srs->sr_state.ctxcsn, op->o_tmpmemctx );
2782 				srs->sr_state.ctxcsn = NULL;
2783 			}
2784 			if ( srs->sr_state.sids ) {
2785 				slap_sl_free( srs->sr_state.sids, op->o_tmpmemctx );
2786 				srs->sr_state.sids = NULL;
2787 			}
2788 			srs->sr_state.numcsns = 0;
2789 		} else {
2790 			gotstate = 1;
2791 			/* If changed and doing Present lookup, send Present UUIDs */
2792 			if ( do_present && syncprov_findcsn( op, FIND_PRESENT, 0 ) !=
2793 				LDAP_SUCCESS ) {
2794 				if ( ctxcsn )
2795 					ber_bvarray_free_x( ctxcsn, op->o_tmpmemctx );
2796 				if ( sids )
2797 					op->o_tmpfree( sids, op->o_tmpmemctx );
2798 				goto bailout;
2799 			}
2800 		}
2801 	} else {
2802 		/* The consumer knows nothing, we know nothing. OK. */
2803 		if (!numcsns)
2804 			goto no_change;
2805 		/* No consumer state, assume something has changed */
2806 		changed = SS_CHANGED;
2807 	}
2808 
2809 shortcut:
2810 	/* Append CSN range to search filter, save original filter
2811 	 * for persistent search evaluation
2812 	 */
2813 	if ( sop ) {
2814 		ldap_pvt_thread_mutex_lock( &sop->s_mutex );
2815 		sop->s_filterstr = op->ors_filterstr;
2816 		/* correct the refcount that was set to 2 before */
2817 		sop->s_inuse--;
2818 	}
2819 
2820 	/* If something changed, find the changes */
2821 	if ( gotstate && ( changed || dirty ) ) {
2822 		Filter *fand, *fava;
2823 
2824 		fand = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
2825 		fand->f_choice = LDAP_FILTER_AND;
2826 		fand->f_next = NULL;
2827 		fava = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
2828 		fand->f_and = fava;
2829 		fava->f_choice = LDAP_FILTER_GE;
2830 		fava->f_ava = op->o_tmpalloc( sizeof(AttributeAssertion), op->o_tmpmemctx );
2831 		fava->f_ava->aa_desc = slap_schema.si_ad_entryCSN;
2832 #ifdef LDAP_COMP_MATCH
2833 		fava->f_ava->aa_cf = NULL;
2834 #endif
2835 		ber_dupbv_x( &fava->f_ava->aa_value, &mincsn, op->o_tmpmemctx );
2836 		fava->f_next = op->ors_filter;
2837 		op->ors_filter = fand;
2838 		filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
2839 		if ( sop ) {
2840 			sop->s_flags |= PS_FIX_FILTER;
2841 		}
2842 	}
2843 	if ( sop ) {
2844 		ldap_pvt_thread_mutex_unlock( &sop->s_mutex );
2845 	}
2846 
2847 	/* Let our callback add needed info to returned entries */
2848 	cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(searchstate), op->o_tmpmemctx);
2849 	ss = (searchstate *)(cb+1);
2850 	ss->ss_on = on;
2851 	ss->ss_so = sop;
2852 	ss->ss_flags = do_present | changed;
2853 	ss->ss_ctxcsn = ctxcsn;
2854 	ss->ss_numcsns = numcsns;
2855 	ss->ss_sids = sids;
2856 	cb->sc_response = syncprov_search_response;
2857 	cb->sc_private = ss;
2858 	cb->sc_next = op->o_callback;
2859 	op->o_callback = cb;
2860 
2861 	/* If this is a persistent search and no changes were reported during
2862 	 * the refresh phase, just invoke the response callback to transition
2863 	 * us into persist phase
2864 	 */
2865 	if ( !changed && !dirty ) {
2866 		rs->sr_err = LDAP_SUCCESS;
2867 		rs->sr_nentries = 0;
2868 		send_ldap_result( op, rs );
2869 		return rs->sr_err;
2870 	}
2871 	return SLAP_CB_CONTINUE;
2872 }
2873 
2874 static int
2875 syncprov_operational(
2876 	Operation *op,
2877 	SlapReply *rs )
2878 {
2879 	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
2880 	syncprov_info_t		*si = (syncprov_info_t *)on->on_bi.bi_private;
2881 
2882 	/* This prevents generating unnecessarily; frontend will strip
2883 	 * any statically stored copy.
2884 	 */
2885 	if ( op->o_sync != SLAP_CONTROL_NONE )
2886 		return SLAP_CB_CONTINUE;
2887 
2888 	if ( rs->sr_entry &&
2889 		dn_match( &rs->sr_entry->e_nname, &si->si_contextdn )) {
2890 
2891 		if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
2892 			ad_inlist( slap_schema.si_ad_contextCSN, rs->sr_attrs )) {
2893 			Attribute *a, **ap = NULL;
2894 
2895 			for ( a=rs->sr_entry->e_attrs; a; a=a->a_next ) {
2896 				if ( a->a_desc == slap_schema.si_ad_contextCSN )
2897 					break;
2898 			}
2899 
2900 			ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
2901 			if ( si->si_ctxcsn ) {
2902 				if ( !a ) {
2903 					for ( ap = &rs->sr_operational_attrs; *ap;
2904 						ap=&(*ap)->a_next );
2905 
2906 					a = attr_alloc( slap_schema.si_ad_contextCSN );
2907 					*ap = a;
2908 				}
2909 
2910 				if ( !ap ) {
2911 					if ( rs_entry2modifiable( op, rs, on )) {
2912 						a = attr_find( rs->sr_entry->e_attrs,
2913 							slap_schema.si_ad_contextCSN );
2914 					}
2915 					if ( a->a_nvals != a->a_vals ) {
2916 						ber_bvarray_free( a->a_nvals );
2917 					}
2918 					a->a_nvals = NULL;
2919 					ber_bvarray_free( a->a_vals );
2920 					a->a_vals = NULL;
2921 					a->a_numvals = 0;
2922 				}
2923 				attr_valadd( a, si->si_ctxcsn, si->si_ctxcsn, si->si_numcsns );
2924 			}
2925 			ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
2926 		}
2927 	}
2928 	return SLAP_CB_CONTINUE;
2929 }
2930 
2931 enum {
2932 	SP_CHKPT = 1,
2933 	SP_SESSL,
2934 	SP_NOPRES,
2935 	SP_USEHINT
2936 };
2937 
2938 static ConfigDriver sp_cf_gen;
2939 
2940 static ConfigTable spcfg[] = {
2941 	{ "syncprov-checkpoint", "ops> <minutes", 3, 3, 0, ARG_MAGIC|SP_CHKPT,
2942 		sp_cf_gen, "( OLcfgOvAt:1.1 NAME 'olcSpCheckpoint' "
2943 			"DESC 'ContextCSN checkpoint interval in ops and minutes' "
2944 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
2945 	{ "syncprov-sessionlog", "ops", 2, 2, 0, ARG_INT|ARG_MAGIC|SP_SESSL,
2946 		sp_cf_gen, "( OLcfgOvAt:1.2 NAME 'olcSpSessionlog' "
2947 			"DESC 'Session log size in ops' "
2948 			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
2949 	{ "syncprov-nopresent", NULL, 2, 2, 0, ARG_ON_OFF|ARG_MAGIC|SP_NOPRES,
2950 		sp_cf_gen, "( OLcfgOvAt:1.3 NAME 'olcSpNoPresent' "
2951 			"DESC 'Omit Present phase processing' "
2952 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
2953 	{ "syncprov-reloadhint", NULL, 2, 2, 0, ARG_ON_OFF|ARG_MAGIC|SP_USEHINT,
2954 		sp_cf_gen, "( OLcfgOvAt:1.4 NAME 'olcSpReloadHint' "
2955 			"DESC 'Observe Reload Hint in Request control' "
2956 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
2957 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
2958 };
2959 
2960 static ConfigOCs spocs[] = {
2961 	{ "( OLcfgOvOc:1.1 "
2962 		"NAME 'olcSyncProvConfig' "
2963 		"DESC 'SyncRepl Provider configuration' "
2964 		"SUP olcOverlayConfig "
2965 		"MAY ( olcSpCheckpoint "
2966 			"$ olcSpSessionlog "
2967 			"$ olcSpNoPresent "
2968 			"$ olcSpReloadHint "
2969 		") )",
2970 			Cft_Overlay, spcfg },
2971 	{ NULL, 0, NULL }
2972 };
2973 
2974 static int
2975 sp_cf_gen(ConfigArgs *c)
2976 {
2977 	slap_overinst		*on = (slap_overinst *)c->bi;
2978 	syncprov_info_t		*si = (syncprov_info_t *)on->on_bi.bi_private;
2979 	int rc = 0;
2980 
2981 	if ( c->op == SLAP_CONFIG_EMIT ) {
2982 		switch ( c->type ) {
2983 		case SP_CHKPT:
2984 			if ( si->si_chkops || si->si_chktime ) {
2985 				struct berval bv;
2986 				/* we assume si_chktime is a multiple of 60
2987 				 * because the parsed value was originally
2988 				 * multiplied by 60 */
2989 				bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
2990 					"%d %d", si->si_chkops, si->si_chktime/60 );
2991 				if ( bv.bv_len >= sizeof( c->cr_msg ) ) {
2992 					rc = 1;
2993 				} else {
2994 					bv.bv_val = c->cr_msg;
2995 					value_add_one( &c->rvalue_vals, &bv );
2996 				}
2997 			} else {
2998 				rc = 1;
2999 			}
3000 			break;
3001 		case SP_SESSL:
3002 			if ( si->si_logs ) {
3003 				c->value_int = si->si_logs->sl_size;
3004 			} else {
3005 				rc = 1;
3006 			}
3007 			break;
3008 		case SP_NOPRES:
3009 			if ( si->si_nopres ) {
3010 				c->value_int = 1;
3011 			} else {
3012 				rc = 1;
3013 			}
3014 			break;
3015 		case SP_USEHINT:
3016 			if ( si->si_usehint ) {
3017 				c->value_int = 1;
3018 			} else {
3019 				rc = 1;
3020 			}
3021 			break;
3022 		}
3023 		return rc;
3024 	} else if ( c->op == LDAP_MOD_DELETE ) {
3025 		switch ( c->type ) {
3026 		case SP_CHKPT:
3027 			si->si_chkops = 0;
3028 			si->si_chktime = 0;
3029 			break;
3030 		case SP_SESSL:
3031 			si->si_logs->sl_size = 0;
3032 			break;
3033 		case SP_NOPRES:
3034 			si->si_nopres = 0;
3035 			break;
3036 		case SP_USEHINT:
3037 			si->si_usehint = 0;
3038 			break;
3039 		}
3040 		return rc;
3041 	}
3042 	switch ( c->type ) {
3043 	case SP_CHKPT:
3044 		if ( lutil_atoi( &si->si_chkops, c->argv[1] ) != 0 ) {
3045 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s unable to parse checkpoint ops # \"%s\"",
3046 				c->argv[0], c->argv[1] );
3047 			Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
3048 				"%s: %s\n", c->log, c->cr_msg, 0 );
3049 			return ARG_BAD_CONF;
3050 		}
3051 		if ( si->si_chkops <= 0 ) {
3052 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid checkpoint ops # \"%d\"",
3053 				c->argv[0], si->si_chkops );
3054 			Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
3055 				"%s: %s\n", c->log, c->cr_msg, 0 );
3056 			return ARG_BAD_CONF;
3057 		}
3058 		if ( lutil_atoi( &si->si_chktime, c->argv[2] ) != 0 ) {
3059 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s unable to parse checkpoint time \"%s\"",
3060 				c->argv[0], c->argv[1] );
3061 			Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
3062 				"%s: %s\n", c->log, c->cr_msg, 0 );
3063 			return ARG_BAD_CONF;
3064 		}
3065 		if ( si->si_chktime <= 0 ) {
3066 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid checkpoint time \"%d\"",
3067 				c->argv[0], si->si_chkops );
3068 			Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
3069 				"%s: %s\n", c->log, c->cr_msg, 0 );
3070 			return ARG_BAD_CONF;
3071 		}
3072 		si->si_chktime *= 60;
3073 		break;
3074 	case SP_SESSL: {
3075 		sessionlog *sl;
3076 		int size = c->value_int;
3077 
3078 		if ( size < 0 ) {
3079 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s size %d is negative",
3080 				c->argv[0], size );
3081 			Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
3082 				"%s: %s\n", c->log, c->cr_msg, 0 );
3083 			return ARG_BAD_CONF;
3084 		}
3085 		sl = si->si_logs;
3086 		if ( !sl ) {
3087 			sl = ch_malloc( sizeof( sessionlog ));
3088 			sl->sl_mincsn = NULL;
3089 			sl->sl_sids = NULL;
3090 			sl->sl_num = 0;
3091 			sl->sl_numcsns = 0;
3092 			sl->sl_head = sl->sl_tail = NULL;
3093 			ldap_pvt_thread_mutex_init( &sl->sl_mutex );
3094 			si->si_logs = sl;
3095 		}
3096 		sl->sl_size = size;
3097 		}
3098 		break;
3099 	case SP_NOPRES:
3100 		si->si_nopres = c->value_int;
3101 		break;
3102 	case SP_USEHINT:
3103 		si->si_usehint = c->value_int;
3104 		break;
3105 	}
3106 	return rc;
3107 }
3108 
3109 /* ITS#3456 we cannot run this search on the main thread, must use a
3110  * child thread in order to insure we have a big enough stack.
3111  */
3112 static void *
3113 syncprov_db_otask(
3114 	void *ptr
3115 )
3116 {
3117 	syncprov_findcsn( ptr, FIND_MAXCSN, 0 );
3118 	return NULL;
3119 }
3120 
3121 
3122 /* Read any existing contextCSN from the underlying db.
3123  * Then search for any entries newer than that. If no value exists,
3124  * just generate it. Cache whatever result.
3125  */
3126 static int
3127 syncprov_db_open(
3128 	BackendDB *be,
3129 	ConfigReply *cr
3130 )
3131 {
3132 	slap_overinst   *on = (slap_overinst *) be->bd_info;
3133 	syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
3134 
3135 	Connection conn = { 0 };
3136 	OperationBuffer opbuf;
3137 	Operation *op;
3138 	Entry *e = NULL;
3139 	Attribute *a;
3140 	int rc;
3141 	void *thrctx = NULL;
3142 
3143 	if ( !SLAP_LASTMOD( be )) {
3144 		Debug( LDAP_DEBUG_ANY,
3145 			"syncprov_db_open: invalid config, lastmod must be enabled\n", 0, 0, 0 );
3146 		return -1;
3147 	}
3148 
3149 	if ( slapMode & SLAP_TOOL_MODE ) {
3150 		return 0;
3151 	}
3152 
3153 	rc = overlay_register_control( be, LDAP_CONTROL_SYNC );
3154 	if ( rc ) {
3155 		return rc;
3156 	}
3157 
3158 	thrctx = ldap_pvt_thread_pool_context();
3159 	connection_fake_init2( &conn, &opbuf, thrctx, 0 );
3160 	op = &opbuf.ob_op;
3161 	op->o_bd = be;
3162 	op->o_dn = be->be_rootdn;
3163 	op->o_ndn = be->be_rootndn;
3164 
3165 	if ( SLAP_SYNC_SUBENTRY( be )) {
3166 		build_new_dn( &si->si_contextdn, be->be_nsuffix,
3167 			(struct berval *)&slap_ldapsync_cn_bv, NULL );
3168 	} else {
3169 		si->si_contextdn = be->be_nsuffix[0];
3170 	}
3171 	rc = overlay_entry_get_ov( op, &si->si_contextdn, NULL,
3172 		slap_schema.si_ad_contextCSN, 0, &e, on );
3173 
3174 	if ( e ) {
3175 		ldap_pvt_thread_t tid;
3176 
3177 		a = attr_find( e->e_attrs, slap_schema.si_ad_contextCSN );
3178 		if ( a ) {
3179 			ber_bvarray_dup_x( &si->si_ctxcsn, a->a_vals, NULL );
3180 			si->si_numcsns = a->a_numvals;
3181 			si->si_sids = slap_parse_csn_sids( si->si_ctxcsn, a->a_numvals, NULL );
3182 			slap_sort_csn_sids( si->si_ctxcsn, si->si_sids, si->si_numcsns, NULL );
3183 		}
3184 		overlay_entry_release_ov( op, e, 0, on );
3185 		if ( si->si_ctxcsn && !SLAP_DBCLEAN( be )) {
3186 			op->o_tag = LDAP_REQ_SEARCH;
3187 			op->o_req_dn = be->be_suffix[0];
3188 			op->o_req_ndn = be->be_nsuffix[0];
3189 			op->ors_scope = LDAP_SCOPE_SUBTREE;
3190 			ldap_pvt_thread_create( &tid, 0, syncprov_db_otask, op );
3191 			ldap_pvt_thread_join( tid, NULL );
3192 		}
3193 	}
3194 
3195 	/* Didn't find a contextCSN, should we generate one? */
3196 	if ( !si->si_ctxcsn ) {
3197 		char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
3198 		struct berval csn;
3199 
3200 		if ( SLAP_SINGLE_SHADOW( op->o_bd ) ) {
3201 			/* Not in charge of this serverID, don't generate anything. */
3202 			goto out;
3203 		}
3204 		if ( !SLAP_SYNC_SUBENTRY( be ) && rc != LDAP_SUCCESS
3205 				&& rc != LDAP_NO_SUCH_ATTRIBUTE ) {
3206 			/* If the DB is genuinely empty, don't generate one either. */
3207 			goto out;
3208 		}
3209 		csn.bv_val = csnbuf;
3210 		csn.bv_len = sizeof( csnbuf );
3211 		slap_get_csn( op, &csn, 0 );
3212 		value_add_one( &si->si_ctxcsn, &csn );
3213 		si->si_numcsns = 1;
3214 		si->si_sids = ch_malloc( sizeof(int) );
3215 		si->si_sids[0] = slap_serverID;
3216 
3217 		/* make sure we do a checkpoint on close */
3218 		si->si_numops++;
3219 	}
3220 
3221 	/* Initialize the sessionlog mincsn */
3222 	if ( si->si_logs && si->si_numcsns ) {
3223 		sessionlog *sl = si->si_logs;
3224 		int i;
3225 		ber_bvarray_dup_x( &sl->sl_mincsn, si->si_ctxcsn, NULL );
3226 		sl->sl_numcsns = si->si_numcsns;
3227 		sl->sl_sids = ch_malloc( si->si_numcsns * sizeof(int) );
3228 		for ( i=0; i < si->si_numcsns; i++ )
3229 			sl->sl_sids[i] = si->si_sids[i];
3230 	}
3231 
3232 out:
3233 	op->o_bd->bd_info = (BackendInfo *)on;
3234 	return 0;
3235 }
3236 
3237 /* Write the current contextCSN into the underlying db.
3238  */
3239 static int
3240 syncprov_db_close(
3241 	BackendDB *be,
3242 	ConfigReply *cr
3243 )
3244 {
3245     slap_overinst   *on = (slap_overinst *) be->bd_info;
3246     syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
3247 #ifdef SLAP_CONFIG_DELETE
3248 	syncops *so, *sonext;
3249 #endif /* SLAP_CONFIG_DELETE */
3250 
3251 	if ( slapMode & SLAP_TOOL_MODE ) {
3252 		return 0;
3253 	}
3254 	if ( si->si_numops ) {
3255 		Connection conn = {0};
3256 		OperationBuffer opbuf;
3257 		Operation *op;
3258 		void *thrctx;
3259 
3260 		thrctx = ldap_pvt_thread_pool_context();
3261 		connection_fake_init2( &conn, &opbuf, thrctx, 0 );
3262 		op = &opbuf.ob_op;
3263 		op->o_bd = be;
3264 		op->o_dn = be->be_rootdn;
3265 		op->o_ndn = be->be_rootndn;
3266 		syncprov_checkpoint( op, on );
3267 	}
3268 
3269 #ifdef SLAP_CONFIG_DELETE
3270 	if ( !slapd_shutdown ) {
3271 		ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
3272 		for ( so=si->si_ops, sonext=so;  so; so=sonext  ) {
3273 			SlapReply rs = {REP_RESULT};
3274 			rs.sr_err = LDAP_UNAVAILABLE;
3275 			send_ldap_result( so->s_op, &rs );
3276 			sonext=so->s_next;
3277 			syncprov_drop_psearch( so, 0);
3278 		}
3279 		si->si_ops=NULL;
3280 		ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
3281 	}
3282 	overlay_unregister_control( be, LDAP_CONTROL_SYNC );
3283 #endif /* SLAP_CONFIG_DELETE */
3284 
3285     return 0;
3286 }
3287 
3288 static int
3289 syncprov_db_init(
3290 	BackendDB *be,
3291 	ConfigReply *cr
3292 )
3293 {
3294 	slap_overinst	*on = (slap_overinst *)be->bd_info;
3295 	syncprov_info_t	*si;
3296 
3297 	if ( SLAP_ISGLOBALOVERLAY( be ) ) {
3298 		Debug( LDAP_DEBUG_ANY,
3299 			"syncprov must be instantiated within a database.\n",
3300 			0, 0, 0 );
3301 		return 1;
3302 	}
3303 
3304 	si = ch_calloc(1, sizeof(syncprov_info_t));
3305 	on->on_bi.bi_private = si;
3306 	ldap_pvt_thread_rdwr_init( &si->si_csn_rwlock );
3307 	ldap_pvt_thread_mutex_init( &si->si_ops_mutex );
3308 	ldap_pvt_thread_mutex_init( &si->si_mods_mutex );
3309 	ldap_pvt_thread_mutex_init( &si->si_resp_mutex );
3310 
3311 	csn_anlist[0].an_desc = slap_schema.si_ad_entryCSN;
3312 	csn_anlist[0].an_name = slap_schema.si_ad_entryCSN->ad_cname;
3313 	csn_anlist[1].an_desc = slap_schema.si_ad_entryUUID;
3314 	csn_anlist[1].an_name = slap_schema.si_ad_entryUUID->ad_cname;
3315 
3316 	uuid_anlist[0].an_desc = slap_schema.si_ad_entryUUID;
3317 	uuid_anlist[0].an_name = slap_schema.si_ad_entryUUID->ad_cname;
3318 
3319 	return 0;
3320 }
3321 
3322 static int
3323 syncprov_db_destroy(
3324 	BackendDB *be,
3325 	ConfigReply *cr
3326 )
3327 {
3328 	slap_overinst	*on = (slap_overinst *)be->bd_info;
3329 	syncprov_info_t	*si = (syncprov_info_t *)on->on_bi.bi_private;
3330 
3331 	if ( si ) {
3332 		if ( si->si_logs ) {
3333 			sessionlog *sl = si->si_logs;
3334 			slog_entry *se = sl->sl_head;
3335 
3336 			while ( se ) {
3337 				slog_entry *se_next = se->se_next;
3338 				ch_free( se );
3339 				se = se_next;
3340 			}
3341 			if ( sl->sl_mincsn )
3342 				ber_bvarray_free( sl->sl_mincsn );
3343 			if ( sl->sl_sids )
3344 				ch_free( sl->sl_sids );
3345 
3346 			ldap_pvt_thread_mutex_destroy(&si->si_logs->sl_mutex);
3347 			ch_free( si->si_logs );
3348 		}
3349 		if ( si->si_ctxcsn )
3350 			ber_bvarray_free( si->si_ctxcsn );
3351 		if ( si->si_sids )
3352 			ch_free( si->si_sids );
3353 		ldap_pvt_thread_mutex_destroy( &si->si_resp_mutex );
3354 		ldap_pvt_thread_mutex_destroy( &si->si_mods_mutex );
3355 		ldap_pvt_thread_mutex_destroy( &si->si_ops_mutex );
3356 		ldap_pvt_thread_rdwr_destroy( &si->si_csn_rwlock );
3357 		ch_free( si );
3358 	}
3359 
3360 	return 0;
3361 }
3362 
3363 static int syncprov_parseCtrl (
3364 	Operation *op,
3365 	SlapReply *rs,
3366 	LDAPControl *ctrl )
3367 {
3368 	ber_tag_t tag;
3369 	BerElementBuffer berbuf;
3370 	BerElement *ber = (BerElement *)&berbuf;
3371 	ber_int_t mode;
3372 	ber_len_t len;
3373 	struct berval cookie = BER_BVNULL;
3374 	sync_control *sr;
3375 	int rhint = 0;
3376 
3377 	if ( op->o_sync != SLAP_CONTROL_NONE ) {
3378 		rs->sr_text = "Sync control specified multiple times";
3379 		return LDAP_PROTOCOL_ERROR;
3380 	}
3381 
3382 	if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
3383 		rs->sr_text = "Sync control specified with pagedResults control";
3384 		return LDAP_PROTOCOL_ERROR;
3385 	}
3386 
3387 	if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
3388 		rs->sr_text = "Sync control value is absent";
3389 		return LDAP_PROTOCOL_ERROR;
3390 	}
3391 
3392 	if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
3393 		rs->sr_text = "Sync control value is empty";
3394 		return LDAP_PROTOCOL_ERROR;
3395 	}
3396 
3397 	/* Parse the control value
3398 	 *      syncRequestValue ::= SEQUENCE {
3399 	 *              mode   ENUMERATED {
3400 	 *                      -- 0 unused
3401 	 *                      refreshOnly		(1),
3402 	 *                      -- 2 reserved
3403 	 *                      refreshAndPersist	(3)
3404 	 *              },
3405 	 *              cookie  syncCookie OPTIONAL
3406 	 *      }
3407 	 */
3408 
3409 	ber_init2( ber, &ctrl->ldctl_value, 0 );
3410 
3411 	if ( (tag = ber_scanf( ber, "{i" /*}*/, &mode )) == LBER_ERROR ) {
3412 		rs->sr_text = "Sync control : mode decoding error";
3413 		return LDAP_PROTOCOL_ERROR;
3414 	}
3415 
3416 	switch( mode ) {
3417 	case LDAP_SYNC_REFRESH_ONLY:
3418 		mode = SLAP_SYNC_REFRESH;
3419 		break;
3420 	case LDAP_SYNC_REFRESH_AND_PERSIST:
3421 		mode = SLAP_SYNC_REFRESH_AND_PERSIST;
3422 		break;
3423 	default:
3424 		rs->sr_text = "Sync control : unknown update mode";
3425 		return LDAP_PROTOCOL_ERROR;
3426 	}
3427 
3428 	tag = ber_peek_tag( ber, &len );
3429 
3430 	if ( tag == LDAP_TAG_SYNC_COOKIE ) {
3431 		if (( ber_scanf( ber, /*{*/ "m", &cookie )) == LBER_ERROR ) {
3432 			rs->sr_text = "Sync control : cookie decoding error";
3433 			return LDAP_PROTOCOL_ERROR;
3434 		}
3435 		tag = ber_peek_tag( ber, &len );
3436 	}
3437 	if ( tag == LDAP_TAG_RELOAD_HINT ) {
3438 		if (( ber_scanf( ber, /*{*/ "b", &rhint )) == LBER_ERROR ) {
3439 			rs->sr_text = "Sync control : rhint decoding error";
3440 			return LDAP_PROTOCOL_ERROR;
3441 		}
3442 	}
3443 	if (( ber_scanf( ber, /*{*/ "}")) == LBER_ERROR ) {
3444 			rs->sr_text = "Sync control : decoding error";
3445 			return LDAP_PROTOCOL_ERROR;
3446 	}
3447 	sr = op->o_tmpcalloc( 1, sizeof(struct sync_control), op->o_tmpmemctx );
3448 	sr->sr_rhint = rhint;
3449 	if (!BER_BVISNULL(&cookie)) {
3450 		ber_dupbv_x( &sr->sr_state.octet_str, &cookie, op->o_tmpmemctx );
3451 		/* If parse fails, pretend no cookie was sent */
3452 		if ( slap_parse_sync_cookie( &sr->sr_state, op->o_tmpmemctx ) ||
3453 			sr->sr_state.rid == -1 ) {
3454 			if ( sr->sr_state.ctxcsn ) {
3455 				ber_bvarray_free_x( sr->sr_state.ctxcsn, op->o_tmpmemctx );
3456 				sr->sr_state.ctxcsn = NULL;
3457 			}
3458 			sr->sr_state.numcsns = 0;
3459 		}
3460 	}
3461 
3462 	op->o_controls[slap_cids.sc_LDAPsync] = sr;
3463 
3464 	op->o_sync = ctrl->ldctl_iscritical
3465 		? SLAP_CONTROL_CRITICAL
3466 		: SLAP_CONTROL_NONCRITICAL;
3467 
3468 	op->o_sync_mode |= mode;	/* o_sync_mode shares o_sync */
3469 
3470 	return LDAP_SUCCESS;
3471 }
3472 
3473 /* This overlay is set up for dynamic loading via moduleload. For static
3474  * configuration, you'll need to arrange for the slap_overinst to be
3475  * initialized and registered by some other function inside slapd.
3476  */
3477 
3478 static slap_overinst 		syncprov;
3479 
3480 int
3481 syncprov_initialize()
3482 {
3483 	int rc;
3484 
3485 	rc = register_supported_control( LDAP_CONTROL_SYNC,
3486 		SLAP_CTRL_SEARCH, NULL,
3487 		syncprov_parseCtrl, &slap_cids.sc_LDAPsync );
3488 	if ( rc != LDAP_SUCCESS ) {
3489 		Debug( LDAP_DEBUG_ANY,
3490 			"syncprov_init: Failed to register control %d\n", rc, 0, 0 );
3491 		return rc;
3492 	}
3493 
3494 	syncprov.on_bi.bi_type = "syncprov";
3495 	syncprov.on_bi.bi_db_init = syncprov_db_init;
3496 	syncprov.on_bi.bi_db_destroy = syncprov_db_destroy;
3497 	syncprov.on_bi.bi_db_open = syncprov_db_open;
3498 	syncprov.on_bi.bi_db_close = syncprov_db_close;
3499 
3500 	syncprov.on_bi.bi_op_abandon = syncprov_op_abandon;
3501 	syncprov.on_bi.bi_op_cancel = syncprov_op_abandon;
3502 
3503 	syncprov.on_bi.bi_op_add = syncprov_op_mod;
3504 	syncprov.on_bi.bi_op_compare = syncprov_op_compare;
3505 	syncprov.on_bi.bi_op_delete = syncprov_op_mod;
3506 	syncprov.on_bi.bi_op_modify = syncprov_op_mod;
3507 	syncprov.on_bi.bi_op_modrdn = syncprov_op_mod;
3508 	syncprov.on_bi.bi_op_search = syncprov_op_search;
3509 	syncprov.on_bi.bi_extended = syncprov_op_extended;
3510 	syncprov.on_bi.bi_operational = syncprov_operational;
3511 
3512 	syncprov.on_bi.bi_cf_ocs = spocs;
3513 
3514 	generic_filter.f_desc = slap_schema.si_ad_objectClass;
3515 
3516 	rc = config_register_schema( spcfg, spocs );
3517 	if ( rc ) return rc;
3518 
3519 	return overlay_register( &syncprov );
3520 }
3521 
3522 #if SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC
3523 int
3524 init_module( int argc, char *argv[] )
3525 {
3526 	return syncprov_initialize();
3527 }
3528 #endif /* SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC */
3529 
3530 #endif /* defined(SLAPD_OVER_SYNCPROV) */
3531