syncprov.c 99.4 KB
Newer Older
Howard Chu's avatar
Howard Chu committed
1
/* $OpenLDAP$ */
Howard Chu's avatar
Howard Chu committed
2
3
4
/* syncprov.c - syncrepl provider */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 2004-2020 The OpenLDAP Foundation.
Howard Chu's avatar
Howard Chu committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Howard Chu for inclusion in
 * OpenLDAP Software.
 */

#include "portable.h"

#ifdef SLAPD_OVER_SYNCPROV

Howard Chu's avatar
Howard Chu committed
25
26
#include <ac/string.h>
#include "lutil.h"
Howard Chu's avatar
Howard Chu committed
27
#include "slap.h"
Howard Chu's avatar
Howard Chu committed
28
#include "config.h"
29
#include "ldap_rq.h"
Howard Chu's avatar
Howard Chu committed
30

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
31
32
33
34
#ifdef LDAP_DEVEL
#define	CHECK_CSN	1
#endif

35
36
37
38
39
40
41
42
43
/* A modify request on a particular entry */
typedef struct modinst {
	struct modinst *mi_next;
	Operation *mi_op;
} modinst;

typedef struct modtarget {
	struct modinst *mt_mods;
	struct modinst *mt_tail;
44
	struct berval mt_dn;
45
46
47
	ldap_pvt_thread_mutex_t mt_mutex;
} modtarget;

Howard Chu's avatar
Howard Chu committed
48
49
50
51
52
53
54
55
56
57
/* All the info of a psearch result that's shared between
 * multiple queues
 */
typedef struct resinfo {
	struct syncres *ri_list;
	Entry *ri_e;
	struct berval ri_dn;
	struct berval ri_ndn;
	struct berval ri_uuid;
	struct berval ri_csn;
Howard Chu's avatar
Howard Chu committed
58
	struct berval ri_cookie;
Howard Chu's avatar
Howard Chu committed
59
60
61
62
	char ri_isref;
	ldap_pvt_thread_mutex_t ri_mutex;
} resinfo;

Howard Chu's avatar
Howard Chu committed
63
64
/* A queued result of a persistent search */
typedef struct syncres {
Howard Chu's avatar
Howard Chu committed
65
66
67
	struct syncres *s_next;	/* list of results on this psearch queue */
	struct syncres *s_rilist;	/* list of psearches using this result */
	resinfo *s_info;
68
	char s_mode;
Howard Chu's avatar
Howard Chu committed
69
70
} syncres;

Howard Chu's avatar
Howard Chu committed
71
72
73
/* Record of a persistent search */
typedef struct syncops {
	struct syncops *s_next;
74
	struct syncprov_info_t *s_si;
Howard Chu's avatar
Howard Chu committed
75
76
77
	struct berval	s_base;		/* ndn of search base */
	ID		s_eid;		/* entryID of search base */
	Operation	*s_op;		/* search op */
78
	int		s_rid;
79
	int		s_sid;
Howard Chu's avatar
Howard Chu committed
80
	struct berval s_filterstr;
Howard Chu's avatar
Howard Chu committed
81
	int		s_flags;	/* search status */
Howard Chu's avatar
Howard Chu committed
82
83
84
85
#define	PS_IS_REFRESHING	0x01
#define	PS_IS_DETACHED		0x02
#define	PS_WROTE_BASE		0x04
#define	PS_FIND_BASE		0x08
86
#define	PS_FIX_FILTER		0x10
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
87
#define	PS_TASK_QUEUED		0x20
Howard Chu's avatar
Howard Chu committed
88

Howard Chu's avatar
Howard Chu committed
89
	int		s_inuse;	/* reference count */
Howard Chu's avatar
Howard Chu committed
90
91
92
	struct syncres *s_res;
	struct syncres *s_restail;
	ldap_pvt_thread_mutex_t	s_mutex;
Howard Chu's avatar
Howard Chu committed
93
94
} syncops;

95
96
97
98
99
100
/* A received sync control */
typedef struct sync_control {
	struct sync_cookie sr_state;
	int sr_rhint;
} sync_control;

101
102
103
#if 0 /* moved back to slap.h */
#define	o_sync	o_ctrlflag[slap_cids.sc_LDAPsync]
#endif
104
/* o_sync_mode uses data bits of o_sync */
105
#define	o_sync_mode	o_ctrlflag[slap_cids.sc_LDAPsync]
106
107
108
109
110
111

#define SLAP_SYNC_NONE					(LDAP_SYNC_NONE<<SLAP_CONTROL_SHIFT)
#define SLAP_SYNC_REFRESH				(LDAP_SYNC_REFRESH_ONLY<<SLAP_CONTROL_SHIFT)
#define SLAP_SYNC_PERSIST				(LDAP_SYNC_RESERVED<<SLAP_CONTROL_SHIFT)
#define SLAP_SYNC_REFRESH_AND_PERSIST	(LDAP_SYNC_REFRESH_AND_PERSIST<<SLAP_CONTROL_SHIFT)

Howard Chu's avatar
Howard Chu committed
112
113
114
115
116
117
/* Record of which searches matched at premodify step */
typedef struct syncmatches {
	struct syncmatches *sm_next;
	syncops *sm_op;
} syncmatches;

118
119
120
121
122
/* Session log data */
typedef struct slog_entry {
	struct slog_entry *se_next;
	struct berval se_uuid;
	struct berval se_csn;
123
	int	se_sid;
124
125
126
127
	ber_tag_t	se_tag;
} slog_entry;

typedef struct sessionlog {
128
129
130
	BerVarray	sl_mincsn;
	int		*sl_sids;
	int		sl_numcsns;
131
132
	int		sl_num;
	int		sl_size;
133
	int		sl_playing;
134
135
136
137
138
	slog_entry *sl_head;
	slog_entry *sl_tail;
	ldap_pvt_thread_mutex_t sl_mutex;
} sessionlog;

