syncprov.c 102 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
/* Session log data */
typedef struct slog_entry {
	struct berval se_uuid;
	struct berval se_csn;
122
	int	se_sid;
123
124
125
126
	ber_tag_t	se_tag;
} slog_entry;

typedef struct sessionlog {
127
128
129
	BerVarray	sl_mincsn;
	int		*sl_sids;
	int		sl_numcsns;
130
131
	int		sl_num;
	int		sl_size;
132
	int		sl_playing;
133
134
	Avlnode *sl_entries;
	ldap_pvt_thread_rdwr_t sl_mutex;
135
136
} sessionlog;

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

typedef struct opcookie {
	slap_overinst *son;
	syncmatches *smatches;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
164
	modtarget *smt;
165
	Entry *se;
166
167
168
169
	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
170
171
172
	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
173
	syncres ssres;
Howard Chu's avatar
Howard Chu committed
174
175
} opcookie;

176
typedef struct fbase_cookie {
177
178
179
180
	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 */
181
} fbase_cookie;
Howard Chu's avatar
Howard Chu committed
182

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

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

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

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

210
211
212
213
214
	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;
215
		}
216
	}
217

218
219
	/* FIXME: what if entryuuid is NULL or empty ? */

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

228
229
230
231
232
233
234
235
236
237
	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;
	}
238
239
240
241
	ber_free_buf( ber );

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

	return LDAP_SUCCESS;
}

Howard Chu's avatar
Cleanup    
Howard Chu committed
251
/* Build a LDAPsync final state control */
252
253
254
255
256
257
258
259
260
261
262
263
264
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;
265
266
	LDAPControl *cp;
	struct berval bv;
267
268
269
270
271
272
273
274
275
276
277

	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 );
	}
278
	ber_printf( ber, "N}" );
279

280
281
282
283
284
285
286
287
288
289
	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;
	}
290
291
292
293
294

	ber_free_buf( ber );

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

	return LDAP_SUCCESS;
}

Howard Chu's avatar
Howard Chu committed
304
static int
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
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:
326
327
328
			Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
				"sending a new cookie=%s\n",
				op->o_log_prefix, cookie->bv_val, 0 );
329
330
331
332
			ber_printf( ber, "tO", type, cookie );
			break;
		case LDAP_TAG_SYNC_REFRESH_DELETE:
		case LDAP_TAG_SYNC_REFRESH_PRESENT:
333
334
335
336
337
			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 : "" );
338
339
340
341
342
343
344
345
346
347
			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:
348
349
350
351
			Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
				"%s syncIdSet cookie=%s\n",
				op->o_log_prefix, refreshDeletes ? "delete" : "present",
				cookie ? cookie->bv_val : "" );
352
353
354
355
356
357
358
359
360
361
362
363
			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,
364
365
				"%s syncprov_sendinfo: invalid syncinfo type (%d)\n",
				op->o_log_prefix, type, 0 );
366
367
368
369
370
371
372
373
			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
374
375
			"syncprov_sendinfo: ber_flatten2 failed (%d)\n",
			ret, 0, 0 );
376
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
377
		return LDAP_OTHER;
378
379
	}

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

	return LDAP_SUCCESS;
}
388

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

	m1 = c1; m2 = c2;
397
	rc = m1->mt_dn.bv_len - m2->mt_dn.bv_len;
398
399

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

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
static int
syncprov_sessionlog_cmp( const void *l, const void *r )
{
	const slog_entry *left = l, *right = r;
	int ret = ber_bvcmp( &left->se_csn, &right->se_csn );
	if ( !ret )
		ret = ber_bvcmp( &left->se_uuid, &right->se_uuid );
	/* Only time we have two modifications with same CSN is when we detect a
	 * rename during replication.
	 * We invert the test here because LDAP_REQ_MODDN is
	 * numerically greater than LDAP_REQ_MODIFY but we
	 * want it to occur first.
	 */
	if ( !ret )
		ret = right->se_tag - left->se_tag;

	return ret;
}

422
423
424
425
426
427
428
429
430
/* 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
431
432
433
434
435
436
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 ) {
437
438
439
440
441
442
		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
443
			fc->fbase = 2;
444
445
			fc->fss->s_eid = rs->sr_entry->e_id;
			ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
446

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

Howard Chu's avatar
Howard Chu committed
450
		/* OK, the DN is the same and the entryID is the same. */
Howard Chu's avatar
Howard Chu committed
451
452
			fc->fbase = 1;
		}
Howard Chu's avatar
Howard Chu committed
453
	}
