syncprov.c 84.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/>.
 *
5
 * Copyright 2004-2009 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
44
45
46
47
/* 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;
	Operation *mt_op;
	ldap_pvt_thread_mutex_t mt_mutex;
} modtarget;

Howard Chu's avatar
Howard Chu committed
48
49
50
/* A queued result of a persistent search */
typedef struct syncres {
	struct syncres *s_next;
51
	struct berval s_dn;
Howard Chu's avatar
Howard Chu committed
52
53
	struct berval s_ndn;
	struct berval s_uuid;
54
55
56
	struct berval s_csn;
	char s_mode;
	char s_isreference;
Howard Chu's avatar
Howard Chu committed
57
58
} syncres;

Howard Chu's avatar
Howard Chu committed
59
60
61
62
63
64
/* Record of a persistent search */
typedef struct syncops {
	struct syncops *s_next;
	struct berval	s_base;		/* ndn of search base */
	ID		s_eid;		/* entryID of search base */
	Operation	*s_op;		/* search op */
65
	int		s_rid;
66
	int		s_sid;
Howard Chu's avatar
Howard Chu committed
67
	struct berval s_filterstr;
Howard Chu's avatar
Howard Chu committed
68
	int		s_flags;	/* search status */
Howard Chu's avatar
Howard Chu committed
69
70
71
72
#define	PS_IS_REFRESHING	0x01
#define	PS_IS_DETACHED		0x02
#define	PS_WROTE_BASE		0x04
#define	PS_FIND_BASE		0x08
73
#define	PS_FIX_FILTER		0x10
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
74
#define	PS_TASK_QUEUED		0x20
Howard Chu's avatar
Howard Chu committed
75

Howard Chu's avatar
Howard Chu committed
76
	int		s_inuse;	/* reference count */
Howard Chu's avatar
Howard Chu committed
77
78
79
	struct syncres *s_res;
	struct syncres *s_restail;
	ldap_pvt_thread_mutex_t	s_mutex;
Howard Chu's avatar
Howard Chu committed
80
81
} syncops;

82
83
84
85
86
87
/* A received sync control */
typedef struct sync_control {
	struct sync_cookie sr_state;
	int sr_rhint;
} sync_control;

88
89
90
#if 0 /* moved back to slap.h */
#define	o_sync	o_ctrlflag[slap_cids.sc_LDAPsync]
#endif
91
/* o_sync_mode uses data bits of o_sync */
92
#define	o_sync_mode	o_ctrlflag[slap_cids.sc_LDAPsync]
93
94
95
96
97
98

#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
99
100
101
102
103
104
/* Record of which searches matched at premodify step */
typedef struct syncmatches {
	struct syncmatches *sm_next;
	syncops *sm_op;
} syncmatches;

105
106
107
108
109
/* Session log data */
typedef struct slog_entry {
	struct slog_entry *se_next;
	struct berval se_uuid;
	struct berval se_csn;
110
	int	se_sid;
111
112
113
114
115
116
117
118
119
120
121
122
	ber_tag_t	se_tag;
} slog_entry;

typedef struct sessionlog {
	struct berval	sl_mincsn;
	int		sl_num;
	int		sl_size;
	slog_entry *sl_head;
	slog_entry *sl_tail;
	ldap_pvt_thread_mutex_t sl_mutex;
} sessionlog;

Howard Chu's avatar
Cleanup    
Howard Chu committed
123
/* The main state for this overlay */
Howard Chu's avatar
Howard Chu committed
124
125
typedef struct syncprov_info_t {
	syncops		*si_ops;
126
127
128
	BerVarray	si_ctxcsn;	/* ldapsync context */
	int		*si_sids;
	int		si_numcsns;
129
130
131
	int		si_chkops;	/* checkpointing info */
	int		si_chktime;
	int		si_numops;	/* number of ops since last checkpoint */
132
	int		si_nopres;	/* Skip present phase */
133
	int		si_usehint;	/* use reload hint */
134
	time_t	si_chklast;	/* time of last checkpoint */
135
	Avlnode	*si_mods;	/* entries being modified */
136
	sessionlog	*si_logs;
Howard Chu's avatar
Howard Chu committed
137
	ldap_pvt_thread_rdwr_t	si_csn_rwlock;
Howard Chu's avatar
Howard Chu committed
138
	ldap_pvt_thread_mutex_t	si_ops_mutex;
Howard Chu's avatar
Howard Chu committed
139
	ldap_pvt_thread_mutex_t	si_mods_mutex;
Howard Chu's avatar
Howard Chu committed
140
141
142
143
144
} syncprov_info_t;

