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

#include "portable.h"

#ifdef SLAPD_OVER_SYNCPROV

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

31
32
33
34
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;
	Operation *mt_op;
	ldap_pvt_thread_mutex_t mt_mutex;
} modtarget;

Howard Chu's avatar
Howard Chu committed
44
45
46
/* A queued result of a persistent search */
typedef struct syncres {
	struct syncres *s_next;
47
	struct berval s_dn;
Howard Chu's avatar
Howard Chu committed
48
49
	struct berval s_ndn;
	struct berval s_uuid;
50
51
52
	struct berval s_csn;
	char s_mode;
	char s_isreference;
Howard Chu's avatar
Howard Chu committed
53
54
} syncres;

Howard Chu's avatar
Howard Chu committed
55
56
57
58
59
60
/* 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 */
61
	int		s_rid;
Howard Chu's avatar
Howard Chu committed
62
	struct berval s_filterstr;
Howard Chu's avatar
Howard Chu committed
63
	int		s_flags;	/* search status */
Howard Chu's avatar
Howard Chu committed
64
65
66
67
68
#define	PS_IS_REFRESHING	0x01
#define	PS_IS_DETACHED		0x02
#define	PS_WROTE_BASE		0x04
#define	PS_FIND_BASE		0x08

Howard Chu's avatar
Howard Chu committed
69
	int		s_inuse;	/* reference count */
Howard Chu's avatar
Howard Chu committed
70
71
	struct syncres *s_res;
	struct syncres *s_restail;
Howard Chu's avatar
Howard Chu committed
72
	struct re_s	*s_qtask;	/* task for playing psearch responses */
73
#define	RUNQ_INTERVAL	36000	/* a long time */
Howard Chu's avatar
Howard Chu committed
74
	ldap_pvt_thread_mutex_t	s_mutex;
Howard Chu's avatar
Howard Chu committed
75
76
} syncops;

77
78
79
80
81
82
/* A received sync control */
typedef struct sync_control {
	struct sync_cookie sr_state;
	int sr_rhint;
} sync_control;

83
84
85
#if 0 /* moved back to slap.h */
#define	o_sync	o_ctrlflag[slap_cids.sc_LDAPsync]
#endif
86
/* o_sync_mode uses data bits of o_sync */
87
#define	o_sync_mode	o_ctrlflag[slap_cids.sc_LDAPsync]
88
89
90
91
92
93

#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
94
95
96
97
98
99
/* Record of which searches matched at premodify step */
typedef struct syncmatches {
	struct syncmatches *sm_next;
	syncops *sm_op;
} syncmatches;

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/* Session log data */
typedef struct slog_entry {
	struct slog_entry *se_next;
	struct berval se_uuid;
	struct berval se_csn;
	ber_tag_t	se_tag;
} slog_entry;

typedef struct sessionlog {
	struct berval	sl_mincsn;
	int		sl_num;
	int		sl_size;
	slog_entry *sl_head;
	slog_entry *sl_tail;
	ldap_pvt_thread_mutex_t sl_mutex;
} sessionlog;

Howard Chu's avatar
Cleanup    
Howard Chu committed
117
/* The main state for this overlay */
Howard Chu's avatar
Howard Chu committed
118
119
typedef struct syncprov_info_t {
	syncops		*si_ops;
Howard Chu's avatar
Howard Chu committed
120
	struct berval	si_ctxcsn;	/* ldapsync context */
121
122
123
	int		si_chkops;	/* checkpointing info */
	int		si_chktime;
	int		si_numops;	/* number of ops since last checkpoint */
124
	int		si_nopres;	/* Skip present phase */
125
	int		si_usehint;	/* use reload hint */
126
	time_t	si_chklast;	/* time of last checkpoint */
127
	Avlnode	*si_mods;	/* entries being modified */
128
	sessionlog	*si_logs;
Howard Chu's avatar
Howard Chu committed
129
130
	ldap_pvt_thread_mutex_t	si_csn_mutex;
	ldap_pvt_thread_mutex_t	si_ops_mutex;
Howard Chu's avatar
Howard Chu committed
131
	ldap_pvt_thread_mutex_t	si_mods_mutex;
132
	char		si_ctxcsnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
Howard Chu's avatar
Howard Chu committed
133
134
135
136
137
} syncprov_info_t;

