syncprov.c 119 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-2021 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"
28
#include "slap-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
/* Session log data */
typedef struct slog_entry {
	struct berval se_uuid;
	struct berval se_csn;
123
	int	se_sid;
124
125
126
127
	ber_tag_t	se_tag;
} slog_entry;

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

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

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

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

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

201
static AttributeDescription *ad_reqType, *ad_reqResult, *ad_reqDN,
Ondřej Kuzník's avatar
Ondřej Kuzník committed
202
							*ad_reqEntryUUID, *ad_minCSN, *ad_reqNewDN;
203

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

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

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

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

236
237
	/* FIXME: what if entryuuid is NULL or empty ? */

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

246
247
248
249
250
251
252
253
254
255
	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;
	}
256
257
258
259
	ber_free_buf( ber );

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

	return LDAP_SUCCESS;
}

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

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

298
299
300
301
302
303
304
305
306
307
	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;
	}
308
309
310
311
312

	ber_free_buf( ber );

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

	return LDAP_SUCCESS;
}

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

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

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

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

	return LDAP_SUCCESS;
}
406

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

	m1 = c1; m2 = c2;
415
	rc = m1->mt_dn.bv_len - m2->mt_dn.bv_len;
416
417

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

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

	return ber_bvcmp( left, right );
}

429
430
431
432
static int
syncprov_sessionlog_cmp( const void *l, const void *r )
{
	const slog_entry *left = l, *right = r;
433
434
435
	int ret = ber_bvcmp( &left->se_csn, &right->se_csn );
	if ( !ret )
		ret = ber_bvcmp( &left->se_uuid, &right->se_uuid );
436
437
438
439
440
441
442
443
	/* Only time we have two modifications with same CSN is when we detect a
	 * rename during replication.
	 * We invert the test here because LDAP_REQ_MODDN is
	 * numerically greater than LDAP_REQ_MODIFY but we
	 * want it to occur first.
	 */
	if ( !ret )
		ret = right->se_tag - left->se_tag;
444

445
	return ret;
446
447
}

448
449
450
451
452
453
454
455
456
/* 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
457
458
459
460
461
462
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 ) {
463
464
465
466
467
468
		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
469
			fc->fbase = 2;
470
471
			fc->fss->s_eid = rs->sr_entry->e_id;
			ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
472

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

Howard Chu's avatar
Howard Chu committed
476
		/* OK, the DN is the same and the entryID is the same. */
Howard Chu's avatar
Howard Chu committed
477
478
			fc->fbase = 1;
		}
Howard Chu's avatar
Howard Chu committed
479
	}
480
	if ( rs->sr_err != LDAP_SUCCESS ) {
481
		Debug( LDAP_DEBUG_ANY, "findbase failed! %d\n", rs->sr_err );
482
	}
Howard Chu's avatar
Howard Chu committed
483
484
485
	return LDAP_SUCCESS;
}

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

Howard Chu's avatar
Howard Chu committed
489
static int
490
syncprov_findbase( Operation *op, fbase_cookie *fc )
Howard Chu's avatar
Howard Chu committed
491
{
Howard Chu's avatar
Howard Chu committed
492
493
494
	/* Use basic parameters from syncrepl search, but use
	 * current op's threadctx / tmpmemctx
	 */
Howard Chu's avatar
Howard Chu committed
495
496
497
498
499
500
	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
501

Howard Chu's avatar
Howard Chu committed
502
503
		fc->fss->s_flags ^= PS_FIND_BASE;
		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
Howard Chu's avatar
Howard Chu committed
504

Howard Chu's avatar
Howard Chu committed
505
		fop = *fc->fss->s_op;
Howard Chu's avatar
Howard Chu committed
506

507
		fop.o_bd = fop.o_bd->bd_self;
Howard Chu's avatar
Howard Chu committed
508
509
510
		fop.o_hdr = op->o_hdr;
		fop.o_time = op->o_time;
		fop.o_tincr = op->o_tincr;
511
		fop.o_extra = op->o_extra;
Howard Chu's avatar
Howard Chu committed
512

Howard Chu's avatar
Howard Chu committed
513
514
		cb.sc_response = findbase_cb;
		cb.sc_private = fc;
Howard Chu's avatar
Howard Chu committed
515

Howard Chu's avatar
Howard Chu committed
516
517
518
519
520
521
522
523
524
525
		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
526
527
		fop.ors_filter = &generic_filter;
		fop.ors_filterstr = generic_filterstr;
Howard Chu's avatar
Howard Chu committed
528

529
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_findbase: searching\n", op->o_log_prefix );
530
		rc = fop.o_bd->be_search( &fop, &frs );
Howard Chu's avatar
Howard Chu committed
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
	} 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
