syncprov.c 81.5 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
Howard Chu's avatar
Howard Chu committed
74

Howard Chu's avatar
Howard Chu committed
75
	int		s_inuse;	/* reference count */
Howard Chu's avatar
Howard Chu committed
76
77
	struct syncres *s_res;
	struct syncres *s_restail;
Howard Chu's avatar
Howard Chu committed
78
	struct re_s	*s_qtask;	/* task for playing psearch responses */
79
#define	RUNQ_INTERVAL	36000	/* a long time */
Howard Chu's avatar
Howard Chu committed
80
	ldap_pvt_thread_mutex_t	s_mutex;
Howard Chu's avatar
Howard Chu committed
81
82
} syncops;

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

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

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

106
107
108
109
110
/* Session log data */
typedef struct slog_entry {
	struct slog_entry *se_next;
	struct berval se_uuid;
	struct berval se_csn;
111
	int	se_sid;
112
113
114
115
116
117
118
119
120
121
122
123
	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
124
/* The main state for this overlay */
Howard Chu's avatar
Howard Chu committed
125
126
typedef struct syncprov_info_t {
	syncops		*si_ops;
127
128
129
	BerVarray	si_ctxcsn;	/* ldapsync context */
	int		*si_sids;
	int		si_numcsns;
130
131
132
	int		si_chkops;	/* checkpointing info */
	int		si_chktime;
	int		si_numops;	/* number of ops since last checkpoint */
133
	int		si_nopres;	/* Skip present phase */
134
	int		si_usehint;	/* use reload hint */
135
	time_t	si_chklast;	/* time of last checkpoint */
136
	Avlnode	*si_mods;	/* entries being modified */
137
	sessionlog	*si_logs;
Howard Chu's avatar
Howard Chu committed
138
	ldap_pvt_thread_rdwr_t	si_csn_rwlock;
Howard Chu's avatar
Howard Chu committed
139
	ldap_pvt_thread_mutex_t	si_ops_mutex;
Howard Chu's avatar
Howard Chu committed
140
	ldap_pvt_thread_mutex_t	si_mods_mutex;
Howard Chu's avatar
Howard Chu committed
141
142
143
144
145
} syncprov_info_t;

typedef struct opcookie {
	slap_overinst *son;
	syncmatches *smatches;
146
147
148
149
150
	struct berval sdn;	/* DN of entry, for deletes */
	struct berval sndn;
	struct berval suuid;	/* UUID of entry */
	struct berval sctxcsn;
	int sreference;	/* Is the entry a reference? */
Howard Chu's avatar
Howard Chu committed
151
152
} opcookie;

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

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

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

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

181
	struct berval	entryuuid_bv = BER_BVNULL;
182
183
184
185

	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
186
	ctrls[num_ctrls] = op->o_tmpalloc( sizeof ( LDAPControl ), op->o_tmpmemctx );
187

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

196
197
	/* FIXME: what if entryuuid is NULL or empty ? */

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

	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
223
/* Build a LDAPsync final state control */
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
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
241
	ctrls[num_ctrls] = op->o_tmpalloc( sizeof ( LDAPControl ), op->o_tmpmemctx );
242
243
244
245
246
247
248
249

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

	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
269
static int
270
271
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
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
333
	rs->sr_rspoid = LDAP_SYNC_INFO;
334
335
336
337
338
339
340
	rs->sr_rspdata = &rspdata;
	send_ldap_intermediate( op, rs );
	rs->sr_rspdata = NULL;
	ber_free_buf( ber );

	return LDAP_SUCCESS;
}
341

Howard Chu's avatar
Cleanup    
Howard Chu committed
342
/* Find a modtarget in an AVL tree */
343
344
345
346
347
348
349
350
351
352
353
354
355
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 );
}

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

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

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

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

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

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

Howard Chu's avatar
Howard Chu committed
413
		fop = *fc->fss->s_op;
Howard Chu's avatar
Howard Chu committed
414

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
415
		fop.o_bd = fop.o_bd->bd_self;
Howard Chu's avatar
Howard Chu committed
416
417
418
		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
419

Howard Chu's avatar
Howard Chu committed
420
421
		cb.sc_response = findbase_cb;
		cb.sc_private = fc;
Howard Chu's avatar
Howard Chu committed
422

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

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
436
		rc = fop.o_bd->be_search( &fop, &frs );
Howard Chu's avatar
Howard Chu committed
437
438
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
	} 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
465
466
467
468
469
470

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

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

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

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

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

525
526
/* Build a list of entryUUIDs for sending in a SyncID set */

527
528
#define UUID_LEN	16

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

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

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

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

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

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

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

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

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

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

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

