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