typedef struct opcookie {
	slap_overinst *son;
	syncmatches *smatches;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
145
	modtarget *smt;
146
147
148
149
	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
150
151
152
	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
153
154
} opcookie;

155
typedef struct fbase_cookie {
156
157
158
159
	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 */
160
} fbase_cookie;
Howard Chu's avatar
Howard Chu committed
161

162
static AttributeName csn_anlist[3];
Howard Chu's avatar
Howard Chu committed
163
164
static AttributeName uuid_anlist[2];

Howard Chu's avatar
Cleanup    
Howard Chu committed
165
/* Build a LDAPsync intermediate state control */
166
167
168
169
170
static int
syncprov_state_ctrl(
	Operation	*op,
	SlapReply	*rs,
	Entry		*e,
171
	int		entry_sync_state,
172
	LDAPControl	**ctrls,
173
174
175
	int		num_ctrls,
	int		send_cookie,
	struct berval	*cookie )
176
177
178
179
180
181
182
{
	Attribute* a;
	int ret;

	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;

183
	struct berval	entryuuid_bv = BER_BVNULL;
184
185
186
187

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

Howard Chu's avatar
Howard Chu committed
188
	ctrls[num_ctrls] = op->o_tmpalloc( sizeof ( LDAPControl ), op->o_tmpmemctx );
189

190
191
192
193
194
	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;
195
		}
196
	}
197

198
199
	/* FIXME: what if entryuuid is NULL or empty ? */

200
201
202
203
204
205
	if ( send_cookie && cookie ) {
		ber_printf( ber, "{eOON}",
			entry_sync_state, &entryuuid_bv, cookie );
	} else {
		ber_printf( ber, "{eON}",
			entry_sync_state, &entryuuid_bv );
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
	}

	ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_STATE;
	ctrls[num_ctrls]->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
	ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );

	ber_free_buf( ber );

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
			"slap_build_sync_ctrl: ber_flatten2 failed\n",
			0, 0, 0 );
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
		return ret;
	}

	return LDAP_SUCCESS;
}

Howard Chu's avatar
Cleanup    
Howard Chu committed
225
/* Build a LDAPsync final state control */
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
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;

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

Howard Chu's avatar
Howard Chu committed
243
	ctrls[num_ctrls] = op->o_tmpalloc( sizeof ( LDAPControl ), op->o_tmpmemctx );
244
245
246
247
248
249
250
251

	ber_printf( ber, "{" );
	if ( send_cookie && cookie ) {
		ber_printf( ber, "O", cookie );
	}
	if ( refreshDeletes == LDAP_SYNC_REFRESH_DELETES ) {
		ber_printf( ber, "b", refreshDeletes );
	}
252
	ber_printf( ber, "N}" );
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270

	ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_DONE;
	ctrls[num_ctrls]->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
	ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );

	ber_free_buf( ber );

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
			"syncprov_done_ctrl: ber_flatten2 failed\n",
			0, 0, 0 );
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
		return ret;
	}

	return LDAP_SUCCESS;
}

Howard Chu's avatar
Howard Chu committed
271
static int
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
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:
			ber_printf( ber, "tO", type, cookie );
			break;
		case LDAP_TAG_SYNC_REFRESH_DELETE:
		case LDAP_TAG_SYNC_REFRESH_PRESENT:
			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:
			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,
				"syncprov_sendinfo: invalid syncinfo type (%d)\n",
				type, 0, 0 );
			return LDAP_OTHER;
		}
	}

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

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
			"syncprov_sendinfo: ber_flatten2 failed\n",
			0, 0, 0 );
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
		return ret;
	}

Howard Chu's avatar
Cleanup    
Howard Chu committed
335
	rs->sr_rspoid = LDAP_SYNC_INFO;
336
337
338
339
340
341
342
	rs->sr_rspdata = &rspdata;
	send_ldap_intermediate( op, rs );
	rs->sr_rspdata = NULL;
	ber_free_buf( ber );

	return LDAP_SUCCESS;
}
343

Howard Chu's avatar
Cleanup    
Howard Chu committed
344
/* Find a modtarget in an AVL tree */
345
346
347
348
349
350
351
352
353
354
355
356
357
static int
sp_avl_cmp( const void *c1, const void *c2 )
{
	const modtarget *m1, *m2;
	int rc;

	m1 = c1; m2 = c2;
	rc = m1->mt_op->o_req_ndn.bv_len - m2->mt_op->o_req_ndn.bv_len;

	if ( rc ) return rc;
	return ber_bvcmp( &m1->mt_op->o_req_ndn, &m2->mt_op->o_req_ndn );
}