typedef struct opcookie {
	slap_overinst *son;
	syncmatches *smatches;
138
139
140
141
142
	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
143
144
} opcookie;

145
typedef struct fbase_cookie {
146
147
148
149
	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 */
150
} fbase_cookie;
Howard Chu's avatar
Howard Chu committed
151

152
static AttributeName csn_anlist[3];
Howard Chu's avatar
Howard Chu committed
153
154
static AttributeName uuid_anlist[2];

Howard Chu's avatar
Cleanup    
Howard Chu committed
155
/* Build a LDAPsync intermediate state control */
156
157
158
159
160
static int
syncprov_state_ctrl(
	Operation	*op,
	SlapReply	*rs,
	Entry		*e,
161
	int		entry_sync_state,
162
	LDAPControl	**ctrls,
163
164
165
	int		num_ctrls,
	int		send_cookie,
	struct berval	*cookie )
166
167
168
169
170
171
172
{
	Attribute* a;
	int ret;

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

173
	struct berval	entryuuid_bv = BER_BVNULL;
174
175
176
177

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

180
181
182
183
184
	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;
185
		}
186
	}
187

188
189
	/* FIXME: what if entryuuid is NULL or empty ? */

190
191
192
193
194
195
	if ( send_cookie && cookie ) {
		ber_printf( ber, "{eOON}",
			entry_sync_state, &entryuuid_bv, cookie );
	} else {
		ber_printf( ber, "{eON}",
			entry_sync_state, &entryuuid_bv );
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
	}

	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
215
/* Build a LDAPsync final state control */
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
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
233
	ctrls[num_ctrls] = op->o_tmpalloc( sizeof ( LDAPControl ), op->o_tmpmemctx );
234
235
236
237
238
239
240
241

	ber_printf( ber, "{" );
	if ( send_cookie && cookie ) {
		ber_printf( ber, "O", cookie );
	}
	if ( refreshDeletes == LDAP_SYNC_REFRESH_DELETES ) {
		ber_printf( ber, "b", refreshDeletes );
	}
242
	ber_printf( ber, "N}" );
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

	ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_DONE;
	ctrls[num_ctrls]->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
	ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );

	ber_free_buf( ber );

	if ( ret < 0 ) {
		Debug( LDAP_DEBUG_TRACE,
			"syncprov_done_ctrl: ber_flatten2 failed\n",
			0, 0, 0 );
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
		return ret;
	}

	return LDAP_SUCCESS;
}

Howard Chu's avatar
Howard Chu committed
261
static int
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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
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
325
	rs->sr_rspoid = LDAP_SYNC_INFO;
326
327
328
329
330
331
332
	rs->sr_rspdata = &rspdata;
	send_ldap_intermediate( op, rs );
	rs->sr_rspdata = NULL;
	ber_free_buf( ber );

	return LDAP_SUCCESS;
}
333

Howard Chu's avatar
Cleanup    
Howard Chu committed
334
/* Find a modtarget in an AVL tree */
335
336
337
338
339
340
341
342
343
344
345
346
347
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 );
}

348
349
350
351
352
353
354
355
356
/* 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
357
358
359
360
361
362
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 ) {
363
364
365
366
367
368
		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
369
			fc->fbase = 2;
370
371
			fc->fss->s_eid = rs->sr_entry->e_id;
			ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
372

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

Howard Chu's avatar
Howard Chu committed
376
		/* OK, the DN is the same and the entryID is the same. */
Howard Chu's avatar
Howard Chu committed
377
378
			fc->fbase = 1;
		}
Howard Chu's avatar
Howard Chu committed
379
	}
380
381
382
	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
383
384
385
	return LDAP_SUCCESS;
}

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