Howard Chu's avatar
Cleanup    
Howard Chu committed
139
/* The main state for this overlay */
Howard Chu's avatar
Howard Chu committed
140
141
typedef struct syncprov_info_t {
	syncops		*si_ops;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
142
	struct berval	si_contextdn;
143
	BerVarray	si_ctxcsn;	/* ldapsync context */
144
145
	int		*si_sids;
	int		si_numcsns;
146
147
148
	int		si_chkops;	/* checkpointing info */
	int		si_chktime;
	int		si_numops;	/* number of ops since last checkpoint */
149
	int		si_nopres;	/* Skip present phase */
150
	int		si_usehint;	/* use reload hint */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
151
	int		si_active;	/* True if there are active mods */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
152
153
	int		si_dirty;	/* True if the context is dirty, i.e changes
						 * have been made without updating the csn. */
154
	time_t	si_chklast;	/* time of last checkpoint */
155
	Avlnode	*si_mods;	/* entries being modified */
156
	sessionlog	*si_logs;
Howard Chu's avatar
Howard Chu committed
157
	ldap_pvt_thread_rdwr_t	si_csn_rwlock;
Howard Chu's avatar
Howard Chu committed
158
	ldap_pvt_thread_mutex_t	si_ops_mutex;
Howard Chu's avatar
Howard Chu committed
159
	ldap_pvt_thread_mutex_t	si_mods_mutex;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
160
	ldap_pvt_thread_mutex_t	si_resp_mutex;
Howard Chu's avatar
Howard Chu committed
161
162
163
164
165
} syncprov_info_t;

typedef struct opcookie {
	slap_overinst *son;
	syncmatches *smatches;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
166
	modtarget *smt;
167
	Entry *se;
168
169
170
171
	struct berval sdn;	/* DN of entry, for deletes */
	struct berval sndn;
	struct berval suuid;	/* UUID of entry */
	struct berval sctxcsn;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
172
173
174
	short osid;	/* sid of op csn */
	short rsid;	/* sid of relay */
	short sreference;	/* Is the entry a reference? */
Howard Chu's avatar
Howard Chu committed
175
	syncres ssres;
Howard Chu's avatar
Howard Chu committed
176
177
} opcookie;

178
typedef struct fbase_cookie {
179
180
181
182
	struct berval *fdn;	/* DN of a modified entry, for scope testing */
	syncops *fss;	/* persistent search we're testing against */
	int fbase;	/* if TRUE we found the search base and it's still valid */
	int fscope;	/* if TRUE then fdn is within the psearch scope */
183
} fbase_cookie;
Howard Chu's avatar
Howard Chu committed
184

185
static AttributeName csn_anlist[3];
Howard Chu's avatar
Howard Chu committed
186
187
static AttributeName uuid_anlist[2];

Howard Chu's avatar
Cleanup    
Howard Chu committed
188
/* Build a LDAPsync intermediate state control */
189
190
191
192
193
static int
syncprov_state_ctrl(
	Operation	*op,
	SlapReply	*rs,
	Entry		*e,
194
	int		entry_sync_state,
195
	LDAPControl	**ctrls,
196
197
198
	int		num_ctrls,
	int		send_cookie,
	struct berval	*cookie )
199
200
201
202
203
204
{
	Attribute* a;
	int ret;

	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;
205
206
	LDAPControl *cp;
	struct berval bv;
207
	struct berval	entryuuid_bv = BER_BVNULL;
208
209
210
211

	ber_init2( ber, 0, LBER_USE_DER );
	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );

212
213
214
215
216
	for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
		AttributeDescription *desc = a->a_desc;
		if ( desc == slap_schema.si_ad_entryUUID ) {
			entryuuid_bv = a->a_nvals[0];
			break;
217
		}
218
	}
219

220
221
	/* FIXME: what if entryuuid is NULL or empty ? */

222
223
224
225
226
227
	if ( send_cookie && cookie ) {
		ber_printf( ber, "{eOON}",
			entry_sync_state, &entryuuid_bv, cookie );
	} else {
		ber_printf( ber, "{eON}",
			entry_sync_state, &entryuuid_bv );
228
229
	}

230
231
232
233
234
235
236
237
238
239
	ret = ber_flatten2( ber, &bv, 0 );
	if ( ret == 0 ) {
		cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
		cp->ldctl_oid = LDAP_CONTROL_SYNC_STATE;
		cp->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
		cp->ldctl_value.bv_val = (char *)&cp[1];
		cp->ldctl_value.bv_len = bv.bv_len;
		AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
		ctrls[num_ctrls] = cp;
	}
240
241
242
243
	ber_free_buf( ber );

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
244
245
			"slap_build_sync_ctrl: ber_flatten2 failed (%d)\n",
			ret, 0, 0 );
246
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
247
		return LDAP_OTHER;
248
249
250
251
252
	}

	return LDAP_SUCCESS;
}

Howard Chu's avatar
Cleanup    
Howard Chu committed
253
/* Build a LDAPsync final state control */
254
255
256
257
258
259
260
261
262
263
264
265
266
static int
syncprov_done_ctrl(
	Operation	*op,
	SlapReply	*rs,
	LDAPControl	**ctrls,
	int			num_ctrls,
	int			send_cookie,
	struct berval *cookie,
	int			refreshDeletes )
{
	int ret;
	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;
267
268
	LDAPControl *cp;
	struct berval bv;
269
270
271
272
273
274
275
276
277
278
279

	ber_init2( ber, NULL, LBER_USE_DER );
	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );

	ber_printf( ber, "{" );
	if ( send_cookie && cookie ) {
		ber_printf( ber, "O", cookie );
	}
	if ( refreshDeletes == LDAP_SYNC_REFRESH_DELETES ) {
		ber_printf( ber, "b", refreshDeletes );
	}
