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

#include "portable.h"

#ifdef SLAPD_OVER_SYNCPROV

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

31
#ifdef LDAP_DEVEL
32
#define	CHECK_CSN	1
33
#endif
34

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

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

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

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

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

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

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

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

#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
113
114
115
116
117
118
/* Record of which searches matched at premodify step */
typedef struct syncmatches {
	struct syncmatches *sm_next;
	syncops *sm_op;
} syncmatches;

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

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

140
141
142
143
144
145
146
147
148
149
150
151
152
/* Accesslog callback data */
typedef struct syncprov_accesslog_deletes {
	Operation *op;
	SlapReply *rs;
	sync_control *srs;
	BerVarray ctxcsn;
	int numcsns, *sids;
	Avlnode *uuids;
	BerVarray uuid_list;
	int ndel, list_len;
	char *uuid_buf;
} syncprov_accesslog_deletes;

Howard Chu's avatar
Cleanup    
Howard Chu committed
153
/* The main state for this overlay */
Howard Chu's avatar
Howard Chu committed
154
155
typedef struct syncprov_info_t {
	syncops		*si_ops;
156
	struct berval	si_contextdn;
157
	struct berval	si_logbase;
158
	BerVarray	si_ctxcsn;	/* ldapsync context */
159
160
	int		*si_sids;
	int		si_numcsns;
161
162
163
	int		si_chkops;	/* checkpointing info */
	int		si_chktime;
	int		si_numops;	/* number of ops since last checkpoint */
164
	int		si_nopres;	/* Skip present phase */
165
	int		si_usehint;	/* use reload hint */
166
	int		si_active;	/* True if there are active mods */
167
168
	int		si_dirty;	/* True if the context is dirty, i.e changes
						 * have been made without updating the csn. */
169
	time_t	si_chklast;	/* time of last checkpoint */
170
	Avlnode	*si_mods;	/* entries being modified */
171
	sessionlog	*si_logs;
Howard Chu's avatar
Howard Chu committed
172
	ldap_pvt_thread_rdwr_t	si_csn_rwlock;
Howard Chu's avatar
Howard Chu committed
173
	ldap_pvt_thread_mutex_t	si_ops_mutex;
Howard Chu's avatar
Howard Chu committed
174
	ldap_pvt_thread_mutex_t	si_mods_mutex;
Howard Chu's avatar
Howard Chu committed
175
	ldap_pvt_thread_mutex_t	si_resp_mutex;
Howard Chu's avatar
Howard Chu committed
176
177
178
179
180
} syncprov_info_t;

typedef struct opcookie {
	slap_overinst *son;
	syncmatches *smatches;
181
	modtarget *smt;
182
	Entry *se;
183
184
185
186
	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
187
188
	short osid;	/* sid of op csn */
	short rsid;	/* sid of relay */
189
	short sreference;	/* Is the entry a reference? */
Howard Chu's avatar
Howard Chu committed
190
	syncres ssres;
Howard Chu's avatar
Howard Chu committed
191
192
} opcookie;

193
typedef struct fbase_cookie {
194
195
196
197
	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 */
198
} fbase_cookie;
Howard Chu's avatar
Howard Chu committed
199

200
static AttributeName csn_anlist[3];
Howard Chu's avatar
Howard Chu committed
201
202
static AttributeName uuid_anlist[2];

203
204
205
static AttributeDescription *ad_reqType, *ad_reqResult, *ad_reqDN,
							*ad_reqEntryUUID, *ad_minCSN;

Howard Chu's avatar
Cleanup    
Howard Chu committed
206
/* Build a LDAPsync intermediate state control */
207
208
209
210
211
static int
syncprov_state_ctrl(
	Operation	*op,
	SlapReply	*rs,
	Entry		*e,
212
	int		entry_sync_state,
213
	LDAPControl	**ctrls,
214
215
216
	int		num_ctrls,
	int		send_cookie,
	struct berval	*cookie )
217
218
219
220
221
222
{
	Attribute* a;
	int ret;

	BerElementBuffer berbuf;
	BerElement *ber = (BerElement *)&berbuf;
223
224
	LDAPControl *cp;
	struct berval bv;
225
	struct berval	entryuuid_bv = BER_BVNULL;
226
227
228
229

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

230
231
232
233
234
	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;
235
		}