559
560
561
562
563
564

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

566
/* syncprov_findcsn:
567
 *   This function has three different purposes, but they all use a search
568
 * that filters on entryCSN so they're combined here.
569
570
571
572
573
 * 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
574
575
 * 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.
576
 *
577
 * 3: during a refresh phase, we search for all entries with CSN <= the cookie
578
579
580
 * CSN, and generate Present records for them. We always collect this result
 * in SyncID sets, even if there's only one match.
 */
581
582
583
584
585
typedef enum find_csn_t {
	FIND_MAXCSN	= 1,
	FIND_CSN	= 2,
	FIND_PRESENT	= 3
} find_csn_t;
586
587
588
589
590
591
592
593
594

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

595
596
		if ( a && ber_bvcmp( &a->a_vals[0], maxcsn ) > 0 &&
			slap_parse_csn_sid( &a->a_vals[0] ) == slap_serverID ) {
597
598
599
600
601
602
			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
603
604
605
606
607
608

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

Howard Chu's avatar
Howard Chu committed
609
610
611
612
613
	/* 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 )) {
614
		sc->sc_private = (void *)1;
Howard Chu's avatar
Howard Chu committed
615
616
617
618
	}
	return LDAP_SUCCESS;
}

619
620
/* Build a list of entryUUIDs for sending in a SyncID set */

621
622
#define UUID_LEN	16

Howard Chu's avatar
Howard Chu committed
623
624
625
typedef struct fpres_cookie {
	int num;
	BerVarray uuids;
626
	char *last;
Howard Chu's avatar
Howard Chu committed
627
628
629
630
631
632
633
} fpres_cookie;

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

637
638
639
	switch ( rs->sr_type ) {
	case REP_SEARCH:
		a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
640
641
642
643
		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
644
			pc->num++;
645
646
			pc->last = pc->uuids[pc->num].bv_val;
			pc->uuids[pc->num].bv_val = NULL;
Howard Chu's avatar
Howard Chu committed
647
		}
648
		ret = LDAP_SUCCESS;
649
650
651
652
		if ( pc->num != SLAP_SYNCUUID_SET_SIZE )
			break;
		/* FALLTHRU */
	case REP_RESULT:
Howard Chu's avatar
Howard Chu committed
653
654
		ret = rs->sr_err;
		if ( pc->num ) {
655
			ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
Howard Chu's avatar
Howard Chu committed
656
				0, pc->uuids, 0 );
657
			pc->uuids[pc->num].bv_val = pc->last;
Howard Chu's avatar
Howard Chu committed
658
			pc->num = 0;
659
			pc->last = pc->uuids[0].bv_val;
Howard Chu's avatar
Howard Chu committed
660
		}
661
662
663
		break;
	default:
		break;
Howard Chu's avatar
Howard Chu committed
664
665
666
667
668
	}
	return ret;
}

static int
cmikk@qwest.net's avatar
cmikk@qwest.net committed
669
syncprov_findcsn( Operation *op, find_csn_t mode, struct berval *csn )
Howard Chu's avatar
Howard Chu committed
670
671
672
673
674
675
676
{
	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 };