Howard Chu's avatar
Howard Chu committed
389
static int
390
syncprov_findbase( Operation *op, fbase_cookie *fc )
Howard Chu's avatar
Howard Chu committed
391
{
392
393
	opcookie *opc = op->o_callback->sc_private;
	slap_overinst *on = opc->son;
Howard Chu's avatar
Howard Chu committed
394

Howard Chu's avatar
Howard Chu committed
395
396
397
	/* Use basic parameters from syncrepl search, but use
	 * current op's threadctx / tmpmemctx
	 */
Howard Chu's avatar
Howard Chu committed
398
399
400
401
402
403
	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
404

Howard Chu's avatar
Howard Chu committed
405
406
		fc->fss->s_flags ^= PS_FIND_BASE;
		ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
Howard Chu's avatar
Howard Chu committed
407

Howard Chu's avatar
Howard Chu committed
408
		fop = *fc->fss->s_op;
Howard Chu's avatar
Howard Chu committed
409

Howard Chu's avatar
Howard Chu committed
410
411
412
413
		fop.o_hdr = op->o_hdr;
		fop.o_bd = op->o_bd;
		fop.o_time = op->o_time;
		fop.o_tincr = op->o_tincr;
Howard Chu's avatar
Howard Chu committed
414

Howard Chu's avatar
Howard Chu committed
415
416
		cb.sc_response = findbase_cb;
		cb.sc_private = fc;
Howard Chu's avatar
Howard Chu committed
417

Howard Chu's avatar
Howard Chu committed
418
419
420
421
422
423
424
425
426
427
		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
428
429
		fop.ors_filter = &generic_filter;
		fop.ors_filterstr = generic_filterstr;
Howard Chu's avatar
Howard Chu committed
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463

		fop.o_bd->bd_info = on->on_info->oi_orig;
		rc = fop.o_bd->be_search( &fop, &frs );
		fop.o_bd->bd_info = (BackendInfo *)on;
	} 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;
#ifdef LDAP_SCOPE_SUBORDINATE
		case LDAP_SCOPE_SUBORDINATE:
			fc->fscope = dnIsSuffix( fc->fdn, &fc->fss->s_base ) &&
				!dn_match( fc->fdn, &fc->fss->s_base );
			break;
#endif
		}
	}

	if ( fc->fbase )
		return LDAP_SUCCESS;
Howard Chu's avatar
Howard Chu committed
464
465
466
467
468
469

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

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

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

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

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 ) {
514
		sc->sc_private = (void *)1;
Howard Chu's avatar
Howard Chu committed
515
516
517
518
	}
	return LDAP_SUCCESS;
}

519
520
/* Build a list of entryUUIDs for sending in a SyncID set */

521
522
#define UUID_LEN	16

Howard Chu's avatar
Howard Chu committed
523
524
525
typedef struct fpres_cookie {
	int num;
	BerVarray uuids;
526
	char *last;
Howard Chu's avatar
Howard Chu committed
527
528
529
530
531
532
533
} fpres_cookie;

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

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

static int
569
syncprov_findcsn( Operation *op, find_csn_t mode )
Howard Chu's avatar
Howard Chu committed
570
571
572
573
574
575
576
577
578
{
	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];
579
	struct berval maxcsn;
580
	Filter cf, af;
581
582
583
584
585
#ifdef LDAP_COMP_MATCH
	AttributeAssertion eq = { NULL, BER_BVNULL, NULL };
#else
	AttributeAssertion eq = { NULL, BER_BVNULL };
#endif
586
	int i, rc = LDAP_SUCCESS;
Howard Chu's avatar
Howard Chu committed
587
	fpres_cookie pcookie;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
588
	sync_control *srs = NULL;
Howard Chu's avatar
Howard Chu committed
589
	int findcsn_retry = 1;
590
591
592

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

594
		if ( srs->sr_state.ctxcsn.bv_len >= LDAP_LUTIL_CSNSTR_BUFSIZE ) {
595
596
			return LDAP_OTHER;
		}
Howard Chu's avatar
Howard Chu committed
597
598
599
	}

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

604
605
606
607
	cf.f_ava = &eq;
	cf.f_av_desc = slap_schema.si_ad_entryCSN;
	cf.f_next = NULL;

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

