syncprov.c 87.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/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 2004-2010 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

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
	Entry *s_e;
52
	struct berval s_dn;
Howard Chu's avatar
Howard Chu committed
53
54
	struct berval s_ndn;
	struct berval s_uuid;
55
56
57
	struct berval s_csn;
	char s_mode;
	char s_isreference;
Howard Chu's avatar
Howard Chu committed
58
59
} syncres;

Howard Chu's avatar
Howard Chu committed
60
61
62
63
64
65
/* 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 */
66
	int		s_rid;
67
	int		s_sid;
Howard Chu's avatar
Howard Chu committed
68
	struct berval s_filterstr;
Howard Chu's avatar
Howard Chu committed
69
	int		s_flags;	/* search status */
Howard Chu's avatar
Howard Chu committed
70
71
72
73
#define	PS_IS_REFRESHING	0x01
#define	PS_IS_DETACHED		0x02
#define	PS_WROTE_BASE		0x04
#define	PS_FIND_BASE		0x08
74
#define	PS_FIX_FILTER		0x10
75
#define	PS_TASK_QUEUED		0x20
Howard Chu's avatar
Howard Chu committed
76

Howard Chu's avatar
Howard Chu committed
77
	int		s_inuse;	/* reference count */
Howard Chu's avatar
Howard Chu committed
78
79
80
	struct syncres *s_res;
	struct syncres *s_restail;
	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
	BerVarray	si_ctxcsn;	/* ldapsync context */
128
	struct berval	si_contextdn;
129
130
	int		*si_sids;
	int		si_numcsns;
131
132
133
	int		si_chkops;	/* checkpointing info */
	int		si_chktime;
	int		si_numops;	/* number of ops since last checkpoint */
134
	int		si_nopres;	/* Skip present phase */
135
	int		si_usehint;	/* use reload hint */
136
	time_t	si_chklast;	/* time of last checkpoint */
137
	Avlnode	*si_mods;	/* entries being modified */
138
	sessionlog	*si_logs;
Howard Chu's avatar
Howard Chu committed
139
	ldap_pvt_thread_rdwr_t	si_csn_rwlock;
Howard Chu's avatar
Howard Chu committed
140
	ldap_pvt_thread_mutex_t	si_ops_mutex;
Howard Chu's avatar
Howard Chu committed
141
	ldap_pvt_thread_mutex_t	si_mods_mutex;
Howard Chu's avatar
Howard Chu committed
142
	ldap_pvt_thread_mutex_t	si_resp_mutex;
Howard Chu's avatar
Howard Chu committed
143
144
145
146
147
} syncprov_info_t;

typedef struct opcookie {
	slap_overinst *son;
	syncmatches *smatches;
148
	modtarget *smt;
149
	Entry *se;
150
151
152
153
	struct berval sdn;	/* DN of entry, for deletes */
	struct berval sndn;
	struct berval suuid;	/* UUID of entry */
	struct berval sctxcsn;
Howard Chu's avatar
Howard Chu committed
154
155
	short osid;	/* sid of op csn */
	short rsid;	/* sid of relay */
156
	short sreference;	/* Is the entry a reference? */
Howard Chu's avatar
Howard Chu committed
157
158
} opcookie;

159
160
161
162
163
typedef struct mutexint {
	ldap_pvt_thread_mutex_t mi_mutex;
	int mi_int;
} mutexint;

164
typedef struct fbase_cookie {
165
166
167
168
	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 */
169
} fbase_cookie;
Howard Chu's avatar
Howard Chu committed
170

171
static AttributeName csn_anlist[3];
Howard Chu's avatar
Howard Chu committed
172
173
static AttributeName uuid_anlist[2];

Howard Chu's avatar
Cleanup    
Howard Chu committed
174
/* Build a LDAPsync intermediate state control */
175
176
177
178
179
static int
syncprov_state_ctrl(
	Operation	*op,
	SlapReply	*rs,
	Entry		*e,
180
	int		entry_sync_state,
181
	LDAPControl	**ctrls,
182
183
184
	int		num_ctrls,
	int		send_cookie,
	struct berval	*cookie )
