syncprov.c 49.8 KB
Newer Older
Howard Chu's avatar
Howard Chu committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* syncprov.c - syncrepl provider */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 * Copyright 2004 The OpenLDAP Foundation.
 * 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
24
25
#include <ac/string.h>
#include "lutil.h"
Howard Chu's avatar
Howard Chu committed
26
27
#include "slap.h"

28
29
30
31
32
33
34
35
36
37
38
39
40
/* 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
41
42
43
/* A queued result of a persistent search */
typedef struct syncres {
	struct syncres *s_next;
44
	struct berval s_dn;
Howard Chu's avatar
Howard Chu committed
45
46
	struct berval s_ndn;
	struct berval s_uuid;
47
48
49
	struct berval s_csn;
	char s_mode;
	char s_isreference;
Howard Chu's avatar
Howard Chu committed
50
51
} syncres;

Howard Chu's avatar
Howard Chu committed
52
53
54
55
56
57
/* 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 */
Howard Chu's avatar
Howard Chu committed
58
59
60
	long	s_sid;
	long	s_rid;
	struct berval s_filterstr;
Howard Chu's avatar
Howard Chu committed
61
	int		s_flags;	/* search status */
Howard Chu's avatar
Howard Chu committed
62
	int		s_inuse;	/* reference count */
Howard Chu's avatar
Howard Chu committed
63
64
65
	struct syncres *s_res;
	struct syncres *s_restail;
	ldap_pvt_thread_mutex_t	s_mutex;
Howard Chu's avatar
Howard Chu committed
66
67
} syncops;

68
69
70
71
72
73
/* A received sync control */
typedef struct sync_control {
	struct sync_cookie sr_state;
	int sr_rhint;
} sync_control;

74
75
76
#if 0 /* moved back to slap.h */
#define	o_sync	o_ctrlflag[slap_cids.sc_LDAPsync]
#endif
77
/* o_sync_mode uses data bits of o_sync */
78
#define	o_sync_mode	o_ctrlflag[slap_cids.sc_LDAPsync]
79
80
81
82
83
84

#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
85
86
#define	PS_IS_REFRESHING	0x01

Howard Chu's avatar
Howard Chu committed
87
88
89
90
91
92
/* Record of which searches matched at premodify step */
typedef struct syncmatches {
	struct syncmatches *sm_next;
	syncops *sm_op;
} syncmatches;

Howard Chu's avatar
Cleanup    
Howard Chu committed
93
/* The main state for this overlay */
Howard Chu's avatar
Howard Chu committed
94
95
typedef struct syncprov_info_t {
	syncops		*si_ops;
Howard Chu's avatar
Howard Chu committed
96
	struct berval	si_ctxcsn;	/* ldapsync context */
97
98
99
100
	int		si_chkops;	/* checkpointing info */
	int		si_chktime;
	int		si_numops;	/* number of ops since last checkpoint */
	time_t	si_chklast;	/* time of last checkpoint */
101
	Avlnode	*si_mods;	/* entries being modified */
Howard Chu's avatar
Howard Chu committed
102
103
	ldap_pvt_thread_mutex_t	si_csn_mutex;
	ldap_pvt_thread_mutex_t	si_ops_mutex;
104
	ldap_pvt_thread_rdwr_t	si_mods_rwlock;
105
	char		si_ctxcsnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
Howard Chu's avatar
Howard Chu committed
106
107
108
109
110
} syncprov_info_t;

typedef struct opcookie {
	slap_overinst *son;
	syncmatches *smatches;
111
112
113
114
115
	struct berval sdn;	/* DN of entry, for deletes */
	struct berval sndn;
	struct berval suuid;	/* UUID of entry */
	struct berval sctxcsn;
	int sreference;	/* Is the entry a reference? */
Howard Chu's avatar
Howard Chu committed
116
117
} opcookie;

118
typedef struct fbase_cookie {
119
120
121
122
	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 */
123
} fbase_cookie;
Howard Chu's avatar
Howard Chu committed
124

Howard Chu's avatar
Howard Chu committed
125
126
127
static AttributeName csn_anlist[2];
static AttributeName uuid_anlist[2];

Howard Chu's avatar
Cleanup    
Howard Chu committed
128
/* Build a LDAPsync intermediate state control */
129
130
131
132
133
static int
syncprov_state_ctrl(
	Operation	*op,
	SlapReply	*rs,
	Entry		*e,
134
	int		entry_sync_state,
135
	LDAPControl	**ctrls,
136
137
138
	int		num_ctrls,
	int		send_cookie,
	struct berval	*cookie )