358
359
360
361
362
363
364
365
366
/* 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
367
368
369
370
371
372
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 ) {
373
374
375
376
377
378
		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
379
			fc->fbase = 2;
380
381
			fc->fss->s_eid = rs->sr_entry->e_id;
			ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
382

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

Howard Chu's avatar
Howard Chu committed
386
		/* OK, the DN is the same and the entryID is the same. */
Howard Chu's avatar
Howard Chu committed
387
388
			fc->fbase = 1;
		}
Howard Chu's avatar
Howard Chu committed
389
	}
390
391
392
	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
393
394
395
	return LDAP_SUCCESS;
}

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

Howard Chu's avatar
Howard Chu committed
399
static int
400
syncprov_findbase( Operation *op, fbase_cookie *fc )
Howard Chu's avatar
Howard Chu committed
401
{
Howard Chu's avatar
Howard Chu committed
402
403
404
	/* Use basic parameters from syncrepl search, but use
	 * current op's threadctx / tmpmemctx
	 */
Howard Chu's avatar
Howard Chu committed
405
406
407
408
409
410
	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
411

Howard Chu's avatar
Howard Chu committed
412
413
		fc->fss->s_flags ^= PS_FIND_BASE;
		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
Howard Chu's avatar
Howard Chu committed
414

Howard Chu's avatar
Howard Chu committed
415
		fop = *fc->fss->s_op;
Howard Chu's avatar
Howard Chu committed
416

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
417
		fop.o_bd = fop.o_bd->bd_self;
Howard Chu's avatar
Howard Chu committed
418
419
420
		fop.o_hdr = op->o_hdr;
		fop.o_time = op->o_time;
		fop.o_tincr = op->o_tincr;
Howard Chu's avatar
Howard Chu committed
421

Howard Chu's avatar
Howard Chu committed
422
423
		cb.sc_response = findbase_cb;
		cb.sc_private = fc;
Howard Chu's avatar
Howard Chu committed
424

Howard Chu's avatar
Howard Chu committed
425
426
427
428
429
430
431
432
433
434
		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
435
436
		fop.ors_filter = &generic_filter;
		fop.ors_filterstr = generic_filterstr;
Howard Chu's avatar
Howard Chu committed
437

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
438
		rc = fop.o_bd->be_search( &fop, &frs );
Howard Chu's avatar
Howard Chu committed
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
	} 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
467
468
469
470
471
472

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

474
/* syncprov_findcsn:
475
 *   This function has three different purposes, but they all use a search
476
 * that filters on entryCSN so they're combined here.
477
478
479
480
481
 * 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
482
483
 * 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.
484
 *
485
 * 3: during a refresh phase, we search for all entries with CSN <= the cookie
486
487
488
 * CSN, and generate Present records for them. We always collect this result
 * in SyncID sets, even if there's only one match.
 */
489
490
491
492
493
typedef enum find_csn_t {
	FIND_MAXCSN	= 1,
	FIND_CSN	= 2,
	FIND_PRESENT	= 3
} find_csn_t;
494
495
496
497
498
499
500
501
502

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
503
504
		if ( a && ber_bvcmp( &a->a_vals[0], maxcsn ) > 0 &&
			slap_parse_csn_sid( &a->a_vals[0] ) == slap_serverID ) {
505
506
507
508
509
510
			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
511
512
513
514
515
516

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

Howard Chu's avatar
Howard Chu committed
517
518
519
520
521
	/* 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 )) {
522
		sc->sc_private = (void *)1;
Howard Chu's avatar
Howard Chu committed
523
524
525
526
	}
	return LDAP_SUCCESS;
}

527
528
/* Build a list of entryUUIDs for sending in a SyncID set */

529
530
#define UUID_LEN	16

Howard Chu's avatar
Howard Chu committed
531
532
533
typedef struct fpres_cookie {
	int num;
	BerVarray uuids;
534
	char *last;
Howard Chu's avatar
Howard Chu committed
535
536
537
538
539
540
541
} fpres_cookie;

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

545
546
547
	switch ( rs->sr_type ) {
	case REP_SEARCH:
		a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
548
549
550
551
		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
552
			pc->num++;
553
554
			pc->last = pc->uuids[pc->num].bv_val;
			pc->uuids[pc->num].bv_val = NULL;
Howard Chu's avatar
Howard Chu committed
555
		}
556
		ret = LDAP_SUCCESS;
557
558
559
560
		if ( pc->num != SLAP_SYNCUUID_SET_SIZE )
			break;
		/* FALLTHRU */
	case REP_RESULT:
Howard Chu's avatar
Howard Chu committed
561
562
		ret = rs->sr_err;
		if ( pc->num ) {
563
			ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
Howard Chu's avatar
Howard Chu committed
564
				0, pc->uuids, 0 );
565
			pc->uuids[pc->num].bv_val = pc->last;
Howard Chu's avatar
Howard Chu committed
566
			pc->num = 0;
567
			pc->last = pc->uuids[0].bv_val;
Howard Chu's avatar
Howard Chu committed
568
		}