280
	ber_printf( ber, "N}" );
281

282
283
284
285
286
287
288
289
290
291
	ret = ber_flatten2( ber, &bv, 0 );
	if ( ret == 0 ) {
		cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
		cp->ldctl_oid = LDAP_CONTROL_SYNC_DONE;
		cp->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
		cp->ldctl_value.bv_val = (char *)&cp[1];
		cp->ldctl_value.bv_len = bv.bv_len;
		AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
		ctrls[num_ctrls] = cp;
	}
292
293
294
295
296

	ber_free_buf( ber );

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
297
298
			"syncprov_done_ctrl: ber_flatten2 failed (%d)\n",
			ret, 0, 0 );
299
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
300
		return LDAP_OTHER;
301
302
303
304
305
	}

	return LDAP_SUCCESS;
}

Howard Chu's avatar
Howard Chu committed
306
static int
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
syncprov_sendinfo(
	Operation	*op,
	SlapReply	*rs,
	int			type,
	struct berval *cookie,
	int			refreshDone,
	BerVarray	syncUUIDs,
	int			refreshDeletes )
{
	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;
	struct berval rspdata;

	int ret;

	ber_init2( ber, NULL, LBER_USE_DER );
	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );

	if ( type ) {
		switch ( type ) {
		case LDAP_TAG_SYNC_NEW_COOKIE:
328
329
330
			Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
				"sending a new cookie=%s\n",
				op->o_log_prefix, cookie->bv_val, 0 );
331
332
333
334
			ber_printf( ber, "tO", type, cookie );
			break;
		case LDAP_TAG_SYNC_REFRESH_DELETE:
		case LDAP_TAG_SYNC_REFRESH_PRESENT:
335
336
337
338
339
			Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
				"%s cookie=%s\n",
				op->o_log_prefix,
				type == LDAP_TAG_SYNC_REFRESH_DELETE ? "refreshDelete" : "refreshPresent",
				cookie ? cookie->bv_val : "" );
340
341
342
343
344
345
346
347
348
349
			ber_printf( ber, "t{", type );
			if ( cookie ) {
				ber_printf( ber, "O", cookie );
			}
			if ( refreshDone == 0 ) {
				ber_printf( ber, "b", refreshDone );
			}
			ber_printf( ber, "N}" );
			break;
		case LDAP_TAG_SYNC_ID_SET:
350
351
352
353
			Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
				"%s syncIdSet cookie=%s\n",
				op->o_log_prefix, refreshDeletes ? "delete" : "present",
				cookie ? cookie->bv_val : "" );
354
355
356
357
358
359
360
361
362
363
364
365
			ber_printf( ber, "t{", type );
			if ( cookie ) {
				ber_printf( ber, "O", cookie );
			}
			if ( refreshDeletes == 1 ) {
				ber_printf( ber, "b", refreshDeletes );
			}
			ber_printf( ber, "[W]", syncUUIDs );
			ber_printf( ber, "N}" );
			break;
		default:
			Debug( LDAP_DEBUG_TRACE,
366
367
				"%s syncprov_sendinfo: invalid syncinfo type (%d)\n",
				op->o_log_prefix, type, 0 );
368
369
370
371
372
373
374
375
			return LDAP_OTHER;
		}
	}

	ret = ber_flatten2( ber, &rspdata, 0 );

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
376
377
			"syncprov_sendinfo: ber_flatten2 failed (%d)\n",
			ret, 0, 0 );
378
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
379
		return LDAP_OTHER;
380
381
	}

Howard Chu's avatar
Cleanup    
Howard Chu committed
382
	rs->sr_rspoid = LDAP_SYNC_INFO;
383
384
385
386
387
388
389
	rs->sr_rspdata = &rspdata;
	send_ldap_intermediate( op, rs );
	rs->sr_rspdata = NULL;
	ber_free_buf( ber );

	return LDAP_SUCCESS;
}
390

Howard Chu's avatar
Cleanup    
Howard Chu committed
391
/* Find a modtarget in an AVL tree */
392
393
394
395
396
397
398
static int
sp_avl_cmp( const void *c1, const void *c2 )
{
	const modtarget *m1, *m2;
	int rc;

	m1 = c1; m2 = c2;
399
	rc = m1->mt_dn.bv_len - m2->mt_dn.bv_len;
400
401

	if ( rc ) return rc;
402
	return ber_bvcmp( &m1->mt_dn, &m2->mt_dn );
403
404
}

405
406
407
408
409
410
411
412
413
/* syncprov_findbase:
 *   finds the true DN of the base of a search (with alias dereferencing) and
 * checks to make sure the base entry doesn't get replaced with a different
 * entry (e.g., swapping trees via ModDN, or retargeting an alias). If a
 * change is detected, any persistent search on this base must be terminated /
 * reloaded.
 *   On the first call, we just save the DN and entryID. On subsequent calls
 * we compare the DN and entryID with the saved values.
 */
Howard Chu's avatar
Howard Chu committed
414
415
416
417
418
419
static int
findbase_cb( Operation *op, SlapReply *rs )
{
	slap_callback *sc = op->o_callback;

	if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
420
421
422
423
424
425
		fbase_cookie *fc = sc->sc_private;

		/* If no entryID, we're looking for the first time.
		 * Just store whatever we got.
		 */
		if ( fc->fss->s_eid == NOID ) {
Howard Chu's avatar
Howard Chu committed
426
			fc->fbase = 2;
427
428
			fc->fss->s_eid = rs->sr_entry->e_id;
			ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
429

430
		} else if ( rs->sr_entry->e_id == fc->fss->s_eid &&
Howard Chu's avatar
Howard Chu committed
431
			dn_match( &rs->sr_entry->e_nname, &fc->fss->s_base )) {
432

Howard Chu's avatar
Howard Chu committed
433
		/* OK, the DN is the same and the entryID is the same. */
Howard Chu's avatar
Howard Chu committed
434
435
			fc->fbase = 1;
		}
Howard Chu's avatar
Howard Chu committed
436
	}