139
140
141
142
143
144
145
146
147
{
	Attribute* a;
	int ret;
	int res;
	const char *text = NULL;

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

148
	struct berval	entryuuid_bv = BER_BVNULL;
149
150
151
152

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

155
156
157
158
159
	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;
160
		}
161
	}
162

163
164
165
166
167
168
	if ( send_cookie && cookie ) {
		ber_printf( ber, "{eOON}",
			entry_sync_state, &entryuuid_bv, cookie );
	} else {
		ber_printf( ber, "{eON}",
			entry_sync_state, &entryuuid_bv );
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
	}

	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
188
/* Build a LDAPsync final state control */
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
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
206
	ctrls[num_ctrls] = op->o_tmpalloc( sizeof ( LDAPControl ), op->o_tmpmemctx );
207
208
209
210
211
212
213
214

	ber_printf( ber, "{" );
	if ( send_cookie && cookie ) {
		ber_printf( ber, "O", cookie );
	}
	if ( refreshDeletes == LDAP_SYNC_REFRESH_DELETES ) {
		ber_printf( ber, "b", refreshDeletes );
	}
215
	ber_printf( ber, "N}" );
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_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
Cleanup    
Howard Chu committed
234
235
#if 0
/* Generate state based on session log - not implemented yet */
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
static int
syncprov_state_ctrl_from_slog(
	Operation	*op,
	SlapReply	*rs,
	struct slog_entry *slog_e,
	int			entry_sync_state,
	LDAPControl	**ctrls,
	int			num_ctrls,
	int			send_cookie,
	struct berval	*cookie)
{
	Attribute* a;
	int ret;
	int res;
	const char *text = NULL;

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

	struct berval entryuuid_bv	= BER_BVNULL;

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

	ctrls[num_ctrls] = ch_malloc ( sizeof ( LDAPControl ) );

	entryuuid_bv = slog_e->sl_uuid;

	if ( send_cookie && cookie ) {
		ber_printf( ber, "{eOON}",
			entry_sync_state, &entryuuid_bv, cookie );
	} else {
		ber_printf( ber, "{eON}",
			entry_sync_state, &entryuuid_bv );
	}

	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
288
#endif
289

Howard Chu's avatar
Howard Chu committed
290
static int
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
344
345
346
347
348
349
350
351
352
353
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
354
	rs->sr_rspoid = LDAP_SYNC_INFO;
355
356
357
358
359
360
361
	rs->sr_rspdata = &rspdata;
	send_ldap_intermediate( op, rs );
	rs->sr_rspdata = NULL;
	ber_free_buf( ber );

	return LDAP_SUCCESS;
}
362

Howard Chu's avatar
Cleanup    
Howard Chu committed
363
/* Find a modtarget in an AVL tree */
364
365
366
367
368
369
370
371
372
373
374
375
376
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 );
}

377
378
379
380
381
382
383
384
385
/* 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
386
387
388
389
390
391
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 ) {
392
393
394
395
396
397
398
399
400
		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 ) {
			fc->fbase = 1;
			fc->fss->s_eid = rs->sr_entry->e_id;
			ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
401

402
		} else if ( rs->sr_entry->e_id == fc->fss->s_eid &&
Howard Chu's avatar
Howard Chu committed
403
			dn_match( &rs->sr_entry->e_nname, &fc->fss->s_base )) {
404
405
406
407

		/* OK, the DN is the same and the entryID is the same. Now
		 * see if the fdn resides in the scope.
		 */
Howard Chu's avatar
Howard Chu committed
408
			fc->fbase = 1;
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
			switch ( fc->fss->s_op->ors_scope ) {
			case LDAP_SCOPE_BASE:
				fc->fscope = dn_match( fc->fdn, &rs->sr_entry->e_nname );
				break;
			case LDAP_SCOPE_ONELEVEL: {
				struct berval pdn;
				dnParent( fc->fdn, &pdn );
				fc->fscope = dn_match( &pdn, &rs->sr_entry->e_nname );
				break; }
			case LDAP_SCOPE_SUBTREE:
				fc->fscope = dnIsSuffix( fc->fdn, &rs->sr_entry->e_nname );
				break;
#ifdef LDAP_SCOPE_SUBORDINATE
			case LDAP_SCOPE_SUBORDINATE:
				fc->fscope = dnIsSuffix( fc->fdn, &rs->sr_entry->e_nname ) &&
					!dn_match( fc->fdn, &rs->sr_entry->e_nname );
				break;
#endif
			}
