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