437
438
439
	if ( rs->sr_err != LDAP_SUCCESS ) {
		Debug( LDAP_DEBUG_ANY, "findbase failed! %d\n", rs->sr_err,0,0 );
	}
Howard Chu's avatar
Howard Chu committed
440
441
442
	return LDAP_SUCCESS;
}

Howard Chu's avatar
Howard Chu committed
443
444
445
static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL };
static struct berval generic_filterstr = BER_BVC("(objectclass=*)");

Howard Chu's avatar
Howard Chu committed
446
static int
447
syncprov_findbase( Operation *op, fbase_cookie *fc )
Howard Chu's avatar
Howard Chu committed
448
{
Howard Chu's avatar
Howard Chu committed
449
450
451
	/* Use basic parameters from syncrepl search, but use
	 * current op's threadctx / tmpmemctx
	 */
Howard Chu's avatar
Howard Chu committed
452
453
454
455
456
457
	ldap_pvt_thread_mutex_lock( &fc->fss->s_mutex );
	if ( fc->fss->s_flags & PS_FIND_BASE ) {
		slap_callback cb = {0};
		Operation fop;
		SlapReply frs = { REP_RESULT };
		int rc;
Howard Chu's avatar
Howard Chu committed
458

Howard Chu's avatar
Howard Chu committed
459
460
		fc->fss->s_flags ^= PS_FIND_BASE;
		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
Howard Chu's avatar
Howard Chu committed
461

Howard Chu's avatar
Howard Chu committed
462
		fop = *fc->fss->s_op;
Howard Chu's avatar
Howard Chu committed
463

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
464
		fop.o_bd = fop.o_bd->bd_self;
Howard Chu's avatar
Howard Chu committed
465
466
467
		fop.o_hdr = op->o_hdr;
		fop.o_time = op->o_time;
		fop.o_tincr = op->o_tincr;
468
		fop.o_extra = op->o_extra;
Howard Chu's avatar
Howard Chu committed
469

Howard Chu's avatar
Howard Chu committed
470
471
		cb.sc_response = findbase_cb;
		cb.sc_private = fc;
Howard Chu's avatar
Howard Chu committed
472

Howard Chu's avatar
Howard Chu committed
473
474
475
476
477
478
479
480
481
482
		fop.o_sync_mode = 0;	/* turn off sync mode */
		fop.o_managedsait = SLAP_CONTROL_CRITICAL;
		fop.o_callback = &cb;
		fop.o_tag = LDAP_REQ_SEARCH;
		fop.ors_scope = LDAP_SCOPE_BASE;
		fop.ors_limit = NULL;
		fop.ors_slimit = 1;
		fop.ors_tlimit = SLAP_NO_LIMIT;
		fop.ors_attrs = slap_anlist_no_attrs;
		fop.ors_attrsonly = 1;
Howard Chu's avatar
Howard Chu committed
483
484
		fop.ors_filter = &generic_filter;
		fop.ors_filterstr = generic_filterstr;
Howard Chu's avatar
Howard Chu committed
485

486
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_findbase: searching\n", op->o_log_prefix, 0, 0 );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
487
		rc = fop.o_bd->be_search( &fop, &frs );
Howard Chu's avatar
Howard Chu committed
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
	} else {
		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
		fc->fbase = 1;
	}

	/* After the first call, see if the fdn resides in the scope */
	if ( fc->fbase == 1 ) {
		switch ( fc->fss->s_op->ors_scope ) {
		case LDAP_SCOPE_BASE:
			fc->fscope = dn_match( fc->fdn, &fc->fss->s_base );
			break;
		case LDAP_SCOPE_ONELEVEL: {
			struct berval pdn;
			dnParent( fc->fdn, &pdn );
			fc->fscope = dn_match( &pdn, &fc->fss->s_base );
			break; }
		case LDAP_SCOPE_SUBTREE:
			fc->fscope = dnIsSuffix( fc->fdn, &fc->fss->s_base );
			break;
		case LDAP_SCOPE_SUBORDINATE:
			fc->fscope = dnIsSuffix( fc->fdn, &fc->fss->s_base ) &&
				!dn_match( fc->fdn, &fc->fss->s_base );
			break;
		}
	}

	if ( fc->fbase )
		return LDAP_SUCCESS;
Howard Chu's avatar
Howard Chu committed
516
517
518
519
520
521

	/* If entryID has changed, then the base of this search has
	 * changed. Invalidate the psearch.
	 */
	return LDAP_NO_SUCH_OBJECT;
}
Howard Chu's avatar
Howard Chu committed
522

523
/* syncprov_findcsn:
524
 *   This function has three different purposes, but they all use a search
525
 * that filters on entryCSN so they're combined here.
526
527
528
529
530
 * 1: at startup time, after a contextCSN has been read from the database,
 * we search for all entries with CSN >= contextCSN in case the contextCSN
 * was not checkpointed at the previous shutdown.
 *
 * 2: when the current contextCSN is known and we have a sync cookie, we search
Howard Chu's avatar
Howard Chu committed
531
532
 * for one entry with CSN = the cookie CSN. If not found, try <= cookie CSN.
 * If an entry is found, the cookie CSN is valid, otherwise it is stale.
533
 *
534
 * 3: during a refresh phase, we search for all entries with CSN <= the cookie
535
536
537
 * CSN, and generate Present records for them. We always collect this result
 * in SyncID sets, even if there's only one match.
 */