Howard Chu's avatar
Howard Chu committed
614
again:
615
616
617
618
	switch( mode ) {
	case FIND_MAXCSN:
		cf.f_choice = LDAP_FILTER_GE;
		cf.f_av_value = si->si_ctxcsn;
619
		fop.ors_filterstr.bv_len = sprintf( buf, "(entryCSN>=%s)",
620
621
622
623
624
625
			cf.f_av_value.bv_val );
		fop.ors_attrsonly = 0;
		fop.ors_attrs = csn_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
		cb.sc_private = &maxcsn;
		cb.sc_response = findmax_cb;
626
		strcpy( cbuf, si->si_ctxcsn.bv_val );
627
		maxcsn.bv_val = cbuf;
628
		maxcsn.bv_len = si->si_ctxcsn.bv_len;
629
630
		break;
	case FIND_CSN:
Howard Chu's avatar
Howard Chu committed
631
		cf.f_av_value = srs->sr_state.ctxcsn;
Howard Chu's avatar
Howard Chu committed
632
633
634
635
636
637
638
639
640
641
642
		/* Look for exact match the first time */
		if ( findcsn_retry ) {
			cf.f_choice = LDAP_FILTER_EQUALITY;
			fop.ors_filterstr.bv_len = sprintf( buf, "(entryCSN=%s)",
				cf.f_av_value.bv_val );
		/* On retry, look for <= */
		} else {
			cf.f_choice = LDAP_FILTER_LE;
			fop.ors_filterstr.bv_len = sprintf( buf, "(entryCSN<=%s)",
				cf.f_av_value.bv_val );
		}
643
644
645
646
		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
647
		cb.sc_response = findcsn_cb;
648
649
		break;
	case FIND_PRESENT:
650
651
652
		af.f_choice = LDAP_FILTER_AND;
		af.f_next = NULL;
		af.f_and = &cf;
653
		cf.f_choice = LDAP_FILTER_LE;
654
		cf.f_av_value = srs->sr_state.ctxcsn;
655
656
657
		cf.f_next = op->ors_filter;
		fop.ors_filter = &af;
		filter2bv_x( &fop, fop.ors_filter, &fop.ors_filterstr );
Howard Chu's avatar
Howard Chu committed
658
659
660
		fop.ors_attrsonly = 0;
		fop.ors_attrs = uuid_anlist;
		fop.ors_slimit = SLAP_NO_LIMIT;
Howard Chu's avatar
Howard Chu committed
661
		cb.sc_private = &pcookie;
Howard Chu's avatar
Howard Chu committed
662
663
		cb.sc_response = findpres_cb;
		pcookie.num = 0;
664
665
666
667
668
669
670
671
672
673
674
675

		/* 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;
		}
676
		break;
Howard Chu's avatar
Howard Chu committed
677
678
679
	}

	fop.o_bd->bd_info = on->on_info->oi_orig;
680
	fop.o_bd->be_search( &fop, &frs );
681
	fop.o_bd->bd_info = (BackendInfo *)on;
Howard Chu's avatar
Howard Chu committed
682

683
684
	switch( mode ) {
	case FIND_MAXCSN:
685
686
		strcpy( si->si_ctxcsnbuf, maxcsn.bv_val );
		si->si_ctxcsn.bv_len = maxcsn.bv_len;
687
688
689
		break;
	case FIND_CSN:
		/* If matching CSN was not found, invalidate the context. */
Howard Chu's avatar
Howard Chu committed
690
691
692
693
694
695
696
697
		if ( !cb.sc_private ) {
			/* If we didn't find an exact match, then try for <= */
			if ( findcsn_retry ) {
				findcsn_retry = 0;
				goto again;
			}
			rc = LDAP_NO_SUCH_OBJECT;
		}
698
699
		break;
	case FIND_PRESENT:
700
		op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
701
		op->o_tmpfree( fop.ors_filterstr.bv_val, op->o_tmpmemctx );
702
		break;
Howard Chu's avatar
Howard Chu committed
703
704
	}

705
	return rc;
Howard Chu's avatar
Howard Chu committed
706
707
}