677
678
	char buf[LDAP_PVT_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
	char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
679
	struct berval maxcsn;
680
	Filter cf;
681
	AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
Howard Chu's avatar
Howard Chu committed
682
	fpres_cookie pcookie;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
683
	sync_control *srs = NULL;
684
685
	struct slap_limits_set fc_limits;
	int i, rc = LDAP_SUCCESS, findcsn_retry = 1;
686
	int maxid;
687
688
689

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

692
693
694
695
696
697
698
699
700
	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
701
	fop = *op;
702
	fop.o_sync_mode &= SLAP_CONTROL_MASK;	/* turn off sync_mode */
703
704
	/* We want pure entries, not referrals */
	fop.o_managedsait = SLAP_CONTROL_CRITICAL;
Howard Chu's avatar
Howard Chu committed
705

706
707
	cf.f_ava = &eq;
	cf.f_av_desc = slap_schema.si_ad_entryCSN;
708
	BER_BVZERO( &cf.f_av_value );
709
710
	cf.f_next = NULL;

711
712
713
714
	fop.o_callback = &cb;
	fop.ors_limit = NULL;
	fop.ors_tlimit = SLAP_NO_LIMIT;
	fop.ors_filter = &cf;
715
	fop.ors_filterstr.bv_val = buf;
716

Howard Chu's avatar
Howard Chu committed
717
again:
718
719
720
	switch( mode ) {
	case FIND_MAXCSN:
		cf.f_choice = LDAP_FILTER_GE;
721
722
723
		/* 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] ) {
724
				maxid = i;
725
				break;
726
727
			}
		}
728
729
730
731
732
733
734
		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];
735
736
		fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
			"(entryCSN>=%s)", cf.f_av_value.bv_val );
737
		if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
738
739
			return LDAP_OTHER;
		}
740
741
742
743
744
		fop.ors_attrsonly = 0;
		fop.ors_attrs = csn_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
		cb.sc_private = &maxcsn;
		cb.sc_response = findmax_cb;
745
		strcpy( cbuf, cf.f_av_value.bv_val );
746
		maxcsn.bv_val = cbuf;
747
		maxcsn.bv_len = cf.f_av_value.bv_len;
748
749
		break;
	case FIND_CSN:
750
		if ( BER_BVISEMPTY( &cf.f_av_value )) {
cmikk@qwest.net's avatar
cmikk@qwest.net committed
751
			cf.f_av_value = *csn;
752
		}
cmikk@qwest.net's avatar
cmikk@qwest.net committed
753
754
755
756
		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
757
758
759
		/* Look for exact match the first time */
		if ( findcsn_retry ) {
			cf.f_choice = LDAP_FILTER_EQUALITY;
760
761
			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
				"(entryCSN=%s)", cf.f_av_value.bv_val );
Howard Chu's avatar
Howard Chu committed
762
763
764
		/* On retry, look for <= */
		} else {
			cf.f_choice = LDAP_FILTER_LE;
765
			fop.ors_limit = &fc_limits;
766
			memset( &fc_limits, 0, sizeof( fc_limits ));
767
			fc_limits.lms_s_unchecked = 1;
768
769
770
			fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
				"(entryCSN<=%s)", cf.f_av_value.bv_val );
		}
771
		if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
772
			return LDAP_OTHER;
Howard Chu's avatar
Howard Chu committed
773
		}
774
775
776
777
		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
778
		cb.sc_response = findcsn_cb;
779
780
		break;
	case FIND_PRESENT:
781
782
		fop.ors_filter = op->ors_filter;
		fop.ors_filterstr = op->ors_filterstr;
Howard Chu's avatar
Howard Chu committed
783
784
785
		fop.ors_attrsonly = 0;
		fop.ors_attrs = uuid_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
Howard Chu's avatar
Howard Chu committed
786
		cb.sc_private = &pcookie;
Howard Chu's avatar
Howard Chu committed
787
788
		cb.sc_response = findpres_cb;
		pcookie.num = 0;
789
790
791
792
793
794
795
796
797
798
799
800

		/* 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;
		}
801
		break;
Howard Chu's avatar
Howard Chu committed
802
803
	}

Howard Chu's avatar
Howard Chu committed
804
	fop.o_bd->bd_info = (BackendInfo *)on->on_info;
805
	fop.o_bd->be_search( &fop, &frs );
806
	fop.o_bd->bd_info = (BackendInfo *)on;
Howard Chu's avatar
Howard Chu committed
807

808
809
	switch( mode ) {
	case FIND_MAXCSN:
810
		if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
Howard Chu's avatar
Howard Chu committed
811
812
813
814
#ifdef CHECK_CSN
			Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
			assert( !syn->ssyn_validate( syn, &maxcsn ));
#endif
815
816
817
			ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
			si->si_numops++;	/* ensure a checkpoint */
		}
818
819
820
		break;
	case FIND_CSN:
		/* If matching CSN was not found, invalidate the context. */
821
822
823
824
		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
825
826
827
828
		if ( !cb.sc_private ) {
			/* If we didn't find an exact match, then try for <= */
			if ( findcsn_retry ) {
				findcsn_retry = 0;
829
				rs_reinit( &frs, REP_RESULT );
Howard Chu's avatar
Howard Chu committed
830
831
832
833
				goto again;
			}
			rc = LDAP_NO_SUCH_OBJECT;
		}
834
835
		break;
	case FIND_PRESENT:
836
		op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
837
		break;
Howard Chu's avatar
Howard Chu committed
838
839
	}

840
	return rc;
Howard Chu's avatar
Howard Chu committed
841
842
}

Howard Chu's avatar
Howard Chu committed
843
static void free_resinfo( syncres *sr )
844
{
Howard Chu's avatar
Howard Chu committed
845
	syncres **st;
Howard Chu's avatar
Howard Chu committed
846
	int freeit = 0;
Howard Chu's avatar
Howard Chu committed
847
848
849
850
851
852
853
	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
854
855
	if ( !sr->s_info->ri_list )
		freeit = 1;
Howard Chu's avatar
Howard Chu committed
856
	ldap_pvt_thread_mutex_unlock( &sr->s_info->ri_mutex );
Howard Chu's avatar
Howard Chu committed
857
	if ( freeit ) {
Howard Chu's avatar
Howard Chu committed
858
859
860
		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
861
862
		if ( !BER_BVISNULL( &sr->s_info->ri_cookie ))
			ch_free( sr->s_info->ri_cookie.bv_val );
Howard Chu's avatar
Howard Chu committed
863
		ch_free( sr->s_info );
864
865
866
	}
}