454
455
456
	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
457
458
459
	return LDAP_SUCCESS;
}

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

Howard Chu's avatar
Howard Chu committed
463
static int
464
syncprov_findbase( Operation *op, fbase_cookie *fc )
Howard Chu's avatar
Howard Chu committed
465
{
Howard Chu's avatar
Howard Chu committed
466
467
468
	/* Use basic parameters from syncrepl search, but use
	 * current op's threadctx / tmpmemctx
	 */
Howard Chu's avatar
Howard Chu committed
469
470
471
472
473
474
	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
475

Howard Chu's avatar
Howard Chu committed
476
477
		fc->fss->s_flags ^= PS_FIND_BASE;
		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
Howard Chu's avatar
Howard Chu committed
478

Howard Chu's avatar
Howard Chu committed
479
		fop = *fc->fss->s_op;
Howard Chu's avatar
Howard Chu committed
480

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
481
		fop.o_bd = fop.o_bd->bd_self;
Howard Chu's avatar
Howard Chu committed
482
483
484
		fop.o_hdr = op->o_hdr;
		fop.o_time = op->o_time;
		fop.o_tincr = op->o_tincr;
485
		fop.o_extra = op->o_extra;
Howard Chu's avatar
Howard Chu committed
486

Howard Chu's avatar
Howard Chu committed
487
488
		cb.sc_response = findbase_cb;
		cb.sc_private = fc;
Howard Chu's avatar
Howard Chu committed
489

Howard Chu's avatar
Howard Chu committed
490
491
492
493
494
495
496
497
498
499
		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
500
501
		fop.ors_filter = &generic_filter;
		fop.ors_filterstr = generic_filterstr;
Howard Chu's avatar
Howard Chu committed
502

503
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_findbase: searching\n", op->o_log_prefix, 0, 0 );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
504
		rc = fop.o_bd->be_search( &fop, &frs );
Howard Chu's avatar
Howard Chu committed
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
	} 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
533
534
535
536
537
538

	/* 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
539

540
/* syncprov_findcsn:
541
 *   This function has three different purposes, but they all use a search
542
 * that filters on entryCSN so they're combined here.
543
544
545
546
547
 * 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
548
549
 * 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.
550
 *
551
 * 3: during a refresh phase, we search for all entries with CSN <= the cookie
552
553
554
 * CSN, and generate Present records for them. We always collect this result
 * in SyncID sets, even if there's only one match.
 */
555
556
557
558
559
typedef enum find_csn_t {
	FIND_MAXCSN	= 1,
	FIND_CSN	= 2,
	FIND_PRESENT	= 3
} find_csn_t;
560
561
562
563
564
565
566
567
568

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
569
570
		if ( a && ber_bvcmp( &a->a_vals[0], maxcsn ) > 0 &&
			slap_parse_csn_sid( &a->a_vals[0] ) == slap_serverID ) {
571
572
573
574
575
576
			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
577
578
579
580
581
582

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

Howard Chu's avatar
Howard Chu committed
583
584
585
586
587
	/* 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 )) {
588
		sc->sc_private = (void *)1;
Howard Chu's avatar
Howard Chu committed
589
590
591
592
	}
	return LDAP_SUCCESS;
}

593
594
/* Build a list of entryUUIDs for sending in a SyncID set */

595
596
#define UUID_LEN	16

Howard Chu's avatar
Howard Chu committed
597
598
599
typedef struct fpres_cookie {
	int num;
	BerVarray uuids;
600
	char *last;
Howard Chu's avatar
Howard Chu committed
601
602
603
604
605
606
607
} fpres_cookie;

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

611
612
613
	switch ( rs->sr_type ) {
	case REP_SEARCH:
		a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
614
615
616
617
		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
618
			pc->num++;
619
620
			pc->last = pc->uuids[pc->num].bv_val;
			pc->uuids[pc->num].bv_val = NULL;
Howard Chu's avatar
Howard Chu committed
621
		}
622
		ret = LDAP_SUCCESS;
623
624
625
626
		if ( pc->num != SLAP_SYNCUUID_SET_SIZE )
			break;
		/* FALLTHRU */
	case REP_RESULT:
Howard Chu's avatar
Howard Chu committed
627
628
		ret = rs->sr_err;
		if ( pc->num ) {
629
			ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
Howard Chu's avatar
Howard Chu committed
630
				0, pc->uuids, 0 );
631
			pc->uuids[pc->num].bv_val = pc->last;
Howard Chu's avatar
Howard Chu committed
632
			pc->num = 0;
633
			pc->last = pc->uuids[0].bv_val;
Howard Chu's avatar
Howard Chu committed
634
		}