185
186
187
188
189
190
191
{
	Attribute* a;
	int ret;

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

192
	struct berval	entryuuid_bv = BER_BVNULL;
193
194
195
196

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

199
200
201
202
203
	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;
204
		}
205
	}
206

207
208
	/* FIXME: what if entryuuid is NULL or empty ? */

209
210
211
212
213
214
	if ( send_cookie && cookie ) {
		ber_printf( ber, "{eOON}",
			entry_sync_state, &entryuuid_bv, cookie );
	} else {
		ber_printf( ber, "{eON}",
			entry_sync_state, &entryuuid_bv );
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
	}

	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
234
/* Build a LDAPsync final state control */
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
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
252
	ctrls[num_ctrls] = op->o_tmpalloc( sizeof ( LDAPControl ), op->o_tmpmemctx );
253
254
255
256
257
258
259
260

	ber_printf( ber, "{" );
	if ( send_cookie && cookie ) {
		ber_printf( ber, "O", cookie );
	}
	if ( refreshDeletes == LDAP_SYNC_REFRESH_DELETES ) {
		ber_printf( ber, "b", refreshDeletes );
	}
261
	ber_printf( ber, "N}" );
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279

	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
280
static int
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
335
336
337
338
339
340
341
342
343
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
344
	rs->sr_rspoid = LDAP_SYNC_INFO;
345
346
347
348
349
350
351
	rs->sr_rspdata = &rspdata;
	send_ldap_intermediate( op, rs );
	rs->sr_rspdata = NULL;
	ber_free_buf( ber );

	return LDAP_SUCCESS;
}
352

Howard Chu's avatar
Cleanup    
Howard Chu committed
353
/* Find a modtarget in an AVL tree */
354
355
356
357
358
359
360
361
362
363
364
365
366
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 );
}

367
368
369
370
371
372
373
374
375
/* 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
376
377
378
379
380
381
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 ) {
382
383
384
385
386
387
		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
388
			fc->fbase = 2;
389
390
			fc->fss->s_eid = rs->sr_entry->e_id;
			ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
391

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

Howard Chu's avatar
Howard Chu committed
395
		/* OK, the DN is the same and the entryID is the same. */
Howard Chu's avatar
Howard Chu committed
396
397
			fc->fbase = 1;
		}
Howard Chu's avatar
Howard Chu committed
398
	}
399
400
401
	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
402
403
404
	return LDAP_SUCCESS;
}

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

Howard Chu's avatar
Howard Chu committed
408
static int
409
syncprov_findbase( Operation *op, fbase_cookie *fc )
Howard Chu's avatar
Howard Chu committed
410
{
Howard Chu's avatar
Howard Chu committed
411
412
413
	/* Use basic parameters from syncrepl search, but use
	 * current op's threadctx / tmpmemctx
	 */
Howard Chu's avatar
Howard Chu committed
414
415
416
417
418
419
	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
420

Howard Chu's avatar
Howard Chu committed
421
422
		fc->fss->s_flags ^= PS_FIND_BASE;
		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
Howard Chu's avatar
Howard Chu committed
423

Howard Chu's avatar
Howard Chu committed
424
		fop = *fc->fss->s_op;
Howard Chu's avatar
Howard Chu committed
425

426
		fop.o_bd = fop.o_bd->bd_self;
Howard Chu's avatar
Howard Chu committed
427
428
429
		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
430

Howard Chu's avatar
Howard Chu committed
431
432
		cb.sc_response = findbase_cb;
		cb.sc_private = fc;
Howard Chu's avatar
Howard Chu committed
433

Howard Chu's avatar
Howard Chu committed
434
435
436
437
438
439
440
441
442
443
		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
444
445
		fop.ors_filter = &generic_filter;
		fop.ors_filterstr = generic_filterstr;
Howard Chu's avatar
Howard Chu committed
446

447
		rc = fop.o_bd->be_search( &fop, &frs );
Howard Chu's avatar
Howard Chu committed
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
	} 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