236
	}
237

238
239
	/* FIXME: what if entryuuid is NULL or empty ? */

240
241
242
243
244
245
	if ( send_cookie && cookie ) {
		ber_printf( ber, "{eOON}",
			entry_sync_state, &entryuuid_bv, cookie );
	} else {
		ber_printf( ber, "{eON}",
			entry_sync_state, &entryuuid_bv );
246
247
	}

248
249
250
251
252
253
254
255
256
257
	ret = ber_flatten2( ber, &bv, 0 );
	if ( ret == 0 ) {
		cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
		cp->ldctl_oid = LDAP_CONTROL_SYNC_STATE;
		cp->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
		cp->ldctl_value.bv_val = (char *)&cp[1];
		cp->ldctl_value.bv_len = bv.bv_len;
		AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
		ctrls[num_ctrls] = cp;
	}
258
259
260
261
	ber_free_buf( ber );

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
Howard Chu's avatar
Howard Chu committed
262
			"slap_build_sync_ctrl: ber_flatten2 failed (%d)\n",
263
			ret );
264
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
Howard Chu's avatar
Howard Chu committed
265
		return LDAP_OTHER;
266
267
268
269
270
	}

	return LDAP_SUCCESS;
}

Howard Chu's avatar
Cleanup    
Howard Chu committed
271
/* Build a LDAPsync final state control */
272
273
274
275
276
277
278
279
280
281
282
283
284
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;
285
286
	LDAPControl *cp;
	struct berval bv;
287
288
289
290
291
292
293
294
295
296
297

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

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

300
301
302
303
304
305
306
307
308
309
	ret = ber_flatten2( ber, &bv, 0 );
	if ( ret == 0 ) {
		cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
		cp->ldctl_oid = LDAP_CONTROL_SYNC_DONE;
		cp->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
		cp->ldctl_value.bv_val = (char *)&cp[1];
		cp->ldctl_value.bv_len = bv.bv_len;
		AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
		ctrls[num_ctrls] = cp;
	}
310
311
312
313
314

	ber_free_buf( ber );

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
Howard Chu's avatar
Howard Chu committed
315
			"syncprov_done_ctrl: ber_flatten2 failed (%d)\n",
316
			ret );
317
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
Howard Chu's avatar
Howard Chu committed
318
		return LDAP_OTHER;
319
320
321
322
323
	}

	return LDAP_SUCCESS;
}

Howard Chu's avatar
Howard Chu committed
324
static int
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
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:
346
347
348
			Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
				"sending a new cookie=%s\n",
				op->o_log_prefix, cookie->bv_val );
349
350
351
352
			ber_printf( ber, "tO", type, cookie );
			break;
		case LDAP_TAG_SYNC_REFRESH_DELETE:
		case LDAP_TAG_SYNC_REFRESH_PRESENT:
353
354
355
356
357
			Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
				"%s cookie=%s\n",
				op->o_log_prefix,
				type == LDAP_TAG_SYNC_REFRESH_DELETE ? "refreshDelete" : "refreshPresent",
				cookie ? cookie->bv_val : "" );
358
359
360
361
362
363
364
365
366
367
			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:
368
369
370
371
			Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
				"%s syncIdSet cookie=%s\n",
				op->o_log_prefix, refreshDeletes ? "delete" : "present",
				cookie ? cookie->bv_val : "" );
372
373
374
375
376
377
378
379
380
381
382
383
			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,
384
385
				"%s syncprov_sendinfo: invalid syncinfo type (%d)\n",
				op->o_log_prefix, type );
386
387
388
389
390
391
392
393
			return LDAP_OTHER;
		}
	}

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

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
Howard Chu's avatar
Howard Chu committed
394
			"syncprov_sendinfo: ber_flatten2 failed (%d)\n",
395
			ret );
396
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
Howard Chu's avatar
Howard Chu committed
397
		return LDAP_OTHER;
398
399
	}

Howard Chu's avatar
Cleanup    
Howard Chu committed
400
	rs->sr_rspoid = LDAP_SYNC_INFO;