635
636
637
		break;
	default:
		break;
Howard Chu's avatar
Howard Chu committed
638
639
640
641
642
	}
	return ret;
}

static int
cmikk@qwest.net's avatar
cmikk@qwest.net committed
643
syncprov_findcsn( Operation *op, find_csn_t mode, struct berval *csn )
Howard Chu's avatar
Howard Chu committed
644
645
646
647
648
649
650
{
	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
651
652
	char buf[LDAP_PVT_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
	char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
653
	struct berval maxcsn;
654
	Filter cf;
Ralf Haferkamp's avatar
Ralf Haferkamp committed
655
	AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
Howard Chu's avatar
Howard Chu committed
656
	fpres_cookie pcookie;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
657
	sync_control *srs = NULL;
658
659
	struct slap_limits_set fc_limits;
	int i, rc = LDAP_SUCCESS, findcsn_retry = 1;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
660
	int maxid;
661
662
663

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

666
667
668
669
670
671
672
673
674
	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
675
	fop = *op;
676
	fop.o_sync_mode &= SLAP_CONTROL_MASK;	/* turn off sync_mode */
677
678
	/* We want pure entries, not referrals */
	fop.o_managedsait = SLAP_CONTROL_CRITICAL;
Howard Chu's avatar
Howard Chu committed
679

680
681
	cf.f_ava = &eq;
	cf.f_av_desc = slap_schema.si_ad_entryCSN;
682
	BER_BVZERO( &cf.f_av_value );
683
684
	cf.f_next = NULL;

685
686
687
688
	fop.o_callback = &cb;
	fop.ors_limit = NULL;
	fop.ors_tlimit = SLAP_NO_LIMIT;
	fop.ors_filter = &cf;
689
	fop.ors_filterstr.bv_val = buf;
690

Howard Chu's avatar
Howard Chu committed
691
again:
692
693
694
	switch( mode ) {
	case FIND_MAXCSN:
		cf.f_choice = LDAP_FILTER_GE;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
695
696
697
		/* 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] ) {
698
				maxid = i;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
699
				break;
700
701
			}
		}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
702
703
704
705
706
707
708
		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];
709
710
		fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
			"(entryCSN>=%s)", cf.f_av_value.bv_val );
711
		if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
712
713
			return LDAP_OTHER;
		}
714
715
716
717
718
		fop.ors_attrsonly = 0;
		fop.ors_attrs = csn_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
		cb.sc_private = &maxcsn;
		cb.sc_response = findmax_cb;
719
		strcpy( cbuf, cf.f_av_value.bv_val );
720
		maxcsn.bv_val = cbuf;
721
		maxcsn.bv_len = cf.f_av_value.bv_len;
722
723
		break;
	case FIND_CSN:
724
		if ( BER_BVISEMPTY( &cf.f_av_value )) {
cmikk@qwest.net's avatar
cmikk@qwest.net committed
725
			cf.f_av_value = *csn;
726
		}
cmikk@qwest.net's avatar
cmikk@qwest.net committed
727
728
729
730
		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
731
732
733
		/* Look for exact match the first time */
		if ( findcsn_retry ) {
			cf.f_choice = LDAP_FILTER_EQUALITY;
734
735
			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
				"(entryCSN=%s)", cf.f_av_value.bv_val );
Howard Chu's avatar
Howard Chu committed
736
737
738
		/* On retry, look for <= */
		} else {
			cf.f_choice = LDAP_FILTER_LE;
739
			fop.ors_limit = &fc_limits;
740
			memset( &fc_limits, 0, sizeof( fc_limits ));
741
			fc_limits.lms_s_unchecked = 1;
742
743
744
			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
				"(entryCSN<=%s)", cf.f_av_value.bv_val );
		}
745
		if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
746
			return LDAP_OTHER;
Howard Chu's avatar
Howard Chu committed
747
		}
748
749
750
751
		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
752
		cb.sc_response = findcsn_cb;
753
754
		break;
	case FIND_PRESENT:
755
756
		fop.ors_filter = op->ors_filter;
		fop.ors_filterstr = op->ors_filterstr;
Howard Chu's avatar
Howard Chu committed
757
758
759
		fop.ors_attrsonly = 0;
		fop.ors_attrs = uuid_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
Howard Chu's avatar
Howard Chu committed
760
		cb.sc_private = &pcookie;
Howard Chu's avatar
Howard Chu committed
761
762
		cb.sc_response = findpres_cb;
		pcookie.num = 0;