Howard Chu's avatar
Howard Chu committed
428
		}
Howard Chu's avatar
Howard Chu committed
429
	}
430
431
432
	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
433
434
435
436
	return LDAP_SUCCESS;
}

static int
437
syncprov_findbase( Operation *op, fbase_cookie *fc )
Howard Chu's avatar
Howard Chu committed
438
{
439
440
	opcookie *opc = op->o_callback->sc_private;
	slap_overinst *on = opc->son;
Howard Chu's avatar
Howard Chu committed
441
442
	syncprov_info_t		*si = on->on_bi.bi_private;

Howard Chu's avatar
Howard Chu committed
443
	slap_callback cb = {0};
Howard Chu's avatar
Howard Chu committed
444
445
446
447
448
449
450
451
452
	Operation fop;
	SlapReply frs = { REP_RESULT };
	int rc;

	fop = *op;

	cb.sc_response = findbase_cb;
	cb.sc_private = fc;

453
	fop.o_sync_mode &= SLAP_CONTROL_MASK;	/* turn off sync mode */
Howard Chu's avatar
Howard Chu committed
454
455
456
	fop.o_callback = &cb;
	fop.o_tag = LDAP_REQ_SEARCH;
	fop.ors_scope = LDAP_SCOPE_BASE;
457
	fop.ors_deref = fc->fss->s_op->ors_deref;
458
	fop.ors_limit = NULL;
Howard Chu's avatar
Howard Chu committed
459
460
461
462
	fop.ors_slimit = 1;
	fop.ors_tlimit = SLAP_NO_LIMIT;
	fop.ors_attrs = slap_anlist_no_attrs;
	fop.ors_attrsonly = 1;
463
464
	fop.ors_filter = fc->fss->s_op->ors_filter;
	fop.ors_filterstr = fc->fss->s_op->ors_filterstr;
Howard Chu's avatar
Howard Chu committed
465

466
	fop.o_req_ndn = fc->fss->s_op->o_req_ndn;
Howard Chu's avatar
Howard Chu committed
467

468
	fop.o_bd->bd_info = on->on_info->oi_orig;
Howard Chu's avatar
Howard Chu committed
469
	rc = fop.o_bd->be_search( &fop, &frs );
470
	fop.o_bd->bd_info = (BackendInfo *)on;
Howard Chu's avatar
Howard Chu committed
471

Howard Chu's avatar
Howard Chu committed
472
	if ( fc->fbase ) return LDAP_SUCCESS;
Howard Chu's avatar
Howard Chu committed
473
474
475
476
477
478

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

480
/* syncprov_findcsn:
481
 *   This function has two different purposes, but they both use a search
482
 * that filters on entryCSN so they're combined here.
483
 * 1: when the current contextCSN is known and we have a sync cookie, we search
484
 * for one entry with CSN <= the cookie CSN. (Used to search for =.) If an
485
 * entry is found, the cookie CSN is valid, otherwise it is stale.
486
 *
487
 * 2: during a refresh phase, we search for all entries with CSN <= the cookie
488
489
490
 * CSN, and generate Present records for them. We always collect this result
 * in SyncID sets, even if there's only one match.
 */
Howard Chu's avatar
Howard Chu committed
491
492
493
494
495
496
497
498
499
#define	FIND_CSN	1
#define	FIND_PRESENT	2

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

	if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
500
		sc->sc_private = (void *)1;
Howard Chu's avatar
Howard Chu committed
501
502
503
504
	}
	return LDAP_SUCCESS;
}

505
506
/* Build a list of entryUUIDs for sending in a SyncID set */

Howard Chu's avatar
Howard Chu committed
507
508
509
510
511
512
513
514
515
516
typedef struct fpres_cookie {
	int num;
	BerVarray uuids;
} fpres_cookie;