708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
static void
syncprov_free_syncop( syncops *so )
{
	syncres *sr, *srnext;
	GroupAssertion *ga, *gnext;

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

737
738
/* Send a persistent search response */
static int
739
740
syncprov_sendresp( Operation *op, opcookie *opc, syncops *so,
	Entry **e, int mode )
741
742
743
744
745
746
747
748
749
{
	slap_overinst *on = opc->son;

	SlapReply rs = { REP_SEARCH };
	LDAPControl *ctrls[2];
	struct berval cookie;
	Entry e_uuid = {0};
	Attribute a_uuid = {0};

Howard Chu's avatar
Howard Chu committed
750
751
752
	if ( so->s_op->o_abandon )
		return SLAPD_ABANDON;

753
	ctrls[1] = NULL;
754
	slap_compose_sync_cookie( op, &cookie, &opc->sctxcsn, so->s_rid );
755
756
757
758

	e_uuid.e_attrs = &a_uuid;
	a_uuid.a_desc = slap_schema.si_ad_entryUUID;
	a_uuid.a_nvals = &opc->suuid;
759
	rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
760
		mode, ctrls, 0, 1, &cookie );
Howard Chu's avatar
Howard Chu committed
761
	op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
762

763
	rs.sr_ctrls = ctrls;
764
	op->o_bd->bd_info = (BackendInfo *)on->on_info;
765
766
	switch( mode ) {
	case LDAP_SYNC_ADD:
767
768
769
		rs.sr_entry = *e;
		if ( rs.sr_entry->e_private )
			rs.sr_flags = REP_ENTRY_MUSTRELEASE;
770
		if ( opc->sreference ) {
771
772
			rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
			send_search_reference( op, &rs );
773
			ber_bvarray_free( rs.sr_ref );
774
775
			if ( !rs.sr_entry )
				*e = NULL;
776
777
778
779
			break;
		}
		/* fallthru */
	case LDAP_SYNC_MODIFY:
780
781
782
		rs.sr_entry = *e;
		if ( rs.sr_entry->e_private )
			rs.sr_flags = REP_ENTRY_MUSTRELEASE;
783
784
		rs.sr_attrs = op->ors_attrs;
		send_search_entry( op, &rs );
785
786
		if ( !rs.sr_entry )
			*e = NULL;
787
788
789
790
791
792
793
		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 ) {
794
			struct berval bv = BER_BVNULL;
795
			rs.sr_ref = &bv;
796
			send_search_reference( op, &rs );
Howard Chu's avatar
Howard Chu committed
797
		} else {
798
			send_search_entry( op, &rs );
799
800
801
802
803
		}
		break;
	default:
		assert(0);
	}
804
805
806
807
808
	/* In case someone else freed it already? */
	if ( rs.sr_ctrls ) {
		op->o_tmpfree( rs.sr_ctrls[0], op->o_tmpmemctx );
		rs.sr_ctrls = NULL;
	}
809

810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
	return rs.sr_err;
}

/* Play back queued responses */
static int
syncprov_qplay( Operation *op, slap_overinst *on, syncops *so )
{
	syncres *sr;
	Entry *e;
	opcookie opc;
	int rc;

	opc.son = on;
	op->o_bd->bd_info = (BackendInfo *)on->on_info;

	for (;;) {
826
		ldap_pvt_thread_mutex_lock( &so->s_mutex );
827
828
829
830
831
		sr = so->s_res;
		if ( sr )
			so->s_res = sr->s_next;
		if ( !so->s_res )
			so->s_restail = NULL;
832
		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
833

834
		if ( !sr || so->s_op->o_abandon )
835
836
			break;

837
838
839
840
841
842
843
844
845
846
847
848
		opc.sdn = sr->s_dn;
		opc.sndn = sr->s_ndn;
		opc.suuid = sr->s_uuid;
		opc.sctxcsn = sr->s_csn;
		opc.sreference = sr->s_isreference;
		e = NULL;

		if ( sr->s_mode != LDAP_SYNC_DELETE ) {
			rc = be_entry_get_rw( op, &opc.sndn, NULL, NULL, 0, &e );
			if ( rc ) {
				ch_free( sr );
				continue;
849
			}
850
851
		}
		rc = syncprov_sendresp( op, &opc, so, &e, sr->s_mode );
852

853
854
		if ( e ) {
			be_entry_release_rw( op, e, 0 );
855
856
857
		}

		ch_free( sr );
858
859
860

		if ( rc )
			break;
861
	}
862
863
864
865
866
867
868
869
870
871
872
	op->o_bd->bd_info = (BackendInfo *)on;
	return rc;
}