401
402
403
404
405
406
407
	rs->sr_rspdata = &rspdata;
	send_ldap_intermediate( op, rs );
	rs->sr_rspdata = NULL;
	ber_free_buf( ber );

	return LDAP_SUCCESS;
}
408

Howard Chu's avatar
Cleanup    
Howard Chu committed
409
/* Find a modtarget in an AVL tree */
410
411
412
413
414
415
416
static int
sp_avl_cmp( const void *c1, const void *c2 )
{
	const modtarget *m1, *m2;
	int rc;

	m1 = c1; m2 = c2;
417
	rc = m1->mt_dn.bv_len - m2->mt_dn.bv_len;
418
419

	if ( rc ) return rc;
420
	return ber_bvcmp( &m1->mt_dn, &m2->mt_dn );
421
422
}

423
424
425
426
427
428
429
430
static int
sp_uuid_cmp( const void *l, const void *r )
{
	const struct berval *left = l, *right = r;

	return ber_bvcmp( left, right );
}

431
432
433
434
435
436
437
438
439
/* 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
440
441
442
443
444
445
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 ) {
446
447
448
449
450
451
		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
452
			fc->fbase = 2;
453
454
			fc->fss->s_eid = rs->sr_entry->e_id;
			ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
455

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

Howard Chu's avatar
Howard Chu committed
459
		/* OK, the DN is the same and the entryID is the same. */
Howard Chu's avatar
Howard Chu committed
460
461
			fc->fbase = 1;
		}
Howard Chu's avatar
Howard Chu committed
462
	}
463
	if ( rs->sr_err != LDAP_SUCCESS ) {
464
		Debug( LDAP_DEBUG_ANY, "findbase failed! %d\n", rs->sr_err );
465
	}
Howard Chu's avatar
Howard Chu committed
466
467
468
	return LDAP_SUCCESS;
}

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

Howard Chu's avatar
Howard Chu committed
472
static int
473
syncprov_findbase( Operation *op, fbase_cookie *fc )
Howard Chu's avatar
Howard Chu committed
474
{
Howard Chu's avatar
Howard Chu committed
475
476
477
	/* Use basic parameters from syncrepl search, but use
	 * current op's threadctx / tmpmemctx
	 */
Howard Chu's avatar
Howard Chu committed
478
479
480
481
482
483
	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
484

Howard Chu's avatar
Howard Chu committed
485
486
		fc->fss->s_flags ^= PS_FIND_BASE;
		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
Howard Chu's avatar
Howard Chu committed
487

Howard Chu's avatar
Howard Chu committed
488
		fop = *fc->fss->s_op;
Howard Chu's avatar
Howard Chu committed
489

490
		fop.o_bd = fop.o_bd->bd_self;
Howard Chu's avatar
Howard Chu committed
491
492
493
		fop.o_hdr = op->o_hdr;
		fop.o_time = op->o_time;
		fop.o_tincr = op->o_tincr;
494
		fop.o_extra = op->o_extra;
Howard Chu's avatar
Howard Chu committed
495

Howard Chu's avatar
Howard Chu committed
496
497
		cb.sc_response = findbase_cb;
		cb.sc_private = fc;
Howard Chu's avatar
Howard Chu committed
498

Howard Chu's avatar
Howard Chu committed
499
500
501
502
503
504
505
506
507
508
		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
509
510
		fop.ors_filter = &generic_filter;
		fop.ors_filterstr = generic_filterstr;
Howard Chu's avatar
Howard Chu committed
511

512
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_findbase: searching\n", op->o_log_prefix );
513
		rc = fop.o_bd->be_search( &fop, &frs );
Howard Chu's avatar
Howard Chu committed
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
	} 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
542
543
544
545
546
547

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

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

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