static int
findpres_cb( Operation *op, SlapReply *rs )
{
	slap_callback *sc = op->o_callback;
	fpres_cookie *pc = sc->sc_private;
Howard Chu's avatar
Howard Chu committed
517
	int ret = SLAP_CB_CONTINUE;
Howard Chu's avatar
Howard Chu committed
518
519

	if ( rs->sr_type == REP_SEARCH ) {
Howard Chu's avatar
Howard Chu committed
520
		ret = slap_build_syncUUID_set( op, &pc->uuids, rs->sr_entry );
Howard Chu's avatar
Howard Chu committed
521
522
		if ( ret > 0 ) {
			pc->num++;
Howard Chu's avatar
Howard Chu committed
523
			ret = LDAP_SUCCESS;
Howard Chu's avatar
Howard Chu committed
524
			if ( pc->num == SLAP_SYNCUUID_SET_SIZE ) {
525
				ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
Howard Chu's avatar
Howard Chu committed
526
527
528
529
530
531
532
533
534
535
536
					0, pc->uuids, 0 );
				ber_bvarray_free_x( pc->uuids, op->o_tmpmemctx );
				pc->uuids = NULL;
				pc->num = 0;
			}
		} else {
			ret = LDAP_OTHER;
		}
	} else if ( rs->sr_type == REP_RESULT ) {
		ret = rs->sr_err;
		if ( pc->num ) {
537
			ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
Howard Chu's avatar
Howard Chu committed
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
				0, pc->uuids, 0 );
			ber_bvarray_free_x( pc->uuids, op->o_tmpmemctx );
			pc->uuids = NULL;
			pc->num = 0;
		}
	}
	return ret;
}