538
539
540
541
542
typedef enum find_csn_t {
	FIND_MAXCSN	= 1,
	FIND_CSN	= 2,
	FIND_PRESENT	= 3
} find_csn_t;
543
544
545
546
547
548
549
550
551

static int
findmax_cb( Operation *op, SlapReply *rs )
{
	if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
		struct berval *maxcsn = op->o_callback->sc_private;
		Attribute *a = attr_find( rs->sr_entry->e_attrs,
			slap_schema.si_ad_entryCSN );

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
552
553
		if ( a && ber_bvcmp( &a->a_vals[0], maxcsn ) > 0 &&
			slap_parse_csn_sid( &a->a_vals[0] ) == slap_serverID ) {
554
555
556
557
558
559
			maxcsn->bv_len = a->a_vals[0].bv_len;
			strcpy( maxcsn->bv_val, a->a_vals[0].bv_val );
		}
	}
	return LDAP_SUCCESS;
}
Howard Chu's avatar
Howard Chu committed
560
561
562
563
564
565

static int
findcsn_cb( Operation *op, SlapReply *rs )
{
	slap_callback *sc = op->o_callback;

Howard Chu's avatar
Howard Chu committed
566
567
568
569
570
	/* We just want to know that at least one exists, so it's OK if
	 * we exceed the unchecked limit.
	 */
	if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ||
		(rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS )) {
571
		sc->sc_private = (void *)1;
Howard Chu's avatar
Howard Chu committed
572
573
574
575
	}
	return LDAP_SUCCESS;
}

576
577
/* Build a list of entryUUIDs for sending in a SyncID set */

578
579
#define UUID_LEN	16

Howard Chu's avatar
Howard Chu committed
580
581
582
typedef struct fpres_cookie {
	int num;
	BerVarray uuids;
583
	char *last;
Howard Chu's avatar
Howard Chu committed
584
585
586
587
588
589
590
} fpres_cookie;

static int
findpres_cb( Operation *op, SlapReply *rs )
{
	slap_callback *sc = op->o_callback;
	fpres_cookie *pc = sc->sc_private;
591
	Attribute *a;
Howard Chu's avatar
Howard Chu committed
592
	int ret = SLAP_CB_CONTINUE;
Howard Chu's avatar
Howard Chu committed
593

594
595
596
	switch ( rs->sr_type ) {
	case REP_SEARCH:
		a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
597
598
599
600
		if ( a ) {
			pc->uuids[pc->num].bv_val = pc->last;
			AC_MEMCPY( pc->uuids[pc->num].bv_val, a->a_nvals[0].bv_val,
				pc->uuids[pc->num].bv_len );
Howard Chu's avatar
Howard Chu committed
601
			pc->num++;
602
603
			pc->last = pc->uuids[pc->num].bv_val;
			pc->uuids[pc->num].bv_val = NULL;
Howard Chu's avatar
Howard Chu committed
604
		}
605
		ret = LDAP_SUCCESS;
606
607
608
609
		if ( pc->num != SLAP_SYNCUUID_SET_SIZE )
			break;
		/* FALLTHRU */
	case REP_RESULT:
Howard Chu's avatar
Howard Chu committed
610
611
		ret = rs->sr_err;
		if ( pc->num ) {
612
			ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
Howard Chu's avatar
Howard Chu committed
613
				0, pc->uuids, 0 );
614
			pc->uuids[pc->num].bv_val = pc->last;
Howard Chu's avatar
Howard Chu committed
615
			pc->num = 0;
616
			pc->last = pc->uuids[0].bv_val;
Howard Chu's avatar
Howard Chu committed
617
		}
618
619
620
		break;
	default:
		break;
Howard Chu's avatar
Howard Chu committed
621
622
623
624
625
	}
	return ret;
}