/* runqueue task for playing back queued responses */
static void *
syncprov_qtask( void *ctx, void *arg )
{
	struct re_s *rtask = arg;
	syncops *so = rtask->arg;
	slap_overinst *on = so->s_op->o_private;
873
	OperationBuffer opbuf;
874
875
876
	Operation *op;
	BackendDB be;

877
	op = (Operation *) &opbuf;
878
	*op = *so->s_op;
879
880
	op->o_hdr = (Opheader *)(op+1);
	op->o_controls = (void **)(op->o_hdr+1);
881
	memset( op->o_controls, 0, SLAP_MAX_CIDS * sizeof(void *));
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897

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

	op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx);
	op->o_tmpmfuncs = &slap_sl_mfuncs;
	op->o_threadctx = ctx;

	/* syncprov_qplay expects a fake db */
	be = *so->s_op->o_bd;
	be.be_flags |= SLAP_DBFLAG_OVERLAY;
	op->o_bd = &be;
	op->o_private = NULL;
	op->o_callback = NULL;

	syncprov_qplay( op, on, so );

898
899
900
	/* decrement use count... */
	syncprov_free_syncop( so );

901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
	/* wait until we get explicitly scheduled again */
	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
	ldap_pvt_runqueue_stoptask( &slapd_rq, so->s_qtask );
	ldap_pvt_runqueue_resched( &slapd_rq, so->s_qtask, 1 );
	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );

	return NULL;
}

/* Queue a persistent search response */
static int
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_dn.bv_len = opc->sdn.bv_len;
	sr->s_mode = mode;
	sr->s_isreference = opc->sreference;
Howard Chu's avatar
Howard Chu committed
923
924
	sr->s_ndn.bv_val = lutil_strcopy( sr->s_dn.bv_val,
		 opc->sdn.bv_val ) + 1;
925
	sr->s_ndn.bv_len = opc->sndn.bv_len;
Howard Chu's avatar
Howard Chu committed
926
927
	sr->s_uuid.bv_val = lutil_strcopy( sr->s_ndn.bv_val,
		 opc->sndn.bv_val ) + 1;
928
	sr->s_uuid.bv_len = opc->suuid.bv_len;
Howard Chu's avatar
Howard Chu committed
929
930
	AC_MEMCPY( sr->s_uuid.bv_val, opc->suuid.bv_val, opc->suuid.bv_len );
	sr->s_csn.bv_val = sr->s_uuid.bv_val + sr->s_uuid.bv_len + 1;
931
932
933
	sr->s_csn.bv_len = opc->sctxcsn.bv_len;
	strcpy( sr->s_csn.bv_val, opc->sctxcsn.bv_val );

Howard Chu's avatar
Howard Chu committed
934
	ldap_pvt_thread_mutex_lock( &so->s_mutex );
935
936
937
938
939
940
	if ( !so->s_res ) {
		so->s_res = sr;
	} else {
		so->s_restail->s_next = sr;
	}
	so->s_restail = sr;
Howard Chu's avatar
Howard Chu committed
941
942
943
944
945
946

	/* If the base of the psearch was modified, check it next time round */
	if ( so->s_flags & PS_WROTE_BASE ) {
		so->s_flags ^= PS_WROTE_BASE;
		so->s_flags |= PS_FIND_BASE;
	}
947
948
949
	if ( so->s_flags & PS_IS_DETACHED ) {
		ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
		if ( !so->s_qtask ) {
950
			so->s_qtask = ldap_pvt_runqueue_insert( &slapd_rq, RUNQ_INTERVAL,
951
952
				syncprov_qtask, so, "syncprov_qtask",
				so->s_op->o_conn->c_peer_name.bv_val );
953
			++so->s_inuse;
954
		} else {
955
956
			if (!ldap_pvt_runqueue_isrunning( &slapd_rq, so->s_qtask ) &&
				!so->s_qtask->next_sched.tv_sec ) {
Howard Chu's avatar
Howard Chu committed
957
				so->s_qtask->interval.tv_sec = 0;
958
				ldap_pvt_runqueue_resched( &slapd_rq, so->s_qtask, 0 );
959
				so->s_qtask->interval.tv_sec = RUNQ_INTERVAL;
960
				++so->s_inuse;
961
962
963
964
965
966
			}
		}
		ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
	}
	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
	return LDAP_SUCCESS;
967
968
}