708
709
	switch( mode ) {
	case FIND_MAXCSN:
710
		if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
711
712
713
714
#ifdef CHECK_CSN
			Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
			assert( !syn->ssyn_validate( syn, &maxcsn ));
#endif
715
716
717
			ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
			si->si_numops++;	/* ensure a checkpoint */
		}
718
719
720
		break;
	case FIND_CSN:
		/* If matching CSN was not found, invalidate the context. */
Howard Chu's avatar
Howard Chu committed
721
722
723
724
725
726
727
728
		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;
		}
729
730
		break;
	case FIND_PRESENT:
731
		op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
732
		break;
Howard Chu's avatar
Howard Chu committed
733
734
	}

735
	return rc;
Howard Chu's avatar
Howard Chu committed
736
737
}

738
739
740
741
742
743
744
745
746
747
748
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;
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
749
750
751
752
753
754
755
	if ( so->s_qtask ) {
		ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
		if ( ldap_pvt_runqueue_isrunning( &slapd_rq, so->s_qtask ) )
			ldap_pvt_runqueue_stoptask( &slapd_rq, so->s_qtask );
		ldap_pvt_runqueue_remove( &slapd_rq, so->s_qtask );
		ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
	}
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
	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 );
}

774
775
/* Send a persistent search response */
static int
776
777
syncprov_sendresp( Operation *op, opcookie *opc, syncops *so,
	Entry **e, int mode )
778
779
780
781
782
{
	slap_overinst *on = opc->son;

	SlapReply rs = { REP_SEARCH };
	LDAPControl *ctrls[2];
783
	struct berval cookie, csns[2];
784
785
786
	Entry e_uuid = {0};
	Attribute a_uuid = {0};

Howard Chu's avatar
Howard Chu committed
787
788
789
	if ( so->s_op->o_abandon )
		return SLAPD_ABANDON;

790
	ctrls[1] = NULL;
791
792
	csns[0] = opc->sctxcsn;
	BER_BVZERO( &csns[1] );
793
	slap_compose_sync_cookie( op, &cookie, csns, so->s_rid, so->s_sid );
794

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
795
796
	Debug( LDAP_DEBUG_SYNC, "syncprov_sendresp: cookie=%s\n", cookie.bv_val, 0, 0 );

797
798
799
	e_uuid.e_attrs = &a_uuid;
	a_uuid.a_desc = slap_schema.si_ad_entryUUID;
	a_uuid.a_nvals = &opc->suuid;
800
	rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
801
		mode, ctrls, 0, 1, &cookie );
Howard Chu's avatar
Howard Chu committed
802
	op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
803

804
	rs.sr_ctrls = ctrls;
805
	op->o_bd->bd_info = (BackendInfo *)on->on_info;
806
807
	switch( mode ) {
	case LDAP_SYNC_ADD:
808
809
810
		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
811
		if ( opc->sreference && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
812
			rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
813
			rs.sr_err = send_search_reference( op, &rs );
814
			ber_bvarray_free( rs.sr_ref );
815
816
			if ( !rs.sr_entry )
				*e = NULL;
817
818
819
820
			break;
		}
		/* fallthru */
	case LDAP_SYNC_MODIFY:
821
822
823
		rs.sr_entry = *e;
		if ( rs.sr_entry->e_private )
			rs.sr_flags = REP_ENTRY_MUSTRELEASE;
824
		rs.sr_attrs = op->ors_attrs;
825
		rs.sr_err = send_search_entry( op, &rs );
826
827
		if ( !rs.sr_entry )
			*e = NULL;
828
829
830
831
832
833
		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
834
		if ( opc->sreference && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
835
			struct berval bv = BER_BVNULL;
836
			rs.sr_ref = &bv;
837
			rs.sr_err = send_search_reference( op, &rs );
Howard Chu's avatar
Howard Chu committed
838
		} else {
839
			rs.sr_err = send_search_entry( op, &rs );
840
841
842
843
844
		}
		break;
	default:
		assert(0);
	}