763
764
765
766
767
768
769
770
771
772
773
774

		/* 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;
		}
775
		break;
Howard Chu's avatar
Howard Chu committed
776
777
	}

778
	fop.o_bd->bd_info = (BackendInfo *)on->on_info;
779
	fop.o_bd->be_search( &fop, &frs );
780
	fop.o_bd->bd_info = (BackendInfo *)on;
Howard Chu's avatar
Howard Chu committed
781

782
783
	switch( mode ) {
	case FIND_MAXCSN:
784
		if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
785
786
787
788
#ifdef CHECK_CSN
			Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
			assert( !syn->ssyn_validate( syn, &maxcsn ));
#endif
789
790
791
			ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
			si->si_numops++;	/* ensure a checkpoint */
		}
792
793
794
		break;
	case FIND_CSN:
		/* If matching CSN was not found, invalidate the context. */
795
796
797
798
		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
799
800
801
802
		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
803
				rs_reinit( &frs, REP_RESULT );
Howard Chu's avatar
Howard Chu committed
804
805
806
807
				goto again;
			}
			rc = LDAP_NO_SUCH_OBJECT;
		}
808
809
		break;
	case FIND_PRESENT:
810
		op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
811
		break;
Howard Chu's avatar
Howard Chu committed
812
813
	}

814
	return rc;
Howard Chu's avatar
Howard Chu committed
815
816
}

Howard Chu's avatar
Howard Chu committed
817
static void free_resinfo( syncres *sr )
818
{
Howard Chu's avatar
Howard Chu committed
819
	syncres **st;
Howard Chu's avatar
Howard Chu committed
820
	int freeit = 0;
Howard Chu's avatar
Howard Chu committed
821
822
823
824
825
826
827
	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
828
829
	if ( !sr->s_info->ri_list )
		freeit = 1;
Howard Chu's avatar
Howard Chu committed
830
	ldap_pvt_thread_mutex_unlock( &sr->s_info->ri_mutex );
Howard Chu's avatar
Howard Chu committed
831
	if ( freeit ) {
Howard Chu's avatar
Howard Chu committed
832
833
834
		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
835
836
		if ( !BER_BVISNULL( &sr->s_info->ri_cookie ))
			ch_free( sr->s_info->ri_cookie.bv_val );
Howard Chu's avatar
Howard Chu committed
837
		ch_free( sr->s_info );
838
839
840
	}
}

841
static int
842
syncprov_free_syncop( syncops *so, int unlink )
843
844
845
846
847
{
	syncres *sr, *srnext;
	GroupAssertion *ga, *gnext;

	ldap_pvt_thread_mutex_lock( &so->s_mutex );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
848
849
	/* already being freed, or still in use */
	if ( !so->s_inuse || --so->s_inuse > 0 ) {
850
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
851
		return 0;
852
853
	}
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
854
855
856
857
858
859
860
861
862
863
864
	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 );
	}
865
866
867
868
869
870
871
872
873
874
875
	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
876
		free_resinfo( sr );
877
878
879
880
		ch_free( sr );
	}
	ldap_pvt_thread_mutex_destroy( &so->s_mutex );
	ch_free( so );
881
	return 1;
882
883
}

884
885
/* Send a persistent search response */
static int
Howard Chu's avatar
Howard Chu committed
886
syncprov_sendresp( Operation *op, resinfo *ri, syncops *so, int mode )
887
888
{
	SlapReply rs = { REP_SEARCH };
889
	struct berval cookie, csns[2];
890
891
892
	Entry e_uuid = {0};
	Attribute a_uuid = {0};

Howard Chu's avatar
Howard Chu committed
893
894
895
	if ( so->s_op->o_abandon )
		return SLAPD_ABANDON;

896
897
898
	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
899
	csns[0] = ri->ri_csn;
900
901
	BER_BVZERO( &csns[1] );
	slap_compose_sync_cookie( op, &cookie, csns, so->s_rid, slap_serverID ? slap_serverID : -1 );
902

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
903
#ifdef LDAP_DEBUG
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
904
	if ( so->s_sid > 0 ) {
905
906
		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
907
	} else {
908
909
		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
910
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
911
#endif
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
912

913
914
	e_uuid.e_attrs = &a_uuid;
	a_uuid.a_desc = slap_schema.si_ad_entryUUID;
Howard Chu's avatar
Howard Chu committed
915
	a_uuid.a_nvals = &ri->ri_uuid;
916
	rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
917
		mode, rs.sr_ctrls, 0, 1, &cookie );
918
	op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
919

920
921
	rs.sr_entry = &e_uuid;
	if ( mode == LDAP_SYNC_ADD || mode == LDAP_SYNC_MODIFY ) {
Howard Chu's avatar
Howard Chu committed
922
		e_uuid = *ri->ri_e;
923
924
925
		e_uuid.e_private = NULL;
	}

926
927
	switch( mode ) {
	case LDAP_SYNC_ADD:
Howard Chu's avatar
Howard Chu committed
928
		if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
929
			rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
930
			rs.sr_err = send_search_reference( op, &rs );
931
932
933
934
935
			ber_bvarray_free( rs.sr_ref );
			break;
		}
		/* fallthru */
	case LDAP_SYNC_MODIFY:
936
937
938
939
		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 );