569
570
571
		break;
	default:
		break;
Howard Chu's avatar
Howard Chu committed
572
573
574
575
576
	}
	return ret;
}

static int
577
syncprov_findcsn( Operation *op, find_csn_t mode )
Howard Chu's avatar
Howard Chu committed
578
579
580
581
582
583
584
585
586
{
	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 };
	char buf[LDAP_LUTIL_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
	char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
587
	struct berval maxcsn;
588
	Filter cf;
Ralf Haferkamp's avatar
Ralf Haferkamp committed
589
	AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
Howard Chu's avatar
Howard Chu committed
590
	fpres_cookie pcookie;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
591
	sync_control *srs = NULL;
592
593
	struct slap_limits_set fc_limits;
	int i, rc = LDAP_SUCCESS, findcsn_retry = 1;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
594
	int maxid;
595
596
597

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

	fop = *op;
601
	fop.o_sync_mode &= SLAP_CONTROL_MASK;	/* turn off sync_mode */
602
603
	/* We want pure entries, not referrals */
	fop.o_managedsait = SLAP_CONTROL_CRITICAL;
Howard Chu's avatar
Howard Chu committed
604

605
606
	cf.f_ava = &eq;
	cf.f_av_desc = slap_schema.si_ad_entryCSN;
607
	BER_BVZERO( &cf.f_av_value );
608
609
	cf.f_next = NULL;

610
611
612
613
	fop.o_callback = &cb;
	fop.ors_limit = NULL;
	fop.ors_tlimit = SLAP_NO_LIMIT;
	fop.ors_filter = &cf;
614
	fop.ors_filterstr.bv_val = buf;
615

Howard Chu's avatar
Howard Chu committed
616
again:
617
618
619
	switch( mode ) {
	case FIND_MAXCSN:
		cf.f_choice = LDAP_FILTER_GE;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
620
621
622
		/* 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] ) {
623
				maxid = i;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
624
				break;
625
626
			}
		}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
627
628
629
630
631
632
633
		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];
634
635
		fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
			"(entryCSN>=%s)", cf.f_av_value.bv_val );
636
		if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
637
638
			return LDAP_OTHER;
		}
639
640
641
642
643
		fop.ors_attrsonly = 0;
		fop.ors_attrs = csn_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
		cb.sc_private = &maxcsn;
		cb.sc_response = findmax_cb;
644
		strcpy( cbuf, cf.f_av_value.bv_val );
645
		maxcsn.bv_val = cbuf;
646
		maxcsn.bv_len = cf.f_av_value.bv_len;
647
648
		break;
	case FIND_CSN:
649
650
651
652
653
654
655
656
657
658
		if ( BER_BVISEMPTY( &cf.f_av_value )) {
			cf.f_av_value = srs->sr_state.ctxcsn[0];
			/* If there are multiple CSNs, use the smallest */
			for ( i=1; i<srs->sr_state.numcsns; i++ ) {
				if ( ber_bvcmp( &cf.f_av_value, &srs->sr_state.ctxcsn[i] )
					> 0 ) {
					cf.f_av_value = srs->sr_state.ctxcsn[i];
				}
			}
		}
Howard Chu's avatar
Howard Chu committed
659
660
661
		/* Look for exact match the first time */
		if ( findcsn_retry ) {
			cf.f_choice = LDAP_FILTER_EQUALITY;
662
663
			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
				"(entryCSN=%s)", cf.f_av_value.bv_val );
Howard Chu's avatar
Howard Chu committed
664
665
666
		/* On retry, look for <= */
		} else {
			cf.f_choice = LDAP_FILTER_LE;
667
			fop.ors_limit = &fc_limits;
668
			memset( &fc_limits, 0, sizeof( fc_limits ));
669
			fc_limits.lms_s_unchecked = 1;
670
671
672
			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
				"(entryCSN<=%s)", cf.f_av_value.bv_val );
		}
673
		if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