static int
cmikk@qwest.net's avatar
cmikk@qwest.net committed
626
syncprov_findcsn( Operation *op, find_csn_t mode, struct berval *csn )
Howard Chu's avatar
Howard Chu committed
627
628
629
630
631
632
633
{
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	syncprov_info_t		*si = on->on_bi.bi_private;

	slap_callback cb = {0};
	Operation fop;
	SlapReply frs = { REP_RESULT };
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
634
635
	char buf[LDAP_PVT_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
	char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
636
	struct berval maxcsn;
637
	Filter cf;
Ralf Haferkamp's avatar
Ralf Haferkamp committed
638
	AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
Howard Chu's avatar
Howard Chu committed
639
	fpres_cookie pcookie;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
640
	sync_control *srs = NULL;
641
642
	struct slap_limits_set fc_limits;
	int i, rc = LDAP_SUCCESS, findcsn_retry = 1;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
643
	int maxid;
644
645
646

	if ( mode != FIND_MAXCSN ) {
		srs = op->o_controls[slap_cids.sc_LDAPsync];
Howard Chu's avatar
Howard Chu committed
647
648
	}

649
650
651
652
653
654
655
656
657
	Debug( LDAP_DEBUG_SYNC, "%s syncprov_findcsn: mode=%s csn=%s\n",
		op->o_log_prefix,
		mode == FIND_MAXCSN ?
			"FIND_MAXCSN" :
			mode == FIND_CSN ?
				"FIND_CSN" :
				"FIND_PRESENT",
		csn ? csn->bv_val : "" );

Howard Chu's avatar
Howard Chu committed
658
	fop = *op;
659
	fop.o_sync_mode &= SLAP_CONTROL_MASK;	/* turn off sync_mode */
660
661
	/* We want pure entries, not referrals */
	fop.o_managedsait = SLAP_CONTROL_CRITICAL;
Howard Chu's avatar
Howard Chu committed
662

663
664
	cf.f_ava = &eq;
	cf.f_av_desc = slap_schema.si_ad_entryCSN;
665
	BER_BVZERO( &cf.f_av_value );
666
667
	cf.f_next = NULL;

668
669
670
671
	fop.o_callback = &cb;
	fop.ors_limit = NULL;
	fop.ors_tlimit = SLAP_NO_LIMIT;
	fop.ors_filter = &cf;
672
	fop.ors_filterstr.bv_val = buf;
673

Howard Chu's avatar
Howard Chu committed
674
again:
675
676
677
	switch( mode ) {
	case FIND_MAXCSN:
		cf.f_choice = LDAP_FILTER_GE;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
678
679
680
		/* If there are multiple CSNs, use the one with our serverID */
		for ( i=0; i<si->si_numcsns; i++) {
			if ( slap_serverID == si->si_sids[i] ) {
681
				maxid = i;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
682
				break;
683
684
			}
		}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
685
686
687
688
689
690
691
		if ( i == si->si_numcsns ) {
			/* No match: this is multimaster, and none of the content in the DB
			 * originated locally. Treat like no CSN.
			 */
			return LDAP_NO_SUCH_OBJECT;
		}
		cf.f_av_value = si->si_ctxcsn[maxid];
692
693
		fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
			"(entryCSN>=%s)", cf.f_av_value.bv_val );
694
		if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
695
696
			return LDAP_OTHER;
		}
697
698
699
700
701
		fop.ors_attrsonly = 0;
		fop.ors_attrs = csn_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
		cb.sc_private = &maxcsn;
		cb.sc_response = findmax_cb;
702
		strcpy( cbuf, cf.f_av_value.bv_val );
703
		maxcsn.bv_val = cbuf;
704
		maxcsn.bv_len = cf.f_av_value.bv_len;
705
706
		break;
	case FIND_CSN:
707
		if ( BER_BVISEMPTY( &cf.f_av_value )) {
cmikk@qwest.net's avatar
cmikk@qwest.net committed
708
			cf.f_av_value = *csn;
709
		}
cmikk@qwest.net's avatar
cmikk@qwest.net committed
710
711
712
713
		fop.o_dn = op->o_bd->be_rootdn;
		fop.o_ndn = op->o_bd->be_rootndn;
		fop.o_req_dn = op->o_bd->be_suffix[0];
		fop.o_req_ndn = op->o_bd->be_nsuffix[0];
Howard Chu's avatar
Howard Chu committed
714
715
716
		/* Look for exact match the first time */
		if ( findcsn_retry ) {
			cf.f_choice = LDAP_FILTER_EQUALITY;
717
718
			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
				"(entryCSN=%s)", cf.f_av_value.bv_val );
Howard Chu's avatar
Howard Chu committed
719
720
721
		/* On retry, look for <= */
		} else {
			cf.f_choice = LDAP_FILTER_LE;
722
			fop.ors_limit = &fc_limits;
723
			memset( &fc_limits, 0, sizeof( fc_limits ));
724
			fc_limits.lms_s_unchecked = 1;
725
726
727
			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
				"(entryCSN<=%s)", cf.f_av_value.bv_val );
		}
728
		if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
729
			return LDAP_OTHER;
Howard Chu's avatar
Howard Chu committed
730
		}
731
732
733
734
		fop.ors_attrsonly = 1;
		fop.ors_attrs = slap_anlist_no_attrs;
		fop.ors_slimit = 1;
		cb.sc_private = NULL;
Howard Chu's avatar
Howard Chu committed
735
		cb.sc_response = findcsn_cb;
736
737
		break;
	case FIND_PRESENT:
738
739
		fop.ors_filter = op->ors_filter;
		fop.ors_filterstr = op->ors_filterstr;
Howard Chu's avatar
Howard Chu committed
740
741
742
		fop.ors_attrsonly = 0;
		fop.ors_attrs = uuid_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
Howard Chu's avatar
Howard Chu committed
743
		cb.sc_private = &pcookie;
Howard Chu's avatar
Howard Chu committed
744
745
		cb.sc_response = findpres_cb;
		pcookie.num = 0;
746
747
748
749
750
751
752
753
754
755
756
757

		/* preallocate storage for a full set */
		pcookie.uuids = op->o_tmpalloc( (SLAP_SYNCUUID_SET_SIZE+1) *
			sizeof(struct berval) + SLAP_SYNCUUID_SET_SIZE * UUID_LEN,
			op->o_tmpmemctx );
		pcookie.last = (char *)(pcookie.uuids + SLAP_SYNCUUID_SET_SIZE+1);
		pcookie.uuids[0].bv_val = pcookie.last;
		pcookie.uuids[0].bv_len = UUID_LEN;
		for (i=1; i<SLAP_SYNCUUID_SET_SIZE; i++) {
			pcookie.uuids[i].bv_val = pcookie.uuids[i-1].bv_val + UUID_LEN;
			pcookie.uuids[i].bv_len = UUID_LEN;
		}
758
		break;
Howard Chu's avatar
Howard Chu committed
759
760
	}

761
	fop.o_bd->bd_info = (BackendInfo *)on->on_info;
762
	fop.o_bd->be_search( &fop, &frs );
763
	fop.o_bd->bd_info = (BackendInfo *)on;
Howard Chu's avatar
Howard Chu committed
764

765
766
	switch( mode ) {
	case FIND_MAXCSN:
767
		if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
768
769
770
771
#ifdef CHECK_CSN
			Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
			assert( !syn->ssyn_validate( syn, &maxcsn ));
#endif
772
773
774
			ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
			si->si_numops++;	/* ensure a checkpoint */
		}
775
776
777
		break;
	case FIND_CSN:
		/* If matching CSN was not found, invalidate the context. */