578
579
		if ( a && ber_bvcmp( &a->a_vals[0], maxcsn ) > 0 &&
			slap_parse_csn_sid( &a->a_vals[0] ) == slap_serverID ) {
580
581
582
583
584
585
			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
586
587
588
589
590
591

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

Howard Chu's avatar
Howard Chu committed
592
593
594
595
596
	/* 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 )) {
597
		sc->sc_private = (void *)1;
Howard Chu's avatar
Howard Chu committed
598
599
600
601
	}
	return LDAP_SUCCESS;
}

602
603
/* Build a list of entryUUIDs for sending in a SyncID set */

604
605
#define UUID_LEN	16

Howard Chu's avatar
Howard Chu committed
606
607
608
typedef struct fpres_cookie {
	int num;
	BerVarray uuids;
609
	char *last;
Howard Chu's avatar
Howard Chu committed
610
611
612
613
614
615
616
} fpres_cookie;

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

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

static int
cmikk@qwest.net's avatar
cmikk@qwest.net committed
652
syncprov_findcsn( Operation *op, find_csn_t mode, struct berval *csn )
Howard Chu's avatar
Howard Chu committed
653
654
655
656
657
658
659
{
	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 };
660
661
	char buf[LDAP_PVT_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
	char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
662
	struct berval maxcsn;
663
	Filter cf;
664
	AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
Howard Chu's avatar
Howard Chu committed
665
	fpres_cookie pcookie;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
666
	sync_control *srs = NULL;
667
668
	struct slap_limits_set fc_limits;
	int i, rc = LDAP_SUCCESS, findcsn_retry = 1;
669
	int maxid;
670
671
672

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

675
676
677
678
679
680
681
682
683
	Debug( LDAP_DEBUG_SYNC, "%s syncprov_findcsn: mode=%s csn=%s\n",
		op->o_log_prefix,
		mode == FIND_MAXCSN ?
			"FIND_MAXCSN" :
			mode == FIND_CSN ?
				"FIND_CSN" :
				"FIND_PRESENT",
		csn ? csn->bv_val : "" );

Howard Chu's avatar
Howard Chu committed
684
	fop = *op;
685
	fop.o_sync_mode &= SLAP_CONTROL_MASK;	/* turn off sync_mode */
686
687
	/* We want pure entries, not referrals */
	fop.o_managedsait = SLAP_CONTROL_CRITICAL;
Howard Chu's avatar
Howard Chu committed
688

689
690
	cf.f_ava = &eq;
	cf.f_av_desc = slap_schema.si_ad_entryCSN;
691
	BER_BVZERO( &cf.f_av_value );
692
693
	cf.f_next = NULL;

694
695
696
697
	fop.o_callback = &cb;
	fop.ors_limit = NULL;
	fop.ors_tlimit = SLAP_NO_LIMIT;
	fop.ors_filter = &cf;
698
	fop.ors_filterstr.bv_val = buf;
699

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

		/* 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;
		}
784
		break;
Howard Chu's avatar
Howard Chu committed
785
786
	}

Howard Chu's avatar
Howard Chu committed
787
	fop.o_bd->bd_info = (BackendInfo *)on->on_info;
788
	fop.o_bd->be_search( &fop, &frs );
789
	fop.o_bd->bd_info = (BackendInfo *)on;
Howard Chu's avatar
Howard Chu committed
790

791
792
	switch( mode ) {
	case FIND_MAXCSN:
793
		if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
Howard Chu's avatar
Howard Chu committed
794
795
796
797
#ifdef CHECK_CSN
			Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
			assert( !syn->ssyn_validate( syn, &maxcsn ));
#endif
798
799
800
			ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
			si->si_numops++;	/* ensure a checkpoint */
		}
801
802
803
		break;
	case FIND_CSN:
		/* If matching CSN was not found, invalidate the context. */
804
805
806
807
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_findcsn: csn%s=%s %sfound\n",
			op->o_log_prefix,
			cf.f_choice == LDAP_FILTER_EQUALITY ? "=" : "<",
			cf.f_av_value.bv_val, cb.sc_private ? "" : "not " );
Howard Chu's avatar
Howard Chu committed
808
809
810
811
		if ( !cb.sc_private ) {
			/* If we didn't find an exact match, then try for <= */
			if ( findcsn_retry ) {
				findcsn_retry = 0;
812
				rs_reinit( &frs, REP_RESULT );
Howard Chu's avatar
Howard Chu committed
813
814
815
816
				goto again;
			}
			rc = LDAP_NO_SUCH_OBJECT;
		}