845
846
847
848
849
	/* 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;
	}
850

851
852
853
854
855
	return rs.sr_err;
}

/* Play back queued responses */
static int
Howard Chu's avatar
Howard Chu committed
856
syncprov_qplay( Operation *op, struct re_s *rtask )
857
{
Howard Chu's avatar
Howard Chu committed
858
859
	syncops *so = rtask->arg;
	slap_overinst *on = LDAP_SLIST_FIRST(&so->s_op->o_extra)->oe_key;
860
861
862
	syncres *sr;
	Entry *e;
	opcookie opc;
863
	int rc = 0;
864
865
866
867

	opc.son = on;

	for (;;) {
868
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
869
870
871
872
873
		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
874
		/* Exit loop with mutex held */
875
		if ( !sr || so->s_op->o_abandon )
876
			break;
Howard Chu's avatar
Howard Chu committed
877
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
878

879
880
881
882
883
884
885
886
		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 ) {
887
			rc = overlay_entry_get_ov( op, &opc.sndn, NULL, NULL, 0, &e, on );
888
			if ( rc ) {
889
				Debug( LDAP_DEBUG_SYNC, "syncprov_qplay: failed to get %s, "
Howard Chu's avatar
Howard Chu committed
890
					"error (%d), ignoring...\n", opc.sndn.bv_val, rc, 0 );
891
				ch_free( sr );
892
				rc = 0;
893
				continue;
894
			}
895
896
		}
		rc = syncprov_sendresp( op, &opc, so, &e, sr->s_mode );
897

898
		if ( e ) {
899
			overlay_entry_release_ov( op, e, 0, on );
900
901
902
		}

		ch_free( sr );
903

Howard Chu's avatar
Howard Chu committed
904
905
906
		if ( rc ) {
			/* Exit loop with mutex held */
			ldap_pvt_thread_mutex_lock( &so->s_mutex );
907
			break;
Howard Chu's avatar
Howard Chu committed
908
909
910
911
912
913
914
915
916
917
918
		}
	}

	/* wait until we get explicitly scheduled again */
	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
	ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
	if ( rc == 0 ) {
		ldap_pvt_runqueue_resched( &slapd_rq, rtask, 1 );
	} else {
		/* bail out on any error */
		ldap_pvt_runqueue_remove( &slapd_rq, rtask );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
919
920
921
922

		/* Prevent duplicate remove */
		if ( so->s_qtask == rtask )
			so->s_qtask = NULL;
923
	}
Howard Chu's avatar
Howard Chu committed
924
925
	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
926
927
928
929
930
931
932
933
934
	return rc;
}

/* runqueue task for playing back queued responses */
static void *
syncprov_qtask( void *ctx, void *arg )
{
	struct re_s *rtask = arg;
	syncops *so = rtask->arg;
935
	OperationBuffer opbuf;
936
937
	Operation *op;
	BackendDB be;
938
	int rc;
939

940
	op = &opbuf.ob_op;
941
	*op = *so->s_op;
942
943
944
	op->o_hdr = &opbuf.ob_hdr;
	op->o_controls = opbuf.ob_controls;
	memset( op->o_controls, 0, sizeof(opbuf.ob_controls) );
945
946
947

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

948
	op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 1);
949
950
951
952
953
954
955
	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
956
	LDAP_SLIST_FIRST(&op->o_extra) = NULL;
957
958
	op->o_callback = NULL;

Howard Chu's avatar
Howard Chu committed
959
	rc = syncprov_qplay( op, rtask );
960

961
962
963
	/* decrement use count... */
	syncprov_free_syncop( so );

964
965
966
967
968
969
970
971
972
973
974
#if 0	/* FIXME: connection_close isn't exported from slapd.
		 * should it be?
		 */
	if ( rc ) {
		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
		if ( connection_state_closing( op->o_conn )) {
			connection_close( op->o_conn );
		}
		ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
	}
#endif
975
976
977
	return NULL;
}

978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
/* Start the task to play back queued psearch responses */
static void
syncprov_qstart( syncops *so )
{
	int wake=0;
	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
	if ( !so->s_qtask ) {
		so->s_qtask = ldap_pvt_runqueue_insert( &slapd_rq, RUNQ_INTERVAL,
			syncprov_qtask, so, "syncprov_qtask",
			so->s_op->o_conn->c_peer_name.bv_val );
		++so->s_inuse;
		wake = 1;
	} else {
		if (!ldap_pvt_runqueue_isrunning( &slapd_rq, so->s_qtask ) &&
			!so->s_qtask->next_sched.tv_sec ) {
			so->s_qtask->interval.tv_sec = 0;
			ldap_pvt_runqueue_resched( &slapd_rq, so->s_qtask, 0 );
			so->s_qtask->interval.tv_sec = RUNQ_INTERVAL;
			++so->s_inuse;
			wake = 1;
		}
	}
	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
	if ( wake )
		slap_wake_listener();
}

1005
1006
1007
1008
1009
/* 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
1010
	int sid, srsize;
1011
1012
1013

	/* Don't send changes back to their originator */
	sid = slap_parse_csn_sid( &opc->sctxcsn );
1014
	if ( sid >= 0 && sid == so->s_sid )
1015
		return LDAP_SUCCESS;