940
		rs.sr_attrs = op->ors_attrs;
941
		rs.sr_err = send_search_entry( op, &rs );
942
943
		break;
	case LDAP_SYNC_DELETE:
944
945
946
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: "
			"sending LDAP_SYNC_DELETE, dn=%s\n",
			op->o_log_prefix, ri->ri_dn.bv_val, 0 );
947
		e_uuid.e_attrs = NULL;
Howard Chu's avatar
Howard Chu committed
948
949
950
		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 ) {
951
			struct berval bv = BER_BVNULL;
952
			rs.sr_ref = &bv;
953
			rs.sr_err = send_search_reference( op, &rs );
Howard Chu's avatar
Howard Chu committed
954
		} else {
955
			rs.sr_err = send_search_entry( op, &rs );
956
957
958
959
960
		}
		break;
	default:
		assert(0);
	}
961
962
963
	return rs.sr_err;
}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
964
965
966
static void
syncprov_qstart( syncops *so );

967
968
/* Play back queued responses */
static int
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
969
syncprov_qplay( Operation *op, syncops *so )
970
971
{
	syncres *sr;
972
	int rc = 0;
973

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
974
	do {
975
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
976
		sr = so->s_res;
Howard Chu's avatar
Howard Chu committed
977
		/* Exit loop with mutex held */
Howard Chu's avatar
Howard Chu committed
978
		if ( !sr )
979
			break;
Howard Chu's avatar
Howard Chu committed
980
981
982
		so->s_res = sr->s_next;
		if ( !so->s_res )
			so->s_restail = NULL;
Howard Chu's avatar
Howard Chu committed
983
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
984

985
		if ( !so->s_op->o_abandon ) {
986

987
988
			if ( sr->s_mode == LDAP_SYNC_NEW_COOKIE ) {
				SlapReply rs = { REP_INTERMEDIATE };
989

990
				rc = syncprov_sendinfo( op, &rs, LDAP_TAG_SYNC_NEW_COOKIE,
Howard Chu's avatar
Howard Chu committed
991
					&sr->s_info->ri_cookie, 0, NULL, 0 );
992
			} else {
Howard Chu's avatar
Howard Chu committed
993
				rc = syncprov_sendresp( op, sr->s_info, so, sr->s_mode );
994
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
995
		}
996

Howard Chu's avatar
Howard Chu committed
997
		free_resinfo( sr );
998
		ch_free( sr );
999

1000
1001
1002
		if ( so->s_op->o_abandon )
			continue;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1003
1004
		/* Exit loop with mutex held */
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
1005
		break;
Howard Chu's avatar
Howard Chu committed
1006

1007
	} while (1);
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1008
1009
1010
1011
1012

	/* 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
1013

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1014
1015
1016
1017
	if ( rc == 0 && so->s_res ) {
		syncprov_qstart( so );
	} else {
		so->s_flags ^= PS_TASK_QUEUED;
1018
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1019

Howard Chu's avatar
Howard Chu committed
1020
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
1021
1022
1023
	return rc;
}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1024
/* task for playing back queued responses */
1025
1026
1027
static void *
syncprov_qtask( void *ctx, void *arg )
{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1028
	syncops *so = arg;
1029
	OperationBuffer opbuf;
1030
1031
	Operation *op;
	BackendDB be;
1032
	int rc;
1033

1034
	op = &opbuf.ob_op;
1035
	*op = *so->s_op;
1036
1037
1038
	op->o_hdr = &opbuf.ob_hdr;
	op->o_controls = opbuf.ob_controls;
	memset( op->o_controls, 0, sizeof(opbuf.ob_controls) );
1039
	op->o_sync = SLAP_CONTROL_IGNORED;
1040
1041
1042

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

1043
	op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 1);