static int
syncprov_findcsn( Operation *op, int mode )
{
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	syncprov_info_t		*si = on->on_bi.bi_private;

	slap_callback cb = {0};
	Operation fop;
	SlapReply frs = { REP_RESULT };
	char buf[LDAP_LUTIL_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
	char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
	struct berval fbuf;
	Filter cf;
	AttributeAssertion eq;
	int rc;
	fpres_cookie pcookie;
	int locked = 0;
565
	sync_control *srs = op->o_controls[slap_cids.sc_LDAPsync];
Howard Chu's avatar
Howard Chu committed
566

567
	if ( srs->sr_state.ctxcsn->bv_len >= LDAP_LUTIL_CSNSTR_BUFSIZE ) {
Howard Chu's avatar
Howard Chu committed
568
569
570
571
		return LDAP_OTHER;
	}

	fop = *op;
572
	fop.o_sync_mode &= SLAP_CONTROL_MASK;	/* turn off sync_mode */
Howard Chu's avatar
Howard Chu committed
573
574
575

	fbuf.bv_val = buf;
	if ( mode == FIND_CSN ) {
576
577
578
579
		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
580
581
582
583
584
585
		cb.sc_response = findcsn_cb;

	} else if ( mode == FIND_PRESENT ) {
		fop.ors_attrsonly = 0;
		fop.ors_attrs = uuid_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
586
587
		/* We want pure entries, not referrals */
		fop.o_managedsait = SLAP_CONTROL_CRITICAL;
Howard Chu's avatar
Howard Chu committed
588
		cb.sc_private = &pcookie;
Howard Chu's avatar
Howard Chu committed
589
590
591
592
		cb.sc_response = findpres_cb;
		pcookie.num = 0;
		pcookie.uuids = NULL;
	}
593
	cf.f_choice = LDAP_FILTER_LE;
Howard Chu's avatar
Howard Chu committed
594
595
	cf.f_ava = &eq;
	cf.f_av_desc = slap_schema.si_ad_entryCSN;
596
	cf.f_av_value = *srs->sr_state.ctxcsn;
Howard Chu's avatar
Howard Chu committed
597
	cf.f_next = NULL;
598
599
	fbuf.bv_len = sprintf( buf, "(entryCSN<=%s)",
		srs->sr_state.ctxcsn->bv_val );
Howard Chu's avatar
Howard Chu committed
600
601

	fop.o_callback = &cb;
602
	fop.ors_limit = NULL;
Howard Chu's avatar
Howard Chu committed
603
604
605
606
607
608
	fop.ors_tlimit = SLAP_NO_LIMIT;
	fop.ors_filter = &cf;
	fop.ors_filterstr = fbuf;

	fop.o_bd->bd_info = on->on_info->oi_orig;
	rc = fop.o_bd->be_search( &fop, &frs );
609
	fop.o_bd->bd_info = (BackendInfo *)on;
Howard Chu's avatar
Howard Chu committed
610
611

	if ( mode == FIND_CSN ) {
612
		if ( cb.sc_private ) return LDAP_SUCCESS;
Howard Chu's avatar
Howard Chu committed
613
614
615
616
617
618
619
620
	} else if ( mode == FIND_PRESENT ) {
		return LDAP_SUCCESS;
	}

	/* If matching CSN was not found, invalidate the context. */
	return LDAP_NO_SUCH_OBJECT;
}

621
/* Queue a persistent search response if still in Refresh stage */
622
static int
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
syncprov_qresp( opcookie *opc, syncops *so, int mode )
{
	syncres *sr;

	sr = ch_malloc(sizeof(syncres) + opc->suuid.bv_len + 1 +
		opc->sdn.bv_len + 1 + opc->sndn.bv_len + 1 + opc->sctxcsn.bv_len + 1 );
	sr->s_next = NULL;
	sr->s_dn.bv_val = (char *)(sr + 1);
	sr->s_mode = mode;
	sr->s_isreference = opc->sreference;
	sr->s_ndn.bv_val = lutil_strcopy( sr->s_dn.bv_val, opc->sdn.bv_val );
	*(sr->s_ndn.bv_val++) = '\0';
	sr->s_uuid.bv_val = lutil_strcopy( sr->s_ndn.bv_val, opc->sndn.bv_val );
	*(sr->s_uuid.bv_val++) = '\0';
	sr->s_csn.bv_val = lutil_strcopy( sr->s_uuid.bv_val, opc->suuid.bv_val );

	if ( !so->s_res ) {
		so->s_res = sr;
	} else {
		so->s_restail->s_next = sr;
	}
	so->s_restail = sr;
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
	return LDAP_SUCCESS;
}

/* Send a persistent search response */
static int
syncprov_sendresp( Operation *op, opcookie *opc, syncops *so, Entry *e, int mode, int queue )
652
653
654
655
656
657
658
659
660
661
{
	slap_overinst *on = opc->son;
	syncprov_info_t *si = on->on_bi.bi_private;

	SlapReply rs = { REP_SEARCH };
	LDAPControl *ctrls[2];
	struct berval cookie;
	Entry e_uuid = {0};
	Attribute a_uuid = {0};
	Operation sop = *so->s_op;
662
	Opheader ohdr;
663

664
665
	ohdr = *sop.o_hdr;
	sop.o_hdr = &ohdr;
666
	sop.o_tmpmemctx = op->o_tmpmemctx;
Howard Chu's avatar
Howard Chu committed
667
668
	sop.o_bd = op->o_bd;
	sop.o_controls = op->o_controls;
669

670
671
672
673
674
675
676
	if ( queue && (so->s_flags & PS_IS_REFRESHING) ) {
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
		if ( so->s_flags & PS_IS_REFRESHING )
			return syncprov_qresp( opc, so, mode );
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
	}

677
678
	ctrls[1] = NULL;
	slap_compose_sync_cookie( op, &cookie, &opc->sctxcsn,
Howard Chu's avatar
Howard Chu committed
679
		so->s_sid, so->s_rid );
680
681
682
683

	e_uuid.e_attrs = &a_uuid;
	a_uuid.a_desc = slap_schema.si_ad_entryUUID;
	a_uuid.a_nvals = &opc->suuid;
684
	rs.sr_err = syncprov_state_ctrl( &sop, &rs, &e_uuid,
685
		mode, ctrls, 0, 1, &cookie );
686

687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
	rs.sr_entry = e;
	rs.sr_ctrls = ctrls;
	switch( mode ) {
	case LDAP_SYNC_ADD:
		if ( opc->sreference ) {
			rs.sr_ref = get_entry_referrals( &sop, e );
			send_search_reference( &sop, &rs );
			ber_bvarray_free( rs.sr_ref );
			break;
		}
		/* fallthru */
	case LDAP_SYNC_MODIFY:
		rs.sr_attrs = sop.ors_attrs;
		send_search_entry( &sop, &rs );
		break;
	case LDAP_SYNC_DELETE:
		e_uuid.e_attrs = NULL;
		e_uuid.e_name = opc->sdn;
		e_uuid.e_nname = opc->sndn;
		rs.sr_entry = &e_uuid;
		if ( opc->sreference ) {
708
			struct berval bv = BER_BVNULL;
709
710
			rs.sr_ref = &bv;
			send_search_reference( &sop, &rs );
Howard Chu's avatar
Howard Chu committed
711
712
		} else {
			send_search_entry( &sop, &rs );
713
714
715
716
717
		}
		break;
	default:
		assert(0);
	}
Howard Chu's avatar
Howard Chu committed
718
719
	op->o_tmpfree( rs.sr_ctrls[0], op->o_tmpmemctx );
	rs.sr_ctrls = NULL;
720
721
722
	return rs.sr_err;
}

723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
static void
syncprov_free_syncop( syncops *so )
{
	syncres *sr, *srnext;

	ldap_pvt_thread_mutex_lock( &so->s_mutex );
	so->s_inuse--;
	if ( so->s_inuse > 0 ) {
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
		return;
	}
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
	filter_free( so->s_op->ors_filter );
	ch_free( so->s_op );
	ch_free( so->s_base.bv_val );
	for ( sr=so->s_res; sr; sr=srnext ) {
		srnext = sr->s_next;
		ch_free( sr );
	}
	ldap_pvt_thread_mutex_destroy( &so->s_mutex );
	ch_free( so );
}

static int
syncprov_drop_psearch( syncops *so )
{
	ldap_pvt_thread_mutex_lock( &so->s_op->o_conn->c_mutex );
	so->s_op->o_conn->c_n_ops_executing--;
	so->s_op->o_conn->c_n_ops_completed++;
	ldap_pvt_thread_mutex_unlock( &so->s_op->o_conn->c_mutex );
	syncprov_free_syncop( so );
}

static int
syncprov_op_abandon( Operation *op, SlapReply *rs )
{
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	syncprov_info_t		*si = on->on_bi.bi_private;
	syncops *so, *soprev;

	ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
	for ( so=si->si_ops, soprev = (syncops *)&si->si_ops; so;
		soprev=so, so=so->s_next ) {
		if ( so->s_op->o_connid == op->o_connid &&
			so->s_op->o_msgid == op->orn_msgid ) {
				so->s_op->o_abandon = 1;
				soprev->s_next = so->s_next;
				break;
		}
	}
	ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
	if ( so ) {
		/* Is this really a Cancel exop? */
		if ( op->o_tag != LDAP_REQ_ABANDON ) {
			rs->sr_err = LDAP_CANCELLED;
			send_ldap_result( so->s_op, rs );
		}
		syncprov_drop_psearch( so );
	}
	return SLAP_CB_CONTINUE;
}

Howard Chu's avatar
Cleanup    
Howard Chu committed
785
/* Find which persistent searches are affected by this operation */
Howard Chu's avatar
Howard Chu committed
786
static void
Howard Chu's avatar
Howard Chu committed
787
syncprov_matchops( Operation *op, opcookie *opc, int saveit )
Howard Chu's avatar
Howard Chu committed
788
{
789
	slap_overinst *on = opc->son;
Howard Chu's avatar
Howard Chu committed
790
791
	syncprov_info_t		*si = on->on_bi.bi_private;

792
	fbase_cookie fc;
793
	syncops *ss, *sprev, *snext;
Howard Chu's avatar
Howard Chu committed
794
795
796
	Entry *e;
	Attribute *a;
	int rc;
Howard Chu's avatar
Howard Chu committed
797
	struct berval newdn;
798
	int freefdn = 0;
Howard Chu's avatar
Howard Chu committed
799

Howard Chu's avatar
Howard Chu committed
800
	fc.fdn = &op->o_req_ndn;
Howard Chu's avatar
Howard Chu committed
801
802
803
804
805
806
807
	/* compute new DN */
	if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
		struct berval pdn;
		if ( op->orr_nnewSup ) pdn = *op->orr_nnewSup;
		else dnParent( fc.fdn, &pdn );
		build_new_dn( &newdn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
		fc.fdn = &newdn;
808
		freefdn = 1;
Howard Chu's avatar
Howard Chu committed
809
	}
810
	if ( op->o_tag != LDAP_REQ_ADD ) {
Howard Chu's avatar
Howard Chu committed
811
		op->o_bd->bd_info = (BackendInfo *)on->on_info;
812
		rc = be_entry_get_rw( op, fc.fdn, NULL, NULL, 0, &e );
Howard Chu's avatar
Howard Chu committed
813
		op->o_bd->bd_info = (BackendInfo *)on;
814
815
816
817
		if ( rc ) return;
	} else {
		e = op->ora_e;
	}
Howard Chu's avatar
Howard Chu committed
818

819
820
821
822
	/* Never replicate these */
	if ( is_entry_syncConsumerSubentry( e )) {
		goto done;
	}
Howard Chu's avatar
Howard Chu committed
823
	if ( saveit ) {
824
825
826
827
828
		ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
		ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
		opc->sreference = is_entry_referral( e );
	}
	if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
Howard Chu's avatar
Howard Chu committed
829
830
		a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
		if ( a )
Howard Chu's avatar
Howard Chu committed
831
			ber_dupbv_x( &opc->suuid, &a->a_nvals[0], op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
832
	}
Howard Chu's avatar
Howard Chu committed
833
834

	ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
835
836
	for (ss = si->si_ops, sprev = (syncops *)&si->si_ops; ss;
		sprev = ss, ss=snext)
Howard Chu's avatar
Howard Chu committed
837
	{
Howard Chu's avatar
Howard Chu committed
838
839
840
		syncmatches *sm;
		int found = 0;

841
		snext = ss->s_next;
Howard Chu's avatar
Howard Chu committed
842
		/* validate base */
Howard Chu's avatar
Howard Chu committed
843
844
		fc.fss = ss;
		fc.fbase = 0;
845
		fc.fscope = 0;
846
847

		/* If the base of the search is missing, signal a refresh */
848
		rc = syncprov_findbase( op, &fc );
849
850
851
852
853
854
855
856
		if ( rc != LDAP_SUCCESS ) {
			SlapReply rs = {REP_RESULT};
			send_ldap_error( ss->s_op, &rs, LDAP_SYNC_REFRESH_REQUIRED,
				"search base has changed" );
			sprev->s_next = snext;
			syncprov_drop_psearch( ss );
			continue;
		}
Howard Chu's avatar
Howard Chu committed
857

Howard Chu's avatar
Howard Chu committed
858
859
860
861
862
863
864
865
866
867
868
869
870
871
		/* If we're sending results now, look for this op in old matches */
		if ( !saveit ) {
			syncmatches *old;
			for ( sm=opc->smatches, old=(syncmatches *)&opc->smatches; sm;
				old=sm, sm=sm->sm_next ) {
				if ( sm->sm_op == ss ) {
					found = 1;
					old->sm_next = sm->sm_next;
					op->o_tmpfree( sm, op->o_tmpmemctx );
					break;
				}
			}
		}

Howard Chu's avatar
Howard Chu committed
872
		/* check if current o_req_dn is in scope and matches filter */
Howard Chu's avatar
Howard Chu committed
873
		if ( fc.fscope && test_filter( op, e, ss->s_op->ors_filter ) ==
Howard Chu's avatar
Howard Chu committed
874
875
876
877
878
			LDAP_COMPARE_TRUE ) {
			if ( saveit ) {
				sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx );
				sm->sm_next = opc->smatches;
				sm->sm_op = ss;
Howard Chu's avatar
Howard Chu committed
879
				ss->s_inuse++;
Howard Chu's avatar
Howard Chu committed
880
881
882
				opc->smatches = sm;
			} else {
				/* if found send UPDATE else send ADD */
883
				syncprov_sendresp( op, opc, ss, e,
884
					found ? LDAP_SYNC_MODIFY : LDAP_SYNC_ADD, 1 );
Howard Chu's avatar
Howard Chu committed
885
886
887
			}
		} else if ( !saveit && found ) {
			/* send DELETE */
888
			syncprov_sendresp( op, opc, ss, NULL, LDAP_SYNC_DELETE, 1 );
Howard Chu's avatar
Howard Chu committed
889
		}
Howard Chu's avatar
Howard Chu committed
890
	}
Howard Chu's avatar
Howard Chu committed
891
	ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
892
done:
893
894
895
896
897
	if ( op->o_tag != LDAP_REQ_ADD ) {
		op->o_bd->bd_info = (BackendInfo *)on->on_info;
		be_entry_release_r( op, e );
		op->o_bd->bd_info = (BackendInfo *)on;
	}
898
899
900
	if ( freefdn ) {
		op->o_tmpfree( fc.fdn->bv_val, op->o_tmpmemctx );
	}
Howard Chu's avatar
Howard Chu committed
901
902
}