817
818
		break;
	case FIND_PRESENT:
819
		op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
820
		break;
Howard Chu's avatar
Howard Chu committed
821
822
	}

823
	return rc;
Howard Chu's avatar
Howard Chu committed
824
825
}

Howard Chu's avatar
Howard Chu committed
826
static void free_resinfo( syncres *sr )
827
{
Howard Chu's avatar
Howard Chu committed
828
	syncres **st;
Howard Chu's avatar
Howard Chu committed
829
	int freeit = 0;
Howard Chu's avatar
Howard Chu committed
830
831
832
833
834
835
836
	ldap_pvt_thread_mutex_lock( &sr->s_info->ri_mutex );
	for (st = &sr->s_info->ri_list; *st; st = &(*st)->s_rilist) {
		if (*st == sr) {
			*st = sr->s_rilist;
			break;
		}
	}
Howard Chu's avatar
Howard Chu committed
837
838
	if ( !sr->s_info->ri_list )
		freeit = 1;
Howard Chu's avatar
Howard Chu committed
839
	ldap_pvt_thread_mutex_unlock( &sr->s_info->ri_mutex );
Howard Chu's avatar
Howard Chu committed
840
	if ( freeit ) {
Howard Chu's avatar
Howard Chu committed
841
842
843
		ldap_pvt_thread_mutex_destroy( &sr->s_info->ri_mutex );
		if ( sr->s_info->ri_e )
			entry_free( sr->s_info->ri_e );
Howard Chu's avatar
Howard Chu committed
844
845
		if ( !BER_BVISNULL( &sr->s_info->ri_cookie ))
			ch_free( sr->s_info->ri_cookie.bv_val );
Howard Chu's avatar
Howard Chu committed
846
		ch_free( sr->s_info );
847
848
849
	}
}

850
851
852
#define FS_UNLINK	1
#define FS_LOCK		2

853
static int
854
syncprov_free_syncop( syncops *so, int flags )
855
856
857
858
{
	syncres *sr, *srnext;
	GroupAssertion *ga, *gnext;

859
860
	if ( flags & FS_LOCK )
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
861
862
	/* already being freed, or still in use */
	if ( !so->s_inuse || --so->s_inuse > 0 ) {
863
864
		if ( flags & FS_LOCK )
			ldap_pvt_thread_mutex_unlock( &so->s_mutex );
865
		return 0;
866
867
	}
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
868
	if (( flags & FS_UNLINK ) && so->s_si ) {
869
870
871
872
873
874
875
876
877
878
		syncops **sop;
		ldap_pvt_thread_mutex_lock( &so->s_si->si_ops_mutex );
		for ( sop = &so->s_si->si_ops; *sop; sop = &(*sop)->s_next ) {
			if ( *sop == so ) {
				*sop = so->s_next;
				break;
			}
		}
		ldap_pvt_thread_mutex_unlock( &so->s_si->si_ops_mutex );
	}
879
880
881
882
883
884
885
886
887
888
889
	if ( so->s_flags & PS_IS_DETACHED ) {
		filter_free( so->s_op->ors_filter );
		for ( ga = so->s_op->o_groups; ga; ga=gnext ) {
			gnext = ga->ga_next;
			ch_free( ga );
		}
		ch_free( so->s_op );
	}
	ch_free( so->s_base.bv_val );
	for ( sr=so->s_res; sr; sr=srnext ) {
		srnext = sr->s_next;
Howard Chu's avatar
Howard Chu committed
890
		free_resinfo( sr );
891
892
893
894
		ch_free( sr );
	}
	ldap_pvt_thread_mutex_destroy( &so->s_mutex );
	ch_free( so );
895
	return 1;
896
897
}

898
899
/* Send a persistent search response */
static int
Howard Chu's avatar
Howard Chu committed
900
syncprov_sendresp( Operation *op, resinfo *ri, syncops *so, int mode )
901
902
{
	SlapReply rs = { REP_SEARCH };
903
	struct berval cookie, csns[2];
904
905
906
	Entry e_uuid = {0};
	Attribute a_uuid = {0};

Howard Chu's avatar
Howard Chu committed
907
908
909
	if ( so->s_op->o_abandon )
		return SLAPD_ABANDON;

910
911
912
	rs.sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2, op->o_tmpmemctx );
	rs.sr_ctrls[1] = NULL;
	rs.sr_flags = REP_CTRLS_MUSTBEFREED;