867
868
869
#define FS_UNLINK	1
#define FS_LOCK		2

870
static int
871
syncprov_free_syncop( syncops *so, int flags )
872
873
874
875
{
	syncres *sr, *srnext;
	GroupAssertion *ga, *gnext;

876
877
	if ( flags & FS_LOCK )
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
878
879
	/* already being freed, or still in use */
	if ( !so->s_inuse || --so->s_inuse > 0 ) {
880
881
		if ( flags & FS_LOCK )
			ldap_pvt_thread_mutex_unlock( &so->s_mutex );
882
		return 0;
883
884
	}
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
885
	if (( flags & FS_UNLINK ) && so->s_si ) {
886
887
888
889
890
891
892
893
894
895
		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 );
	}
896
897
898
899
900
901
902
903
904
905
906
	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
907
		free_resinfo( sr );
908
909
910
911
		ch_free( sr );
	}
	ldap_pvt_thread_mutex_destroy( &so->s_mutex );
	ch_free( so );
912
	return 1;
913
914
}

915
916
/* Send a persistent search response */
static int
Howard Chu's avatar
Howard Chu committed
917
syncprov_sendresp( Operation *op, resinfo *ri, syncops *so, int mode )
918
919
{
	SlapReply rs = { REP_SEARCH };
920
	struct berval cookie, csns[2];
921
922
923
	Entry e_uuid = {0};
	Attribute a_uuid = {0};

Howard Chu's avatar
Howard Chu committed
924
925
926
	if ( so->s_op->o_abandon )
		return SLAPD_ABANDON;

927
928
929
	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
930
	csns[0] = ri->ri_csn;
931
	BER_BVZERO( &csns[1] );
932
933
	slap_compose_sync_cookie( op, &cookie, csns, so->s_rid,
				 slap_serverID ? slap_serverID : -1, NULL );
934

935
#ifdef LDAP_DEBUG
936
	if ( so->s_sid > 0 ) {
937
938
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: to=%03x, cookie=%s\n",
			op->o_log_prefix, so->s_sid, cookie.bv_val );
939
	} else {
940
941
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: cookie=%s\n",
			op->o_log_prefix, cookie.bv_val );
942
	}
943
#endif
944

945
946
	e_uuid.e_attrs = &a_uuid;
	a_uuid.a_desc = slap_schema.si_ad_entryUUID;
Howard Chu's avatar
Howard Chu committed
947
	a_uuid.a_nvals = &ri->ri_uuid;
948
	rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
949
		mode, rs.sr_ctrls, 0, 1, &cookie );
950
	op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
951

952
953
	rs.sr_entry = &e_uuid;
	if ( mode == LDAP_SYNC_ADD || mode == LDAP_SYNC_MODIFY ) {
Howard Chu's avatar
Howard Chu committed
954
		e_uuid = *ri->ri_e;
955
956
957
		e_uuid.e_private = NULL;
	}

958
959
	switch( mode ) {
	case LDAP_SYNC_ADD:
Howard Chu's avatar
Howard Chu committed
960
		if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
961
			rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
962
			rs.sr_err = send_search_reference( op, &rs );
963
964
965
966
967
			ber_bvarray_free( rs.sr_ref );
			break;
		}
		/* fallthru */
	case LDAP_SYNC_MODIFY:
968
969
970
971
		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 );
972
		rs.sr_attrs = op->ors_attrs;
973
		rs.sr_err = send_search_entry( op, &rs );
974
975
		break;
	case LDAP_SYNC_DELETE:
976
977
978
		Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: "
			"sending LDAP_SYNC_DELETE, dn=%s\n",
			op->o_log_prefix, ri->ri_dn.bv_val );
979
		e_uuid.e_attrs = NULL;
Howard Chu's avatar
Howard Chu committed
980
981
982
		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 ) {
983
			struct berval bv = BER_BVNULL;
984
			rs.sr_ref = &bv;
985
			rs.sr_err = send_search_reference( op, &rs );
Howard Chu's avatar
Howard Chu committed
986
		} else {
987
			rs.sr_err = send_search_entry( op, &rs );
988
989
990
991
992
		}
		break;
	default:
		assert(0);
	}
993
994
995
	return rs.sr_err;
}

996
997
998
static void
syncprov_qstart( syncops *so );

999
1000
/* Play back queued responses */
static int
For faster browsing, not all history is shown. View entire blame