778
779
780
781
		Log4( LDAP_DEBUG_SYNC, ldap_syslog_level, "%s syncprov_findcsn: csn%s=%s %sfound\n",
			op->o_log_prefix,
			cf.f_choice == LDAP_FILTER_EQUALITY ? "=" : "<",
			cf.f_av_value.bv_val, cb.sc_private ? "" : "not " );
Howard Chu's avatar
Howard Chu committed
782
783
784
785
		if ( !cb.sc_private ) {
			/* If we didn't find an exact match, then try for <= */
			if ( findcsn_retry ) {
				findcsn_retry = 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
786
				rs_reinit( &frs, REP_RESULT );
Howard Chu's avatar
Howard Chu committed
787
788
789
790
				goto again;
			}
			rc = LDAP_NO_SUCH_OBJECT;
		}
791
792
		break;
	case FIND_PRESENT:
793
		op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
794
		break;
Howard Chu's avatar
Howard Chu committed
795
796
	}

797
	return rc;
Howard Chu's avatar
Howard Chu committed
798
799
}

Howard Chu's avatar
Howard Chu committed
800
static void free_resinfo( syncres *sr )
801
{
Howard Chu's avatar
Howard Chu committed
802
	syncres **st;
Howard Chu's avatar
Howard Chu committed
803
	int freeit = 0;
Howard Chu's avatar
Howard Chu committed
804
805
806
807
808
809
810
	ldap_pvt_thread_mutex_lock( &sr->s_info->ri_mutex );
	for (st = &sr->s_info->ri_list; *st; st = &(*st)->s_rilist) {
		if (*st == sr) {
			*st = sr->s_rilist;
			break;
		}
	}
Howard Chu's avatar
Howard Chu committed
811
812
	if ( !sr->s_info->ri_list )
		freeit = 1;
Howard Chu's avatar
Howard Chu committed
813
	ldap_pvt_thread_mutex_unlock( &sr->s_info->ri_mutex );
Howard Chu's avatar
Howard Chu committed
814
	if ( freeit ) {
Howard Chu's avatar
Howard Chu committed
815
816
817
		ldap_pvt_thread_mutex_destroy( &sr->s_info->ri_mutex );
		if ( sr->s_info->ri_e )
			entry_free( sr->s_info->ri_e );
Howard Chu's avatar
Howard Chu committed
818
819
		if ( !BER_BVISNULL( &sr->s_info->ri_cookie ))
			ch_free( sr->s_info->ri_cookie.bv_val );
Howard Chu's avatar
Howard Chu committed
820
		ch_free( sr->s_info );
821
822
823
	}
}

824
static int
825
syncprov_free_syncop( syncops *so, int unlink )
826
827
828
829
830
{
	syncres *sr, *srnext;
	GroupAssertion *ga, *gnext;

	ldap_pvt_thread_mutex_lock( &so->s_mutex );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
831
832
	/* already being freed, or still in use */
	if ( !so->s_inuse || --so->s_inuse > 0 ) {
833
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
834
		return 0;
835
836
	}
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
837
838
839
840
841
842
843
844
845
846
847
	if ( unlink ) {
		syncops **sop;
		ldap_pvt_thread_mutex_lock( &so->s_si->si_ops_mutex );
		for ( sop = &so->s_si->si_ops; *sop; sop = &(*sop)->s_next ) {
			if ( *sop == so ) {
				*sop = so->s_next;
				break;
			}
		}
		ldap_pvt_thread_mutex_unlock( &so->s_si->si_ops_mutex );
	}
848
849
850
851
852
853
854
855
856
857
858
	if ( so->s_flags & PS_IS_DETACHED ) {
		filter_free( so->s_op->ors_filter );
		for ( ga = so->s_op->o_groups; ga; ga=gnext ) {
			gnext = ga->ga_next;
			ch_free( ga );
		}
		ch_free( so->s_op );
	}
	ch_free( so->s_base.bv_val );
	for ( sr=so->s_res; sr; sr=srnext ) {
		srnext = sr->s_next;
Howard Chu's avatar
Howard Chu committed
859
		free_resinfo( sr );
860
861
862
863
		ch_free( sr );
	}
	ldap_pvt_thread_mutex_destroy( &so->s_mutex );
	ch_free( so );
864
	return 1;
865
866
}

867
868
/* Send a persistent search response */
static int
Howard Chu's avatar
Howard Chu committed
869
syncprov_sendresp( Operation *op, resinfo *ri, syncops *so, int mode )
870
871
{
	SlapReply rs = { REP_SEARCH };
872
	struct berval cookie, csns[2];
873
874
875
	Entry e_uuid = {0};
	Attribute a_uuid = {0};

Howard Chu's avatar
Howard Chu committed
876
877
878
	if ( so->s_op->o_abandon )
		return SLAPD_ABANDON;

879
880
881
	rs.sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2, op->o_tmpmemctx );
	rs.sr_ctrls[1] = NULL;
	rs.sr_flags = REP_CTRLS_MUSTBEFREED;
Howard Chu's avatar
Howard Chu committed
882
	csns[0] = ri->ri_csn;
883
884
	BER_BVZERO( &csns[1] );
	slap_compose_sync_cookie( op, &cookie, csns, so->s_rid, slap_serverID ? slap_serverID : -1 );
885

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
886
#ifdef LDAP_DEBUG
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
887
	if ( so->s_sid > 0 ) {
888
889
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: to=%03x, cookie=%s\n",
			op->o_log_prefix, so->s_sid, cookie.bv_val );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
890
	} else {
891
892
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: cookie=%s\n",
			op->o_log_prefix, cookie.bv_val, 0 );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
893
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
894
#endif
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
895

896
897
	e_uuid.e_attrs = &a_uuid;
	a_uuid.a_desc = slap_schema.si_ad_entryUUID;