674
			return LDAP_OTHER;
Howard Chu's avatar
Howard Chu committed
675
		}
676
677
678
679
		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
680
		cb.sc_response = findcsn_cb;
681
682
		break;
	case FIND_PRESENT:
683
684
		fop.ors_filter = op->ors_filter;
		fop.ors_filterstr = op->ors_filterstr;
Howard Chu's avatar
Howard Chu committed
685
686
687
		fop.ors_attrsonly = 0;
		fop.ors_attrs = uuid_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
Howard Chu's avatar
Howard Chu committed
688
		cb.sc_private = &pcookie;
Howard Chu's avatar
Howard Chu committed
689
690
		cb.sc_response = findpres_cb;
		pcookie.num = 0;
691
692
693
694
695
696
697
698
699
700
701
702

		/* 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;
		}
703
		break;
Howard Chu's avatar
Howard Chu committed
704
705
	}

706
	fop.o_bd->bd_info = (BackendInfo *)on->on_info;
707
	fop.o_bd->be_search( &fop, &frs );
708
	fop.o_bd->bd_info = (BackendInfo *)on;
Howard Chu's avatar
Howard Chu committed
709

710
711
	switch( mode ) {
	case FIND_MAXCSN:
712
		if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
713
714
715
716
#ifdef CHECK_CSN
			Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
			assert( !syn->ssyn_validate( syn, &maxcsn ));
#endif
717
718
719
			ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
			si->si_numops++;	/* ensure a checkpoint */
		}
720
721
722
		break;
	case FIND_CSN:
		/* If matching CSN was not found, invalidate the context. */
Howard Chu's avatar
Howard Chu committed
723
724
725
726
727
728
729
730
		if ( !cb.sc_private ) {
			/* If we didn't find an exact match, then try for <= */
			if ( findcsn_retry ) {
				findcsn_retry = 0;
				goto again;
			}
			rc = LDAP_NO_SUCH_OBJECT;
		}
731
732
		break;
	case FIND_PRESENT:
733
		op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
734
		break;
Howard Chu's avatar
Howard Chu committed
735
736
	}

737
	return rc;
Howard Chu's avatar
Howard Chu committed
738
739
}

740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
static void
syncprov_free_syncop( syncops *so )
{
	syncres *sr, *srnext;
	GroupAssertion *ga, *gnext;

	ldap_pvt_thread_mutex_lock( &so->s_mutex );
	if ( --so->s_inuse > 0 ) {
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
		return;
	}
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
	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;
		ch_free( sr );
	}
	ldap_pvt_thread_mutex_destroy( &so->s_mutex );
	ch_free( so );
}

769
770
/* Send a persistent search response */
static int
771
772
syncprov_sendresp( Operation *op, opcookie *opc, syncops *so,
	Entry **e, int mode )
773
774
775
776
777
{
	slap_overinst *on = opc->son;

	SlapReply rs = { REP_SEARCH };
	LDAPControl *ctrls[2];
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
778
	struct berval cookie = BER_BVNULL, csns[2];
779
780
781
	Entry e_uuid = {0};
	Attribute a_uuid = {0};

Howard Chu's avatar
Howard Chu committed
782
783
784
	if ( so->s_op->o_abandon )
		return SLAPD_ABANDON;

785
	ctrls[1] = NULL;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
786
787
788
789
790
	if ( !BER_BVISNULL( &opc->sctxcsn )) {
		csns[0] = opc->sctxcsn;
		BER_BVZERO( &csns[1] );
		slap_compose_sync_cookie( op, &cookie, csns, so->s_rid, slap_serverID ? slap_serverID : -1 );
	}
791

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
792
#ifdef LDAP_DEBUG
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
793
794
795
796
797
798
799
800
801
	if ( !BER_BVISNULL( &cookie )) {
		if ( so->s_sid > 0 ) {
			Debug( LDAP_DEBUG_SYNC, "syncprov_sendresp: to=%03x, cookie=%s\n",
				so->s_sid, cookie.bv_val , 0 );
		} else {
			Debug( LDAP_DEBUG_SYNC, "syncprov_sendresp: cookie=%s\n",
				cookie.bv_val, 0, 0 );
		}
	}		
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
802
#endif
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
803

804
805
806
	e_uuid.e_attrs = &a_uuid;
	a_uuid.a_desc = slap_schema.si_ad_entryUUID;
	a_uuid.a_nvals = &opc->suuid;
807
	rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
808
		mode, ctrls, 0, 1, &cookie );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
809
810
811
	if ( !BER_BVISNULL( &cookie )) {
		op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
	}