476
477
478
479
480
481

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

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

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 );

512
513
		if ( a && ber_bvcmp( &a->a_vals[0], maxcsn ) > 0 &&
			slap_parse_csn_sid( &a->a_vals[0] ) == slap_serverID ) {
514
515
516
517
518
519
			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
520
521
522
523
524
525

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

Howard Chu's avatar
Howard Chu committed
526
527
528
529
530
	/* 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 )) {
531
		sc->sc_private = (void *)1;
Howard Chu's avatar
Howard Chu committed
532
533
534
535
	}
	return LDAP_SUCCESS;
}

536
537
/* Build a list of entryUUIDs for sending in a SyncID set */

538
539
#define UUID_LEN	16

Howard Chu's avatar
Howard Chu committed
540
541
542
typedef struct fpres_cookie {
	int num;
	BerVarray uuids;
543
	char *last;
Howard Chu's avatar
Howard Chu committed
544
545
546
547
548
549
550
} fpres_cookie;

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

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

static int
586
syncprov_findcsn( Operation *op, find_csn_t mode )
Howard Chu's avatar
Howard Chu committed
587
588
589
590
591
592
593
{
	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 };
594
595
	char buf[LDAP_PVT_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
	char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
596
	struct berval maxcsn;
597
	Filter cf;
598
	AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
Howard Chu's avatar
Howard Chu committed
599
	fpres_cookie pcookie;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
600
	sync_control *srs = NULL;
601
602
	struct slap_limits_set fc_limits;
	int i, rc = LDAP_SUCCESS, findcsn_retry = 1;
603
	int maxid;
604
605
606

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

	fop = *op;
610
	fop.o_sync_mode &= SLAP_CONTROL_MASK;	/* turn off sync_mode */
611
612
	/* We want pure entries, not referrals */
	fop.o_managedsait = SLAP_CONTROL_CRITICAL;
Howard Chu's avatar
Howard Chu committed
613

614
615
	cf.f_ava = &eq;
	cf.f_av_desc = slap_schema.si_ad_entryCSN;
616
	BER_BVZERO( &cf.f_av_value );
617
618
	cf.f_next = NULL;

619
620
621
622
	fop.o_callback = &cb;
	fop.ors_limit = NULL;
	fop.ors_tlimit = SLAP_NO_LIMIT;
	fop.ors_filter = &cf;
623
	fop.ors_filterstr.bv_val = buf;
624

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

		/* 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;
		}
712
		break;
Howard Chu's avatar
Howard Chu committed
713
714
	}

Howard Chu's avatar
Howard Chu committed
715
	fop.o_bd->bd_info = (BackendInfo *)on->on_info;
716
	fop.o_bd->be_search( &fop, &frs );
717
	fop.o_bd->bd_info = (BackendInfo *)on;
Howard Chu's avatar
Howard Chu committed
718

719
720
	switch( mode ) {
	case FIND_MAXCSN:
721
		if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
Howard Chu's avatar
Howard Chu committed
722
723
724
725
#ifdef CHECK_CSN
			Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
			assert( !syn->ssyn_validate( syn, &maxcsn ));
#endif
726
727
728
			ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
			si->si_numops++;	/* ensure a checkpoint */
		}
729
730
731
		break;
	case FIND_CSN:
		/* If matching CSN was not found, invalidate the context. */
Howard Chu's avatar
Howard Chu committed
732
733
734
735
736
737
738
739
		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;
		}
740
741
		break;
	case FIND_PRESENT:
742
		op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
743
		break;
Howard Chu's avatar
Howard Chu committed
744
745
	}

746
	return rc;
Howard Chu's avatar
Howard Chu committed
747
748
}

749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
/* Should find a place to cache these */
static mutexint *get_mutexint()
{
	mutexint *mi = ch_malloc( sizeof( mutexint ));
	ldap_pvt_thread_mutex_init( &mi->mi_mutex );
	mi->mi_int = 1;
	return mi;
}