Howard Chu's avatar
Howard Chu committed
903
904
905
906
static int
syncprov_op_cleanup( Operation *op, SlapReply *rs )
{
	slap_callback *cb = op->o_callback;
907
	opcookie *opc = cb->sc_private;
908
909
	slap_overinst *on = opc->son;
	syncprov_info_t		*si = on->on_bi.bi_private;
Howard Chu's avatar
Howard Chu committed
910
	syncmatches *sm, *snext;
911
	modtarget *mt, mtdummy;
Howard Chu's avatar
Howard Chu committed
912
913
914

	for (sm = opc->smatches; sm; sm=snext) {
		snext = sm->sm_next;
Howard Chu's avatar
Howard Chu committed
915
		syncprov_free_syncop( sm->sm_op );
Howard Chu's avatar
Howard Chu committed
916
917
		op->o_tmpfree( sm, op->o_tmpmemctx );
	}
918
919
920

	/* Remove op from lock table */
	mtdummy.mt_op = op;
921
	ldap_pvt_thread_rdwr_rlock( &si->si_mods_rwlock );
922
	mt = avl_find( si->si_mods, &mtdummy, sp_avl_cmp );
923
	ldap_pvt_thread_rdwr_runlock( &si->si_mods_rwlock );
924
925
	if ( mt ) {
		modinst *mi = mt->mt_mods;
926

927
928
929
930
931
932
933
		/* If there are more, promote the next one */
		ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
		if ( mi->mi_next ) {
			mt->mt_mods = mi->mi_next;
			mt->mt_op = mt->mt_mods->mi_op;
			ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
		} else {
934
			ldap_pvt_thread_rdwr_wlock( &si->si_mods_rwlock );
935
			avl_delete( &si->si_mods, mt, sp_avl_cmp );
936
			ldap_pvt_thread_rdwr_wunlock( &si->si_mods_rwlock );
937
938
939
940
941
			ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
			ldap_pvt_thread_mutex_destroy( &mt->mt_mutex );
			ch_free( mt );
		}
	}
942
943
944
945
946
947
	if ( !BER_BVISNULL( &opc->suuid ))
		op->o_tmpfree( opc->suuid.bv_val, op->o_tmpmemctx );
	if ( !BER_BVISNULL( &opc->sndn ))
		op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
	if ( !BER_BVISNULL( &opc->sdn ))
		op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
948
949
950
951
	op->o_callback = cb->sc_next;
	op->o_tmpfree(cb, op->o_tmpmemctx);
}