969
static int
Howard Chu's avatar
Howard Chu committed
970
syncprov_drop_psearch( syncops *so, int lock )
971
{
972
973
974
975
976
977
978
979
980
981
	if ( so->s_flags & PS_IS_DETACHED ) {
		if ( lock )
			ldap_pvt_thread_mutex_lock( &so->s_op->o_conn->c_mutex );
		so->s_op->o_conn->c_n_ops_executing--;
		so->s_op->o_conn->c_n_ops_completed++;
		LDAP_STAILQ_REMOVE( &so->s_op->o_conn->c_ops, so->s_op, slap_op,
			o_next );
		if ( lock )
			ldap_pvt_thread_mutex_unlock( &so->s_op->o_conn->c_mutex );
	}
982
	syncprov_free_syncop( so );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
983
984

	return 0;
985
986
}

Howard Chu's avatar
Howard Chu committed
987
988
989
990
991
992
993
994
995
996
static int
syncprov_ab_cleanup( Operation *op, SlapReply *rs )
{
	slap_callback *sc = op->o_callback;
	op->o_callback = sc->sc_next;
	syncprov_drop_psearch( op->o_callback->sc_private, 0 );
	op->o_tmpfree( sc, op->o_tmpmemctx );
	return 0;
}

997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
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 ) {
1018
			so->s_op->o_cancel = SLAP_CANCEL_ACK;
1019
1020
			rs->sr_err = LDAP_CANCELLED;
			send_ldap_result( so->s_op, rs );
Howard Chu's avatar
Howard Chu committed
1021
1022
1023
1024
1025
1026
1027
			if ( so->s_flags & PS_IS_DETACHED ) {
				slap_callback *cb;
				cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
				cb->sc_cleanup = syncprov_ab_cleanup;
				cb->sc_next = op->o_callback;
				cb->sc_private = so;
				return SLAP_CB_CONTINUE;
1028
			}
1029
		}
Howard Chu's avatar
Howard Chu committed
1030
		syncprov_drop_psearch( so, 0 );
1031
1032
1033
1034
	}
	return SLAP_CB_CONTINUE;
}

Howard Chu's avatar
Cleanup    
Howard Chu committed
1035
/* Find which persistent searches are affected by this operation */
Howard Chu's avatar
Howard Chu committed
1036
static void
Howard Chu's avatar
Howard Chu committed
1037
syncprov_matchops( Operation *op, opcookie *opc, int saveit )
Howard Chu's avatar
Howard Chu committed
1038
{
1039
	slap_overinst *on = opc->son;
Howard Chu's avatar
Howard Chu committed
1040
1041
	syncprov_info_t		*si = on->on_bi.bi_private;

1042
	fbase_cookie fc;
1043
	syncops *ss, *sprev, *snext;
Howard Chu's avatar
Howard Chu committed
1044
1045
1046
	Entry *e;
	Attribute *a;
	int rc;
Howard Chu's avatar
Howard Chu committed
1047
	struct berval newdn;
1048
	int freefdn = 0;
Howard Chu's avatar
Howard Chu committed
1049

Howard Chu's avatar
Howard Chu committed
1050
	fc.fdn = &op->o_req_ndn;
Howard Chu's avatar
Howard Chu committed
1051
1052
1053
1054
1055
1056
1057
	/* 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;
1058
		freefdn = 1;
Howard Chu's avatar
Howard Chu committed
1059
	}
1060
	if ( op->o_tag != LDAP_REQ_ADD ) {
Howard Chu's avatar
Howard Chu committed
1061
		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1062
		rc = be_entry_get_rw( op, fc.fdn, NULL, NULL, 0, &e );
1063
1064
1065
1066
1067
1068
		/* If we're sending responses now, make a copy and unlock the DB */
		if ( e && !saveit ) {
			Entry *e2 = entry_dup( e );
			be_entry_release_rw( op, e, 0 );
			e = e2;
		}
Howard Chu's avatar
Howard Chu committed
1069
		op->o_bd->bd_info = (BackendInfo *)on;
1070
1071
1072
1073
		if ( rc ) return;
	} else {
		e = op->ora_e;
	}
Howard Chu's avatar
Howard Chu committed
1074

1075
	if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
1076
1077
1078
		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 );
Howard Chu's avatar
Howard Chu committed
1079
1080
		a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
		if ( a )
Howard Chu's avatar
Howard Chu committed
1081
			ber_dupbv_x( &opc->suuid, &a->a_nvals[0], op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
1082
1083
1084
1085
1086
	} else if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
		op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
		op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
		ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
		ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