1016

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1017
1018
1019
1020
1021
	srsize = sizeof(syncres) + opc->suuid.bv_len + 1 +
		opc->sdn.bv_len + 1 + opc->sndn.bv_len + 1;
	if ( opc->sctxcsn.bv_len )
		srsize += opc->sctxcsn.bv_len + 1;
	sr = ch_malloc( srsize );
1022
1023
1024
1025
1026
	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
1027
1028
	sr->s_ndn.bv_val = lutil_strcopy( sr->s_dn.bv_val,
		 opc->sdn.bv_val ) + 1;
1029
	sr->s_ndn.bv_len = opc->sndn.bv_len;
Howard Chu's avatar
Howard Chu committed
1030
1031
	sr->s_uuid.bv_val = lutil_strcopy( sr->s_ndn.bv_val,
		 opc->sndn.bv_val ) + 1;
1032
	sr->s_uuid.bv_len = opc->suuid.bv_len;
Howard Chu's avatar
Howard Chu committed
1033
	AC_MEMCPY( sr->s_uuid.bv_val, opc->suuid.bv_val, opc->suuid.bv_len );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1034
1035
1036
1037
1038
1039
	if ( opc->sctxcsn.bv_len ) {
		sr->s_csn.bv_val = sr->s_uuid.bv_val + sr->s_uuid.bv_len + 1;
		strcpy( sr->s_csn.bv_val, opc->sctxcsn.bv_val );
	} else {
		sr->s_csn.bv_val = NULL;
	}
1040
1041
	sr->s_csn.bv_len = opc->sctxcsn.bv_len;

Howard Chu's avatar
Howard Chu committed
1042
	ldap_pvt_thread_mutex_lock( &so->s_mutex );
1043
1044
1045
1046
1047
1048
	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
1049
1050
1051
1052
1053
1054

	/* 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;
	}
1055
	if ( so->s_flags & PS_IS_DETACHED ) {
1056
		syncprov_qstart( so );
1057
1058
1059
	}
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
	return LDAP_SUCCESS;
1060
1061
}

1062
static int
Howard Chu's avatar
Howard Chu committed
1063
syncprov_drop_psearch( syncops *so, int lock )
1064
{
1065
1066
1067
1068
1069
	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++;
1070
		LDAP_STAILQ_REMOVE( &so->s_op->o_conn->c_ops, so->s_op, Operation,
1071
1072
1073
1074
			o_next );
		if ( lock )
			ldap_pvt_thread_mutex_unlock( &so->s_op->o_conn->c_mutex );
	}
1075
	syncprov_free_syncop( so );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1076
1077

	return 0;
1078
1079
}

Howard Chu's avatar
Howard Chu committed
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
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;
}

1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
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 ) {
1111
			so->s_op->o_cancel = SLAP_CANCEL_ACK;
1112
1113
			rs->sr_err = LDAP_CANCELLED;
			send_ldap_result( so->s_op, rs );
Howard Chu's avatar
Howard Chu committed
1114
1115
1116
1117
1118
1119
1120
			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;
1121
			}
1122
		}
Howard Chu's avatar
Howard Chu committed
1123
		syncprov_drop_psearch( so, 0 );
1124
1125
1126
1127
	}
	return SLAP_CB_CONTINUE;
}

Howard Chu's avatar
Cleanup    
Howard Chu committed
1128
/* Find which persistent searches are affected by this operation */
Howard Chu's avatar
Howard Chu committed
1129
static void
Howard Chu's avatar
Howard Chu committed
1130
syncprov_matchops( Operation *op, opcookie *opc, int saveit )
Howard Chu's avatar
Howard Chu committed
1131
{
1132
	slap_overinst *on = opc->son;
Howard Chu's avatar
Howard Chu committed
1133
1134
	syncprov_info_t		*si = on->on_bi.bi_private;

1135
	fbase_cookie fc;
1136
	syncops *ss, *sprev, *snext;
1137
	Entry *e = NULL;
Howard Chu's avatar
Howard Chu committed
1138
1139
	Attribute *a;
	int rc;
Howard Chu's avatar
Howard Chu committed
1140
	struct berval newdn;
1141
	int freefdn = 0;
1142
	BackendDB *b0 = op->o_bd, db;
Howard Chu's avatar
Howard Chu committed
1143

Howard Chu's avatar
Howard Chu committed
1144
	fc.fdn = &op->o_req_ndn;
Howard Chu's avatar
Howard Chu committed
1145
1146
1147
1148
1149
1150
1151
	/* compute new DN */
	if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
		struct berval pdn;
		if ( op->orr_nnewSup ) pdn = *op->orr_nnewSup;
		else dnParent( fc.fdn, &pdn );
		build_new_dn( &newdn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
		fc.fdn = &newdn;