812

813
	rs.sr_ctrls = ctrls;
814
	op->o_bd->bd_info = (BackendInfo *)on->on_info;
815
816
	switch( mode ) {
	case LDAP_SYNC_ADD:
817
818
819
		rs.sr_entry = *e;
		if ( rs.sr_entry->e_private )
			rs.sr_flags = REP_ENTRY_MUSTRELEASE;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
820
		if ( opc->sreference && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
821
			rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
822
			rs.sr_err = send_search_reference( op, &rs );
823
			ber_bvarray_free( rs.sr_ref );
824
825
			if ( !rs.sr_entry )
				*e = NULL;
826
827
828
829
			break;
		}
		/* fallthru */
	case LDAP_SYNC_MODIFY:
830
831
832
		rs.sr_entry = *e;
		if ( rs.sr_entry->e_private )
			rs.sr_flags = REP_ENTRY_MUSTRELEASE;
833
		rs.sr_attrs = op->ors_attrs;
834
		rs.sr_err = send_search_entry( op, &rs );
835
836
		if ( !rs.sr_entry )
			*e = NULL;
837
838
839
840
841
842
		break;
	case LDAP_SYNC_DELETE:
		e_uuid.e_attrs = NULL;
		e_uuid.e_name = opc->sdn;
		e_uuid.e_nname = opc->sndn;
		rs.sr_entry = &e_uuid;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
843
		if ( opc->sreference && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
844
			struct berval bv = BER_BVNULL;
845
			rs.sr_ref = &bv;
846
			rs.sr_err = send_search_reference( op, &rs );
Howard Chu's avatar
Howard Chu committed
847
		} else {
848
			rs.sr_err = send_search_entry( op, &rs );
849
850
851
852
853
		}
		break;
	default:
		assert(0);
	}
854
855
856
857
858
	/* In case someone else freed it already? */
	if ( rs.sr_ctrls ) {
		op->o_tmpfree( rs.sr_ctrls[0], op->o_tmpmemctx );
		rs.sr_ctrls = NULL;
	}
859

860
861
862
	return rs.sr_err;
}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
863
864
865
static void
syncprov_qstart( syncops *so );

