]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/syncprov.c
Sync with HEAD
[openldap] / servers / slapd / overlays / syncprov.c
1 /* $OpenLDAP$ */
2 /* syncprov.c - syncrepl provider */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2004-2005 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Howard Chu for inclusion in
18  * OpenLDAP Software.
19  */
20
21 #include "portable.h"
22
23 #ifdef SLAPD_OVER_SYNCPROV
24
25 #include <ac/string.h>
26 #include "lutil.h"
27 #include "slap.h"
28 #include "config.h"
29
30 /* A modify request on a particular entry */
31 typedef struct modinst {
32         struct modinst *mi_next;
33         Operation *mi_op;
34 } modinst;
35
36 typedef struct modtarget {
37         struct modinst *mt_mods;
38         struct modinst *mt_tail;
39         Operation *mt_op;
40         ldap_pvt_thread_mutex_t mt_mutex;
41 } modtarget;
42
43 /* A queued result of a persistent search */
44 typedef struct syncres {
45         struct syncres *s_next;
46         struct berval s_dn;
47         struct berval s_ndn;
48         struct berval s_uuid;
49         struct berval s_csn;
50         char s_mode;
51         char s_isreference;
52 } syncres;
53
54 /* Record of a persistent search */
55 typedef struct syncops {
56         struct syncops *s_next;
57         struct berval   s_base;         /* ndn of search base */
58         ID              s_eid;          /* entryID of search base */
59         Operation       *s_op;          /* search op */
60         int             s_rid;
61         struct berval s_filterstr;
62         int             s_flags;        /* search status */
63         int             s_inuse;        /* reference count */
64         struct syncres *s_res;
65         struct syncres *s_restail;
66         ldap_pvt_thread_mutex_t s_mutex;
67 } syncops;
68
69 /* A received sync control */
70 typedef struct sync_control {
71         struct sync_cookie sr_state;
72         int sr_rhint;
73 } sync_control;
74
75 #if 0 /* moved back to slap.h */
76 #define o_sync  o_ctrlflag[slap_cids.sc_LDAPsync]
77 #endif
78 /* o_sync_mode uses data bits of o_sync */
79 #define o_sync_mode     o_ctrlflag[slap_cids.sc_LDAPsync]
80
81 #define SLAP_SYNC_NONE                                  (LDAP_SYNC_NONE<<SLAP_CONTROL_SHIFT)
82 #define SLAP_SYNC_REFRESH                               (LDAP_SYNC_REFRESH_ONLY<<SLAP_CONTROL_SHIFT)
83 #define SLAP_SYNC_PERSIST                               (LDAP_SYNC_RESERVED<<SLAP_CONTROL_SHIFT)
84 #define SLAP_SYNC_REFRESH_AND_PERSIST   (LDAP_SYNC_REFRESH_AND_PERSIST<<SLAP_CONTROL_SHIFT)
85
86 #define PS_IS_REFRESHING        0x01
87 #define PS_IS_DETACHED          0x02
88
89 /* Record of which searches matched at premodify step */
90 typedef struct syncmatches {
91         struct syncmatches *sm_next;
92         syncops *sm_op;
93 } syncmatches;
94
95 /* Session log data */
96 typedef struct slog_entry {
97         struct slog_entry *se_next;
98         struct berval se_uuid;
99         struct berval se_csn;
100         ber_tag_t       se_tag;
101 } slog_entry;
102
103 typedef struct sessionlog {
104         struct berval   sl_mincsn;
105         int             sl_num;
106         int             sl_size;
107         slog_entry *sl_head;
108         slog_entry *sl_tail;
109         ldap_pvt_thread_mutex_t sl_mutex;
110 } sessionlog;
111
112 /* The main state for this overlay */
113 typedef struct syncprov_info_t {
114         syncops         *si_ops;
115         struct berval   si_ctxcsn;      /* ldapsync context */
116         int             si_chkops;      /* checkpointing info */
117         int             si_chktime;
118         int             si_numops;      /* number of ops since last checkpoint */
119         time_t  si_chklast;     /* time of last checkpoint */
120         Avlnode *si_mods;       /* entries being modified */
121         sessionlog      *si_logs;
122         ldap_pvt_thread_mutex_t si_csn_mutex;
123         ldap_pvt_thread_mutex_t si_ops_mutex;
124         ldap_pvt_thread_mutex_t si_mods_mutex;
125         char            si_ctxcsnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
126 } syncprov_info_t;
127
128 typedef struct opcookie {
129         slap_overinst *son;
130         syncmatches *smatches;
131         struct berval sdn;      /* DN of entry, for deletes */
132         struct berval sndn;
133         struct berval suuid;    /* UUID of entry */
134         struct berval sctxcsn;
135         int sreference; /* Is the entry a reference? */
136 } opcookie;
137
138 typedef struct fbase_cookie {
139         struct berval *fdn;     /* DN of a modified entry, for scope testing */
140         syncops *fss;   /* persistent search we're testing against */
141         int fbase;      /* if TRUE we found the search base and it's still valid */
142         int fscope;     /* if TRUE then fdn is within the psearch scope */
143 } fbase_cookie;
144
145 static AttributeName csn_anlist[2];
146 static AttributeName uuid_anlist[2];
147
148 /* Build a LDAPsync intermediate state control */
149 static int
150 syncprov_state_ctrl(
151         Operation       *op,
152         SlapReply       *rs,
153         Entry           *e,
154         int             entry_sync_state,
155         LDAPControl     **ctrls,
156         int             num_ctrls,
157         int             send_cookie,
158         struct berval   *cookie )
159 {
160         Attribute* a;
161         int ret;
162
163         BerElementBuffer berbuf;
164         BerElement *ber = (BerElement *)&berbuf;
165
166         struct berval   entryuuid_bv = BER_BVNULL;
167
168         ber_init2( ber, 0, LBER_USE_DER );
169         ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
170
171         ctrls[num_ctrls] = op->o_tmpalloc( sizeof ( LDAPControl ), op->o_tmpmemctx );
172
173         for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
174                 AttributeDescription *desc = a->a_desc;
175                 if ( desc == slap_schema.si_ad_entryUUID ) {
176                         entryuuid_bv = a->a_nvals[0];
177                         break;
178                 }
179         }
180
181         if ( send_cookie && cookie ) {
182                 ber_printf( ber, "{eOON}",
183                         entry_sync_state, &entryuuid_bv, cookie );
184         } else {
185                 ber_printf( ber, "{eON}",
186                         entry_sync_state, &entryuuid_bv );
187         }
188
189         ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_STATE;
190         ctrls[num_ctrls]->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
191         ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );
192
193         ber_free_buf( ber );
194
195         if ( ret < 0 ) {
196                 Debug( LDAP_DEBUG_TRACE,
197                         "slap_build_sync_ctrl: ber_flatten2 failed\n",
198                         0, 0, 0 );
199                 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
200                 return ret;
201         }
202
203         return LDAP_SUCCESS;
204 }
205
206 /* Build a LDAPsync final state control */
207 static int
208 syncprov_done_ctrl(
209         Operation       *op,
210         SlapReply       *rs,
211         LDAPControl     **ctrls,
212         int                     num_ctrls,
213         int                     send_cookie,
214         struct berval *cookie,
215         int                     refreshDeletes )
216 {
217         int ret;
218         BerElementBuffer berbuf;
219         BerElement *ber = (BerElement *)&berbuf;
220
221         ber_init2( ber, NULL, LBER_USE_DER );
222         ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
223
224         ctrls[num_ctrls] = op->o_tmpalloc( sizeof ( LDAPControl ), op->o_tmpmemctx );
225
226         ber_printf( ber, "{" );
227         if ( send_cookie && cookie ) {
228                 ber_printf( ber, "O", cookie );
229         }
230         if ( refreshDeletes == LDAP_SYNC_REFRESH_DELETES ) {
231                 ber_printf( ber, "b", refreshDeletes );
232         }
233         ber_printf( ber, "N}" );
234
235         ctrls[num_ctrls]->ldctl_oid = LDAP_CONTROL_SYNC_DONE;
236         ctrls[num_ctrls]->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
237         ret = ber_flatten2( ber, &ctrls[num_ctrls]->ldctl_value, 1 );
238
239         ber_free_buf( ber );
240
241         if ( ret < 0 ) {
242                 Debug( LDAP_DEBUG_TRACE,
243                         "syncprov_done_ctrl: ber_flatten2 failed\n",
244                         0, 0, 0 );
245                 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
246                 return ret;
247         }
248
249         return LDAP_SUCCESS;
250 }
251
252 static int
253 syncprov_sendinfo(
254         Operation       *op,
255         SlapReply       *rs,
256         int                     type,
257         struct berval *cookie,
258         int                     refreshDone,
259         BerVarray       syncUUIDs,
260         int                     refreshDeletes )
261 {
262         BerElementBuffer berbuf;
263         BerElement *ber = (BerElement *)&berbuf;
264         struct berval rspdata;
265
266         int ret;
267
268         ber_init2( ber, NULL, LBER_USE_DER );
269         ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
270
271         if ( type ) {
272                 switch ( type ) {
273                 case LDAP_TAG_SYNC_NEW_COOKIE:
274                         ber_printf( ber, "tO", type, cookie );
275                         break;
276                 case LDAP_TAG_SYNC_REFRESH_DELETE:
277                 case LDAP_TAG_SYNC_REFRESH_PRESENT:
278                         ber_printf( ber, "t{", type );
279                         if ( cookie ) {
280                                 ber_printf( ber, "O", cookie );
281                         }
282                         if ( refreshDone == 0 ) {
283                                 ber_printf( ber, "b", refreshDone );
284                         }
285                         ber_printf( ber, "N}" );
286                         break;
287                 case LDAP_TAG_SYNC_ID_SET:
288                         ber_printf( ber, "t{", type );
289                         if ( cookie ) {
290                                 ber_printf( ber, "O", cookie );
291                         }
292                         if ( refreshDeletes == 1 ) {
293                                 ber_printf( ber, "b", refreshDeletes );
294                         }
295                         ber_printf( ber, "[W]", syncUUIDs );
296                         ber_printf( ber, "N}" );
297                         break;
298                 default:
299                         Debug( LDAP_DEBUG_TRACE,
300                                 "syncprov_sendinfo: invalid syncinfo type (%d)\n",
301                                 type, 0, 0 );
302                         return LDAP_OTHER;
303                 }
304         }
305
306         ret = ber_flatten2( ber, &rspdata, 0 );
307
308         if ( ret < 0 ) {
309                 Debug( LDAP_DEBUG_TRACE,
310                         "syncprov_sendinfo: ber_flatten2 failed\n",
311                         0, 0, 0 );
312                 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
313                 return ret;
314         }
315
316         rs->sr_rspoid = LDAP_SYNC_INFO;
317         rs->sr_rspdata = &rspdata;
318         send_ldap_intermediate( op, rs );
319         rs->sr_rspdata = NULL;
320         ber_free_buf( ber );
321
322         return LDAP_SUCCESS;
323 }
324
325 /* Find a modtarget in an AVL tree */
326 static int
327 sp_avl_cmp( const void *c1, const void *c2 )
328 {
329         const modtarget *m1, *m2;
330         int rc;
331
332         m1 = c1; m2 = c2;
333         rc = m1->mt_op->o_req_ndn.bv_len - m2->mt_op->o_req_ndn.bv_len;
334
335         if ( rc ) return rc;
336         return ber_bvcmp( &m1->mt_op->o_req_ndn, &m2->mt_op->o_req_ndn );
337 }
338
339 /* syncprov_findbase:
340  *   finds the true DN of the base of a search (with alias dereferencing) and
341  * checks to make sure the base entry doesn't get replaced with a different
342  * entry (e.g., swapping trees via ModDN, or retargeting an alias). If a
343  * change is detected, any persistent search on this base must be terminated /
344  * reloaded.
345  *   On the first call, we just save the DN and entryID. On subsequent calls
346  * we compare the DN and entryID with the saved values.
347  */
348 static int
349 findbase_cb( Operation *op, SlapReply *rs )
350 {
351         slap_callback *sc = op->o_callback;
352
353         if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
354                 fbase_cookie *fc = sc->sc_private;
355
356                 /* If no entryID, we're looking for the first time.
357                  * Just store whatever we got.
358                  */
359                 if ( fc->fss->s_eid == NOID ) {
360                         fc->fbase = 1;
361                         fc->fss->s_eid = rs->sr_entry->e_id;
362                         ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
363
364                 } else if ( rs->sr_entry->e_id == fc->fss->s_eid &&
365                         dn_match( &rs->sr_entry->e_nname, &fc->fss->s_base )) {
366
367                 /* OK, the DN is the same and the entryID is the same. Now
368                  * see if the fdn resides in the scope.
369                  */
370                         fc->fbase = 1;
371                         switch ( fc->fss->s_op->ors_scope ) {
372                         case LDAP_SCOPE_BASE:
373                                 fc->fscope = dn_match( fc->fdn, &rs->sr_entry->e_nname );
374                                 break;
375                         case LDAP_SCOPE_ONELEVEL: {
376                                 struct berval pdn;
377                                 dnParent( fc->fdn, &pdn );
378                                 fc->fscope = dn_match( &pdn, &rs->sr_entry->e_nname );
379                                 break; }
380                         case LDAP_SCOPE_SUBTREE:
381                                 fc->fscope = dnIsSuffix( fc->fdn, &rs->sr_entry->e_nname );
382                                 break;
383 #ifdef LDAP_SCOPE_SUBORDINATE
384                         case LDAP_SCOPE_SUBORDINATE:
385                                 fc->fscope = dnIsSuffix( fc->fdn, &rs->sr_entry->e_nname ) &&
386                                         !dn_match( fc->fdn, &rs->sr_entry->e_nname );
387                                 break;
388 #endif
389                         }
390                 }
391         }
392         if ( rs->sr_err != LDAP_SUCCESS ) {
393                 Debug( LDAP_DEBUG_ANY, "findbase failed! %d\n", rs->sr_err,0,0 );
394         }
395         return LDAP_SUCCESS;
396 }
397
398 static int
399 syncprov_findbase( Operation *op, fbase_cookie *fc )
400 {
401         opcookie *opc = op->o_callback->sc_private;
402         slap_overinst *on = opc->son;
403
404         slap_callback cb = {0};
405         Operation fop;
406         SlapReply frs = { REP_RESULT };
407         int rc;
408
409         fop = *op;
410
411         cb.sc_response = findbase_cb;
412         cb.sc_private = fc;
413
414         fop.o_sync_mode &= SLAP_CONTROL_MASK;   /* turn off sync mode */
415         fop.o_managedsait = SLAP_CONTROL_CRITICAL;
416         fop.o_callback = &cb;
417         fop.o_tag = LDAP_REQ_SEARCH;
418         fop.ors_scope = LDAP_SCOPE_BASE;
419         fop.ors_deref = fc->fss->s_op->ors_deref;
420         fop.ors_limit = NULL;
421         fop.ors_slimit = 1;
422         fop.ors_tlimit = SLAP_NO_LIMIT;
423         fop.ors_attrs = slap_anlist_no_attrs;
424         fop.ors_attrsonly = 1;
425         fop.ors_filter = fc->fss->s_op->ors_filter;
426         fop.ors_filterstr = fc->fss->s_op->ors_filterstr;
427
428         fop.o_req_ndn = fc->fss->s_op->o_req_ndn;
429
430         fop.o_bd->bd_info = on->on_info->oi_orig;
431         rc = fop.o_bd->be_search( &fop, &frs );
432         fop.o_bd->bd_info = (BackendInfo *)on;
433
434         if ( fc->fbase ) return LDAP_SUCCESS;
435
436         /* If entryID has changed, then the base of this search has
437          * changed. Invalidate the psearch.
438          */
439         return LDAP_NO_SUCH_OBJECT;
440 }
441
442 /* syncprov_findcsn:
443  *   This function has three different purposes, but they all use a search
444  * that filters on entryCSN so they're combined here.
445  * 1: at startup time, after a contextCSN has been read from the database,
446  * we search for all entries with CSN >= contextCSN in case the contextCSN
447  * was not checkpointed at the previous shutdown.
448  *
449  * 2: when the current contextCSN is known and we have a sync cookie, we search
450  * for one entry with CSN = the cookie CSN. If not found, try <= cookie CSN.
451  * If an entry is found, the cookie CSN is valid, otherwise it is stale.
452  *
453  * 3: during a refresh phase, we search for all entries with CSN <= the cookie
454  * CSN, and generate Present records for them. We always collect this result
455  * in SyncID sets, even if there's only one match.
456  */
457 #define FIND_MAXCSN     1
458 #define FIND_CSN        2
459 #define FIND_PRESENT    3
460
461 static int
462 findmax_cb( Operation *op, SlapReply *rs )
463 {
464         if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
465                 struct berval *maxcsn = op->o_callback->sc_private;
466                 Attribute *a = attr_find( rs->sr_entry->e_attrs,
467                         slap_schema.si_ad_entryCSN );
468
469                 if ( a && ber_bvcmp( &a->a_vals[0], maxcsn ) > 0 ) {
470                         maxcsn->bv_len = a->a_vals[0].bv_len;
471                         strcpy( maxcsn->bv_val, a->a_vals[0].bv_val );
472                 }
473         }
474         return LDAP_SUCCESS;
475 }
476
477 static int
478 findcsn_cb( Operation *op, SlapReply *rs )
479 {
480         slap_callback *sc = op->o_callback;
481
482         if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
483                 sc->sc_private = (void *)1;
484         }
485         return LDAP_SUCCESS;
486 }
487
488 /* Build a list of entryUUIDs for sending in a SyncID set */
489
490 #define UUID_LEN        16
491
492 typedef struct fpres_cookie {
493         int num;
494         BerVarray uuids;
495         char *last;
496 } fpres_cookie;
497
498 static int
499 findpres_cb( Operation *op, SlapReply *rs )
500 {
501         slap_callback *sc = op->o_callback;
502         fpres_cookie *pc = sc->sc_private;
503         Attribute *a;
504         int ret = SLAP_CB_CONTINUE;
505
506         switch ( rs->sr_type ) {
507         case REP_SEARCH:
508                 a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
509                 if ( a ) {
510                         pc->uuids[pc->num].bv_val = pc->last;
511                         AC_MEMCPY( pc->uuids[pc->num].bv_val, a->a_nvals[0].bv_val,
512                                 pc->uuids[pc->num].bv_len );
513                         pc->num++;
514                         pc->last = pc->uuids[pc->num].bv_val;
515                         pc->uuids[pc->num].bv_val = NULL;
516                 }
517                 ret = LDAP_SUCCESS;
518                 if ( pc->num != SLAP_SYNCUUID_SET_SIZE )
519                         break;
520                 /* FALLTHRU */
521         case REP_RESULT:
522                 ret = rs->sr_err;
523                 if ( pc->num ) {
524                         ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
525                                 0, pc->uuids, 0 );
526                         pc->uuids[pc->num].bv_val = pc->last;
527                         pc->num = 0;
528                         pc->last = pc->uuids[0].bv_val;
529                 }
530                 break;
531         default:
532                 break;
533         }
534         return ret;
535 }
536
537 static int
538 syncprov_findcsn( Operation *op, int mode )
539 {
540         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
541         syncprov_info_t         *si = on->on_bi.bi_private;
542
543         slap_callback cb = {0};
544         Operation fop;
545         SlapReply frs = { REP_RESULT };
546         char buf[LDAP_LUTIL_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
547         char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
548         struct berval maxcsn;
549         Filter cf, af;
550 #ifdef LDAP_COMP_MATCH
551         AttributeAssertion eq = { NULL, BER_BVNULL, NULL };
552 #else
553         AttributeAssertion eq = { NULL, BER_BVNULL };
554 #endif
555         int i, rc = LDAP_SUCCESS;
556         fpres_cookie pcookie;
557         sync_control *srs = NULL;
558         int findcsn_retry = 1;
559
560         if ( mode != FIND_MAXCSN ) {
561                 srs = op->o_controls[slap_cids.sc_LDAPsync];
562
563                 if ( srs->sr_state.ctxcsn.bv_len >= LDAP_LUTIL_CSNSTR_BUFSIZE ) {
564                         return LDAP_OTHER;
565                 }
566         }
567
568         fop = *op;
569         fop.o_sync_mode &= SLAP_CONTROL_MASK;   /* turn off sync_mode */
570         /* We want pure entries, not referrals */
571         fop.o_managedsait = SLAP_CONTROL_CRITICAL;
572
573         cf.f_ava = &eq;
574         cf.f_av_desc = slap_schema.si_ad_entryCSN;
575         cf.f_next = NULL;
576
577         fop.o_callback = &cb;
578         fop.ors_limit = NULL;
579         fop.ors_tlimit = SLAP_NO_LIMIT;
580         fop.ors_filter = &cf;
581         fop.ors_filterstr.bv_val = buf;
582
583 again:
584         switch( mode ) {
585         case FIND_MAXCSN:
586                 cf.f_choice = LDAP_FILTER_GE;
587                 cf.f_av_value = si->si_ctxcsn;
588                 fop.ors_filterstr.bv_len = sprintf( buf, "(entryCSN>=%s)",
589                         cf.f_av_value.bv_val );
590                 fop.ors_attrsonly = 0;
591                 fop.ors_attrs = csn_anlist;
592                 fop.ors_slimit = SLAP_NO_LIMIT;
593                 cb.sc_private = &maxcsn;
594                 cb.sc_response = findmax_cb;
595                 strcpy( cbuf, si->si_ctxcsn.bv_val );
596                 maxcsn.bv_val = cbuf;
597                 maxcsn.bv_len = si->si_ctxcsn.bv_len;
598                 break;
599         case FIND_CSN:
600                 cf.f_av_value = srs->sr_state.ctxcsn;
601                 /* Look for exact match the first time */
602                 if ( findcsn_retry ) {
603                         cf.f_choice = LDAP_FILTER_EQUALITY;
604                         fop.ors_filterstr.bv_len = sprintf( buf, "(entryCSN=%s)",
605                                 cf.f_av_value.bv_val );
606                 /* On retry, look for <= */
607                 } else {
608                         cf.f_choice = LDAP_FILTER_LE;
609                         fop.ors_filterstr.bv_len = sprintf( buf, "(entryCSN<=%s)",
610                                 cf.f_av_value.bv_val );
611                 }
612                 fop.ors_attrsonly = 1;
613                 fop.ors_attrs = slap_anlist_no_attrs;
614                 fop.ors_slimit = 1;
615                 cb.sc_private = NULL;
616                 cb.sc_response = findcsn_cb;
617                 break;
618         case FIND_PRESENT:
619                 af.f_choice = LDAP_FILTER_AND;
620                 af.f_next = NULL;
621                 af.f_and = &cf;
622                 cf.f_choice = LDAP_FILTER_LE;
623                 cf.f_av_value = srs->sr_state.ctxcsn;
624                 cf.f_next = op->ors_filter;
625                 fop.ors_filter = &af;
626                 filter2bv_x( &fop, fop.ors_filter, &fop.ors_filterstr );
627                 fop.ors_attrsonly = 0;
628                 fop.ors_attrs = uuid_anlist;
629                 fop.ors_slimit = SLAP_NO_LIMIT;
630                 cb.sc_private = &pcookie;
631                 cb.sc_response = findpres_cb;
632                 pcookie.num = 0;
633
634                 /* preallocate storage for a full set */
635                 pcookie.uuids = op->o_tmpalloc( (SLAP_SYNCUUID_SET_SIZE+1) *
636                         sizeof(struct berval) + SLAP_SYNCUUID_SET_SIZE * UUID_LEN,
637                         op->o_tmpmemctx );
638                 pcookie.last = (char *)(pcookie.uuids + SLAP_SYNCUUID_SET_SIZE+1);
639                 pcookie.uuids[0].bv_val = pcookie.last;
640                 pcookie.uuids[0].bv_len = UUID_LEN;
641                 for (i=1; i<SLAP_SYNCUUID_SET_SIZE; i++) {
642                         pcookie.uuids[i].bv_val = pcookie.uuids[i-1].bv_val + UUID_LEN;
643                         pcookie.uuids[i].bv_len = UUID_LEN;
644                 }
645                 break;
646         }
647
648         fop.o_bd->bd_info = on->on_info->oi_orig;
649         fop.o_bd->be_search( &fop, &frs );
650         fop.o_bd->bd_info = (BackendInfo *)on;
651
652         switch( mode ) {
653         case FIND_MAXCSN:
654                 strcpy( si->si_ctxcsnbuf, maxcsn.bv_val );
655                 si->si_ctxcsn.bv_len = maxcsn.bv_len;
656                 break;
657         case FIND_CSN:
658                 /* If matching CSN was not found, invalidate the context. */
659                 if ( !cb.sc_private ) {
660                         /* If we didn't find an exact match, then try for <= */
661                         if ( findcsn_retry ) {
662                                 findcsn_retry = 0;
663                                 goto again;
664                         }
665                         rc = LDAP_NO_SUCH_OBJECT;
666                 }
667                 break;
668         case FIND_PRESENT:
669                 op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
670                 op->o_tmpfree( fop.ors_filterstr.bv_val, op->o_tmpmemctx );
671                 break;
672         }
673
674         return rc;
675 }
676
677 /* Queue a persistent search response */
678 static int
679 syncprov_qresp( opcookie *opc, syncops *so, int mode )
680 {
681         syncres *sr;
682
683         sr = ch_malloc(sizeof(syncres) + opc->suuid.bv_len + 1 +
684                 opc->sdn.bv_len + 1 + opc->sndn.bv_len + 1 + opc->sctxcsn.bv_len + 1 );
685         sr->s_next = NULL;
686         sr->s_dn.bv_val = (char *)(sr + 1);
687         sr->s_dn.bv_len = opc->sdn.bv_len;
688         sr->s_mode = mode;
689         sr->s_isreference = opc->sreference;
690         sr->s_ndn.bv_val = lutil_strcopy( sr->s_dn.bv_val, opc->sdn.bv_val );
691         sr->s_ndn.bv_len = opc->sndn.bv_len;
692         *(sr->s_ndn.bv_val++) = '\0';
693         sr->s_uuid.bv_val = lutil_strcopy( sr->s_ndn.bv_val, opc->sndn.bv_val );
694         sr->s_uuid.bv_len = opc->suuid.bv_len;
695         *(sr->s_uuid.bv_val++) = '\0';
696         sr->s_csn.bv_val = lutil_strcopy( sr->s_uuid.bv_val, opc->suuid.bv_val );
697         sr->s_csn.bv_len = opc->sctxcsn.bv_len;
698         strcpy( sr->s_csn.bv_val, opc->sctxcsn.bv_val );
699
700         if ( !so->s_res ) {
701                 so->s_res = sr;
702         } else {
703                 so->s_restail->s_next = sr;
704         }
705         so->s_restail = sr;
706         ldap_pvt_thread_mutex_unlock( &so->s_mutex );
707         return LDAP_SUCCESS;
708 }
709
710 /* Play back queued responses */
711 static int
712 syncprov_sendresp( Operation *op, opcookie *opc, syncops *so, Entry **e, int mode, int queue );
713
714 static int
715 syncprov_qplay( Operation *op, slap_overinst *on, syncops *so )
716 {
717         syncres *sr, *srnext;
718         Entry *e;
719         opcookie opc;
720         int rc;
721
722         opc.son = on;
723         op->o_bd->bd_info = (BackendInfo *)on->on_info;
724         for (sr = so->s_res; sr; sr=srnext) {
725                 srnext = sr->s_next;
726                 opc.sdn = sr->s_dn;
727                 opc.sndn = sr->s_ndn;
728                 opc.suuid = sr->s_uuid;
729                 opc.sctxcsn = sr->s_csn;
730                 opc.sreference = sr->s_isreference;
731                 e = NULL;
732
733                 if ( sr->s_mode != LDAP_SYNC_DELETE ) {
734                         rc = be_entry_get_rw( op, &opc.sndn, NULL, NULL, 0, &e );
735                         if ( rc ) {
736                                 ch_free( sr );
737                                 so->s_res = srnext;
738                                 continue;
739                         }
740                 }
741                 rc = syncprov_sendresp( op, &opc, so, &e, sr->s_mode, 0 );
742
743                 if ( e ) {
744                         be_entry_release_rw( op, e, 0 );
745                 }
746                 if ( rc )
747                         break;
748
749                 ch_free( sr );
750                 so->s_res = srnext;
751         }
752         op->o_bd->bd_info = (BackendInfo *)on;
753         if ( !so->s_res )
754                 so->s_restail = NULL;
755         return rc;
756 }
757
758 /* Send a persistent search response */
759 static int
760 syncprov_sendresp( Operation *op, opcookie *opc, syncops *so, Entry **e, int mode, int queue )
761 {
762         slap_overinst *on = opc->son;
763
764         SlapReply rs = { REP_SEARCH };
765         LDAPControl *ctrls[2];
766         struct berval cookie;
767         Entry e_uuid = {0};
768         Attribute a_uuid = {0};
769         Operation sop = *so->s_op;
770         Opheader ohdr;
771
772         if ( so->s_op->o_abandon )
773                 return SLAPD_ABANDON;
774
775         ohdr = *sop.o_hdr;
776         sop.o_hdr = &ohdr;
777         sop.o_tmpmemctx = op->o_tmpmemctx;
778         sop.o_bd = op->o_bd;
779         sop.o_controls = op->o_controls;
780         sop.o_private = op->o_private;
781
782         /* If queueing is allowed */
783         if ( queue ) {
784                 ldap_pvt_thread_mutex_lock( &so->s_mutex );
785                 /* If we're still in refresh mode, must queue */
786                 if (so->s_flags & PS_IS_REFRESHING) {
787                         return syncprov_qresp( opc, so, mode );
788                 }
789                 /* If connection is free but queue is non-empty,
790                  * try to flush the queue.
791                  */
792                 if ( so->s_res ) {
793                         rs.sr_err = syncprov_qplay( &sop, on, so );
794                 }
795                 /* If the connection is busy, must queue */
796                 if ( sop.o_conn->c_writewaiter || rs.sr_err == LDAP_BUSY ) {
797                         return syncprov_qresp( opc, so, mode );
798                 }
799                 ldap_pvt_thread_mutex_unlock( &so->s_mutex );
800
801                 /* If syncprov_qplay returned any other error, bail out. */
802                 if ( rs.sr_err ) {
803                         return rs.sr_err;
804                 }
805         } else {
806                 /* Queueing not allowed and conn is busy, give up */
807                 if ( sop.o_conn->c_writewaiter )
808                         return LDAP_BUSY;
809         }
810
811         ctrls[1] = NULL;
812         slap_compose_sync_cookie( op, &cookie, &opc->sctxcsn, so->s_rid );
813
814         e_uuid.e_attrs = &a_uuid;
815         a_uuid.a_desc = slap_schema.si_ad_entryUUID;
816         a_uuid.a_nvals = &opc->suuid;
817         rs.sr_err = syncprov_state_ctrl( &sop, &rs, &e_uuid,
818                 mode, ctrls, 0, 1, &cookie );
819
820         rs.sr_ctrls = ctrls;
821         op->o_bd->bd_info = (BackendInfo *)on->on_info;
822         switch( mode ) {
823         case LDAP_SYNC_ADD:
824                 rs.sr_entry = *e;
825                 if ( rs.sr_entry->e_private )
826                         rs.sr_flags = REP_ENTRY_MUSTRELEASE;
827                 if ( opc->sreference ) {
828                         rs.sr_ref = get_entry_referrals( &sop, rs.sr_entry );
829                         send_search_reference( &sop, &rs );
830                         ber_bvarray_free( rs.sr_ref );
831                         if ( !rs.sr_entry )
832                                 *e = NULL;
833                         break;
834                 }
835                 /* fallthru */
836         case LDAP_SYNC_MODIFY:
837                 rs.sr_entry = *e;
838                 if ( rs.sr_entry->e_private )
839                         rs.sr_flags = REP_ENTRY_MUSTRELEASE;
840                 rs.sr_attrs = sop.ors_attrs;
841                 send_search_entry( &sop, &rs );
842                 if ( !rs.sr_entry )
843                         *e = NULL;
844                 break;
845         case LDAP_SYNC_DELETE:
846                 e_uuid.e_attrs = NULL;
847                 e_uuid.e_name = opc->sdn;
848                 e_uuid.e_nname = opc->sndn;
849                 rs.sr_entry = &e_uuid;
850                 if ( opc->sreference ) {
851                         struct berval bv = BER_BVNULL;
852                         rs.sr_ref = &bv;
853                         send_search_reference( &sop, &rs );
854                 } else {
855                         send_search_entry( &sop, &rs );
856                 }
857                 break;
858         default:
859                 assert(0);
860         }
861         op->o_tmpfree( rs.sr_ctrls[0], op->o_tmpmemctx );
862         op->o_private = sop.o_private;
863         rs.sr_ctrls = NULL;
864         /* Check queue again here; if we were hanging in a send and eventually
865          * recovered, there may be more to send now. But don't check if the
866          * original psearch has been abandoned.
867          */
868         if ( so->s_op->o_abandon )
869                 return SLAPD_ABANDON;
870
871         if ( rs.sr_err == LDAP_SUCCESS && queue && so->s_res ) {
872                 ldap_pvt_thread_mutex_lock( &so->s_mutex );
873                 rs.sr_err = syncprov_qplay( &sop, on, so );
874                 ldap_pvt_thread_mutex_unlock( &so->s_mutex );
875         }
876         return rs.sr_err;
877 }
878
879 static void
880 syncprov_free_syncop( syncops *so )
881 {
882         syncres *sr, *srnext;
883         GroupAssertion *ga, *gnext;
884
885         ldap_pvt_thread_mutex_lock( &so->s_mutex );
886         so->s_inuse--;
887         if ( so->s_inuse > 0 ) {
888                 ldap_pvt_thread_mutex_unlock( &so->s_mutex );
889                 return;
890         }
891         ldap_pvt_thread_mutex_unlock( &so->s_mutex );
892         if ( so->s_flags & PS_IS_DETACHED ) {
893                 filter_free( so->s_op->ors_filter );
894                 for ( ga = so->s_op->o_groups; ga; ga=gnext ) {
895                         gnext = ga->ga_next;
896                         ch_free( ga );
897                 }
898                 ch_free( so->s_op );
899         }
900         ch_free( so->s_base.bv_val );
901         for ( sr=so->s_res; sr; sr=srnext ) {
902                 srnext = sr->s_next;
903                 ch_free( sr );
904         }
905         ldap_pvt_thread_mutex_destroy( &so->s_mutex );
906         ch_free( so );
907 }
908
909 static int
910 syncprov_drop_psearch( syncops *so, int lock )
911 {
912         if ( so->s_flags & PS_IS_DETACHED ) {
913                 if ( lock )
914                         ldap_pvt_thread_mutex_lock( &so->s_op->o_conn->c_mutex );
915                 so->s_op->o_conn->c_n_ops_executing--;
916                 so->s_op->o_conn->c_n_ops_completed++;
917                 LDAP_STAILQ_REMOVE( &so->s_op->o_conn->c_ops, so->s_op, slap_op,
918                         o_next );
919                 if ( lock )
920                         ldap_pvt_thread_mutex_unlock( &so->s_op->o_conn->c_mutex );
921         }
922         syncprov_free_syncop( so );
923
924         return 0;
925 }
926
927 static int
928 syncprov_ab_cleanup( Operation *op, SlapReply *rs )
929 {
930         slap_callback *sc = op->o_callback;
931         op->o_callback = sc->sc_next;
932         syncprov_drop_psearch( op->o_callback->sc_private, 0 );
933         op->o_tmpfree( sc, op->o_tmpmemctx );
934         return 0;
935 }
936
937 static int
938 syncprov_op_abandon( Operation *op, SlapReply *rs )
939 {
940         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
941         syncprov_info_t         *si = on->on_bi.bi_private;
942         syncops *so, *soprev;
943
944         ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
945         for ( so=si->si_ops, soprev = (syncops *)&si->si_ops; so;
946                 soprev=so, so=so->s_next ) {
947                 if ( so->s_op->o_connid == op->o_connid &&
948                         so->s_op->o_msgid == op->orn_msgid ) {
949                                 so->s_op->o_abandon = 1;
950                                 soprev->s_next = so->s_next;
951                                 break;
952                 }
953         }
954         ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
955         if ( so ) {
956                 /* Is this really a Cancel exop? */
957                 if ( op->o_tag != LDAP_REQ_ABANDON ) {
958                         so->s_op->o_cancel = SLAP_CANCEL_ACK;
959                         rs->sr_err = LDAP_CANCELLED;
960                         send_ldap_result( so->s_op, rs );
961                         if ( so->s_flags & PS_IS_DETACHED ) {
962                                 slap_callback *cb;
963                                 cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
964                                 cb->sc_cleanup = syncprov_ab_cleanup;
965                                 cb->sc_next = op->o_callback;
966                                 cb->sc_private = so;
967                                 return SLAP_CB_CONTINUE;
968                         }
969                 }
970                 syncprov_drop_psearch( so, 0 );
971         }
972         return SLAP_CB_CONTINUE;
973 }
974
975 /* Find which persistent searches are affected by this operation */
976 static void
977 syncprov_matchops( Operation *op, opcookie *opc, int saveit )
978 {
979         slap_overinst *on = opc->son;
980         syncprov_info_t         *si = on->on_bi.bi_private;
981
982         fbase_cookie fc;
983         syncops *ss, *sprev, *snext;
984         Entry *e;
985         Attribute *a;
986         int rc;
987         struct berval newdn;
988         int freefdn = 0;
989
990         fc.fdn = &op->o_req_ndn;
991         /* compute new DN */
992         if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
993                 struct berval pdn;
994                 if ( op->orr_nnewSup ) pdn = *op->orr_nnewSup;
995                 else dnParent( fc.fdn, &pdn );
996                 build_new_dn( &newdn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
997                 fc.fdn = &newdn;
998                 freefdn = 1;
999         }
1000         if ( op->o_tag != LDAP_REQ_ADD ) {
1001                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1002                 rc = be_entry_get_rw( op, fc.fdn, NULL, NULL, 0, &e );
1003                 /* If we're sending responses now, make a copy and unlock the DB */
1004                 if ( e && !saveit ) {
1005                         Entry *e2 = entry_dup( e );
1006                         be_entry_release_rw( op, e, 0 );
1007                         e = e2;
1008                 }
1009                 op->o_bd->bd_info = (BackendInfo *)on;
1010                 if ( rc ) return;
1011         } else {
1012                 e = op->ora_e;
1013         }
1014
1015         if ( saveit ) {
1016                 ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
1017                 ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
1018                 opc->sreference = is_entry_referral( e );
1019         }
1020         if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
1021                 a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
1022                 if ( a )
1023                         ber_dupbv_x( &opc->suuid, &a->a_nvals[0], op->o_tmpmemctx );
1024         }
1025
1026         ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1027         for (ss = si->si_ops, sprev = (syncops *)&si->si_ops; ss;
1028                 sprev = ss, ss=snext)
1029         {
1030                 syncmatches *sm;
1031                 int found = 0;
1032
1033                 snext = ss->s_next;
1034                 /* validate base */
1035                 fc.fss = ss;
1036                 fc.fbase = 0;
1037                 fc.fscope = 0;
1038
1039                 /* If the base of the search is missing, signal a refresh */
1040                 rc = syncprov_findbase( op, &fc );
1041                 if ( rc != LDAP_SUCCESS ) {
1042                         SlapReply rs = {REP_RESULT};
1043                         send_ldap_error( ss->s_op, &rs, LDAP_SYNC_REFRESH_REQUIRED,
1044                                 "search base has changed" );
1045                         sprev->s_next = snext;
1046                         syncprov_drop_psearch( ss, 1 );
1047                         continue;
1048                 }
1049
1050                 /* If we're sending results now, look for this op in old matches */
1051                 if ( !saveit ) {
1052                         syncmatches *old;
1053                         for ( sm=opc->smatches, old=(syncmatches *)&opc->smatches; sm;
1054                                 old=sm, sm=sm->sm_next ) {
1055                                 if ( sm->sm_op == ss ) {
1056                                         found = 1;
1057                                         old->sm_next = sm->sm_next;
1058                                         op->o_tmpfree( sm, op->o_tmpmemctx );
1059                                         break;
1060                                 }
1061                         }
1062                 }
1063
1064                 /* check if current o_req_dn is in scope and matches filter */
1065                 if ( fc.fscope && test_filter( op, e, ss->s_op->ors_filter ) ==
1066                         LDAP_COMPARE_TRUE ) {
1067                         if ( saveit ) {
1068                                 sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx );
1069                                 sm->sm_next = opc->smatches;
1070                                 sm->sm_op = ss;
1071                                 ss->s_inuse++;
1072                                 opc->smatches = sm;
1073                         } else {
1074                                 /* if found send UPDATE else send ADD */
1075                                 ss->s_inuse++;
1076                                 ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1077                                 syncprov_sendresp( op, opc, ss, &e,
1078                                         found ? LDAP_SYNC_MODIFY : LDAP_SYNC_ADD, 1 );
1079                                 ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1080                                 ss->s_inuse--;
1081                         }
1082                 } else if ( !saveit && found ) {
1083                         /* send DELETE */
1084                         ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1085                         syncprov_sendresp( op, opc, ss, NULL, LDAP_SYNC_DELETE, 1 );
1086                         ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1087                 }
1088         }
1089         ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1090
1091         if ( op->o_tag != LDAP_REQ_ADD && e ) {
1092                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1093                 be_entry_release_rw( op, e, 0 );
1094                 op->o_bd->bd_info = (BackendInfo *)on;
1095         }
1096         if ( freefdn ) {
1097                 op->o_tmpfree( fc.fdn->bv_val, op->o_tmpmemctx );
1098         }
1099 }
1100
1101 static int
1102 syncprov_op_cleanup( Operation *op, SlapReply *rs )
1103 {
1104         slap_callback *cb = op->o_callback;
1105         opcookie *opc = cb->sc_private;
1106         slap_overinst *on = opc->son;
1107         syncprov_info_t         *si = on->on_bi.bi_private;
1108         syncmatches *sm, *snext;
1109         modtarget *mt, mtdummy;
1110
1111         for (sm = opc->smatches; sm; sm=snext) {
1112                 snext = sm->sm_next;
1113                 syncprov_free_syncop( sm->sm_op );
1114                 op->o_tmpfree( sm, op->o_tmpmemctx );
1115         }
1116
1117         /* Remove op from lock table */
1118         mtdummy.mt_op = op;
1119         ldap_pvt_thread_mutex_lock( &si->si_mods_mutex );
1120         mt = avl_find( si->si_mods, &mtdummy, sp_avl_cmp );
1121         if ( mt ) {
1122                 modinst *mi = mt->mt_mods;
1123
1124                 /* If there are more, promote the next one */
1125                 ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
1126                 if ( mi->mi_next ) {
1127                         mt->mt_mods = mi->mi_next;
1128                         mt->mt_op = mt->mt_mods->mi_op;
1129                         ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
1130                 } else {
1131                         avl_delete( &si->si_mods, mt, sp_avl_cmp );
1132                         ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
1133                         ldap_pvt_thread_mutex_destroy( &mt->mt_mutex );
1134                         ch_free( mt );
1135                 }
1136         }
1137         ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
1138         if ( !BER_BVISNULL( &opc->suuid ))
1139                 op->o_tmpfree( opc->suuid.bv_val, op->o_tmpmemctx );
1140         if ( !BER_BVISNULL( &opc->sndn ))
1141                 op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
1142         if ( !BER_BVISNULL( &opc->sdn ))
1143                 op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
1144         op->o_callback = cb->sc_next;
1145         op->o_tmpfree(cb, op->o_tmpmemctx);
1146
1147         return 0;
1148 }
1149
1150 static void
1151 syncprov_checkpoint( Operation *op, SlapReply *rs, slap_overinst *on )
1152 {
1153         syncprov_info_t         *si = on->on_bi.bi_private;
1154         Modifications mod;
1155         Operation opm;
1156         struct berval bv[2];
1157         slap_callback cb = {0};
1158         int manage = get_manageDSAit(op);
1159
1160         mod.sml_values = bv;
1161         bv[1].bv_val = NULL;
1162         bv[0] = si->si_ctxcsn;
1163         mod.sml_nvalues = NULL;
1164         mod.sml_desc = slap_schema.si_ad_contextCSN;
1165         mod.sml_op = LDAP_MOD_REPLACE;
1166         mod.sml_flags = 0;
1167         mod.sml_next = NULL;
1168
1169         cb.sc_response = slap_null_cb;
1170         opm = *op;
1171         opm.o_tag = LDAP_REQ_MODIFY;
1172         opm.o_callback = &cb;
1173         opm.orm_modlist = &mod;
1174         opm.o_req_dn = op->o_bd->be_suffix[0];
1175         opm.o_req_ndn = op->o_bd->be_nsuffix[0];
1176         opm.o_bd->bd_info = on->on_info->oi_orig;
1177         opm.o_managedsait = SLAP_CONTROL_NONCRITICAL;
1178         opm.o_bd->be_modify( &opm, rs );
1179         opm.o_managedsait = manage;
1180 }
1181
1182 static void
1183 syncprov_add_slog( Operation *op, struct berval *csn )
1184 {
1185         opcookie *opc = op->o_callback->sc_private;
1186         slap_overinst *on = opc->son;
1187         syncprov_info_t         *si = on->on_bi.bi_private;
1188         sessionlog *sl;
1189         slog_entry *se;
1190
1191         sl = si->si_logs;
1192         {
1193                 /* Allocate a record. UUIDs are not NUL-terminated. */
1194                 se = ch_malloc( sizeof( slog_entry ) + opc->suuid.bv_len + 
1195                         csn->bv_len + 1 );
1196                 se->se_next = NULL;
1197                 se->se_tag = op->o_tag;
1198
1199                 se->se_uuid.bv_val = (char *)(se+1);
1200                 AC_MEMCPY( se->se_uuid.bv_val, opc->suuid.bv_val, opc->suuid.bv_len );
1201                 se->se_uuid.bv_len = opc->suuid.bv_len;
1202
1203                 se->se_csn.bv_val = se->se_uuid.bv_val + opc->suuid.bv_len;
1204                 AC_MEMCPY( se->se_csn.bv_val, csn->bv_val, csn->bv_len );
1205                 se->se_csn.bv_val[csn->bv_len] = '\0';
1206                 se->se_csn.bv_len = csn->bv_len;
1207
1208                 ldap_pvt_thread_mutex_lock( &sl->sl_mutex );
1209                 if ( sl->sl_head ) {
1210                         sl->sl_tail->se_next = se;
1211                 } else {
1212                         sl->sl_head = se;
1213                 }
1214                 sl->sl_tail = se;
1215                 sl->sl_num++;
1216                 while ( sl->sl_num > sl->sl_size ) {
1217                         se = sl->sl_head;
1218                         sl->sl_head = se->se_next;
1219                         strcpy( sl->sl_mincsn.bv_val, se->se_csn.bv_val );
1220                         sl->sl_mincsn.bv_len = se->se_csn.bv_len;
1221                         ch_free( se );
1222                         sl->sl_num--;
1223                         if ( !sl->sl_head ) {
1224                                 sl->sl_tail = NULL;
1225                         }
1226                 }
1227                 ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
1228         }
1229 }
1230
1231 /* Just set a flag if we found the matching entry */
1232 static int
1233 playlog_cb( Operation *op, SlapReply *rs )
1234 {
1235         if ( rs->sr_type == REP_SEARCH ) {
1236                 op->o_callback->sc_private = (void *)1;
1237         }
1238         return rs->sr_err;
1239 }
1240
1241 /* enter with sl->sl_mutex locked, release before returning */
1242 static void
1243 syncprov_playlog( Operation *op, SlapReply *rs, sessionlog *sl,
1244         struct berval *oldcsn, struct berval *ctxcsn )
1245 {
1246         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1247         slog_entry *se;
1248         int i, j, ndel, num, nmods, mmods;
1249         BerVarray uuids;
1250
1251         if ( !sl->sl_num ) {
1252                 ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
1253                 return;
1254         }
1255
1256         num = sl->sl_num;
1257         i = 0;
1258         nmods = 0;
1259
1260         uuids = op->o_tmpalloc( (num+1) * sizeof( struct berval ) +
1261                 num * UUID_LEN, op->o_tmpmemctx );
1262
1263         uuids[0].bv_val = (char *)(uuids + num + 1);
1264
1265         /* Make a copy of the relevant UUIDs. Put the Deletes up front
1266          * and everything else at the end. Do this first so we can
1267          * unlock the list mutex.
1268          */
1269         for ( se=sl->sl_head; se; se=se->se_next ) {
1270                 if ( ber_bvcmp( &se->se_csn, oldcsn ) < 0 ) continue;
1271                 if ( ber_bvcmp( &se->se_csn, ctxcsn ) > 0 ) break;
1272                 if ( se->se_tag == LDAP_REQ_DELETE ) {
1273                         j = i;
1274                         i++;
1275                 } else {
1276                         nmods++;
1277                         j = num - nmods;
1278                 }
1279                 uuids[j].bv_val = uuids[0].bv_val + (j * UUID_LEN);
1280                 AC_MEMCPY(uuids[j].bv_val, se->se_uuid.bv_val, UUID_LEN);
1281                 uuids[j].bv_len = UUID_LEN;
1282         }
1283         ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
1284
1285         ndel = i;
1286
1287         /* Zero out unused slots */
1288         for ( i=ndel; i < num - nmods; i++ )
1289                 uuids[i].bv_len = 0;
1290
1291         /* Mods must be validated to see if they belong in this delete set.
1292          */
1293
1294         mmods = nmods;
1295         /* Strip any duplicates */
1296         for ( i=0; i<nmods; i++ ) {
1297                 for ( j=0; j<ndel; j++ ) {
1298                         if ( bvmatch( &uuids[j], &uuids[num - 1 - i] )) {
1299                                 uuids[num - 1 - i].bv_len = 0;
1300                                 mmods --;
1301                                 break;
1302                         }
1303                 }
1304                 if ( uuids[num - 1 - i].bv_len == 0 ) continue;
1305                 for ( j=0; j<i; j++ ) {
1306                         if ( bvmatch( &uuids[num - 1 - j], &uuids[num - 1 - i] )) {
1307                                 uuids[num - 1 - i].bv_len = 0;
1308                                 mmods --;
1309                                 break;
1310                         }
1311                 }
1312         }
1313
1314         if ( mmods ) {
1315                 Operation fop;
1316                 SlapReply frs = { REP_RESULT };
1317                 int rc;
1318                 Filter mf, af;
1319 #ifdef LDAP_COMP_MATCH
1320                 AttributeAssertion eq = { NULL, BER_BVNULL, NULL };
1321 #else
1322                 AttributeAssertion eq;
1323 #endif
1324                 slap_callback cb = {0};
1325
1326                 fop = *op;
1327
1328                 fop.o_sync_mode = 0;
1329                 fop.o_callback = &cb;
1330                 fop.ors_limit = NULL;
1331                 fop.ors_tlimit = SLAP_NO_LIMIT;
1332                 fop.ors_attrs = slap_anlist_all_attributes;
1333                 fop.ors_attrsonly = 0;
1334                 fop.o_managedsait = SLAP_CONTROL_CRITICAL;
1335
1336                 af.f_choice = LDAP_FILTER_AND;
1337                 af.f_next = NULL;
1338                 af.f_and = &mf;
1339                 mf.f_choice = LDAP_FILTER_EQUALITY;
1340                 mf.f_ava = &eq;
1341                 mf.f_av_desc = slap_schema.si_ad_entryUUID;
1342                 mf.f_next = fop.ors_filter;
1343
1344                 fop.ors_filter = &af;
1345
1346                 cb.sc_response = playlog_cb;
1347                 fop.o_bd->bd_info = on->on_info->oi_orig;
1348
1349                 for ( i=ndel; i<num; i++ ) {
1350                         if ( uuids[i].bv_len == 0 ) continue;
1351
1352                         mf.f_av_value = uuids[i];
1353                         cb.sc_private = NULL;
1354                         fop.ors_slimit = 1;
1355                         rc = fop.o_bd->be_search( &fop, &frs );
1356
1357                         /* If entry was not found, add to delete list */
1358                         if ( !cb.sc_private ) {
1359                                 uuids[ndel++] = uuids[i];
1360                         }
1361                 }
1362                 fop.o_bd->bd_info = (BackendInfo *)on;
1363         }
1364         if ( ndel ) {
1365                 uuids[ndel].bv_val = NULL;
1366                 syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL, 0, uuids, 1 );
1367         }
1368 }
1369
1370 static int
1371 syncprov_op_response( Operation *op, SlapReply *rs )
1372 {
1373         opcookie *opc = op->o_callback->sc_private;
1374         slap_overinst *on = opc->son;
1375         syncprov_info_t         *si = on->on_bi.bi_private;
1376         syncmatches *sm;
1377
1378         if ( rs->sr_err == LDAP_SUCCESS )
1379         {
1380                 struct berval maxcsn = BER_BVNULL, curcsn = BER_BVNULL;
1381                 char cbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
1382
1383                 /* Update our context CSN */
1384                 cbuf[0] = '\0';
1385                 ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
1386                 slap_get_commit_csn( op, &maxcsn, &curcsn );
1387                 if ( !BER_BVISNULL( &maxcsn ) ) {
1388                         strcpy( cbuf, maxcsn.bv_val );
1389                         if ( ber_bvcmp( &maxcsn, &si->si_ctxcsn ) > 0 ) {
1390                                 strcpy( si->si_ctxcsnbuf, cbuf );
1391                                 si->si_ctxcsn.bv_len = maxcsn.bv_len;
1392                         }
1393                 }
1394
1395                 /* Don't do any processing for consumer contextCSN updates */
1396                 if ( SLAP_SYNC_SHADOW( op->o_bd ) && 
1397                         op->o_msgid == SLAP_SYNC_UPDATE_MSGID ) {
1398                         ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
1399                         return SLAP_CB_CONTINUE;
1400                 }
1401
1402                 si->si_numops++;
1403                 if ( si->si_chkops || si->si_chktime ) {
1404                         int do_check=0;
1405                         if ( si->si_chkops && si->si_numops >= si->si_chkops ) {
1406                                 do_check = 1;
1407                                 si->si_numops = 0;
1408                         }
1409                         if ( si->si_chktime &&
1410                                 (op->o_time - si->si_chklast >= si->si_chktime )) {
1411                                 do_check = 1;
1412                                 si->si_chklast = op->o_time;
1413                         }
1414                         if ( do_check ) {
1415                                 syncprov_checkpoint( op, rs, on );
1416                         }
1417                 }
1418                 ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
1419
1420                 opc->sctxcsn.bv_len = maxcsn.bv_len;
1421                 opc->sctxcsn.bv_val = cbuf;
1422
1423                 /* Handle any persistent searches */
1424                 if ( si->si_ops ) {
1425                         switch(op->o_tag) {
1426                         case LDAP_REQ_ADD:
1427                         case LDAP_REQ_MODIFY:
1428                         case LDAP_REQ_MODRDN:
1429                         case LDAP_REQ_EXTENDED:
1430                                 syncprov_matchops( op, opc, 0 );
1431                                 break;
1432                         case LDAP_REQ_DELETE:
1433                                 /* for each match in opc->smatches:
1434                                  *   send DELETE msg
1435                                  */
1436                                 for ( sm = opc->smatches; sm; sm=sm->sm_next ) {
1437                                         if ( sm->sm_op->s_op->o_abandon )
1438                                                 continue;
1439                                         syncprov_sendresp( op, opc, sm->sm_op, NULL,
1440                                                 LDAP_SYNC_DELETE, 1 );
1441                                 }
1442                                 break;
1443                         }
1444                 }
1445
1446                 /* Add any log records */
1447                 if ( si->si_logs && op->o_tag != LDAP_REQ_ADD ) {
1448                         syncprov_add_slog( op, &curcsn );
1449                 }
1450
1451         }
1452         return SLAP_CB_CONTINUE;
1453 }
1454
1455 /* We don't use a subentry to store the context CSN any more.
1456  * We expose the current context CSN as an operational attribute
1457  * of the suffix entry.
1458  */
1459 static int
1460 syncprov_op_compare( Operation *op, SlapReply *rs )
1461 {
1462         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1463         syncprov_info_t         *si = on->on_bi.bi_private;
1464         int rc = SLAP_CB_CONTINUE;
1465
1466         if ( dn_match( &op->o_req_ndn, op->o_bd->be_nsuffix ) &&
1467                 op->oq_compare.rs_ava->aa_desc == slap_schema.si_ad_contextCSN )
1468         {
1469                 Entry e = {0};
1470                 Attribute a = {0};
1471                 struct berval bv[2];
1472
1473                 e.e_name = op->o_bd->be_suffix[0];
1474                 e.e_nname = op->o_bd->be_nsuffix[0];
1475
1476                 BER_BVZERO( &bv[1] );
1477                 bv[0] = si->si_ctxcsn;
1478
1479                 a.a_desc = slap_schema.si_ad_contextCSN;
1480                 a.a_vals = bv;
1481                 a.a_nvals = a.a_vals;
1482
1483                 ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
1484
1485                 rs->sr_err = access_allowed( op, &e, op->oq_compare.rs_ava->aa_desc,
1486                         &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
1487                 if ( ! rs->sr_err ) {
1488                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1489                         goto return_results;
1490                 }
1491
1492                 if ( get_assert( op ) &&
1493                         ( test_filter( op, &e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) )
1494                 {
1495                         rs->sr_err = LDAP_ASSERTION_FAILED;
1496                         goto return_results;
1497                 }
1498
1499
1500                 rs->sr_err = LDAP_COMPARE_FALSE;
1501
1502                 if ( value_find_ex( op->oq_compare.rs_ava->aa_desc,
1503                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1504                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1505                                 a.a_nvals, &op->oq_compare.rs_ava->aa_value, op->o_tmpmemctx ) == 0 )
1506                 {
1507                         rs->sr_err = LDAP_COMPARE_TRUE;
1508                 }
1509
1510 return_results:;
1511
1512                 ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
1513
1514                 send_ldap_result( op, rs );
1515
1516                 if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
1517                         rs->sr_err = LDAP_SUCCESS;
1518                 }
1519                 rc = rs->sr_err;
1520         }
1521
1522         return rc;
1523 }
1524
1525 static int
1526 syncprov_op_mod( Operation *op, SlapReply *rs )
1527 {
1528         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1529         syncprov_info_t         *si = on->on_bi.bi_private;
1530
1531         slap_callback *cb = op->o_tmpcalloc(1, sizeof(slap_callback)+
1532                 sizeof(opcookie) +
1533                 (si->si_ops ? sizeof(modinst) : 0 ),
1534                 op->o_tmpmemctx);
1535         opcookie *opc = (opcookie *)(cb+1);
1536         opc->son = on;
1537         cb->sc_response = syncprov_op_response;
1538         cb->sc_cleanup = syncprov_op_cleanup;
1539         cb->sc_private = opc;
1540         cb->sc_next = op->o_callback;
1541         op->o_callback = cb;
1542
1543         /* If there are active persistent searches, lock this operation.
1544          * See seqmod.c for the locking logic on its own.
1545          */
1546         if ( si->si_ops ) {
1547                 modtarget *mt, mtdummy;
1548                 modinst *mi;
1549
1550                 mi = (modinst *)(opc+1);
1551                 mi->mi_op = op;
1552
1553                 /* See if we're already modifying this entry... */
1554                 mtdummy.mt_op = op;
1555                 ldap_pvt_thread_mutex_lock( &si->si_mods_mutex );
1556                 mt = avl_find( si->si_mods, &mtdummy, sp_avl_cmp );
1557                 if ( mt ) {
1558                         ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
1559                         ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
1560                         mt->mt_tail->mi_next = mi;
1561                         mt->mt_tail = mi;
1562                         /* wait for this op to get to head of list */
1563                         while ( mt->mt_mods != mi ) {
1564                                 ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
1565                                 ldap_pvt_thread_yield();
1566                                 ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
1567
1568                                 /* clean up if the caller is giving up */
1569                                 if ( op->o_abandon ) {
1570                                         modinst *m2;
1571                                         for ( m2 = mt->mt_mods; m2->mi_next != mi;
1572                                                 m2 = m2->mi_next );
1573                                         m2->mi_next = mi->mi_next;
1574                                         if ( mt->mt_tail == mi ) mt->mt_tail = m2;
1575                                         op->o_tmpfree( cb, op->o_tmpmemctx );
1576                                         ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
1577                                         return SLAPD_ABANDON;
1578                                 }
1579                         }
1580                         ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
1581                 } else {
1582                         /* Record that we're modifying this entry now */
1583                         mt = ch_malloc( sizeof(modtarget) );
1584                         mt->mt_mods = mi;
1585                         mt->mt_tail = mi;
1586                         mt->mt_op = mi->mi_op;
1587                         ldap_pvt_thread_mutex_init( &mt->mt_mutex );
1588                         avl_insert( &si->si_mods, mt, sp_avl_cmp, avl_dup_error );
1589                         ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
1590                 }
1591         }
1592
1593         if (( si->si_ops || si->si_logs ) && op->o_tag != LDAP_REQ_ADD )
1594                 syncprov_matchops( op, opc, 1 );
1595
1596         return SLAP_CB_CONTINUE;
1597 }
1598
1599 static int
1600 syncprov_op_extended( Operation *op, SlapReply *rs )
1601 {
1602         if ( exop_is_write( op ))
1603                 return syncprov_op_mod( op, rs );
1604
1605         return SLAP_CB_CONTINUE;
1606 }
1607
1608 typedef struct searchstate {
1609         slap_overinst *ss_on;
1610         syncops *ss_so;
1611         int ss_present;
1612         struct berval ss_ctxcsn;
1613         char ss_csnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
1614 } searchstate;
1615
1616 static int
1617 syncprov_search_cleanup( Operation *op, SlapReply *rs )
1618 {
1619         if ( rs->sr_ctrls ) {
1620                 op->o_tmpfree( rs->sr_ctrls[0], op->o_tmpmemctx );
1621                 op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
1622                 rs->sr_ctrls = NULL;
1623         }
1624         return 0;
1625 }
1626
1627 static void
1628 syncprov_detach_op( Operation *op, syncops *so )
1629 {
1630         Operation *op2;
1631         int i, alen = 0;
1632         size_t size;
1633         char *ptr;
1634         GroupAssertion *g1, *g2;
1635
1636         /* count the search attrs */
1637         for (i=0; op->ors_attrs && !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++) {
1638                 alen += op->ors_attrs[i].an_name.bv_len + 1;
1639         }
1640         /* Make a new copy of the operation */
1641         size = sizeof(Operation) + sizeof(Opheader) +
1642                 (i ? ( (i+1) * sizeof(AttributeName) + alen) : 0) +
1643                 op->o_req_dn.bv_len + 1 +
1644                 op->o_req_ndn.bv_len + 1 +
1645                 op->o_ndn.bv_len + 1 +
1646                 so->s_filterstr.bv_len + 1;
1647         op2 = (Operation *)ch_calloc( 1, size );
1648         op2->o_hdr = (Opheader *)(op2+1);
1649
1650         /* Copy the fields we care about explicitly, leave the rest alone */
1651         *op2->o_hdr = *op->o_hdr;
1652         op2->o_tag = op->o_tag;
1653         op2->o_time = op->o_time;
1654         op2->o_bd = op->o_bd;
1655         op2->o_request = op->o_request;
1656
1657         if ( i ) {
1658                 op2->ors_attrs = (AttributeName *)(op2->o_hdr + 1);
1659                 ptr = (char *)(op2->ors_attrs+i+1);
1660                 for (i=0; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++) {
1661                         op2->ors_attrs[i] = op->ors_attrs[i];
1662                         op2->ors_attrs[i].an_name.bv_val = ptr;
1663                         ptr = lutil_strcopy( ptr, op->ors_attrs[i].an_name.bv_val ) + 1;
1664                 }
1665                 BER_BVZERO( &op2->ors_attrs[i].an_name );
1666         } else {
1667                 ptr = (char *)(op2->o_hdr + 1);
1668         }
1669         op2->o_authz = op->o_authz;
1670         op2->o_ndn.bv_val = ptr;
1671         ptr = lutil_strcopy(ptr, op->o_ndn.bv_val) + 1;
1672         op2->o_dn = op2->o_ndn;
1673         op2->o_req_dn.bv_len = op->o_req_dn.bv_len;
1674         op2->o_req_dn.bv_val = ptr;
1675         ptr = lutil_strcopy(ptr, op->o_req_dn.bv_val) + 1;
1676         op2->o_req_ndn.bv_len = op->o_req_ndn.bv_len;
1677         op2->o_req_ndn.bv_val = ptr;
1678         ptr = lutil_strcopy(ptr, op->o_req_ndn.bv_val) + 1;
1679         op2->ors_filterstr.bv_val = ptr;
1680         strcpy( ptr, so->s_filterstr.bv_val );
1681         op2->ors_filterstr.bv_len = so->s_filterstr.bv_len;
1682         op2->ors_filter = str2filter( ptr );
1683         so->s_op = op2;
1684
1685         /* Copy any cached group ACLs individually */
1686         op2->o_groups = NULL;
1687         for ( g1=op->o_groups; g1; g1=g1->ga_next ) {
1688                 g2 = ch_malloc( sizeof(GroupAssertion) + g1->ga_len );
1689                 *g2 = *g1;
1690                 strcpy( g2->ga_ndn, g1->ga_ndn );
1691                 g2->ga_next = op2->o_groups;
1692                 op2->o_groups = g2;
1693         }
1694
1695         /* Add op2 to conn so abandon will find us */
1696         ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
1697         op->o_conn->c_n_ops_executing++;
1698         op->o_conn->c_n_ops_completed--;
1699         LDAP_STAILQ_INSERT_TAIL( &op->o_conn->c_ops, op2, o_next );
1700         so->s_flags |= PS_IS_DETACHED;
1701         ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
1702 }
1703
1704 static int
1705 syncprov_search_response( Operation *op, SlapReply *rs )
1706 {
1707         searchstate *ss = op->o_callback->sc_private;
1708         slap_overinst *on = ss->ss_on;
1709         sync_control *srs = op->o_controls[slap_cids.sc_LDAPsync];
1710
1711         if ( rs->sr_type == REP_SEARCH || rs->sr_type == REP_SEARCHREF ) {
1712                 Attribute *a;
1713                 /* If we got a referral without a referral object, there's
1714                  * something missing that we cannot replicate. Just ignore it.
1715                  * The consumer will abort because we didn't send the expected
1716                  * control.
1717                  */
1718                 if ( !rs->sr_entry ) {
1719                         assert( rs->sr_entry != NULL );
1720                         Debug( LDAP_DEBUG_ANY, "bogus referral in context\n",0,0,0 );
1721                         return SLAP_CB_CONTINUE;
1722                 }
1723                 a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryCSN );
1724                 if ( a ) {
1725                         /* Make sure entry is less than the snaphot'd contextCSN */
1726                         if ( ber_bvcmp( &a->a_nvals[0], &ss->ss_ctxcsn ) > 0 )
1727                                 return LDAP_SUCCESS;
1728
1729                         /* Don't send the ctx entry twice */
1730                         if ( !BER_BVISNULL( &srs->sr_state.ctxcsn ) &&
1731                                 bvmatch( &a->a_nvals[0], &srs->sr_state.ctxcsn ) )
1732                                 return LDAP_SUCCESS;
1733                 }
1734                 rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
1735                         op->o_tmpmemctx );
1736                 rs->sr_ctrls[1] = NULL;
1737                 rs->sr_err = syncprov_state_ctrl( op, rs, rs->sr_entry,
1738                         LDAP_SYNC_ADD, rs->sr_ctrls, 0, 0, NULL );
1739         } else if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS ) {
1740                 struct berval cookie;
1741
1742                 slap_compose_sync_cookie( op, &cookie, &ss->ss_ctxcsn,
1743                         srs->sr_state.rid );
1744
1745                 /* Is this a regular refresh? */
1746                 if ( !ss->ss_so ) {
1747                         rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
1748                                 op->o_tmpmemctx );
1749                         rs->sr_ctrls[1] = NULL;
1750                         rs->sr_err = syncprov_done_ctrl( op, rs, rs->sr_ctrls,
1751                                 0, 1, &cookie, ss->ss_present ?  LDAP_SYNC_REFRESH_PRESENTS :
1752                                         LDAP_SYNC_REFRESH_DELETES );
1753                 } else {
1754                         int locked = 0;
1755                 /* It's RefreshAndPersist, transition to Persist phase */
1756                         syncprov_sendinfo( op, rs, ( ss->ss_present && rs->sr_nentries ) ?
1757                                 LDAP_TAG_SYNC_REFRESH_PRESENT : LDAP_TAG_SYNC_REFRESH_DELETE,
1758                                 &cookie, 1, NULL, 0 );
1759                         /* Flush any queued persist messages */
1760                         if ( ss->ss_so->s_res ) {
1761                                 ldap_pvt_thread_mutex_lock( &ss->ss_so->s_mutex );
1762                                 locked = 1;
1763                                 syncprov_qplay( op, on, ss->ss_so );
1764                         }
1765
1766                         /* Turn off the refreshing flag */
1767                         ss->ss_so->s_flags ^= PS_IS_REFRESHING;
1768                         if ( locked )
1769                                 ldap_pvt_thread_mutex_unlock( &ss->ss_so->s_mutex );
1770
1771                         /* Detach this Op from frontend control */
1772                         syncprov_detach_op( op, ss->ss_so );
1773
1774                         return LDAP_SUCCESS;
1775                 }
1776         }
1777
1778         return SLAP_CB_CONTINUE;
1779 }
1780
1781 static int
1782 syncprov_op_search( Operation *op, SlapReply *rs )
1783 {
1784         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1785         syncprov_info_t         *si = (syncprov_info_t *)on->on_bi.bi_private;
1786         slap_callback   *cb;
1787         int gotstate = 0, nochange = 0, do_present = 1;
1788         syncops *sop = NULL;
1789         searchstate *ss;
1790         sync_control *srs;
1791         struct berval ctxcsn;
1792         char csnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
1793
1794         if ( !(op->o_sync_mode & SLAP_SYNC_REFRESH) ) return SLAP_CB_CONTINUE;
1795
1796         if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
1797                 send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "illegal value for derefAliases" );
1798                 return rs->sr_err;
1799         }
1800
1801         srs = op->o_controls[slap_cids.sc_LDAPsync];
1802         op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
1803
1804         /* If this is a persistent search, set it up right away */
1805         if ( op->o_sync_mode & SLAP_SYNC_PERSIST ) {
1806                 syncops so = {0};
1807                 fbase_cookie fc;
1808                 opcookie opc;
1809                 slap_callback sc;
1810
1811                 fc.fss = &so;
1812                 fc.fbase = 0;
1813                 so.s_eid = NOID;
1814                 so.s_op = op;
1815                 so.s_flags = PS_IS_REFRESHING;
1816                 /* syncprov_findbase expects to be called as a callback... */
1817                 sc.sc_private = &opc;
1818                 opc.son = on;
1819                 cb = op->o_callback;
1820                 op->o_callback = &sc;
1821                 rs->sr_err = syncprov_findbase( op, &fc );
1822                 op->o_callback = cb;
1823
1824                 if ( rs->sr_err != LDAP_SUCCESS ) {
1825                         send_ldap_result( op, rs );
1826                         return rs->sr_err;
1827                 }
1828                 sop = ch_malloc( sizeof( syncops ));
1829                 *sop = so;
1830                 ldap_pvt_thread_mutex_init( &sop->s_mutex );
1831                 sop->s_rid = srs->sr_state.rid;
1832                 sop->s_inuse = 1;
1833
1834                 ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1835                 sop->s_next = si->si_ops;
1836                 si->si_ops = sop;
1837                 ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1838         }
1839
1840         /* snapshot the ctxcsn */
1841         ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
1842         strcpy( csnbuf, si->si_ctxcsnbuf );
1843         ctxcsn.bv_len = si->si_ctxcsn.bv_len;
1844         ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
1845         ctxcsn.bv_val = csnbuf;
1846         
1847         /* If we have a cookie, handle the PRESENT lookups */
1848         if ( !BER_BVISNULL( &srs->sr_state.ctxcsn )) {
1849                 sessionlog *sl;
1850
1851                 /* The cookie was validated when it was parsed, just use it */
1852
1853                 /* If just Refreshing and nothing has changed, shortcut it */
1854                 if ( bvmatch( &srs->sr_state.ctxcsn, &ctxcsn )) {
1855                         nochange = 1;
1856                         if ( !(op->o_sync_mode & SLAP_SYNC_PERSIST) ) {
1857                                 LDAPControl     *ctrls[2];
1858
1859                                 ctrls[0] = NULL;
1860                                 ctrls[1] = NULL;
1861                                 syncprov_done_ctrl( op, rs, ctrls, 0, 0,
1862                                         NULL, LDAP_SYNC_REFRESH_DELETES );
1863                                 rs->sr_ctrls = ctrls;
1864                                 rs->sr_err = LDAP_SUCCESS;
1865                                 send_ldap_result( op, rs );
1866                                 rs->sr_ctrls = NULL;
1867                                 return rs->sr_err;
1868                         }
1869                         goto shortcut;
1870                 }
1871                 /* Do we have a sessionlog for this search? */
1872                 sl=si->si_logs;
1873                 if ( sl ) {
1874                         ldap_pvt_thread_mutex_lock( &sl->sl_mutex );
1875                         if ( ber_bvcmp( &srs->sr_state.ctxcsn, &sl->sl_mincsn ) >= 0 ) {
1876                                 do_present = 0;
1877                                 /* mutex is unlocked in playlog */
1878                                 syncprov_playlog( op, rs, sl, &srs->sr_state.ctxcsn, &ctxcsn );
1879                         } else {
1880                                 ldap_pvt_thread_mutex_unlock( &sl->sl_mutex );
1881                         }
1882                 }
1883                 /* Is the CSN still present in the database? */
1884                 if ( syncprov_findcsn( op, FIND_CSN ) != LDAP_SUCCESS ) {
1885                         /* No, so a reload is required */
1886 #if 0           /* the consumer doesn't seem to send this hint */
1887                         if ( op->o_sync_rhint == 0 ) {
1888                                 send_ldap_error( op, rs, LDAP_SYNC_REFRESH_REQUIRED, "sync cookie is stale" );
1889                                 return rs->sr_err;
1890                         }
1891 #endif
1892                 } else {
1893                         gotstate = 1;
1894                         /* If changed and doing Present lookup, send Present UUIDs */
1895                         if ( do_present && syncprov_findcsn( op, FIND_PRESENT ) !=
1896                                 LDAP_SUCCESS ) {
1897                                 send_ldap_result( op, rs );
1898                                 return rs->sr_err;
1899                         }
1900                 }
1901         }
1902
1903 shortcut:
1904         /* Append CSN range to search filter, save original filter
1905          * for persistent search evaluation
1906          */
1907         if ( sop ) {
1908                 sop->s_filterstr= op->ors_filterstr;
1909         }
1910
1911         /* If something changed, find the changes */
1912         if ( gotstate && !nochange ) {
1913                 Filter *fand, *fava;
1914
1915                 fand = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1916                 fand->f_choice = LDAP_FILTER_AND;
1917                 fand->f_next = NULL;
1918                 fava = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
1919                 fand->f_and = fava;
1920                 fava->f_choice = LDAP_FILTER_GE;
1921                 fava->f_ava = op->o_tmpalloc( sizeof(AttributeAssertion), op->o_tmpmemctx );
1922                 fava->f_ava->aa_desc = slap_schema.si_ad_entryCSN;
1923 #ifdef LDAP_COMP_MATCH
1924                 fava->f_ava->aa_cf = NULL;
1925 #endif
1926                 ber_dupbv_x( &fava->f_ava->aa_value, &srs->sr_state.ctxcsn, op->o_tmpmemctx );
1927                 fava->f_next = op->ors_filter;
1928                 op->ors_filter = fand;
1929                 filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
1930         }
1931
1932         /* Let our callback add needed info to returned entries */
1933         cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(searchstate), op->o_tmpmemctx);
1934         ss = (searchstate *)(cb+1);
1935         ss->ss_on = on;
1936         ss->ss_so = sop;
1937         ss->ss_present = do_present;
1938         ss->ss_ctxcsn.bv_len = ctxcsn.bv_len;
1939         ss->ss_ctxcsn.bv_val = ss->ss_csnbuf;
1940         strcpy( ss->ss_ctxcsn.bv_val, ctxcsn.bv_val );
1941         cb->sc_response = syncprov_search_response;
1942         cb->sc_cleanup = syncprov_search_cleanup;
1943         cb->sc_private = ss;
1944         cb->sc_next = op->o_callback;
1945         op->o_callback = cb;
1946
1947 #if 0   /* I don't think we need to shortcircuit back-bdb any more */
1948         op->o_sync_mode &= SLAP_CONTROL_MASK;
1949 #endif
1950
1951         /* If this is a persistent search and no changes were reported during
1952          * the refresh phase, just invoke the response callback to transition
1953          * us into persist phase
1954          */
1955         if ( nochange ) {
1956                 rs->sr_err = LDAP_SUCCESS;
1957                 rs->sr_nentries = 0;
1958                 send_ldap_result( op, rs );
1959                 return rs->sr_err;
1960         }
1961         return SLAP_CB_CONTINUE;
1962 }
1963
1964 static int
1965 syncprov_operational(
1966         Operation *op,
1967         SlapReply *rs )
1968 {
1969         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1970         syncprov_info_t         *si = (syncprov_info_t *)on->on_bi.bi_private;
1971
1972         if ( rs->sr_entry &&
1973                 dn_match( &rs->sr_entry->e_nname, op->o_bd->be_nsuffix )) {
1974
1975                 if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
1976                         ad_inlist( slap_schema.si_ad_contextCSN, rs->sr_attrs )) {
1977                         Attribute *a, **ap = NULL;
1978
1979                         for ( a=rs->sr_entry->e_attrs; a; a=a->a_next ) {
1980                                 if ( a->a_desc == slap_schema.si_ad_contextCSN )
1981                                         break;
1982                         }
1983
1984                         if ( !a ) {
1985                                 for ( ap = &rs->sr_operational_attrs; *ap; ap=&(*ap)->a_next );
1986
1987                                 a = ch_malloc( sizeof(Attribute));
1988                                 a->a_desc = slap_schema.si_ad_contextCSN;
1989                                 a->a_vals = ch_malloc( 2 * sizeof(struct berval));
1990                                 a->a_vals[1].bv_val = NULL;
1991                                 a->a_nvals = a->a_vals;
1992                                 a->a_next = NULL;
1993                                 a->a_flags = 0;
1994                                 *ap = a;
1995                         }
1996
1997                         ldap_pvt_thread_mutex_lock( &si->si_csn_mutex );
1998                         if ( !ap ) {
1999                                 strcpy( a->a_vals[0].bv_val, si->si_ctxcsnbuf );
2000                         } else {
2001                                 ber_dupbv( &a->a_vals[0], &si->si_ctxcsn );
2002                         }
2003                         ldap_pvt_thread_mutex_unlock( &si->si_csn_mutex );
2004                 }
2005         }
2006         return SLAP_CB_CONTINUE;
2007 }
2008
2009 enum {
2010         SP_CHKPT = 1,
2011         SP_SESSL
2012 };
2013
2014 static ConfigDriver sp_cf_gen;
2015
2016 static ConfigTable spcfg[] = {
2017         { "syncprov-checkpoint", "ops> <minutes", 3, 3, 0, ARG_MAGIC|SP_CHKPT,
2018                 sp_cf_gen, "( OLcfgOvAt:1.1 NAME 'olcSpCheckpoint' "
2019                         "DESC 'ContextCSN checkpoint interval in ops and minutes' "
2020                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
2021         { "syncprov-sessionlog", "ops", 2, 2, 0, ARG_INT|ARG_MAGIC|SP_SESSL,
2022                 sp_cf_gen, "( OLcfgOvAt:1.2 NAME 'olcSpSessionlog' "
2023                         "DESC 'Session log size in ops' "
2024                         "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
2025         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
2026 };
2027
2028 static ConfigOCs spocs[] = {
2029         { "( OLcfgOvOc:1.1 "
2030                 "NAME 'olcSyncProvConfig' "
2031                 "DESC 'SyncRepl Provider configuration' "
2032                 "SUP olcOverlayConfig "
2033                 "MAY ( olcSpCheckpoint $ olcSpSessionlog ) )",
2034                         Cft_Overlay, spcfg },
2035         { NULL, 0, NULL }
2036 };
2037
2038 static int
2039 sp_cf_gen(ConfigArgs *c)
2040 {
2041         slap_overinst           *on = (slap_overinst *)c->bi;
2042         syncprov_info_t         *si = (syncprov_info_t *)on->on_bi.bi_private;
2043         int rc = 0;
2044
2045         if ( c->op == SLAP_CONFIG_EMIT ) {
2046                 switch ( c->type ) {
2047                 case SP_CHKPT:
2048                         if ( si->si_chkops || si->si_chktime ) {
2049                                 struct berval bv;
2050                                 bv.bv_len = sprintf( c->msg, "%d %d",
2051                                         si->si_chkops, si->si_chktime );
2052                                 bv.bv_val = c->msg;
2053                                 value_add_one( &c->rvalue_vals, &bv );
2054                         } else {
2055                                 rc = 1;
2056                         }
2057                         break;
2058                 case SP_SESSL:
2059                         if ( si->si_logs ) {
2060                                 c->value_int = si->si_logs->sl_size;
2061                         } else {
2062                                 rc = 1;
2063                         }
2064                         break;
2065                 }
2066                 return rc;
2067         } else if ( c->op == LDAP_MOD_DELETE ) {
2068                 switch ( c->type ) {
2069                 case SP_CHKPT:
2070                         si->si_chkops = 0;
2071                         si->si_chktime = 0;
2072                         break;
2073                 case SP_SESSL:
2074                         if ( si->si_logs )
2075                                 si->si_logs->sl_size = 0;
2076                         else
2077                                 rc = LDAP_NO_SUCH_ATTRIBUTE;
2078                         break;
2079                 }
2080                 return rc;
2081         }
2082         switch ( c->type ) {
2083         case SP_CHKPT:
2084                 si->si_chkops = atoi( c->argv[1] );
2085                 si->si_chktime = atoi( c->argv[2] ) * 60;
2086                 break;
2087         case SP_SESSL: {
2088                 sessionlog *sl;
2089                 int size = c->value_int;
2090
2091                 if ( size < 0 ) {
2092                         sprintf( c->msg, "%s size %d is negative",
2093                                 c->argv[0], size );
2094                         Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->msg, 0 );
2095                         return ARG_BAD_CONF;
2096                 }
2097                 sl = si->si_logs;
2098                 if ( !sl ) {
2099                         sl = ch_malloc( sizeof( sessionlog ) + LDAP_LUTIL_CSNSTR_BUFSIZE );
2100                         sl->sl_mincsn.bv_val = (char *)(sl+1);
2101                         sl->sl_mincsn.bv_len = 0;
2102                         sl->sl_num = 0;
2103                         sl->sl_head = sl->sl_tail = NULL;
2104                         ldap_pvt_thread_mutex_init( &sl->sl_mutex );
2105                         si->si_logs = sl;
2106                 }
2107                 sl->sl_size = size;
2108                 }
2109                 break;
2110         }
2111         return rc;
2112 }
2113
2114 /* Cheating - we have no thread pool context for these functions,
2115  * so make one.
2116  */
2117 typedef struct thread_keys {
2118         void *key;
2119         void *data;
2120         ldap_pvt_thread_pool_keyfree_t *xfree;
2121 } thread_keys;
2122
2123 #define MAXKEYS 32
2124 /* A fake thread context */
2125 static thread_keys thrctx[MAXKEYS];
2126
2127 /* ITS#3456 we cannot run this search on the main thread, must use a
2128  * child thread in order to insure we have a big enough stack.
2129  */
2130 static void *
2131 syncprov_db_otask(
2132         void *ptr
2133 )
2134 {
2135         syncprov_findcsn( ptr, FIND_MAXCSN );
2136         return NULL;
2137 }
2138
2139 /* Read any existing contextCSN from the underlying db.
2140  * Then search for any entries newer than that. If no value exists,
2141  * just generate it. Cache whatever result.
2142  */
2143 static int
2144 syncprov_db_open(
2145     BackendDB *be
2146 )
2147 {
2148         slap_overinst   *on = (slap_overinst *) be->bd_info;
2149         syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
2150
2151         Connection conn;
2152         char opbuf[OPERATION_BUFFER_SIZE];
2153         char ctxcsnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
2154         Operation *op = (Operation *)opbuf;
2155         Entry *e;
2156         Attribute *a;
2157         int rc;
2158
2159         if ( slapMode & SLAP_TOOL_MODE ) {
2160                 return 0;
2161         }
2162
2163         rc = overlay_register_control( be, LDAP_CONTROL_SYNC );
2164         if ( rc ) {
2165                 return rc;
2166         }
2167
2168         connection_fake_init( &conn, op, thrctx );
2169         op->o_bd = be;
2170         op->o_dn = be->be_rootdn;
2171         op->o_ndn = be->be_rootndn;
2172
2173         ctxcsnbuf[0] = '\0';
2174
2175         op->o_bd->bd_info = on->on_info->oi_orig;
2176         rc = be_entry_get_rw( op, be->be_nsuffix, NULL,
2177                 slap_schema.si_ad_contextCSN, 0, &e );
2178
2179         if ( e ) {
2180                 ldap_pvt_thread_t tid;
2181
2182                 a = attr_find( e->e_attrs, slap_schema.si_ad_contextCSN );
2183                 if ( a ) {
2184                         si->si_ctxcsn.bv_len = a->a_nvals[0].bv_len;
2185                         if ( si->si_ctxcsn.bv_len >= sizeof(si->si_ctxcsnbuf ))
2186                                 si->si_ctxcsn.bv_len = sizeof(si->si_ctxcsnbuf)-1;
2187                         strncpy( si->si_ctxcsnbuf, a->a_nvals[0].bv_val,
2188                                 si->si_ctxcsn.bv_len );
2189                         si->si_ctxcsnbuf[si->si_ctxcsn.bv_len] = '\0';
2190                         strcpy( ctxcsnbuf, si->si_ctxcsnbuf );
2191                 }
2192                 be_entry_release_rw( op, e, 0 );
2193                 op->o_bd->bd_info = (BackendInfo *)on;
2194                 op->o_req_dn = be->be_suffix[0];
2195                 op->o_req_ndn = be->be_nsuffix[0];
2196                 op->ors_scope = LDAP_SCOPE_SUBTREE;
2197                 ldap_pvt_thread_create( &tid, 0, syncprov_db_otask, op );
2198                 ldap_pvt_thread_join( tid, NULL );
2199         } else if ( SLAP_SYNC_SHADOW( op->o_bd )) {
2200                 /* If we're also a consumer, and we didn't find the context entry,
2201                  * then don't generate anything, wait for our provider to send it
2202                  * to us.
2203                  */
2204                 goto out;
2205         }
2206
2207         if ( BER_BVISEMPTY( &si->si_ctxcsn ) ) {
2208                 slap_get_csn( op, si->si_ctxcsnbuf, sizeof(si->si_ctxcsnbuf),
2209                                 &si->si_ctxcsn, 0 );
2210         }
2211
2212         /* If our ctxcsn is different from what was read from the root
2213          * entry, make sure we do a checkpoint on close
2214          */
2215         if ( strcmp( si->si_ctxcsnbuf, ctxcsnbuf )) {
2216                 si->si_numops++;
2217         }
2218
2219 out:
2220         op->o_bd->bd_info = (BackendInfo *)on;
2221         return 0;
2222 }
2223
2224 /* Write the current contextCSN into the underlying db.
2225  */
2226 static int
2227 syncprov_db_close(
2228     BackendDB *be
2229 )
2230 {
2231     slap_overinst   *on = (slap_overinst *) be->bd_info;
2232     syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
2233         int i;
2234
2235         if ( slapMode & SLAP_TOOL_MODE ) {
2236                 return 0;
2237         }
2238         if ( si->si_numops ) {
2239                 Connection conn;
2240                 char opbuf[OPERATION_BUFFER_SIZE];
2241                 Operation *op = (Operation *)opbuf;
2242                 SlapReply rs = {REP_RESULT};
2243
2244                 connection_fake_init( &conn, op, thrctx );
2245                 op->o_bd = be;
2246                 op->o_dn = be->be_rootdn;
2247                 op->o_ndn = be->be_rootndn;
2248                 syncprov_checkpoint( op, &rs, on );
2249         }
2250         for ( i=0; thrctx[i].key; i++) {
2251                 if ( thrctx[i].xfree )
2252                         thrctx[i].xfree( thrctx[i].key, thrctx[i].data );
2253                 thrctx[i].key = NULL;
2254         }
2255
2256     return 0;
2257 }
2258
2259 static int
2260 syncprov_db_init(
2261         BackendDB *be
2262 )
2263 {
2264         slap_overinst   *on = (slap_overinst *)be->bd_info;
2265         syncprov_info_t *si;
2266
2267         si = ch_calloc(1, sizeof(syncprov_info_t));
2268         on->on_bi.bi_private = si;
2269         ldap_pvt_thread_mutex_init( &si->si_csn_mutex );
2270         ldap_pvt_thread_mutex_init( &si->si_ops_mutex );
2271         ldap_pvt_thread_mutex_init( &si->si_mods_mutex );
2272         si->si_ctxcsn.bv_val = si->si_ctxcsnbuf;
2273
2274         csn_anlist[0].an_desc = slap_schema.si_ad_entryCSN;
2275         csn_anlist[0].an_name = slap_schema.si_ad_entryCSN->ad_cname;
2276
2277         uuid_anlist[0].an_desc = slap_schema.si_ad_entryUUID;
2278         uuid_anlist[0].an_name = slap_schema.si_ad_entryUUID->ad_cname;
2279
2280         return 0;
2281 }
2282
2283 static int
2284 syncprov_db_destroy(
2285         BackendDB *be
2286 )
2287 {
2288         slap_overinst   *on = (slap_overinst *)be->bd_info;
2289         syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
2290
2291         if ( si ) {
2292                 ldap_pvt_thread_mutex_destroy( &si->si_mods_mutex );
2293                 ldap_pvt_thread_mutex_destroy( &si->si_ops_mutex );
2294                 ldap_pvt_thread_mutex_destroy( &si->si_csn_mutex );
2295                 ch_free( si );
2296         }
2297
2298         return 0;
2299 }
2300
2301 static int syncprov_parseCtrl (
2302         Operation *op,
2303         SlapReply *rs,
2304         LDAPControl *ctrl )
2305 {
2306         ber_tag_t tag;
2307         BerElementBuffer berbuf;
2308         BerElement *ber = (BerElement *)&berbuf;
2309         ber_int_t mode;
2310         ber_len_t len;
2311         struct berval cookie = BER_BVNULL;
2312         sync_control *sr;
2313         int rhint = 0;
2314
2315         if ( op->o_sync != SLAP_CONTROL_NONE ) {
2316                 rs->sr_text = "Sync control specified multiple times";
2317                 return LDAP_PROTOCOL_ERROR;
2318         }
2319
2320         if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2321                 rs->sr_text = "Sync control specified with pagedResults control";
2322                 return LDAP_PROTOCOL_ERROR;
2323         }
2324
2325         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2326                 rs->sr_text = "Sync control value is empty (or absent)";
2327                 return LDAP_PROTOCOL_ERROR;
2328         }
2329
2330         /* Parse the control value
2331          *      syncRequestValue ::= SEQUENCE {
2332          *              mode   ENUMERATED {
2333          *                      -- 0 unused
2334          *                      refreshOnly             (1),
2335          *                      -- 2 reserved
2336          *                      refreshAndPersist       (3)
2337          *              },
2338          *              cookie  syncCookie OPTIONAL
2339          *      }
2340          */
2341
2342         ber_init2( ber, &ctrl->ldctl_value, 0 );
2343
2344         if ( (tag = ber_scanf( ber, "{i" /*}*/, &mode )) == LBER_ERROR ) {
2345                 rs->sr_text = "Sync control : mode decoding error";
2346                 return LDAP_PROTOCOL_ERROR;
2347         }
2348
2349         switch( mode ) {
2350         case LDAP_SYNC_REFRESH_ONLY:
2351                 mode = SLAP_SYNC_REFRESH;
2352                 break;
2353         case LDAP_SYNC_REFRESH_AND_PERSIST:
2354                 mode = SLAP_SYNC_REFRESH_AND_PERSIST;
2355                 break;
2356         default:
2357                 rs->sr_text = "Sync control : unknown update mode";
2358                 return LDAP_PROTOCOL_ERROR;
2359         }
2360
2361         tag = ber_peek_tag( ber, &len );
2362
2363         if ( tag == LDAP_TAG_SYNC_COOKIE ) {
2364                 if (( ber_scanf( ber, /*{*/ "m", &cookie )) == LBER_ERROR ) {
2365                         rs->sr_text = "Sync control : cookie decoding error";
2366                         return LDAP_PROTOCOL_ERROR;
2367                 }
2368         }
2369         if ( tag == LDAP_TAG_RELOAD_HINT ) {
2370                 if (( ber_scanf( ber, /*{*/ "b", &rhint )) == LBER_ERROR ) {
2371                         rs->sr_text = "Sync control : rhint decoding error";
2372                         return LDAP_PROTOCOL_ERROR;
2373                 }
2374         }
2375         if (( ber_scanf( ber, /*{*/ "}")) == LBER_ERROR ) {
2376                         rs->sr_text = "Sync control : decoding error";
2377                         return LDAP_PROTOCOL_ERROR;
2378         }
2379         sr = op->o_tmpcalloc( 1, sizeof(struct sync_control), op->o_tmpmemctx );
2380         sr->sr_rhint = rhint;
2381         if (!BER_BVISNULL(&cookie)) {
2382                 ber_dupbv_x( &sr->sr_state.octet_str, &cookie, op->o_tmpmemctx );
2383                 slap_parse_sync_cookie( &sr->sr_state, op->o_tmpmemctx );
2384                 if ( sr->sr_state.rid == -1 ) {
2385                         rs->sr_text = "Sync control : cookie parsing error";
2386                         return LDAP_PROTOCOL_ERROR;
2387                 }
2388         }
2389
2390         op->o_controls[slap_cids.sc_LDAPsync] = sr;
2391
2392         op->o_sync = ctrl->ldctl_iscritical
2393                 ? SLAP_CONTROL_CRITICAL
2394                 : SLAP_CONTROL_NONCRITICAL;
2395
2396         op->o_sync_mode |= mode;        /* o_sync_mode shares o_sync */
2397
2398         return LDAP_SUCCESS;
2399 }
2400
2401 /* This overlay is set up for dynamic loading via moduleload. For static
2402  * configuration, you'll need to arrange for the slap_overinst to be
2403  * initialized and registered by some other function inside slapd.
2404  */
2405
2406 static slap_overinst            syncprov;
2407
2408 int
2409 syncprov_init()
2410 {
2411         int rc;
2412
2413         rc = register_supported_control( LDAP_CONTROL_SYNC,
2414                 SLAP_CTRL_HIDE|SLAP_CTRL_SEARCH, NULL,
2415                 syncprov_parseCtrl, &slap_cids.sc_LDAPsync );
2416         if ( rc != LDAP_SUCCESS ) {
2417                 Debug( LDAP_DEBUG_ANY,
2418                         "syncprov_init: Failed to register control %d\n", rc, 0, 0 );
2419                 return rc;
2420         }
2421
2422         syncprov.on_bi.bi_type = "syncprov";
2423         syncprov.on_bi.bi_db_init = syncprov_db_init;
2424         syncprov.on_bi.bi_db_destroy = syncprov_db_destroy;
2425         syncprov.on_bi.bi_db_open = syncprov_db_open;
2426         syncprov.on_bi.bi_db_close = syncprov_db_close;
2427
2428         syncprov.on_bi.bi_op_abandon = syncprov_op_abandon;
2429         syncprov.on_bi.bi_op_cancel = syncprov_op_abandon;
2430
2431         syncprov.on_bi.bi_op_add = syncprov_op_mod;
2432         syncprov.on_bi.bi_op_compare = syncprov_op_compare;
2433         syncprov.on_bi.bi_op_delete = syncprov_op_mod;
2434         syncprov.on_bi.bi_op_modify = syncprov_op_mod;
2435         syncprov.on_bi.bi_op_modrdn = syncprov_op_mod;
2436         syncprov.on_bi.bi_op_search = syncprov_op_search;
2437         syncprov.on_bi.bi_extended = syncprov_op_extended;
2438         syncprov.on_bi.bi_operational = syncprov_operational;
2439
2440         syncprov.on_bi.bi_cf_ocs = spocs;
2441
2442         rc = config_register_schema( spcfg, spocs );
2443         if ( rc ) return rc;
2444
2445         return overlay_register( &syncprov );
2446 }
2447
2448 #if SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC
2449 int
2450 init_module( int argc, char *argv[] )
2451 {
2452         return syncprov_init();
2453 }
2454 #endif /* SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC */
2455
2456 #endif /* defined(SLAPD_OVER_SYNCPROV) */