static void inc_mutexint( mutexint *mi )
{
	ldap_pvt_thread_mutex_lock( &mi->mi_mutex );
	mi->mi_int++;
	ldap_pvt_thread_mutex_unlock( &mi->mi_mutex );
}

/* return resulting counter */
static int dec_mutexint( mutexint *mi )
{
	int i;
	ldap_pvt_thread_mutex_lock( &mi->mi_mutex );
	i = --mi->mi_int;
	ldap_pvt_thread_mutex_unlock( &mi->mi_mutex );
	if ( !i ) {
		ldap_pvt_thread_mutex_destroy( &mi->mi_mutex );
		ch_free( mi );
	}
	return i;
}

779
780
781
782
783
784
785
static void
syncprov_free_syncop( syncops *so )
{
	syncres *sr, *srnext;
	GroupAssertion *ga, *gnext;

	ldap_pvt_thread_mutex_lock( &so->s_mutex );
786
787
	/* already being freed, or still in use */
	if ( !so->s_inuse || --so->s_inuse > 0 ) {
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
		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;
803
804
805
806
807
808
		if ( sr->s_e ) {
			if ( !dec_mutexint( sr->s_e->e_private )) {
				sr->s_e->e_private = NULL;
				entry_free( sr->s_e );
			}
		}
809
810
811
812
813
814
		ch_free( sr );
	}
	ldap_pvt_thread_mutex_destroy( &so->s_mutex );
	ch_free( so );
}

815
816
/* Send a persistent search response */
static int
817
syncprov_sendresp( Operation *op, opcookie *opc, syncops *so, int mode )
818
819
820
821
822
{
	slap_overinst *on = opc->son;

	SlapReply rs = { REP_SEARCH };
	LDAPControl *ctrls[2];
823
	struct berval cookie = BER_BVNULL, csns[2];
824
825
826
	Entry e_uuid = {0};
	Attribute a_uuid = {0};

Howard Chu's avatar
Howard Chu committed
827
828
829
	if ( so->s_op->o_abandon )
		return SLAPD_ABANDON;

830
	ctrls[1] = NULL;
831
832
833
834
835
	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 );
	}
836

837
#ifdef LDAP_DEBUG
838
839
840
841
842
843
844
	if ( so->s_sid > 0 ) {
		Debug( LDAP_DEBUG_SYNC, "syncprov_sendresp: to=%03x, cookie=%s\n",
			so->s_sid, cookie.bv_val ? cookie.bv_val : "", 0 );
	} else {
		Debug( LDAP_DEBUG_SYNC, "syncprov_sendresp: cookie=%s\n",
			cookie.bv_val ? cookie.bv_val : "", 0, 0 );
	}
845
#endif
846

847
848
849
	e_uuid.e_attrs = &a_uuid;
	a_uuid.a_desc = slap_schema.si_ad_entryUUID;
	a_uuid.a_nvals = &opc->suuid;
850
	rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
851
		mode, ctrls, 0, 1, &cookie );
852
853
854
	if ( !BER_BVISNULL( &cookie )) {
		op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
	}
855

856
	rs.sr_ctrls = ctrls;
857
858
859
860
861
862
	rs.sr_entry = &e_uuid;
	if ( mode == LDAP_SYNC_ADD || mode == LDAP_SYNC_MODIFY ) {
		e_uuid = *opc->se;
		e_uuid.e_private = NULL;
	}

863
864
	switch( mode ) {
	case LDAP_SYNC_ADD:
865
		if ( opc->sreference && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
866
			rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
867
			rs.sr_err = send_search_reference( op, &rs );
868
869
870
871
872
			ber_bvarray_free( rs.sr_ref );
			break;
		}
		/* fallthru */
	case LDAP_SYNC_MODIFY:
873
		rs.sr_attrs = op->ors_attrs;
874
		rs.sr_err = send_search_entry( op, &rs );
875
876
877
878
879
		break;
	case LDAP_SYNC_DELETE:
		e_uuid.e_attrs = NULL;
		e_uuid.e_name = opc->sdn;
		e_uuid.e_nname = opc->sndn;
880
		if ( opc->sreference && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
881
			struct berval bv = BER_BVNULL;
882
			rs.sr_ref = &bv;
883
			rs.sr_err = send_search_reference( op, &rs );
Howard Chu's avatar
Howard Chu committed
884
		} else {
885
			rs.sr_err = send_search_entry( op, &rs );
886
887
888
889
890
		}
		break;
	default:
		assert(0);
	}