1087
	}
Howard Chu's avatar
Howard Chu committed
1088

Howard Chu's avatar
Howard Chu committed
1089
1090
	ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
	for (ss = si->si_ops, sprev = (syncops *)&si->si_ops; ss;
1091
		sprev = ss, ss=snext)
Howard Chu's avatar
Howard Chu committed
1092
	{
Howard Chu's avatar
Howard Chu committed
1093
1094
1095
		syncmatches *sm;
		int found = 0;

1096
		snext = ss->s_next;
Howard Chu's avatar
Howard Chu committed
1097
		/* validate base */
Howard Chu's avatar
Howard Chu committed
1098
1099
		fc.fss = ss;
		fc.fbase = 0;
1100
		fc.fscope = 0;
1101
1102

		/* If the base of the search is missing, signal a refresh */
1103
		rc = syncprov_findbase( op, &fc );
1104
1105
1106
1107
1108
		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;
Howard Chu's avatar
Howard Chu committed
1109
			syncprov_drop_psearch( ss, 1 );
1110
			ss = sprev;
1111
1112
			continue;
		}
Howard Chu's avatar
Howard Chu committed
1113

Howard Chu's avatar
Howard Chu committed
1114

Howard Chu's avatar
Howard Chu committed
1115
1116
1117
		/* If we're sending results now, look for this op in old matches */
		if ( !saveit ) {
			syncmatches *old;
Howard Chu's avatar
Howard Chu committed
1118
1119
1120
1121
1122
1123
1124
1125

			/* Did we modify the search base? */
			if ( dn_match( &op->o_req_ndn, &ss->s_base )) {
				ldap_pvt_thread_mutex_lock( &ss->s_mutex );
				ss->s_flags |= PS_WROTE_BASE;
				ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
			}

Howard Chu's avatar
Howard Chu committed
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
			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
1137
		/* check if current o_req_dn is in scope and matches filter */
Howard Chu's avatar
Howard Chu committed
1138
		if ( fc.fscope && test_filter( op, e, ss->s_op->ors_filter ) ==
Howard Chu's avatar
Howard Chu committed
1139
1140
1141
1142
1143
			LDAP_COMPARE_TRUE ) {
			if ( saveit ) {
				sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx );
				sm->sm_next = opc->smatches;
				sm->sm_op = ss;
1144
1145
1146
				ldap_pvt_thread_mutex_lock( &ss->s_mutex );
				++ss->s_inuse;
				ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
Howard Chu's avatar
Howard Chu committed
1147
1148
1149
				opc->smatches = sm;
			} else {
				/* if found send UPDATE else send ADD */
1150
1151
				syncprov_qresp( opc, ss,
					found ? LDAP_SYNC_MODIFY : LDAP_SYNC_ADD );
Howard Chu's avatar
Howard Chu committed
1152
1153
1154
			}
		} else if ( !saveit && found ) {
			/* send DELETE */
1155
			syncprov_qresp( opc, ss, LDAP_SYNC_DELETE );
Howard Chu's avatar
Howard Chu committed
1156
		}
Howard Chu's avatar
Howard Chu committed
1157
	}
Howard Chu's avatar
Howard Chu committed
1158
	ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1159

1160
	if ( op->o_tag != LDAP_REQ_ADD && e ) {
1161
		op->o_bd->bd_info = (BackendInfo *)on->on_info;
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
1162
		be_entry_release_rw( op, e, 0 );
1163
1164
		op->o_bd->bd_info = (BackendInfo *)on;
	}
1165
1166
1167
	if ( freefdn ) {
		op->o_tmpfree( fc.fdn->bv_val, op->o_tmpmemctx );
	}
Howard Chu's avatar
Howard Chu committed
1168
1169
}

Howard Chu's avatar
Howard Chu committed
1170
1171
1172
1173
static int
syncprov_op_cleanup( Operation *op, SlapReply *rs )
{
	slap_callback *cb = op->o_callback;
1174
	opcookie *opc = cb->sc_private;
1175
1176
	slap_overinst *on = opc->son;
	syncprov_info_t		*si = on->on_bi.bi_private;
Howard Chu's avatar
Howard Chu committed
1177
	syncmatches *sm, *snext;
1178
	modtarget *mt, mtdummy;