952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
static void
syncprov_checkpoint( Operation *op, SlapReply *rs, slap_overinst *on )
{
	syncprov_info_t		*si = on->on_bi.bi_private;
	Modifications mod;
	Operation opm;
	struct berval bv[2];
	BackendInfo *orig;
	slap_callback cb = {0};

	mod.sml_values = bv;
	bv[1].bv_val = NULL;
	bv[0] = si->si_ctxcsn;
	mod.sml_nvalues = NULL;
	mod.sml_desc = slap_schema.si_ad_contextCSN;
	mod.sml_op = LDAP_MOD_REPLACE;
	mod.sml_next = NULL;

	cb.sc_response = slap_null_cb;
	opm = *op;
	opm.o_tag = LDAP_REQ_MODIFY;
	opm.o_callback = &cb;
	opm.orm_modlist = &mod;
	opm.o_req_dn = op->o_bd->be_suffix[0];
	opm.o_req_ndn = op->o_bd->be_nsuffix[0];
	orig = opm.o_bd->bd_info;
	opm.o_bd->bd_info = on->on_info->oi_orig;
	opm.o_bd->be_modify( &opm, rs );
}

Howard Chu's avatar
Howard Chu committed
982
983
984
static int
syncprov_op_response( Operation *op, SlapReply *rs )
{
985
	opcookie *opc = op->o_callback->sc_private;
Howard Chu's avatar
Howard Chu committed
986
987
	slap_overinst *on = opc->son;
	syncprov_info_t		*si = on->on_bi.bi_private;
Howard Chu's avatar
Howard Chu committed
988
	syncmatches *sm;
Howard Chu's avatar
Howard Chu committed
989
990
991

	if ( rs->sr_err == LDAP_SUCCESS )
	{
Howard Chu's avatar
Howard Chu committed
992
		struct berval maxcsn;
993
		char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
Howard Chu's avatar
Howard Chu committed
994

Howard Chu's avatar
Cleanup    
Howard Chu committed
995
		/* Update our context CSN */
996
		cbuf[0] = '\0';
Howard Chu's avatar
Howard Chu committed
997
998
		ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
		slap_get_commit_csn( op, &maxcsn );
999
		if ( !BER_BVISNULL( &maxcsn ) ) {
1000
			strcpy( cbuf, maxcsn.bv_val );
For faster browsing, not all history is shown. View entire blame