891
892
	/* In case someone else freed it already? */
	if ( rs.sr_ctrls ) {
Howard Chu's avatar
Howard Chu committed
893
894
895
896
897
898
899
900
		int i;
		for ( i=0; rs.sr_ctrls[i]; i++ ) {
			if ( rs.sr_ctrls[i] == ctrls[0] ) {
				op->o_tmpfree( ctrls[0]->ldctl_value.bv_val, op->o_tmpmemctx );
				ctrls[0]->ldctl_value.bv_val = NULL;
				break;
			}
		}
901
902
		rs.sr_ctrls = NULL;
	}
903

904
905
906
	return rs.sr_err;
}

907
908
909
static void
syncprov_qstart( syncops *so );

910
911
/* Play back queued responses */
static int
912
syncprov_qplay( Operation *op, syncops *so )
913
{
914
	slap_overinst *on = LDAP_SLIST_FIRST(&so->s_op->o_extra)->oe_key;
915
916
917
	syncres *sr;
	Entry *e;
	opcookie opc;
918
	int rc = 0;
919
920
921

	opc.son = on;

922
	do {
923
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
924
925
926
927
928
		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
929
		/* Exit loop with mutex held */
930
		if ( !sr || so->s_op->o_abandon )
931
			break;
Howard Chu's avatar
Howard Chu committed
932
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
933

934
		if ( sr->s_mode == LDAP_SYNC_NEW_COOKIE ) {
935
			SlapReply rs = { REP_INTERMEDIATE };
936

937
			rc = syncprov_sendinfo( op, &rs, LDAP_TAG_SYNC_NEW_COOKIE,
938
939
940
941
942
943
944
				&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;
945
946
947
			opc.se = sr->s_e;

			rc = syncprov_sendresp( op, &opc, so, sr->s_mode );
948

949
950
951
952
953
		}
		if ( sr->s_e ) {
			if ( !dec_mutexint( sr->s_e->e_private )) {
				sr->s_e->e_private = NULL;
				entry_free ( sr->s_e );
954
			}
955
956
957
		}

		ch_free( sr );
958

959
960
		/* Exit loop with mutex held */
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
Howard Chu's avatar
Howard Chu committed
961

962
963
964
	} while (0);

	/* We now only send one change at a time, to prevent one
Howard Chu's avatar
Howard Chu committed
965
966
	 * psearch from hogging all the CPU. Resubmit this task if
	 * there are more responses queued and no errors occurred.
967
	 */
968

Howard Chu's avatar
Howard Chu committed
969
	if ( rc == 0 && so->s_res ) {
970
971
972
		syncprov_qstart( so );
	} else {
		so->s_flags ^= PS_TASK_QUEUED;
973
	}
974

Howard Chu's avatar
Howard Chu committed
975
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
976
977
978
	return rc;
}

979
/* task for playing back queued responses */
980
981
982
static void *
syncprov_qtask( void *ctx, void *arg )
{
983
	syncops *so = arg;
984
	OperationBuffer opbuf;
985
986
	Operation *op;
	BackendDB be;
987
	int rc;
988

989
	op = &opbuf.ob_op;
990
	*op = *so->s_op;
991
992
993
	op->o_hdr = &opbuf.ob_hdr;
	op->o_controls = opbuf.ob_controls;
	memset( op->o_controls, 0, sizeof(opbuf.ob_controls) );
994
995
996

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

997
	op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 1);
998
999
1000
	op->o_tmpmfuncs = &slap_sl_mfuncs;
	op->o_threadctx = ctx;

For faster browsing, not all history is shown. View entire blame