Howard Chu's avatar
Howard Chu committed
913
	csns[0] = ri->ri_csn;
914
	BER_BVZERO( &csns[1] );
915
916
	slap_compose_sync_cookie( op, &cookie, csns, so->s_rid,
				 slap_serverID ? slap_serverID : -1, NULL );
917

918
#ifdef LDAP_DEBUG
919
	if ( so->s_sid > 0 ) {
920
921
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: to=%03x, cookie=%s\n",
			op->o_log_prefix, so->s_sid, cookie.bv_val );
922
	} else {
923
924
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: cookie=%s\n",
			op->o_log_prefix, cookie.bv_val );
925
	}
926
#endif
927

928
929
	e_uuid.e_attrs = &a_uuid;
	a_uuid.a_desc = slap_schema.si_ad_entryUUID;
Howard Chu's avatar
Howard Chu committed
930
	a_uuid.a_nvals = &ri->ri_uuid;
931
	rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
932
		mode, rs.sr_ctrls, 0, 1, &cookie );
933
	op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
934

935
936
	rs.sr_entry = &e_uuid;
	if ( mode == LDAP_SYNC_ADD || mode == LDAP_SYNC_MODIFY ) {
Howard Chu's avatar
Howard Chu committed
937
		e_uuid = *ri->ri_e;
938
939
940
		e_uuid.e_private = NULL;
	}

941
942
	switch( mode ) {
	case LDAP_SYNC_ADD:
Howard Chu's avatar
Howard Chu committed
943
		if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
944
			rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
945
			rs.sr_err = send_search_reference( op, &rs );
946
947
948
949
950
			ber_bvarray_free( rs.sr_ref );
			break;
		}
		/* fallthru */
	case LDAP_SYNC_MODIFY:
951
952
953
954
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: sending %s, dn=%s\n",
			op->o_log_prefix,
			mode == LDAP_SYNC_ADD ? "LDAP_SYNC_ADD" : "LDAP_SYNC_MODIFY",
			e_uuid.e_nname.bv_val );
955
		rs.sr_attrs = op->ors_attrs;
956
		rs.sr_err = send_search_entry( op, &rs );
957
958
		break;
	case LDAP_SYNC_DELETE:
959
960
961
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: "
			"sending LDAP_SYNC_DELETE, dn=%s\n",
			op->o_log_prefix, ri->ri_dn.bv_val );
962
		e_uuid.e_attrs = NULL;
Howard Chu's avatar
Howard Chu committed
963
964
965
		e_uuid.e_name = ri->ri_dn;
		e_uuid.e_nname = ri->ri_ndn;
		if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
966
			struct berval bv = BER_BVNULL;
967
			rs.sr_ref = &bv;
968
			rs.sr_err = send_search_reference( op, &rs );
Howard Chu's avatar
Howard Chu committed
969
		} else {
970
			rs.sr_err = send_search_entry( op, &rs );
971
972
973
974
975
		}
		break;
	default:
		assert(0);
	}
976
977
978
	return rs.sr_err;
}

979
980
981
static void
syncprov_qstart( syncops *so );

982
983
/* Play back queued responses */
static int
984
syncprov_qplay( Operation *op, syncops *so )
985
986
{
	syncres *sr;
987
	int rc = 0;
988

989
	do {
990
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
991
		sr = so->s_res;
Howard Chu's avatar
Howard Chu committed
992
		/* Exit loop with mutex held */
Howard Chu's avatar
Howard Chu committed
993
		if ( !sr )
994
			break;
Howard Chu's avatar
Howard Chu committed
995
996
997
		so->s_res = sr->s_next;
		if ( !so->s_res )
			so->s_restail = NULL;
Howard Chu's avatar
Howard Chu committed
998
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
999

1000
		if ( !so->s_op->o_abandon ) {
For faster browsing, not all history is shown. View entire blame