866
867
/* Play back queued responses */
static int
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
868
syncprov_qplay( Operation *op, syncops *so )
869
{
Howard Chu's avatar
Howard Chu committed
870
	slap_overinst *on = LDAP_SLIST_FIRST(&so->s_op->o_extra)->oe_key;
871
872
873
	syncres *sr;
	Entry *e;
	opcookie opc;
874
	int rc = 0;
875
876
877

	opc.son = on;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
878
	do {
879
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
880
881
882
883
884
		sr = so->s_res;
		if ( sr )
			so->s_res = sr->s_next;
		if ( !so->s_res )
			so->s_restail = NULL;
Howard Chu's avatar
Howard Chu committed
885
		/* Exit loop with mutex held */
886
		if ( !sr || so->s_op->o_abandon )
887
			break;
Howard Chu's avatar
Howard Chu committed
888
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
889

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
		if ( sr->s_mode == LDAP_SYNC_NEW_COOKIE ) {
		    SlapReply rs = { REP_INTERMEDIATE };

		    rc = syncprov_sendinfo( op, &rs, LDAP_TAG_SYNC_NEW_COOKIE,
				&sr->s_csn, 0, NULL, 0 );
		} else {
			opc.sdn = sr->s_dn;
			opc.sndn = sr->s_ndn;
			opc.suuid = sr->s_uuid;
			opc.sctxcsn = sr->s_csn;
			opc.sreference = sr->s_isreference;
			e = NULL;

			if ( sr->s_mode != LDAP_SYNC_DELETE ) {
				rc = overlay_entry_get_ov( op, &opc.sndn, NULL, NULL, 0, &e, on );
				if ( rc ) {
					Debug( LDAP_DEBUG_SYNC, "syncprov_qplay: failed to get %s, "
						"error (%d), ignoring...\n", opc.sndn.bv_val, rc, 0 );
					ch_free( sr );
					rc = 0;
					continue;
				}
912
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
913
			rc = syncprov_sendresp( op, &opc, so, &e, sr->s_mode );
914

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
915
916
917
			if ( e ) {
				overlay_entry_release_ov( op, e, 0, on );
			}
918
919
920
		}

		ch_free( sr );
921

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
922
923
		/* Exit loop with mutex held */
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
Howard Chu's avatar
Howard Chu committed
924

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
925
926
927
928
929
930
	} while (0);

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

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
932
933
934
935
	if ( rc == 0 && so->s_res ) {
		syncprov_qstart( so );
	} else {
		so->s_flags ^= PS_TASK_QUEUED;
936
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
937

Howard Chu's avatar
Howard Chu committed
938
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
939
940
941
	return rc;
}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
942
/* task for playing back queued responses */
943
944
945
static void *
syncprov_qtask( void *ctx, void *arg )
{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
946
	syncops *so = arg;
947
	OperationBuffer opbuf;
948
949
	Operation *op;
	BackendDB be;
950
	int rc;
951

952
	op = &opbuf.ob_op;
953
	*op = *so->s_op;
954
955
956
	op->o_hdr = &opbuf.ob_hdr;
	op->o_controls = opbuf.ob_controls;
	memset( op->o_controls, 0, sizeof(opbuf.ob_controls) );
957
958
959

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

960
	op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 1);
961
962
963
964
965
966
967
	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
968
	LDAP_SLIST_FIRST(&op->o_extra) = NULL;
969
970
	op->o_callback = NULL;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
971
	rc = syncprov_qplay( op, so );
972

973
974
975
	/* decrement use count... */
	syncprov_free_syncop( so );

976
977
978
	return NULL;
}

979
980
981
982
/* Start the task to play back queued psearch responses */
static void
syncprov_qstart( syncops *so )
{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
983
984
985
986
	so->s_flags |= PS_TASK_QUEUED;
	so->s_inuse++;
	ldap_pvt_thread_pool_submit( &connection_pool, 
		syncprov_qtask, so );
987
988
}

989
990
991
992
993
/* Queue a persistent search response */
static int
syncprov_qresp( opcookie *opc, syncops *so, int mode )
{
	syncres *sr;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
994
995
996
997
998
999
1000
	int srsize;
	struct berval cookie = opc->sctxcsn;

	if ( mode == LDAP_SYNC_NEW_COOKIE ) {
		syncprov_info_t	*si = opc->son->on_bi.bi_private;

		slap_compose_sync_cookie( NULL, &cookie, si->si_ctxcsn,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1001
			so->s_rid, slap_serverID ? slap_serverID : -1);
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1002
	}
1003

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1004
1005
	srsize = sizeof(syncres) + opc->suuid.bv_len + 1 +
		opc->sdn.bv_len + 1 + opc->sndn.bv_len + 1;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1006
1007
	if ( cookie.bv_len )
		srsize += cookie.bv_len + 1;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1008
	sr = ch_malloc( srsize );
1009
1010
1011
1012
1013
	sr->s_next = NULL;
	sr->s_dn.bv_val = (char *)(sr + 1);
	sr->s_dn.bv_len = opc->sdn.bv_len;
	sr->s_mode = mode;
	sr->s_isreference = opc->sreference;
Howard Chu's avatar
Howard Chu committed
1014
1015
	sr->s_ndn.bv_val = lutil_strcopy( sr->s_dn.bv_val,
		 opc->sdn.bv_val ) + 1;
1016
	sr->s_ndn.bv_len = opc->sndn.bv_len;
Howard Chu's avatar
Howard Chu committed
1017
1018
	sr->s_uuid.bv_val = lutil_strcopy( sr->s_ndn.bv_val,
		 opc->sndn.bv_val ) + 1;
1019
	sr->s_uuid.bv_len = opc->suuid.bv_len;
Howard Chu's avatar
Howard Chu committed
1020
	AC_MEMCPY( sr->s_uuid.bv_val, opc->suuid.bv_val, opc->suuid.bv_len );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1021
	if ( cookie.bv_len ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1022
		sr->s_csn.bv_val = sr->s_uuid.bv_val + sr->s_uuid.bv_len + 1;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1023
		strcpy( sr->s_csn.bv_val, cookie.bv_val );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1024
1025
1026
	} else {
		sr->s_csn.bv_val = NULL;
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1027
1028
1029
1030
1031
	sr->s_csn.bv_len = cookie.bv_len;

	if ( mode == LDAP_SYNC_NEW_COOKIE && cookie.bv_val ) {
		ch_free( cookie.bv_val );
	}
1032

Howard Chu's avatar
Howard Chu committed
1033
	ldap_pvt_thread_mutex_lock( &so->s_mutex );
1034
1035
1036
1037
1038
1039
	if ( !so->s_res ) {
		so->s_res = sr;
	} else {
		so->s_restail->s_next = sr;
	}
	so->s_restail = sr;
Howard Chu's avatar
Howard Chu committed
1040
1041
1042
1043
1044
1045

	/* If the base of the psearch was modified, check it next time round */
	if ( so->s_flags & PS_WROTE_BASE ) {
		so->s_flags ^= PS_WROTE_BASE;
		so->s_flags |= PS_FIND_BASE;
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1046
	if (( so->s_flags & (PS_IS_DETACHED|PS_TASK_QUEUED)) == PS_IS_DETACHED ) {
1047
		syncprov_qstart( so );
1048
1049
1050
	}
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
	return LDAP_SUCCESS;
1051
1052
}

1053
static int
Howard Chu's avatar
Howard Chu committed
1054
syncprov_drop_psearch( syncops *so, int lock )
1055
{
1056
1057
1058
1059
1060
	if ( so->s_flags & PS_IS_DETACHED ) {
		if ( lock )
			ldap_pvt_thread_mutex_lock( &so->s_op->o_conn->c_mutex );
		so->s_op->o_conn->c_n_ops_executing--;
		so->s_op->o_conn->c_n_ops_completed++;
1061
		LDAP_STAILQ_REMOVE( &so->s_op->o_conn->c_ops, so->s_op, Operation,
1062
1063
1064
1065
			o_next );
		if ( lock )
			ldap_pvt_thread_mutex_unlock( &so->s_op->o_conn->c_mutex );
	}
1066
	syncprov_free_syncop( so );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1067
1068

	return 0;
1069
1070
}

Howard Chu's avatar
Howard Chu committed
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
static int
syncprov_ab_cleanup( Operation *op, SlapReply *rs )
{
	slap_callback *sc = op->o_callback;
	op->o_callback = sc->sc_next;
	syncprov_drop_psearch( op->o_callback->sc_private, 0 );
	op->o_tmpfree( sc, op->o_tmpmemctx );
	return 0;
}

1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
static int
syncprov_op_abandon( Operation *op, SlapReply *rs )
{
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	syncprov_info_t		*si = on->on_bi.bi_private;
	syncops *so, *soprev;

	ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
	for ( so=si->si_ops, soprev = (syncops *)&si->si_ops; so;
		soprev=so, so=so->s_next ) {
		if ( so->s_op->o_connid == op->o_connid &&
			so->s_op->o_msgid == op->orn_msgid ) {
				so->s_op->o_abandon = 1;
				soprev->s_next = so->s_next;
				break;
		}
	}
	ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
	if ( so ) {
		/* Is this really a Cancel exop? */
		if ( op->o_tag != LDAP_REQ_ABANDON ) {
1102
			so->s_op->o_cancel = SLAP_CANCEL_ACK;
1103
1104
			rs->sr_err = LDAP_CANCELLED;
			send_ldap_result( so->s_op, rs );
Howard Chu's avatar
Howard Chu committed
1105
1106
1107
1108
1109
1110
1111
			if ( so->s_flags & PS_IS_DETACHED ) {
				slap_callback *cb;
				cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
				cb->sc_cleanup = syncprov_ab_cleanup;
				cb->sc_next = op->o_callback;
				cb->sc_private = so;
				return SLAP_CB_CONTINUE;
1112
			}
1113
		}
Howard Chu's avatar
Howard Chu committed
1114
		syncprov_drop_psearch( so, 0 );
1115
1116
1117
1118
	}
	return SLAP_CB_CONTINUE;
}

Howard Chu's avatar
Cleanup    
Howard Chu committed
1119
/* Find which persistent searches are affected by this operation */
Howard Chu's avatar
Howard Chu committed
1120
static void
Howard Chu's avatar
Howard Chu committed
1121
syncprov_matchops( Operation *op, opcookie *opc, int saveit )
Howard Chu's avatar
Howard Chu committed
1122
{
1123
	slap_overinst *on = opc->son;
Howard Chu's avatar
Howard Chu committed
1124
1125
	syncprov_info_t		*si = on->on_bi.bi_private;

1126
	fbase_cookie fc;
1127
	syncops *ss, *sprev, *snext;
1128
	Entry *e = NULL;
Howard Chu's avatar
Howard Chu committed
1129
1130
	Attribute *a;
	int rc;
Howard Chu's avatar
Howard Chu committed
1131
	struct berval newdn;
1132
	int freefdn = 0;
1133
	BackendDB *b0 = op->o_bd, db;
Howard Chu's avatar
Howard Chu committed
1134

Howard Chu's avatar
Howard Chu committed
1135
	fc.fdn = &op->o_req_ndn;
Howard Chu's avatar
Howard Chu committed
1136