Howard Chu's avatar
Howard Chu committed
898
	a_uuid.a_nvals = &ri->ri_uuid;
899
	rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
900
		mode, rs.sr_ctrls, 0, 1, &cookie );
901
	op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
902

903
904
	rs.sr_entry = &e_uuid;
	if ( mode == LDAP_SYNC_ADD || mode == LDAP_SYNC_MODIFY ) {
Howard Chu's avatar
Howard Chu committed
905
		e_uuid = *ri->ri_e;
906
907
908
		e_uuid.e_private = NULL;
	}

909
910
	switch( mode ) {
	case LDAP_SYNC_ADD:
Howard Chu's avatar
Howard Chu committed
911
		if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
912
			rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
913
			rs.sr_err = send_search_reference( op, &rs );
914
915
916
917
918
			ber_bvarray_free( rs.sr_ref );
			break;
		}
		/* fallthru */
	case LDAP_SYNC_MODIFY:
919
920
921
922
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: sending %s, dn=%s\n",
			op->o_log_prefix,
			mode == LDAP_SYNC_ADD ? "LDAP_SYNC_ADD" : "LDAP_SYNC_MODIFY",
			e_uuid.e_nname.bv_val );
923
		rs.sr_attrs = op->ors_attrs;
924
		rs.sr_err = send_search_entry( op, &rs );
925
926
		break;
	case LDAP_SYNC_DELETE:
927
928
929
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: "
			"sending LDAP_SYNC_DELETE, dn=%s\n",
			op->o_log_prefix, ri->ri_dn.bv_val, 0 );
930
		e_uuid.e_attrs = NULL;
Howard Chu's avatar
Howard Chu committed
931
932
933
		e_uuid.e_name = ri->ri_dn;
		e_uuid.e_nname = ri->ri_ndn;
		if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
934
			struct berval bv = BER_BVNULL;
935
			rs.sr_ref = &bv;
936
			rs.sr_err = send_search_reference( op, &rs );
Howard Chu's avatar
Howard Chu committed
937
		} else {
938
			rs.sr_err = send_search_entry( op, &rs );
939
940
941
942
943
		}
		break;
	default:
		assert(0);
	}
944
945
946
	return rs.sr_err;
}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
947
948
949
static void
syncprov_qstart( syncops *so );

950
951
/* Play back queued responses */
static int
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
952
syncprov_qplay( Operation *op, syncops *so )
953
954
{
	syncres *sr;
955
	int rc = 0;
956

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
957
	do {
958
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
959
		sr = so->s_res;
Howard Chu's avatar
Howard Chu committed
960
		/* Exit loop with mutex held */
Howard Chu's avatar
Howard Chu committed
961
		if ( !sr )
962
			break;
Howard Chu's avatar
Howard Chu committed
963
964
965
		so->s_res = sr->s_next;
		if ( !so->s_res )
			so->s_restail = NULL;
Howard Chu's avatar
Howard Chu committed
966
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
967

968
		if ( !so->s_op->o_abandon ) {
969

970
971
			if ( sr->s_mode == LDAP_SYNC_NEW_COOKIE ) {
				SlapReply rs = { REP_INTERMEDIATE };
972

973
				rc = syncprov_sendinfo( op, &rs, LDAP_TAG_SYNC_NEW_COOKIE,
Howard Chu's avatar
Howard Chu committed
974
					&sr->s_info->ri_cookie, 0, NULL, 0 );
975
			} else {
Howard Chu's avatar
Howard Chu committed
976
				rc = syncprov_sendresp( op, sr->s_info, so, sr->s_mode );
977
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
978
		}
979

Howard Chu's avatar
Howard Chu committed
980
		free_resinfo( sr );
981
		ch_free( sr );
982

983
984
985
		if ( so->s_op->o_abandon )
			continue;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
986
987
		/* Exit loop with mutex held */
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
988
		break;
Howard Chu's avatar
Howard Chu committed
989

990
	} while (1);
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
991
992
993
994
995

	/* We now only send one change at a time, to prevent one
	 * psearch from hogging all the CPU. Resubmit this task if
	 * there are more responses queued and no errors occurred.
	 */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
996

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
997
998
999
1000
	if ( rc == 0 && so->s_res ) {
		syncprov_qstart( so );
	} else {
		so->s_flags ^= PS_TASK_QUEUED;
1001
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1002

Howard Chu's avatar
Howard Chu committed
1003
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
1004
1005
1006
	return rc;
}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1007
/* task for playing back queued responses */
1008
1009
1010
static void *
syncprov_qtask( void *ctx, void *arg )
{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1011
	syncops *so = arg;
1012
	OperationBuffer opbuf;
1013
1014
	Operation *op;
	BackendDB be;
1015
	int rc;
1016

1017
	op = &opbuf.ob_op;
1018
	*op = *so->s_op;
1019
1020
1021
	op->o_hdr = &opbuf.ob_hdr;
	op->o_controls = opbuf.ob_controls;
	memset( op->o_controls, 0, sizeof(opbuf.ob_controls) );
1022
	op->o_sync = SLAP_CONTROL_IGNORED;
1023
1024
1025

	*op->o_hdr = *so->s_op->o_hdr;

1026
	op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 1);
1027
1028
1029
1030
1031
1032
1033
	op->o_tmpmfuncs = &slap_sl_mfuncs;
	op->o_threadctx = ctx;

	/* syncprov_qplay expects a fake db */
	be = *so->s_op->o_bd;
	be.be_flags |= SLAP_DBFLAG_OVERLAY;
	op->o_bd = &be;
Howard Chu's avatar
Howard Chu committed
1034
	LDAP_SLIST_FIRST(&op->o_extra) = NULL;
1035
1036
	op->o_callback = NULL;