1 /*        $NetBSD: syncprov.c,v 1.3 2021/08/14 16:15:02 christos Exp $          */
2 
3 /* $OpenLDAP$ */
4 /* syncprov.c - syncrepl provider */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2004-2021 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by Howard Chu for inclusion in
20  * OpenLDAP Software.
21  */
22 
23 #include <sys/cdefs.h>
24 __RCSID("$NetBSD: syncprov.c,v 1.3 2021/08/14 16:15:02 christos Exp $");
25 
26 #include "portable.h"
27 
28 #ifdef SLAPD_OVER_SYNCPROV
29 
30 #include <ac/string.h>
31 #include "lutil.h"
32 #include "slap.h"
33 #include "slap-config.h"
34 #include "ldap_rq.h"
35 
36 #ifdef LDAP_DEVEL
37 #define   CHECK_CSN 1
38 #endif
39 
40 /* A modify request on a particular entry */
41 typedef struct modinst {
42           struct modinst *mi_next;
43           Operation *mi_op;
44 } modinst;
45 
46 typedef struct modtarget {
47           struct modinst *mt_mods;
48           struct modinst *mt_tail;
49           struct berval mt_dn;
50           ldap_pvt_thread_mutex_t mt_mutex;
51 } modtarget;
52 
53 /* All the info of a psearch result that's shared between
54  * multiple queues
55  */
56 typedef struct resinfo {
57           struct syncres *ri_list;
58           Entry *ri_e;
59           struct berval ri_dn;
60           struct berval ri_ndn;
61           struct berval ri_uuid;
62           struct berval ri_csn;
63           struct berval ri_cookie;
64           char ri_isref;
65           ldap_pvt_thread_mutex_t ri_mutex;
66 } resinfo;
67 
68 /* A queued result of a persistent search */
69 typedef struct syncres {
70           struct syncres *s_next;       /* list of results on this psearch queue */
71           struct syncres *s_rilist;     /* list of psearches using this result */
72           resinfo *s_info;
73           char s_mode;
74 } syncres;
75 
76 /* Record of a persistent search */
77 typedef struct syncops {
78           struct syncops *s_next;
79           struct syncprov_info_t *s_si;
80           struct berval       s_base;             /* ndn of search base */
81           ID                  s_eid;              /* entryID of search base */
82           Operation *s_op;              /* search op */
83           int                 s_rid;
84           int                 s_sid;
85           struct berval s_filterstr;
86           int                 s_flags;  /* search status */
87 #define   PS_IS_REFRESHING    0x01
88 #define   PS_IS_DETACHED                0x02
89 #define   PS_WROTE_BASE                 0x04
90 #define   PS_FIND_BASE                  0x08
91 #define   PS_FIX_FILTER                 0x10
92 #define   PS_TASK_QUEUED                0x20
93 
94           int                 s_inuse;  /* reference count */
95           struct syncres *s_res;
96           struct syncres *s_restail;
97           void *s_pool_cookie;
98           ldap_pvt_thread_mutex_t       s_mutex;
99 } syncops;
100 
101 /* A received sync control */
102 typedef struct sync_control {
103           struct sync_cookie sr_state;
104           int sr_rhint;
105 } sync_control;
106 
107 #if 0 /* moved back to slap.h */
108 #define   o_sync    o_ctrlflag[slap_cids.sc_LDAPsync]
109 #endif
110 /* o_sync_mode uses data bits of o_sync */
111 #define   o_sync_mode         o_ctrlflag[slap_cids.sc_LDAPsync]
112 
113 #define SLAP_SYNC_NONE                                                (LDAP_SYNC_NONE<<SLAP_CONTROL_SHIFT)
114 #define SLAP_SYNC_REFRESH                                   (LDAP_SYNC_REFRESH_ONLY<<SLAP_CONTROL_SHIFT)
115 #define SLAP_SYNC_PERSIST                                   (LDAP_SYNC_RESERVED<<SLAP_CONTROL_SHIFT)
116 #define SLAP_SYNC_REFRESH_AND_PERSIST   (LDAP_SYNC_REFRESH_AND_PERSIST<<SLAP_CONTROL_SHIFT)
117 
118 /* Record of which searches matched at premodify step */
119 typedef struct syncmatches {
120           struct syncmatches *sm_next;
121           syncops *sm_op;
122 } syncmatches;
123 
124 /* Session log data */
125 typedef struct slog_entry {
126           struct berval se_uuid;
127           struct berval se_csn;
128           int       se_sid;
129           ber_tag_t se_tag;
130 } slog_entry;
131 
132 typedef struct sessionlog {
133           BerVarray sl_mincsn;
134           int                 *sl_sids;
135           int                 sl_numcsns;
136           int                 sl_num;
137           int                 sl_size;
138           int                 sl_playing;
139           TAvlnode *sl_entries;
140           ldap_pvt_thread_rdwr_t sl_mutex;
141 } sessionlog;
142 
143 /* Accesslog callback data */
144 typedef struct syncprov_accesslog_deletes {
145           Operation *op;
146           SlapReply *rs;
147           sync_control *srs;
148           BerVarray ctxcsn;
149           int numcsns, *sids;
150           Avlnode *uuids;
151           BerVarray uuid_list;
152           int ndel, list_len;
153           char *uuid_buf;
154 } syncprov_accesslog_deletes;
155 
156 /* The main state for this overlay */
157 typedef struct syncprov_info_t {
158           syncops             *si_ops;
159           struct berval       si_contextdn;
160           struct berval       si_logbase;
161           BerVarray si_ctxcsn;          /* ldapsync context */
162           int                 *si_sids;
163           int                 si_numcsns;
164           int                 si_chkops;          /* checkpointing info */
165           int                 si_chktime;
166           int                 si_numops;          /* number of ops since last checkpoint */
167           int                 si_nopres;          /* Skip present phase */
168           int                 si_usehint;         /* use reload hint */
169           int                 si_active;          /* True if there are active mods */
170           int                 si_dirty; /* True if the context is dirty, i.e changes
171                                                              * have been made without updating the csn. */
172           time_t    si_chklast;         /* time of last checkpoint */
173           Avlnode   *si_mods; /* entries being modified */
174           sessionlog          *si_logs;
175           ldap_pvt_thread_rdwr_t        si_csn_rwlock;
176           ldap_pvt_thread_mutex_t       si_ops_mutex;
177           ldap_pvt_thread_mutex_t       si_mods_mutex;
178           ldap_pvt_thread_mutex_t       si_resp_mutex;
179 } syncprov_info_t;
180 
181 typedef struct opcookie {
182           slap_overinst *son;
183           syncmatches *smatches;
184           modtarget *smt;
185           Entry *se;
186           struct berval sdn;  /* DN of entry, for deletes */
187           struct berval sndn;
188           struct berval suuid;          /* UUID of entry */
189           struct berval sctxcsn;
190           short osid;         /* sid of op csn */
191           short rsid;         /* sid of relay */
192           short sreference;   /* Is the entry a reference? */
193           syncres ssres;
194 } opcookie;
195 
196 typedef struct fbase_cookie {
197           struct berval *fdn; /* DN of a modified entry, for scope testing */
198           syncops *fss;       /* persistent search we're testing against */
199           int fbase;          /* if TRUE we found the search base and it's still valid */
200           int fscope;         /* if TRUE then fdn is within the psearch scope */
201 } fbase_cookie;
202 
203 static AttributeName csn_anlist[3];
204 static AttributeName uuid_anlist[2];
205 
206 static AttributeDescription *ad_reqType, *ad_reqResult, *ad_reqDN,
207                                                                       *ad_reqEntryUUID, *ad_minCSN, *ad_reqNewDN;
208 
209 /* Build a LDAPsync intermediate state control */
210 static int
syncprov_state_ctrl(Operation * op,SlapReply * rs,Entry * e,int entry_sync_state,LDAPControl ** ctrls,int num_ctrls,int send_cookie,struct berval * cookie)211 syncprov_state_ctrl(
212           Operation *op,
213           SlapReply *rs,
214           Entry               *e,
215           int                 entry_sync_state,
216           LDAPControl         **ctrls,
217           int                 num_ctrls,
218           int                 send_cookie,
219           struct berval       *cookie )
220 {
221           Attribute* a;
222           int ret;
223 
224           BerElementBuffer berbuf;
225           BerElement *ber = (BerElement *)&berbuf;
226           LDAPControl *cp;
227           struct berval bv;
228           struct berval       entryuuid_bv = BER_BVNULL;
229 
230           ber_init2( ber, 0, LBER_USE_DER );
231           ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
232 
233           for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
234                     AttributeDescription *desc = a->a_desc;
235                     if ( desc == slap_schema.si_ad_entryUUID ) {
236                               entryuuid_bv = a->a_nvals[0];
237                               break;
238                     }
239           }
240 
241           /* FIXME: what if entryuuid is NULL or empty ? */
242 
243           if ( send_cookie && cookie ) {
244                     ber_printf( ber, "{eOON}",
245                               entry_sync_state, &entryuuid_bv, cookie );
246           } else {
247                     ber_printf( ber, "{eON}",
248                               entry_sync_state, &entryuuid_bv );
249           }
250 
251           ret = ber_flatten2( ber, &bv, 0 );
252           if ( ret == 0 ) {
253                     cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
254                     cp->ldctl_oid = LDAP_CONTROL_SYNC_STATE;
255                     cp->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
256                     cp->ldctl_value.bv_val = (char *)&cp[1];
257                     cp->ldctl_value.bv_len = bv.bv_len;
258                     AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
259                     ctrls[num_ctrls] = cp;
260           }
261           ber_free_buf( ber );
262 
263           if ( ret < 0 ) {
264                     Debug( LDAP_DEBUG_TRACE,
265                               "slap_build_sync_ctrl: ber_flatten2 failed (%d)\n",
266                               ret );
267                     send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
268                     return LDAP_OTHER;
269           }
270 
271           return LDAP_SUCCESS;
272 }
273 
274 /* Build a LDAPsync final state control */
275 static int
syncprov_done_ctrl(Operation * op,SlapReply * rs,LDAPControl ** ctrls,int num_ctrls,int send_cookie,struct berval * cookie,int refreshDeletes)276 syncprov_done_ctrl(
277           Operation *op,
278           SlapReply *rs,
279           LDAPControl         **ctrls,
280           int                           num_ctrls,
281           int                           send_cookie,
282           struct berval *cookie,
283           int                           refreshDeletes )
284 {
285           int ret;
286           BerElementBuffer berbuf;
287           BerElement *ber = (BerElement *)&berbuf;
288           LDAPControl *cp;
289           struct berval bv;
290 
291           ber_init2( ber, NULL, LBER_USE_DER );
292           ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
293 
294           ber_printf( ber, "{" );
295           if ( send_cookie && cookie ) {
296                     ber_printf( ber, "O", cookie );
297           }
298           if ( refreshDeletes == LDAP_SYNC_REFRESH_DELETES ) {
299                     ber_printf( ber, "b", refreshDeletes );
300           }
301           ber_printf( ber, "N}" );
302 
303           ret = ber_flatten2( ber, &bv, 0 );
304           if ( ret == 0 ) {
305                     cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
306                     cp->ldctl_oid = LDAP_CONTROL_SYNC_DONE;
307                     cp->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
308                     cp->ldctl_value.bv_val = (char *)&cp[1];
309                     cp->ldctl_value.bv_len = bv.bv_len;
310                     AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
311                     ctrls[num_ctrls] = cp;
312           }
313 
314           ber_free_buf( ber );
315 
316           if ( ret < 0 ) {
317                     Debug( LDAP_DEBUG_TRACE,
318                               "syncprov_done_ctrl: ber_flatten2 failed (%d)\n",
319                               ret );
320                     send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
321                     return LDAP_OTHER;
322           }
323 
324           return LDAP_SUCCESS;
325 }
326 
327 static int
syncprov_sendinfo(Operation * op,SlapReply * rs,int type,struct berval * cookie,int refreshDone,BerVarray syncUUIDs,int refreshDeletes)328 syncprov_sendinfo(
329           Operation *op,
330           SlapReply *rs,
331           int                           type,
332           struct berval *cookie,
333           int                           refreshDone,
334           BerVarray syncUUIDs,
335           int                           refreshDeletes )
336 {
337           BerElementBuffer berbuf;
338           BerElement *ber = (BerElement *)&berbuf;
339           struct berval rspdata;
340 
341           int ret;
342 
343           ber_init2( ber, NULL, LBER_USE_DER );
344           ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
345 
346           if ( type ) {
347                     switch ( type ) {
348                     case LDAP_TAG_SYNC_NEW_COOKIE:
349                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
350                                         "sending a new cookie=%s\n",
351                                         op->o_log_prefix, cookie->bv_val );
352                               ber_printf( ber, "tO", type, cookie );
353                               break;
354                     case LDAP_TAG_SYNC_REFRESH_DELETE:
355                     case LDAP_TAG_SYNC_REFRESH_PRESENT:
356                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
357                                         "%s cookie=%s\n",
358                                         op->o_log_prefix,
359                                         type == LDAP_TAG_SYNC_REFRESH_DELETE ? "refreshDelete" : "refreshPresent",
360                                         cookie ? cookie->bv_val : "" );
361                               ber_printf( ber, "t{", type );
362                               if ( cookie ) {
363                                         ber_printf( ber, "O", cookie );
364                               }
365                               if ( refreshDone == 0 ) {
366                                         ber_printf( ber, "b", refreshDone );
367                               }
368                               ber_printf( ber, "N}" );
369                               break;
370                     case LDAP_TAG_SYNC_ID_SET:
371                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
372                                         "%s syncIdSet cookie=%s\n",
373                                         op->o_log_prefix, refreshDeletes ? "delete" : "present",
374                                         cookie ? cookie->bv_val : "" );
375                               ber_printf( ber, "t{", type );
376                               if ( cookie ) {
377                                         ber_printf( ber, "O", cookie );
378                               }
379                               if ( refreshDeletes == 1 ) {
380                                         ber_printf( ber, "b", refreshDeletes );
381                               }
382                               ber_printf( ber, "[W]", syncUUIDs );
383                               ber_printf( ber, "N}" );
384                               break;
385                     default:
386                               Debug( LDAP_DEBUG_TRACE,
387                                         "%s syncprov_sendinfo: invalid syncinfo type (%d)\n",
388                                         op->o_log_prefix, type );
389                               return LDAP_OTHER;
390                     }
391           }
392 
393           ret = ber_flatten2( ber, &rspdata, 0 );
394 
395           if ( ret < 0 ) {
396                     Debug( LDAP_DEBUG_TRACE,
397                               "syncprov_sendinfo: ber_flatten2 failed (%d)\n",
398                               ret );
399                     send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
400                     return LDAP_OTHER;
401           }
402 
403           rs->sr_rspoid = LDAP_SYNC_INFO;
404           rs->sr_rspdata = &rspdata;
405           send_ldap_intermediate( op, rs );
406           rs->sr_rspdata = NULL;
407           ber_free_buf( ber );
408 
409           return LDAP_SUCCESS;
410 }
411 
412 /* Find a modtarget in an AVL tree */
413 static int
sp_avl_cmp(const void * c1,const void * c2)414 sp_avl_cmp( const void *c1, const void *c2 )
415 {
416           const modtarget *m1, *m2;
417           int rc;
418 
419           m1 = c1; m2 = c2;
420           rc = m1->mt_dn.bv_len - m2->mt_dn.bv_len;
421 
422           if ( rc ) return rc;
423           return ber_bvcmp( &m1->mt_dn, &m2->mt_dn );
424 }
425 
426 static int
sp_uuid_cmp(const void * l,const void * r)427 sp_uuid_cmp( const void *l, const void *r )
428 {
429           const struct berval *left = l, *right = r;
430 
431           return ber_bvcmp( left, right );
432 }
433 
434 static int
syncprov_sessionlog_cmp(const void * l,const void * r)435 syncprov_sessionlog_cmp( const void *l, const void *r )
436 {
437           const slog_entry *left = l, *right = r;
438           int ret = ber_bvcmp( &left->se_csn, &right->se_csn );
439           if ( !ret )
440                     ret = ber_bvcmp( &left->se_uuid, &right->se_uuid );
441           /* Only time we have two modifications with same CSN is when we detect a
442            * rename during replication.
443            * We invert the test here because LDAP_REQ_MODDN is
444            * numerically greater than LDAP_REQ_MODIFY but we
445            * want it to occur first.
446            */
447           if ( !ret )
448                     ret = right->se_tag - left->se_tag;
449 
450           return ret;
451 }
452 
453 /* syncprov_findbase:
454  *   finds the true DN of the base of a search (with alias dereferencing) and
455  * checks to make sure the base entry doesn't get replaced with a different
456  * entry (e.g., swapping trees via ModDN, or retargeting an alias). If a
457  * change is detected, any persistent search on this base must be terminated /
458  * reloaded.
459  *   On the first call, we just save the DN and entryID. On subsequent calls
460  * we compare the DN and entryID with the saved values.
461  */
462 static int
findbase_cb(Operation * op,SlapReply * rs)463 findbase_cb( Operation *op, SlapReply *rs )
464 {
465           slap_callback *sc = op->o_callback;
466 
467           if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
468                     fbase_cookie *fc = sc->sc_private;
469 
470                     /* If no entryID, we're looking for the first time.
471                      * Just store whatever we got.
472                      */
473                     if ( fc->fss->s_eid == NOID ) {
474                               fc->fbase = 2;
475                               fc->fss->s_eid = rs->sr_entry->e_id;
476                               ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
477 
478                     } else if ( rs->sr_entry->e_id == fc->fss->s_eid &&
479                               dn_match( &rs->sr_entry->e_nname, &fc->fss->s_base )) {
480 
481                     /* OK, the DN is the same and the entryID is the same. */
482                               fc->fbase = 1;
483                     }
484           }
485           if ( rs->sr_err != LDAP_SUCCESS ) {
486                     Debug( LDAP_DEBUG_ANY, "findbase failed! %d\n", rs->sr_err );
487           }
488           return LDAP_SUCCESS;
489 }
490 
491 static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL };
492 static struct berval generic_filterstr = BER_BVC("(objectclass=*)");
493 
494 static int
syncprov_findbase(Operation * op,fbase_cookie * fc)495 syncprov_findbase( Operation *op, fbase_cookie *fc )
496 {
497           /* Use basic parameters from syncrepl search, but use
498            * current op's threadctx / tmpmemctx
499            */
500           ldap_pvt_thread_mutex_lock( &fc->fss->s_mutex );
501           if ( fc->fss->s_flags & PS_FIND_BASE ) {
502                     slap_callback cb = {0};
503                     Operation fop;
504                     SlapReply frs = { REP_RESULT };
505                     int rc;
506 
507                     fc->fss->s_flags ^= PS_FIND_BASE;
508                     ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
509 
510                     fop = *fc->fss->s_op;
511 
512                     fop.o_bd = fop.o_bd->bd_self;
513                     fop.o_hdr = op->o_hdr;
514                     fop.o_time = op->o_time;
515                     fop.o_tincr = op->o_tincr;
516                     fop.o_extra = op->o_extra;
517 
518                     cb.sc_response = findbase_cb;
519                     cb.sc_private = fc;
520 
521                     fop.o_sync_mode = 0;          /* turn off sync mode */
522                     fop.o_managedsait = SLAP_CONTROL_CRITICAL;
523                     fop.o_callback = &cb;
524                     fop.o_tag = LDAP_REQ_SEARCH;
525                     fop.ors_scope = LDAP_SCOPE_BASE;
526                     fop.ors_limit = NULL;
527                     fop.ors_slimit = 1;
528                     fop.ors_tlimit = SLAP_NO_LIMIT;
529                     fop.ors_attrs = slap_anlist_no_attrs;
530                     fop.ors_attrsonly = 1;
531                     fop.ors_filter = &generic_filter;
532                     fop.ors_filterstr = generic_filterstr;
533 
534                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_findbase: searching\n", op->o_log_prefix );
535                     rc = fop.o_bd->be_search( &fop, &frs );
536           } else {
537                     ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
538                     fc->fbase = 1;
539           }
540 
541           /* After the first call, see if the fdn resides in the scope */
542           if ( fc->fbase == 1 ) {
543                     switch ( fc->fss->s_op->ors_scope ) {
544                     case LDAP_SCOPE_BASE:
545                               fc->fscope = dn_match( fc->fdn, &fc->fss->s_base );
546                               break;
547                     case LDAP_SCOPE_ONELEVEL: {
548                               struct berval pdn;
549                               dnParent( fc->fdn, &pdn );
550                               fc->fscope = dn_match( &pdn, &fc->fss->s_base );
551                               break; }
552                     case LDAP_SCOPE_SUBTREE:
553                               fc->fscope = dnIsSuffix( fc->fdn, &fc->fss->s_base );
554                               break;
555                     case LDAP_SCOPE_SUBORDINATE:
556                               fc->fscope = dnIsSuffix( fc->fdn, &fc->fss->s_base ) &&
557                                         !dn_match( fc->fdn, &fc->fss->s_base );
558                               break;
559                     }
560           }
561 
562           if ( fc->fbase )
563                     return LDAP_SUCCESS;
564 
565           /* If entryID has changed, then the base of this search has
566            * changed. Invalidate the psearch.
567            */
568           return LDAP_NO_SUCH_OBJECT;
569 }
570 
571 /* syncprov_findcsn:
572  *   This function has three different purposes, but they all use a search
573  * that filters on entryCSN so they're combined here.
574  * 1: at startup time, after a contextCSN has been read from the database,
575  * we search for all entries with CSN >= contextCSN in case the contextCSN
576  * was not checkpointed at the previous shutdown.
577  *
578  * 2: when the current contextCSN is known and we have a sync cookie, we search
579  * for one entry with CSN = the cookie CSN. If not found, try <= cookie CSN.
580  * If an entry is found, the cookie CSN is valid, otherwise it is stale.
581  *
582  * 3: during a refresh phase, we search for all entries with CSN <= the cookie
583  * CSN, and generate Present records for them. We always collect this result
584  * in SyncID sets, even if there's only one match.
585  */
586 typedef enum find_csn_t {
587           FIND_MAXCSN         = 1,
588           FIND_CSN  = 2,
589           FIND_PRESENT        = 3
590 } find_csn_t;
591 
592 static int
findmax_cb(Operation * op,SlapReply * rs)593 findmax_cb( Operation *op, SlapReply *rs )
594 {
595           if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
596                     struct berval *maxcsn = op->o_callback->sc_private;
597                     Attribute *a = attr_find( rs->sr_entry->e_attrs,
598                               slap_schema.si_ad_entryCSN );
599 
600                     if ( a && ber_bvcmp( &a->a_vals[0], maxcsn ) > 0 &&
601                               slap_parse_csn_sid( &a->a_vals[0] ) == slap_serverID ) {
602                               maxcsn->bv_len = a->a_vals[0].bv_len;
603                               strcpy( maxcsn->bv_val, a->a_vals[0].bv_val );
604                     }
605           }
606           return LDAP_SUCCESS;
607 }
608 
609 static int
findcsn_cb(Operation * op,SlapReply * rs)610 findcsn_cb( Operation *op, SlapReply *rs )
611 {
612           slap_callback *sc = op->o_callback;
613 
614           /* We just want to know that at least one exists, so it's OK if
615            * we exceed the unchecked limit.
616            */
617           if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ||
618                     (rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS )) {
619                     sc->sc_private = (void *)1;
620           }
621           return LDAP_SUCCESS;
622 }
623 
624 /* Build a list of entryUUIDs for sending in a SyncID set */
625 
626 #define UUID_LEN    16
627 
628 typedef struct fpres_cookie {
629           int num;
630           BerVarray uuids;
631           char *last;
632 } fpres_cookie;
633 
634 static int
findpres_cb(Operation * op,SlapReply * rs)635 findpres_cb( Operation *op, SlapReply *rs )
636 {
637           slap_callback *sc = op->o_callback;
638           fpres_cookie *pc = sc->sc_private;
639           Attribute *a;
640           int ret = SLAP_CB_CONTINUE;
641 
642           switch ( rs->sr_type ) {
643           case REP_SEARCH:
644                     a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
645                     if ( a ) {
646                               pc->uuids[pc->num].bv_val = pc->last;
647                               AC_MEMCPY( pc->uuids[pc->num].bv_val, a->a_nvals[0].bv_val,
648                                         pc->uuids[pc->num].bv_len );
649                               pc->num++;
650                               pc->last = pc->uuids[pc->num].bv_val;
651                               pc->uuids[pc->num].bv_val = NULL;
652                     }
653                     ret = LDAP_SUCCESS;
654                     if ( pc->num != SLAP_SYNCUUID_SET_SIZE )
655                               break;
656                     /* FALLTHRU */
657           case REP_RESULT:
658                     ret = rs->sr_err;
659                     if ( pc->num ) {
660                               ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
661                                         0, pc->uuids, 0 );
662                               pc->uuids[pc->num].bv_val = pc->last;
663                               pc->num = 0;
664                               pc->last = pc->uuids[0].bv_val;
665                     }
666                     break;
667           default:
668                     break;
669           }
670           return ret;
671 }
672 
673 static int
syncprov_findcsn(Operation * op,find_csn_t mode,struct berval * csn)674 syncprov_findcsn( Operation *op, find_csn_t mode, struct berval *csn )
675 {
676           slap_overinst                 *on = (slap_overinst *)op->o_bd->bd_info;
677           syncprov_info_t               *si = on->on_bi.bi_private;
678 
679           slap_callback cb = {0};
680           Operation fop;
681           SlapReply frs = { REP_RESULT };
682           char buf[LDAP_PVT_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
683           char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
684           struct berval maxcsn;
685           Filter cf;
686           AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
687           fpres_cookie pcookie;
688           sync_control *srs = NULL;
689           struct slap_limits_set fc_limits;
690           int i, rc = LDAP_SUCCESS, findcsn_retry = 1;
691           int maxid;
692 
693           if ( mode != FIND_MAXCSN ) {
694                     srs = op->o_controls[slap_cids.sc_LDAPsync];
695           }
696 
697           Debug( LDAP_DEBUG_SYNC, "%s syncprov_findcsn: mode=%s csn=%s\n",
698                     op->o_log_prefix,
699                     mode == FIND_MAXCSN ?
700                               "FIND_MAXCSN" :
701                               mode == FIND_CSN ?
702                                         "FIND_CSN" :
703                                         "FIND_PRESENT",
704                     csn ? csn->bv_val : "" );
705 
706           fop = *op;
707           fop.o_sync_mode &= SLAP_CONTROL_MASK;   /* turn off sync_mode */
708           /* We want pure entries, not referrals */
709           fop.o_managedsait = SLAP_CONTROL_CRITICAL;
710 
711           cf.f_ava = &eq;
712           cf.f_av_desc = slap_schema.si_ad_entryCSN;
713           BER_BVZERO( &cf.f_av_value );
714           cf.f_next = NULL;
715 
716           fop.o_callback = &cb;
717           fop.ors_limit = NULL;
718           fop.ors_tlimit = SLAP_NO_LIMIT;
719           fop.ors_filter = &cf;
720           fop.ors_filterstr.bv_val = buf;
721 
722 again:
723           switch( mode ) {
724           case FIND_MAXCSN:
725                     cf.f_choice = LDAP_FILTER_GE;
726                     /* If there are multiple CSNs, use the one with our serverID */
727                     for ( i=0; i<si->si_numcsns; i++) {
728                               if ( slap_serverID == si->si_sids[i] ) {
729                                         maxid = i;
730                                         break;
731                               }
732                     }
733                     if ( i == si->si_numcsns ) {
734                               /* No match: this is multimaster, and none of the content in the DB
735                                * originated locally. Treat like no CSN.
736                                */
737                               return LDAP_NO_SUCH_OBJECT;
738                     }
739                     cf.f_av_value = si->si_ctxcsn[maxid];
740                     fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
741                               "(entryCSN>=%s)", cf.f_av_value.bv_val );
742                     if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
743                               return LDAP_OTHER;
744                     }
745                     fop.ors_attrsonly = 0;
746                     fop.ors_attrs = csn_anlist;
747                     fop.ors_slimit = SLAP_NO_LIMIT;
748                     cb.sc_private = &maxcsn;
749                     cb.sc_response = findmax_cb;
750                     strcpy( cbuf, cf.f_av_value.bv_val );
751                     maxcsn.bv_val = cbuf;
752                     maxcsn.bv_len = cf.f_av_value.bv_len;
753                     break;
754           case FIND_CSN:
755                     if ( BER_BVISEMPTY( &cf.f_av_value )) {
756                               cf.f_av_value = *csn;
757                     }
758                     fop.o_dn = op->o_bd->be_rootdn;
759                     fop.o_ndn = op->o_bd->be_rootndn;
760                     fop.o_req_dn = op->o_bd->be_suffix[0];
761                     fop.o_req_ndn = op->o_bd->be_nsuffix[0];
762                     /* Look for exact match the first time */
763                     if ( findcsn_retry ) {
764                               cf.f_choice = LDAP_FILTER_EQUALITY;
765                               fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
766                                         "(entryCSN=%s)", cf.f_av_value.bv_val );
767                     /* On retry, look for <= */
768                     } else {
769                               cf.f_choice = LDAP_FILTER_LE;
770                               fop.ors_limit = &fc_limits;
771                               memset( &fc_limits, 0, sizeof( fc_limits ));
772                               fc_limits.lms_s_unchecked = 1;
773                               fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
774                                         "(entryCSN<=%s)", cf.f_av_value.bv_val );
775                     }
776                     if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
777                               return LDAP_OTHER;
778                     }
779                     fop.ors_attrsonly = 1;
780                     fop.ors_attrs = slap_anlist_no_attrs;
781                     fop.ors_slimit = 1;
782                     cb.sc_private = NULL;
783                     cb.sc_response = findcsn_cb;
784                     break;
785           case FIND_PRESENT:
786                     fop.ors_filter = op->ors_filter;
787                     fop.ors_filterstr = op->ors_filterstr;
788                     fop.ors_attrsonly = 0;
789                     fop.ors_attrs = uuid_anlist;
790                     fop.ors_slimit = SLAP_NO_LIMIT;
791                     cb.sc_private = &pcookie;
792                     cb.sc_response = findpres_cb;
793                     pcookie.num = 0;
794 
795                     /* preallocate storage for a full set */
796                     pcookie.uuids = op->o_tmpalloc( (SLAP_SYNCUUID_SET_SIZE+1) *
797                               sizeof(struct berval) + SLAP_SYNCUUID_SET_SIZE * UUID_LEN,
798                               op->o_tmpmemctx );
799                     pcookie.last = (char *)(pcookie.uuids + SLAP_SYNCUUID_SET_SIZE+1);
800                     pcookie.uuids[0].bv_val = pcookie.last;
801                     pcookie.uuids[0].bv_len = UUID_LEN;
802                     for (i=1; i<SLAP_SYNCUUID_SET_SIZE; i++) {
803                               pcookie.uuids[i].bv_val = pcookie.uuids[i-1].bv_val + UUID_LEN;
804                               pcookie.uuids[i].bv_len = UUID_LEN;
805                     }
806                     break;
807           }
808 
809           fop.o_bd->bd_info = (BackendInfo *)on->on_info;
810           fop.o_bd->be_search( &fop, &frs );
811           fop.o_bd->bd_info = (BackendInfo *)on;
812 
813           switch( mode ) {
814           case FIND_MAXCSN:
815                     if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
816 #ifdef CHECK_CSN
817                               Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
818                               assert( !syn->ssyn_validate( syn, &maxcsn ));
819 #endif
820                               ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
821                               si->si_numops++;    /* ensure a checkpoint */
822                     }
823                     break;
824           case FIND_CSN:
825                     /* If matching CSN was not found, invalidate the context. */
826                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_findcsn: csn%s=%s %sfound\n",
827                               op->o_log_prefix,
828                               cf.f_choice == LDAP_FILTER_EQUALITY ? "=" : "<",
829                               cf.f_av_value.bv_val, cb.sc_private ? "" : "not " );
830                     if ( !cb.sc_private ) {
831                               /* If we didn't find an exact match, then try for <= */
832                               if ( findcsn_retry ) {
833                                         findcsn_retry = 0;
834                                         rs_reinit( &frs, REP_RESULT );
835                                         goto again;
836                               }
837                               rc = LDAP_NO_SUCH_OBJECT;
838                     }
839                     break;
840           case FIND_PRESENT:
841                     op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
842                     break;
843           }
844 
845           return rc;
846 }
847 
free_resinfo(syncres * sr)848 static void free_resinfo( syncres *sr )
849 {
850           syncres **st;
851           int freeit = 0;
852           ldap_pvt_thread_mutex_lock( &sr->s_info->ri_mutex );
853           for (st = &sr->s_info->ri_list; *st; st = &(*st)->s_rilist) {
854                     if (*st == sr) {
855                               *st = sr->s_rilist;
856                               break;
857                     }
858           }
859           if ( !sr->s_info->ri_list )
860                     freeit = 1;
861           ldap_pvt_thread_mutex_unlock( &sr->s_info->ri_mutex );
862           if ( freeit ) {
863                     ldap_pvt_thread_mutex_destroy( &sr->s_info->ri_mutex );
864                     if ( sr->s_info->ri_e )
865                               entry_free( sr->s_info->ri_e );
866                     if ( !BER_BVISNULL( &sr->s_info->ri_cookie ))
867                               ch_free( sr->s_info->ri_cookie.bv_val );
868                     ch_free( sr->s_info );
869           }
870 }
871 
872 #define FS_UNLINK   1
873 #define FS_LOCK               2
874 
875 static int
syncprov_free_syncop(syncops * so,int flags)876 syncprov_free_syncop( syncops *so, int flags )
877 {
878           syncres *sr, *srnext;
879           GroupAssertion *ga, *gnext;
880 
881           if ( flags & FS_LOCK )
882                     ldap_pvt_thread_mutex_lock( &so->s_mutex );
883           /* already being freed, or still in use */
884           if ( !so->s_inuse || --so->s_inuse > 0 ) {
885                     if ( flags & FS_LOCK )
886                               ldap_pvt_thread_mutex_unlock( &so->s_mutex );
887                     return 0;
888           }
889           ldap_pvt_thread_mutex_unlock( &so->s_mutex );
890           if (( flags & FS_UNLINK ) && so->s_si ) {
891                     syncops **sop;
892                     ldap_pvt_thread_mutex_lock( &so->s_si->si_ops_mutex );
893                     for ( sop = &so->s_si->si_ops; *sop; sop = &(*sop)->s_next ) {
894                               if ( *sop == so ) {
895                                         *sop = so->s_next;
896                                         break;
897                               }
898                     }
899                     ldap_pvt_thread_mutex_unlock( &so->s_si->si_ops_mutex );
900           }
901           if ( so->s_flags & PS_IS_DETACHED ) {
902                     filter_free( so->s_op->ors_filter );
903                     for ( ga = so->s_op->o_groups; ga; ga=gnext ) {
904                               gnext = ga->ga_next;
905                               ch_free( ga );
906                     }
907                     ch_free( so->s_op );
908           }
909           ch_free( so->s_base.bv_val );
910           for ( sr=so->s_res; sr; sr=srnext ) {
911                     srnext = sr->s_next;
912                     free_resinfo( sr );
913                     ch_free( sr );
914           }
915           ldap_pvt_thread_mutex_destroy( &so->s_mutex );
916           ch_free( so );
917           return 1;
918 }
919 
920 /* Send a persistent search response */
921 static int
syncprov_sendresp(Operation * op,resinfo * ri,syncops * so,int mode)922 syncprov_sendresp( Operation *op, resinfo *ri, syncops *so, int mode )
923 {
924           SlapReply rs = { REP_SEARCH };
925           struct berval cookie, csns[2];
926           Entry e_uuid = {0};
927           Attribute a_uuid = {0};
928 
929           if ( so->s_op->o_abandon )
930                     return SLAPD_ABANDON;
931 
932           rs.sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2, op->o_tmpmemctx );
933           rs.sr_ctrls[1] = NULL;
934           rs.sr_flags = REP_CTRLS_MUSTBEFREED;
935           csns[0] = ri->ri_csn;
936           BER_BVZERO( &csns[1] );
937           slap_compose_sync_cookie( op, &cookie, csns, so->s_rid,
938                                          slap_serverID ? slap_serverID : -1, NULL );
939 
940 #ifdef LDAP_DEBUG
941           if ( so->s_sid > 0 ) {
942                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: to=%03x, cookie=%s\n",
943                               op->o_log_prefix, so->s_sid, cookie.bv_val );
944           } else {
945                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: cookie=%s\n",
946                               op->o_log_prefix, cookie.bv_val );
947           }
948 #endif
949 
950           e_uuid.e_attrs = &a_uuid;
951           a_uuid.a_desc = slap_schema.si_ad_entryUUID;
952           a_uuid.a_nvals = &ri->ri_uuid;
953           rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
954                     mode, rs.sr_ctrls, 0, 1, &cookie );
955           op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
956 
957           rs.sr_entry = &e_uuid;
958           if ( mode == LDAP_SYNC_ADD || mode == LDAP_SYNC_MODIFY ) {
959                     e_uuid = *ri->ri_e;
960                     e_uuid.e_private = NULL;
961           }
962 
963           switch( mode ) {
964           case LDAP_SYNC_ADD:
965                     if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
966                               rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
967                               rs.sr_err = send_search_reference( op, &rs );
968                               ber_bvarray_free( rs.sr_ref );
969                               break;
970                     }
971                     /* fallthru */
972           case LDAP_SYNC_MODIFY:
973                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: sending %s, dn=%s\n",
974                               op->o_log_prefix,
975                               mode == LDAP_SYNC_ADD ? "LDAP_SYNC_ADD" : "LDAP_SYNC_MODIFY",
976                               e_uuid.e_nname.bv_val );
977                     rs.sr_attrs = op->ors_attrs;
978                     rs.sr_err = send_search_entry( op, &rs );
979                     break;
980           case LDAP_SYNC_DELETE:
981                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: "
982                               "sending LDAP_SYNC_DELETE, dn=%s\n",
983                               op->o_log_prefix, ri->ri_dn.bv_val );
984                     e_uuid.e_attrs = NULL;
985                     e_uuid.e_name = ri->ri_dn;
986                     e_uuid.e_nname = ri->ri_ndn;
987                     if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
988                               struct berval bv = BER_BVNULL;
989                               rs.sr_ref = &bv;
990                               rs.sr_err = send_search_reference( op, &rs );
991                     } else {
992                               rs.sr_err = send_search_entry( op, &rs );
993                     }
994                     break;
995           default:
996                     assert(0);
997           }
998           return rs.sr_err;
999 }
1000 
1001 static void
1002 syncprov_qstart( syncops *so );
1003 
1004 /* Play back queued responses */
1005 static int
syncprov_qplay(Operation * op,syncops * so)1006 syncprov_qplay( Operation *op, syncops *so )
1007 {
1008           syncres *sr;
1009           int rc = 0;
1010 
1011           do {
1012                     ldap_pvt_thread_mutex_lock( &so->s_mutex );
1013                     sr = so->s_res;
1014                     /* Exit loop with mutex held */
1015                     if ( !sr )
1016                               break;
1017                     so->s_res = sr->s_next;
1018                     if ( !so->s_res )
1019                               so->s_restail = NULL;
1020                     ldap_pvt_thread_mutex_unlock( &so->s_mutex );
1021 
1022                     if ( !so->s_op->o_abandon ) {
1023 
1024                               if ( sr->s_mode == LDAP_SYNC_NEW_COOKIE ) {
1025                                         SlapReply rs = { REP_INTERMEDIATE };
1026 
1027                                         rc = syncprov_sendinfo( op, &rs, LDAP_TAG_SYNC_NEW_COOKIE,
1028                                                   &sr->s_info->ri_cookie, 0, NULL, 0 );
1029                               } else {
1030                                         rc = syncprov_sendresp( op, sr->s_info, so, sr->s_mode );
1031                               }
1032                     }
1033 
1034                     free_resinfo( sr );
1035                     ch_free( sr );
1036 
1037                     if ( so->s_op->o_abandon )
1038                               continue;
1039 
1040                     /* Exit loop with mutex held */
1041                     ldap_pvt_thread_mutex_lock( &so->s_mutex );
1042                     break;
1043 
1044           } while (1);
1045 
1046           /* We now only send one change at a time, to prevent one
1047            * psearch from hogging all the CPU. Resubmit this task if
1048            * there are more responses queued and no errors occurred.
1049            */
1050 
1051           if ( rc == 0 && so->s_res ) {
1052                     syncprov_qstart( so );
1053           }
1054 
1055           return rc;
1056 }
1057 
1058 /* task for playing back queued responses */
1059 static void *
syncprov_qtask(void * ctx,void * arg)1060 syncprov_qtask( void *ctx, void *arg )
1061 {
1062           syncops *so = arg;
1063           OperationBuffer opbuf;
1064           Operation *op;
1065           BackendDB be;
1066           int rc;
1067 
1068           op = &opbuf.ob_op;
1069           *op = *so->s_op;
1070           op->o_hdr = &opbuf.ob_hdr;
1071           op->o_controls = opbuf.ob_controls;
1072           memset( op->o_controls, 0, sizeof(opbuf.ob_controls) );
1073           op->o_sync = SLAP_CONTROL_IGNORED;
1074 
1075           *op->o_hdr = *so->s_op->o_hdr;
1076 
1077           op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 1);
1078           op->o_tmpmfuncs = &slap_sl_mfuncs;
1079           op->o_threadctx = ctx;
1080 
1081           /* syncprov_qplay expects a fake db */
1082           be = *so->s_op->o_bd;
1083           be.be_flags |= SLAP_DBFLAG_OVERLAY;
1084           op->o_bd = &be;
1085           LDAP_SLIST_FIRST(&op->o_extra) = NULL;
1086           op->o_callback = NULL;
1087 
1088           rc = syncprov_qplay( op, so );
1089 
1090           /* if an error occurred, or no responses left, task is no longer queued */
1091           if ( !rc && !so->s_res )
1092                     rc = 1;
1093 
1094           /* decrement use count... */
1095           if ( !syncprov_free_syncop( so, FS_UNLINK )) {
1096                     if ( rc )
1097                               /* if we didn't unlink, and task is no longer queued, clear flag */
1098                               so->s_flags ^= PS_TASK_QUEUED;
1099                     ldap_pvt_thread_mutex_unlock( &so->s_mutex );
1100           }
1101 
1102           return NULL;
1103 }
1104 
1105 /* Start the task to play back queued psearch responses */
1106 static void
syncprov_qstart(syncops * so)1107 syncprov_qstart( syncops *so )
1108 {
1109           so->s_flags |= PS_TASK_QUEUED;
1110           so->s_inuse++;
1111           ldap_pvt_thread_pool_submit2( &connection_pool,
1112                     syncprov_qtask, so, &so->s_pool_cookie );
1113 }
1114 
1115 /* Queue a persistent search response */
1116 static int
syncprov_qresp(opcookie * opc,syncops * so,int mode)1117 syncprov_qresp( opcookie *opc, syncops *so, int mode )
1118 {
1119           syncres *sr;
1120           resinfo *ri;
1121           int srsize;
1122           struct berval csn = opc->sctxcsn;
1123 
1124           sr = ch_malloc( sizeof( syncres ));
1125           sr->s_next = NULL;
1126           sr->s_mode = mode;
1127           if ( !opc->ssres.s_info ) {
1128 
1129                     srsize = sizeof( resinfo );
1130                     if ( csn.bv_len )
1131                               srsize += csn.bv_len + 1;
1132 
1133                     if ( opc->se ) {
1134                               Attribute *a;
1135                               ri = ch_malloc( srsize );
1136                               ri->ri_dn = opc->se->e_name;
1137                               ri->ri_ndn = opc->se->e_nname;
1138                               a = attr_find( opc->se->e_attrs, slap_schema.si_ad_entryUUID );
1139                               if ( a )
1140                                         ri->ri_uuid = a->a_nvals[0];
1141                               else
1142                                         ri->ri_uuid.bv_len = 0;
1143                               if ( csn.bv_len ) {
1144                                         ri->ri_csn.bv_val = (char *)(ri + 1);
1145                                         ri->ri_csn.bv_len = csn.bv_len;
1146                                         memcpy( ri->ri_csn.bv_val, csn.bv_val, csn.bv_len );
1147                                         ri->ri_csn.bv_val[csn.bv_len] = '\0';
1148                               } else {
1149                                         ri->ri_csn.bv_val = NULL;
1150                               }
1151                     } else {
1152                               srsize += opc->suuid.bv_len +
1153                                         opc->sdn.bv_len + 1 + opc->sndn.bv_len + 1;
1154                               ri = ch_malloc( srsize );
1155                               ri->ri_dn.bv_val = (char *)(ri + 1);
1156                               ri->ri_dn.bv_len = opc->sdn.bv_len;
1157                               ri->ri_ndn.bv_val = lutil_strcopy( ri->ri_dn.bv_val,
1158                                         opc->sdn.bv_val ) + 1;
1159                               ri->ri_ndn.bv_len = opc->sndn.bv_len;
1160                               ri->ri_uuid.bv_val = lutil_strcopy( ri->ri_ndn.bv_val,
1161                                         opc->sndn.bv_val ) + 1;
1162                               ri->ri_uuid.bv_len = opc->suuid.bv_len;
1163                               AC_MEMCPY( ri->ri_uuid.bv_val, opc->suuid.bv_val, opc->suuid.bv_len );
1164                               if ( csn.bv_len ) {
1165                                         ri->ri_csn.bv_val = ri->ri_uuid.bv_val + ri->ri_uuid.bv_len;
1166                                         memcpy( ri->ri_csn.bv_val, csn.bv_val, csn.bv_len );
1167                                         ri->ri_csn.bv_val[csn.bv_len] = '\0';
1168                               } else {
1169                                         ri->ri_csn.bv_val = NULL;
1170                               }
1171                     }
1172                     ri->ri_list = &opc->ssres;
1173                     ri->ri_e = opc->se;
1174                     ri->ri_csn.bv_len = csn.bv_len;
1175                     ri->ri_isref = opc->sreference;
1176                     BER_BVZERO( &ri->ri_cookie );
1177                     ldap_pvt_thread_mutex_init( &ri->ri_mutex );
1178                     opc->se = NULL;
1179                     opc->ssres.s_info = ri;
1180           }
1181           ri = opc->ssres.s_info;
1182           sr->s_info = ri;
1183           ldap_pvt_thread_mutex_lock( &ri->ri_mutex );
1184           sr->s_rilist = ri->ri_list;
1185           ri->ri_list = sr;
1186           if ( mode == LDAP_SYNC_NEW_COOKIE && BER_BVISNULL( &ri->ri_cookie )) {
1187                     syncprov_info_t     *si = opc->son->on_bi.bi_private;
1188 
1189                     slap_compose_sync_cookie( NULL, &ri->ri_cookie, si->si_ctxcsn,
1190                               so->s_rid, slap_serverID ? slap_serverID : -1, NULL );
1191           }
1192           Debug( LDAP_DEBUG_SYNC, "%s syncprov_qresp: "
1193                     "set up a new syncres mode=%d csn=%s\n",
1194                     so->s_op->o_log_prefix, mode, csn.bv_val ? csn.bv_val : "" );
1195           ldap_pvt_thread_mutex_unlock( &ri->ri_mutex );
1196 
1197           ldap_pvt_thread_mutex_lock( &so->s_mutex );
1198           if ( !so->s_res ) {
1199                     so->s_res = sr;
1200           } else {
1201                     so->s_restail->s_next = sr;
1202           }
1203           so->s_restail = sr;
1204 
1205           /* If the base of the psearch was modified, check it next time round */
1206           if ( so->s_flags & PS_WROTE_BASE ) {
1207                     so->s_flags ^= PS_WROTE_BASE;
1208                     so->s_flags |= PS_FIND_BASE;
1209           }
1210           if (( so->s_flags & (PS_IS_DETACHED|PS_TASK_QUEUED)) == PS_IS_DETACHED ) {
1211                     syncprov_qstart( so );
1212           }
1213           ldap_pvt_thread_mutex_unlock( &so->s_mutex );
1214           return LDAP_SUCCESS;
1215 }
1216 
1217 static int
syncprov_drop_psearch(syncops * so,int lock)1218 syncprov_drop_psearch( syncops *so, int lock )
1219 {
1220           if ( so->s_flags & PS_IS_DETACHED ) {
1221                     if ( lock )
1222                               ldap_pvt_thread_mutex_lock( &so->s_op->o_conn->c_mutex );
1223                     so->s_op->o_conn->c_n_ops_executing--;
1224                     so->s_op->o_conn->c_n_ops_completed++;
1225                     LDAP_STAILQ_REMOVE( &so->s_op->o_conn->c_ops, so->s_op, Operation,
1226                               o_next );
1227                     if ( lock )
1228                               ldap_pvt_thread_mutex_unlock( &so->s_op->o_conn->c_mutex );
1229           }
1230           return syncprov_free_syncop( so, FS_LOCK );
1231 }
1232 
1233 static int
syncprov_ab_cleanup(Operation * op,SlapReply * rs)1234 syncprov_ab_cleanup( Operation *op, SlapReply *rs )
1235 {
1236           slap_callback *sc = op->o_callback;
1237           op->o_callback = sc->sc_next;
1238           syncprov_drop_psearch( sc->sc_private, 0 );
1239           op->o_tmpfree( sc, op->o_tmpmemctx );
1240           return 0;
1241 }
1242 
1243 static int
syncprov_op_abandon(Operation * op,SlapReply * rs)1244 syncprov_op_abandon( Operation *op, SlapReply *rs )
1245 {
1246           slap_overinst                 *on = (slap_overinst *)op->o_bd->bd_info;
1247           syncprov_info_t               *si = on->on_bi.bi_private;
1248           syncops *so, **sop;
1249 
1250           ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1251           for ( sop=&si->si_ops; (so = *sop); sop = &(*sop)->s_next ) {
1252                     if ( so->s_op->o_connid == op->o_connid &&
1253                               so->s_op->o_msgid == op->orn_msgid ) {
1254                                         so->s_op->o_abandon = 1;
1255                                         *sop = so->s_next;
1256                                         break;
1257                     }
1258           }
1259           ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1260           if ( so ) {
1261                     /* Is this really a Cancel exop? */
1262                     if ( op->o_tag != LDAP_REQ_ABANDON ) {
1263                               so->s_op->o_cancel = SLAP_CANCEL_ACK;
1264                               rs->sr_err = LDAP_CANCELLED;
1265                               send_ldap_result( so->s_op, rs );
1266                               if ( so->s_flags & PS_IS_DETACHED ) {
1267                                         slap_callback *cb;
1268                                         cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
1269                                         cb->sc_cleanup = syncprov_ab_cleanup;
1270                                         cb->sc_next = op->o_callback;
1271                                         cb->sc_private = so;
1272                                         op->o_callback = cb;
1273                                         return SLAP_CB_CONTINUE;
1274                               }
1275                     }
1276                     syncprov_drop_psearch( so, 0 );
1277           }
1278           return SLAP_CB_CONTINUE;
1279 }
1280 
1281 /* Find which persistent searches are affected by this operation */
1282 static void
syncprov_matchops(Operation * op,opcookie * opc,int saveit)1283 syncprov_matchops( Operation *op, opcookie *opc, int saveit )
1284 {
1285           slap_overinst *on = opc->son;
1286           syncprov_info_t               *si = on->on_bi.bi_private;
1287 
1288           fbase_cookie fc;
1289           syncops **pss;
1290           Entry *e = NULL;
1291           Attribute *a;
1292           int rc, gonext;
1293           struct berval newdn;
1294           int freefdn = 0;
1295           BackendDB *b0 = op->o_bd, db;
1296 
1297           fc.fdn = &op->o_req_ndn;
1298           /* compute new DN */
1299           if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
1300                     struct berval pdn;
1301                     if ( op->orr_nnewSup ) pdn = *op->orr_nnewSup;
1302                     else dnParent( fc.fdn, &pdn );
1303                     build_new_dn( &newdn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
1304                     fc.fdn = &newdn;
1305                     freefdn = 1;
1306           }
1307           if ( op->o_tag != LDAP_REQ_ADD ) {
1308                     if ( !SLAP_ISOVERLAY( op->o_bd )) {
1309                               db = *op->o_bd;
1310                               op->o_bd = &db;
1311                     }
1312                     rc = overlay_entry_get_ov( op, fc.fdn, NULL, NULL, 0, &e, on );
1313                     /* If we're sending responses now, make a copy and unlock the DB */
1314                     if ( e && !saveit ) {
1315                               if ( !opc->se )
1316                                         opc->se = entry_dup( e );
1317                               overlay_entry_release_ov( op, e, 0, on );
1318                               e = opc->se;
1319                     }
1320                     if ( rc ) {
1321                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_matchops: "
1322                                         "%s check, error finding entry dn=%s in database\n",
1323                                         op->o_log_prefix, saveit ? "initial" : "final", fc.fdn->bv_val );
1324                               op->o_bd = b0;
1325                               return;
1326                     }
1327           } else {
1328                     e = op->ora_e;
1329                     if ( !saveit ) {
1330                               if ( !opc->se )
1331                                         opc->se = entry_dup( e );
1332                               e = opc->se;
1333                     }
1334           }
1335 
1336           if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
1337                     ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
1338                     ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
1339                     opc->sreference = is_entry_referral( e );
1340                     a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
1341                     if ( a )
1342                               ber_dupbv_x( &opc->suuid, &a->a_nvals[0], op->o_tmpmemctx );
1343                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_matchops: "
1344                               "%srecording uuid for dn=%s on opc=%p\n",
1345                               op->o_log_prefix, a ? "" : "not ", opc->sdn.bv_val, opc );
1346           } else if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
1347                     op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
1348                     op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
1349                     ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
1350                     ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
1351           }
1352 
1353           ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1354           for (pss = &si->si_ops; *pss; pss = gonext ? &(*pss)->s_next : pss)
1355           {
1356                     Operation op2;
1357                     Opheader oh;
1358                     syncmatches *sm;
1359                     int found = 0;
1360                     syncops *snext, *ss = *pss;
1361 
1362                     gonext = 1;
1363                     if ( ss->s_op->o_abandon )
1364                               continue;
1365 
1366                     /* Don't send ops back to the originator */
1367                     if ( opc->osid > 0 && opc->osid == ss->s_sid ) {
1368                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_matchops: "
1369                                         "skipping original sid %03x\n",
1370                                         ss->s_op->o_log_prefix, opc->osid );
1371                               continue;
1372                     }
1373 
1374                     /* Don't send ops back to the messenger */
1375                     if ( opc->rsid > 0 && opc->rsid == ss->s_sid ) {
1376                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_matchops: "
1377                                         "skipping relayed sid %03x\n",
1378                                         ss->s_op->o_log_prefix, opc->rsid );
1379                               continue;
1380                     }
1381 
1382                     /* validate base */
1383                     fc.fss = ss;
1384                     fc.fbase = 0;
1385                     fc.fscope = 0;
1386 
1387                     /* If the base of the search is missing, signal a refresh */
1388                     rc = syncprov_findbase( op, &fc );
1389                     if ( rc != LDAP_SUCCESS ) {
1390                               SlapReply rs = {REP_RESULT};
1391                               send_ldap_error( ss->s_op, &rs, LDAP_SYNC_REFRESH_REQUIRED,
1392                                         "search base has changed" );
1393                               snext = ss->s_next;
1394                               if ( syncprov_drop_psearch( ss, 1 ) )
1395                                         *pss = snext;
1396                               gonext = 0;
1397                               continue;
1398                     }
1399 
1400                     /* If we're sending results now, look for this op in old matches */
1401                     if ( !saveit ) {
1402                               syncmatches *old;
1403 
1404                               /* Did we modify the search base? */
1405                               if ( dn_match( &op->o_req_ndn, &ss->s_base )) {
1406                                         ldap_pvt_thread_mutex_lock( &ss->s_mutex );
1407                                         ss->s_flags |= PS_WROTE_BASE;
1408                                         ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
1409                               }
1410 
1411                               for ( sm=opc->smatches, old=(syncmatches *)&opc->smatches; sm;
1412                                         old=sm, sm=sm->sm_next ) {
1413                                         if ( sm->sm_op == ss ) {
1414                                                   found = 1;
1415                                                   old->sm_next = sm->sm_next;
1416                                                   op->o_tmpfree( sm, op->o_tmpmemctx );
1417                                                   break;
1418                                         }
1419                               }
1420                     }
1421 
1422                     if ( fc.fscope ) {
1423                               ldap_pvt_thread_mutex_lock( &ss->s_mutex );
1424                               op2 = *ss->s_op;
1425                               oh = *op->o_hdr;
1426                               oh.oh_conn = ss->s_op->o_conn;
1427                               oh.oh_connid = ss->s_op->o_connid;
1428                               op2.o_bd = op->o_bd->bd_self;
1429                               op2.o_hdr = &oh;
1430                               op2.o_extra = op->o_extra;
1431                               op2.o_callback = NULL;
1432                               if (ss->s_flags & PS_FIX_FILTER) {
1433                                         /* Skip the AND/GE clause that we stuck on in front. We
1434                                            would lose deletes/mods that happen during the refresh
1435                                            phase otherwise (ITS#6555) */
1436                                         op2.ors_filter = ss->s_op->ors_filter->f_and->f_next;
1437                               }
1438                               rc = test_filter( &op2, e, op2.ors_filter );
1439                               ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
1440                     }
1441 
1442                     Debug( LDAP_DEBUG_TRACE, "%s syncprov_matchops: "
1443                               "sid %03x fscope %d rc %d\n",
1444                               ss->s_op->o_log_prefix, ss->s_sid, fc.fscope, rc );
1445 
1446                     /* check if current o_req_dn is in scope and matches filter */
1447                     if ( fc.fscope && rc == LDAP_COMPARE_TRUE ) {
1448                               if ( saveit ) {
1449                                         sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx );
1450                                         sm->sm_next = opc->smatches;
1451                                         sm->sm_op = ss;
1452                                         ldap_pvt_thread_mutex_lock( &ss->s_mutex );
1453                                         ++ss->s_inuse;
1454                                         ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
1455                                         opc->smatches = sm;
1456                               } else {
1457                                         /* if found send UPDATE else send ADD */
1458                                         syncprov_qresp( opc, ss,
1459                                                   found ? LDAP_SYNC_MODIFY : LDAP_SYNC_ADD );
1460                               }
1461                     } else if ( !saveit && found ) {
1462                               /* send DELETE */
1463                               syncprov_qresp( opc, ss, LDAP_SYNC_DELETE );
1464                     } else if ( !saveit ) {
1465                               syncprov_qresp( opc, ss, LDAP_SYNC_NEW_COOKIE );
1466                     }
1467                     if ( !saveit && found ) {
1468                               /* Decrement s_inuse, was incremented when called
1469                                * with saveit == TRUE
1470                                */
1471                               snext = ss->s_next;
1472                               if ( syncprov_free_syncop( ss, FS_LOCK ) ) {
1473                                         *pss = snext;
1474                                         gonext = 0;
1475                               }
1476                     }
1477           }
1478           ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1479 
1480           if ( op->o_tag != LDAP_REQ_ADD && e ) {
1481                     if ( !SLAP_ISOVERLAY( op->o_bd )) {
1482                               op->o_bd = &db;
1483                     }
1484                     if ( saveit )
1485                               overlay_entry_release_ov( op, e, 0, on );
1486                     op->o_bd = b0;
1487           }
1488           if ( !saveit ) {
1489                     if ( opc->ssres.s_info )
1490                               free_resinfo( &opc->ssres );
1491                     else if ( opc->se )
1492                               entry_free( opc->se );
1493           }
1494           if ( freefdn ) {
1495                     op->o_tmpfree( fc.fdn->bv_val, op->o_tmpmemctx );
1496           }
1497           op->o_bd = b0;
1498 }
1499 
1500 static int
syncprov_op_cleanup(Operation * op,SlapReply * rs)1501 syncprov_op_cleanup( Operation *op, SlapReply *rs )
1502 {
1503           slap_callback *cb = op->o_callback;
1504           opcookie *opc = cb->sc_private;
1505           slap_overinst *on = opc->son;
1506           syncprov_info_t               *si = on->on_bi.bi_private;
1507           syncmatches *sm, *snext;
1508           modtarget *mt;
1509 
1510           ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
1511           if ( si->si_active )
1512                     si->si_active--;
1513           ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
1514 
1515           for (sm = opc->smatches; sm; sm=snext) {
1516                     snext = sm->sm_next;
1517                     syncprov_free_syncop( sm->sm_op, FS_LOCK|FS_UNLINK );
1518                     op->o_tmpfree( sm, op->o_tmpmemctx );
1519           }
1520 
1521           /* Remove op from lock table */
1522           mt = opc->smt;
1523           if ( mt ) {
1524                     modinst *mi = (modinst *)(opc+1), **m2;
1525                     ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
1526                     for (m2 = &mt->mt_mods; ; m2 = &(*m2)->mi_next) {
1527                               if ( *m2 == mi ) {
1528                                         *m2 = mi->mi_next;
1529                                         if ( mt->mt_tail == mi )
1530                                                   mt->mt_tail = ( m2 == &mt->mt_mods ) ? NULL : (modinst *)m2;
1531                                         break;
1532                               }
1533                     }
1534                     /* If there are more, promote the next one */
1535                     if ( mt->mt_mods ) {
1536                               ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
1537                     } else {
1538                               ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
1539                               ldap_pvt_thread_mutex_lock( &si->si_mods_mutex );
1540                               ldap_avl_delete( &si->si_mods, mt, sp_avl_cmp );
1541                               ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
1542                               ldap_pvt_thread_mutex_destroy( &mt->mt_mutex );
1543                               ch_free( mt->mt_dn.bv_val );
1544                               ch_free( mt );
1545                     }
1546           }
1547           if ( !BER_BVISNULL( &opc->suuid ))
1548                     op->o_tmpfree( opc->suuid.bv_val, op->o_tmpmemctx );
1549           if ( !BER_BVISNULL( &opc->sndn ))
1550                     op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
1551           if ( !BER_BVISNULL( &opc->sdn ))
1552                     op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
1553           op->o_callback = cb->sc_next;
1554           op->o_tmpfree(cb, op->o_tmpmemctx);
1555 
1556           return 0;
1557 }
1558 
1559 static void
syncprov_checkpoint(Operation * op,slap_overinst * on)1560 syncprov_checkpoint( Operation *op, slap_overinst *on )
1561 {
1562           syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
1563           Modifications mod;
1564           Operation opm;
1565           SlapReply rsm = {REP_RESULT};
1566           slap_callback cb = {0};
1567           BackendDB be;
1568           BackendInfo *bi;
1569 
1570 #ifdef CHECK_CSN
1571           Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
1572 
1573           int i;
1574           for ( i=0; i<si->si_numcsns; i++ ) {
1575                     assert( !syn->ssyn_validate( syn, si->si_ctxcsn+i ));
1576           }
1577 #endif
1578 
1579           Debug( LDAP_DEBUG_SYNC, "%s syncprov_checkpoint: running checkpoint\n",
1580                     op->o_log_prefix );
1581 
1582           mod.sml_numvals = si->si_numcsns;
1583           mod.sml_values = si->si_ctxcsn;
1584           mod.sml_nvalues = NULL;
1585           mod.sml_desc = slap_schema.si_ad_contextCSN;
1586           mod.sml_op = LDAP_MOD_REPLACE;
1587           mod.sml_flags = SLAP_MOD_INTERNAL;
1588           mod.sml_next = NULL;
1589 
1590           cb.sc_response = slap_null_cb;
1591           opm = *op;
1592           opm.o_tag = LDAP_REQ_MODIFY;
1593           opm.o_callback = &cb;
1594           opm.orm_modlist = &mod;
1595           opm.orm_no_opattrs = 1;
1596           if ( SLAP_GLUE_SUBORDINATE( op->o_bd )) {
1597                     be = *on->on_info->oi_origdb;
1598                     opm.o_bd = &be;
1599           }
1600           opm.o_req_dn = si->si_contextdn;
1601           opm.o_req_ndn = si->si_contextdn;
1602           bi = opm.o_bd->bd_info;
1603           opm.o_bd->bd_info = on->on_info->oi_orig;
1604           opm.o_managedsait = SLAP_CONTROL_NONCRITICAL;
1605           opm.o_no_schema_check = 1;
1606           opm.o_dont_replicate = 1;
1607           opm.o_opid = -1;
1608           opm.o_bd->be_modify( &opm, &rsm );
1609 
1610           if ( rsm.sr_err == LDAP_NO_SUCH_OBJECT &&
1611                     SLAP_SYNC_SUBENTRY( opm.o_bd )) {
1612                     const char          *text;
1613                     char txtbuf[SLAP_TEXT_BUFLEN];
1614                     size_t textlen = sizeof txtbuf;
1615                     Entry *e = slap_create_context_csn_entry( opm.o_bd, NULL );
1616                     rs_reinit( &rsm, REP_RESULT );
1617                     slap_mods2entry( &mod, &e, 0, 1, &text, txtbuf, textlen);
1618                     opm.ora_e = e;
1619                     opm.o_bd->be_add( &opm, &rsm );
1620                     if ( e == opm.ora_e )
1621                               be_entry_release_w( &opm, opm.ora_e );
1622           }
1623           opm.o_bd->bd_info = bi;
1624 
1625           if ( mod.sml_next != NULL ) {
1626                     slap_mods_free( mod.sml_next, 1 );
1627           }
1628 #ifdef CHECK_CSN
1629           for ( i=0; i<si->si_numcsns; i++ ) {
1630                     assert( !syn->ssyn_validate( syn, si->si_ctxcsn+i ));
1631           }
1632 #endif
1633 }
1634 
1635 static void
syncprov_add_slog(Operation * op)1636 syncprov_add_slog( Operation *op )
1637 {
1638           opcookie *opc = op->o_callback->sc_private;
1639           slap_overinst *on = opc->son;
1640           syncprov_info_t               *si = on->on_bi.bi_private;
1641           sessionlog *sl;
1642           slog_entry *se;
1643           char uuidstr[40];
1644           int rc;
1645 
1646           sl = si->si_logs;
1647           {
1648                     if ( BER_BVISEMPTY( &op->o_csn ) ) {
1649                               /* During the syncrepl refresh phase we can receive operations
1650                                * without a csn.  We cannot reliably determine the consumers
1651                                * state with respect to such operations, so we ignore them and
1652                                * wipe out anything in the log if we see them.
1653                                */
1654                               ldap_pvt_thread_rdwr_wlock( &sl->sl_mutex );
1655                               /* can only do this if no one else is reading the log at the moment */
1656                               if ( !sl->sl_playing ) {
1657                                         ldap_tavl_free( sl->sl_entries, (AVL_FREE)ch_free );
1658                                         sl->sl_num = 0;
1659                                         sl->sl_entries = NULL;
1660                               }
1661                               ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
1662                               return;
1663                     }
1664 
1665                     /* Allocate a record. UUIDs are not NUL-terminated. */
1666                     se = ch_malloc( sizeof( slog_entry ) + opc->suuid.bv_len +
1667                               op->o_csn.bv_len + 1 );
1668                     se->se_tag = op->o_tag;
1669 
1670                     se->se_uuid.bv_val = (char *)(&se[1]);
1671                     AC_MEMCPY( se->se_uuid.bv_val, opc->suuid.bv_val, opc->suuid.bv_len );
1672                     se->se_uuid.bv_len = opc->suuid.bv_len;
1673 
1674                     se->se_csn.bv_val = se->se_uuid.bv_val + opc->suuid.bv_len;
1675                     AC_MEMCPY( se->se_csn.bv_val, op->o_csn.bv_val, op->o_csn.bv_len );
1676                     se->se_csn.bv_val[op->o_csn.bv_len] = '\0';
1677                     se->se_csn.bv_len = op->o_csn.bv_len;
1678                     se->se_sid = slap_parse_csn_sid( &se->se_csn );
1679 
1680                     ldap_pvt_thread_rdwr_wlock( &sl->sl_mutex );
1681                     if ( LogTest( LDAP_DEBUG_SYNC ) ) {
1682                               uuidstr[0] = 0;
1683                               if ( !BER_BVISEMPTY( &opc->suuid ) ) {
1684                                         lutil_uuidstr_from_normalized( opc->suuid.bv_val, opc->suuid.bv_len,
1685                                                   uuidstr, 40 );
1686                               }
1687                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_add_slog: "
1688                                         "adding csn=%s to sessionlog, uuid=%s\n",
1689                                         op->o_log_prefix, se->se_csn.bv_val, uuidstr );
1690                     }
1691                     if ( !sl->sl_entries ) {
1692                               if ( !sl->sl_mincsn ) {
1693                                         sl->sl_numcsns = 1;
1694                                         sl->sl_mincsn = ch_malloc( 2*sizeof( struct berval ));
1695                                         sl->sl_sids = ch_malloc( sizeof( int ));
1696                                         sl->sl_sids[0] = se->se_sid;
1697                                         ber_dupbv( sl->sl_mincsn, &se->se_csn );
1698                                         BER_BVZERO( &sl->sl_mincsn[1] );
1699                               }
1700                     }
1701                     rc = ldap_tavl_insert( &sl->sl_entries, se, syncprov_sessionlog_cmp, ldap_avl_dup_error );
1702                     if ( rc ) {
1703                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_add_slog: "
1704                                         "duplicate sessionlog entry ignored: csn=%s, uuid=%s\n",
1705                                         op->o_log_prefix, se->se_csn.bv_val, uuidstr );
1706                               ch_free( se );
1707                               goto leave;
1708                     }
1709                     sl->sl_num++;
1710                     if ( !sl->sl_playing && sl->sl_num > sl->sl_size ) {
1711                               TAvlnode *edge = ldap_tavl_end( sl->sl_entries, TAVL_DIR_LEFT );
1712                               while ( sl->sl_num > sl->sl_size ) {
1713                                         int i;
1714                                         TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
1715                                         se = edge->avl_data;
1716                                         Debug( LDAP_DEBUG_SYNC, "%s syncprov_add_slog: "
1717                                                   "expiring csn=%s from sessionlog (sessionlog size=%d)\n",
1718                                                   op->o_log_prefix, se->se_csn.bv_val, sl->sl_num );
1719                                         for ( i=0; i<sl->sl_numcsns; i++ )
1720                                                   if ( sl->sl_sids[i] >= se->se_sid )
1721                                                             break;
1722                                         if  ( i == sl->sl_numcsns || sl->sl_sids[i] != se->se_sid ) {
1723                                                   Debug( LDAP_DEBUG_SYNC, "%s syncprov_add_slog: "
1724                                                             "adding csn=%s to mincsn\n",
1725                                                             op->o_log_prefix, se->se_csn.bv_val );
1726                                                   slap_insert_csn_sids( (struct sync_cookie *)sl,
1727                                                             i, se->se_sid, &se->se_csn );
1728                                         } else {
1729                                                   Debug( LDAP_DEBUG_SYNC, "%s syncprov_add_slog: "
1730                                                             "updating mincsn for sid=%d csn=%s to %s\n",
1731                                                             op->o_log_prefix, se->se_sid, sl->sl_mincsn[i].bv_val, se->se_csn.bv_val );
1732                                                   ber_bvreplace( &sl->sl_mincsn[i], &se->se_csn );
1733                                         }
1734                                         ldap_tavl_delete( &sl->sl_entries, se, syncprov_sessionlog_cmp );
1735                                         ch_free( se );
1736                                         edge = next;
1737                                         sl->sl_num--;
1738                               }
1739                     }
1740 leave:
1741                     ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
1742           }
1743 }
1744 
1745 /* Just set a flag if we found the matching entry */
1746 static int
playlog_cb(Operation * op,SlapReply * rs)1747 playlog_cb( Operation *op, SlapReply *rs )
1748 {
1749           if ( rs->sr_type == REP_SEARCH ) {
1750                     op->o_callback->sc_private = (void *)1;
1751           }
1752           return rs->sr_err;
1753 }
1754 
1755 /*
1756  * Check whether the last nmods UUIDs in the uuids list exist in the database
1757  * and (still) match the op filter, zero out the bv_len of any that still exist
1758  * and return the number of UUIDs we have confirmed are gone now.
1759  */
1760 static int
check_uuidlist_presence(Operation * op,struct berval * uuids,int len,int nmods)1761 check_uuidlist_presence(
1762                     Operation *op,
1763                     struct berval *uuids,
1764                     int len,
1765                     int nmods )
1766 {
1767           slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1768           Operation fop = *op;
1769           SlapReply frs = { REP_RESULT };
1770           Filter mf, af;
1771           AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
1772           slap_callback cb = {0};
1773           int i, mods = nmods;
1774 
1775           fop.o_sync_mode = 0;
1776           fop.o_callback = &cb;
1777           fop.ors_limit = NULL;
1778           fop.ors_tlimit = SLAP_NO_LIMIT;
1779           fop.ors_attrs = slap_anlist_all_attributes;
1780           fop.ors_attrsonly = 0;
1781           fop.o_managedsait = SLAP_CONTROL_CRITICAL;
1782 
1783           af.f_choice = LDAP_FILTER_AND;
1784           af.f_next = NULL;
1785           af.f_and = &mf;
1786           mf.f_choice = LDAP_FILTER_EQUALITY;
1787           mf.f_ava = &eq;
1788           mf.f_av_desc = slap_schema.si_ad_entryUUID;
1789           mf.f_next = fop.ors_filter;
1790 
1791           fop.ors_filter = &af;
1792 
1793           cb.sc_response = playlog_cb;
1794 
1795           fop.o_bd->bd_info = (BackendInfo *)on->on_info;
1796           for ( i=0; i<nmods; i++ ) {
1797                     mf.f_av_value = uuids[ len - 1 - i ];
1798                     cb.sc_private = NULL;
1799                     fop.ors_slimit = 1;
1800 
1801                     if ( BER_BVISEMPTY( &mf.f_av_value ) ) {
1802                               mods--;
1803                               continue;
1804                     }
1805 
1806                     rs_reinit( &frs, REP_RESULT );
1807                     fop.o_bd->be_search( &fop, &frs );
1808                     if ( cb.sc_private ) {
1809                               uuids[ len - 1 - i ].bv_len = 0;
1810                               mods--;
1811                     }
1812           }
1813           fop.o_bd->bd_info = (BackendInfo *)on;
1814 
1815           return mods;
1816 }
1817 
1818 /*
1819  * On each entry we get from the DB:
1820  * - if it's an ADD, skip
1821  * - check we've not handled it yet, skip if we have
1822  * - check if it's a DELETE or missing from the DB now
1823  *   - send a new syncinfo entry
1824  * - remember we've handled it already
1825  *
1826  * If we exhaust the list, clear it, forgetting entries we've handled so far.
1827  */
1828 static int
syncprov_accesslog_uuid_cb(Operation * op,SlapReply * rs)1829 syncprov_accesslog_uuid_cb( Operation *op, SlapReply *rs )
1830 {
1831           slap_callback *sc = op->o_callback;
1832           syncprov_accesslog_deletes *uuid_progress = sc->sc_private;
1833           Attribute *a, *attrs;
1834           sync_control *srs = uuid_progress->srs;
1835           struct berval *bv, csn[2] = {}, uuid[2] = {},
1836                                           add = BER_BVC("add"),
1837                                           delete = BER_BVC("delete"),
1838                                           modrdn = BER_BVC("modrdn");
1839           int cmp, sid, i, is_delete = 0, rc;
1840 
1841           if ( rs->sr_type != REP_SEARCH ) {
1842                     return rs->sr_err;
1843           }
1844           attrs = rs->sr_entry->e_attrs;
1845 
1846           a = attr_find( attrs, ad_reqType );
1847           if ( !a || a->a_numvals == 0 ) {
1848                     rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1849                     return rs->sr_err;
1850           }
1851 
1852           if ( bvmatch( &a->a_nvals[0], &add ) ) {
1853                     return rs->sr_err;
1854           }
1855 
1856           if ( bvmatch( &a->a_nvals[0], &delete ) ) {
1857                     is_delete = 1;
1858           }
1859 
1860           if ( bvmatch( &a->a_nvals[0], &modrdn ) ) {
1861                     a = attr_find( attrs, ad_reqDN );
1862                     if ( !a || a->a_numvals == 0 ) {
1863                               rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1864                               return rs->sr_err;
1865                     }
1866 
1867                     /* Was it present in the first place? If not, skip: */
1868                     if ( !dnIsSuffix( &a->a_nvals[0], &uuid_progress->op->o_req_ndn ) ) {
1869                               return rs->sr_err;
1870                     }
1871 
1872                     a = attr_find( attrs, ad_reqNewDN );
1873                     if ( !a || a->a_numvals == 0 ) {
1874                               rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1875                               return rs->sr_err;
1876                     }
1877 
1878                     /* Has it gone away? */
1879                     if ( !dnIsSuffix( &a->a_nvals[0], &uuid_progress->op->o_req_ndn ) ) {
1880                               is_delete = 1;
1881                     }
1882           }
1883 
1884           /*
1885            * Only pick entries that are both:
1886            */
1887           a = attr_find( attrs, slap_schema.si_ad_entryCSN );
1888           if ( !a || a->a_numvals == 0 ) {
1889                     rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1890                     return rs->sr_err;
1891           }
1892           csn[0] = a->a_nvals[0];
1893 
1894           sid = slap_parse_csn_sid( &csn[0] );
1895 
1896           /*
1897            * newer than cookieCSN (srs->sr_state.ctxcsn)
1898            */
1899           cmp = 1;
1900           for ( i=0; i<srs->sr_state.numcsns; i++ ) {
1901                     if ( sid == srs->sr_state.sids[i] ) {
1902                               cmp = ber_bvcmp( &csn[0], &srs->sr_state.ctxcsn[i] );
1903                               break;
1904                     }
1905           }
1906           if ( cmp <= 0 ) {
1907                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_accesslog_uuid_cb: "
1908                                         "cmp %d, csn %s too old\n",
1909                                         op->o_log_prefix, cmp, csn[0].bv_val );
1910                     return rs->sr_err;
1911           }
1912 
1913           /*
1914            * not newer than snapshot ctxcsn (uuid_progress->ctxcsn)
1915            */
1916           cmp = 0;
1917           for ( i=0; i<uuid_progress->numcsns; i++ ) {
1918                     if ( sid == uuid_progress->sids[i] ) {
1919                               cmp = ber_bvcmp( &csn[0], &uuid_progress->ctxcsn[i] );
1920                               break;
1921                     }
1922           }
1923           if ( cmp > 0 ) {
1924                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_accesslog_uuid_cb: "
1925                                         "cmp %d, csn %s too new\n",
1926                                         op->o_log_prefix, cmp, csn[0].bv_val );
1927                     return rs->sr_err;
1928           }
1929 
1930           a = attr_find( attrs, ad_reqEntryUUID );
1931           if ( !a || a->a_numvals == 0 ) {
1932                     rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
1933                     return rs->sr_err;
1934           }
1935           uuid[0] = a->a_nvals[0];
1936 
1937           bv = ldap_avl_find( uuid_progress->uuids, uuid, sp_uuid_cmp );
1938           if ( bv ) {
1939                     /* Already checked or sent, no change */
1940                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_accesslog_uuid_cb: "
1941                                         "uuid %s already checked\n",
1942                                         op->o_log_prefix, a->a_vals[0].bv_val );
1943                     return rs->sr_err;
1944           }
1945 
1946           if ( !is_delete ) {
1947                     is_delete = check_uuidlist_presence( uuid_progress->op, uuid, 1, 1 );
1948           }
1949           Debug( LDAP_DEBUG_SYNC, "%s syncprov_accesslog_uuid_cb: "
1950                               "uuid %s is %s present\n",
1951                               op->o_log_prefix, a->a_vals[0].bv_val,
1952                               is_delete ? "no longer" : "still" );
1953 
1954           i = uuid_progress->ndel++;
1955 
1956           bv = &uuid_progress->uuid_list[i];
1957           bv->bv_val = &uuid_progress->uuid_buf[i*UUID_LEN];
1958           bv->bv_len = a->a_nvals[0].bv_len;
1959           AC_MEMCPY( bv->bv_val, a->a_nvals[0].bv_val, a->a_nvals[0].bv_len );
1960 
1961           rc = ldap_avl_insert( &uuid_progress->uuids, bv, sp_uuid_cmp, ldap_avl_dup_error );
1962           assert( rc == LDAP_SUCCESS );
1963 
1964           if ( is_delete ) {
1965                     struct berval cookie;
1966 
1967                     slap_compose_sync_cookie( op, &cookie, srs->sr_state.ctxcsn,
1968                                         srs->sr_state.rid, slap_serverID ? slap_serverID : -1, csn );
1969                     syncprov_sendinfo( uuid_progress->op, uuid_progress->rs,
1970                                         LDAP_TAG_SYNC_ID_SET, &cookie, 0, uuid, 1 );
1971                     op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
1972           }
1973 
1974           if ( uuid_progress->ndel >= uuid_progress->list_len ) {
1975                     int ndel;
1976 
1977                     assert( uuid_progress->ndel == uuid_progress->list_len );
1978                     ndel = ldap_avl_free( uuid_progress->uuids, NULL );
1979                     assert( ndel == uuid_progress->ndel );
1980                     uuid_progress->uuids = NULL;
1981                     uuid_progress->ndel = 0;
1982           }
1983 
1984           return rs->sr_err;
1985 }
1986 
1987 static int
syncprov_play_sessionlog(Operation * op,SlapReply * rs,sync_control * srs,BerVarray ctxcsn,int numcsns,int * sids,struct berval * mincsn,int minsid)1988 syncprov_play_sessionlog( Operation *op, SlapReply *rs, sync_control *srs,
1989                     BerVarray ctxcsn, int numcsns, int *sids,
1990                     struct berval *mincsn, int minsid )
1991 {
1992           slap_overinst                 *on = (slap_overinst *)op->o_bd->bd_info;
1993           syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
1994           sessionlog *sl = si->si_logs;
1995           int i, j, ndel, num, nmods, mmods, do_play = 0, rc = -1;
1996           BerVarray uuids, csns;
1997           struct berval uuid[2] = {}, csn[2] = {};
1998           slog_entry *se;
1999           TAvlnode *entry;
2000           char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
2001           struct berval delcsn[2];
2002 
2003           ldap_pvt_thread_rdwr_wlock( &sl->sl_mutex );
2004           /* Are there any log entries, and is the consumer state
2005            * present in the session log?
2006            */
2007           if ( !sl->sl_num ) {
2008                     ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
2009                     return rc;
2010           }
2011           assert( sl->sl_num > 0 );
2012 
2013           for ( i=0; i<sl->sl_numcsns; i++ ) {
2014                     /* SID not present == new enough */
2015                     if ( minsid < sl->sl_sids[i] ) {
2016                               do_play = 1;
2017                               break;
2018                     }
2019                     /* SID present */
2020                     if ( minsid == sl->sl_sids[i] ) {
2021                               /* new enough? */
2022                               if ( ber_bvcmp( mincsn, &sl->sl_mincsn[i] ) >= 0 )
2023                                         do_play = 1;
2024                               break;
2025                     }
2026           }
2027           /* SID not present == new enough */
2028           if ( i == sl->sl_numcsns )
2029                     do_play = 1;
2030 
2031           if ( !do_play ) {
2032                     ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
2033                     return rc;
2034           }
2035 
2036           num = sl->sl_num;
2037           i = 0;
2038           nmods = 0;
2039           sl->sl_playing++;
2040           ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
2041 
2042           uuids = op->o_tmpalloc( (num) * sizeof( struct berval ) +
2043                               num * UUID_LEN, op->o_tmpmemctx );
2044           uuids[0].bv_val = (char *)(uuids + num);
2045           csns = op->o_tmpalloc( (num) * sizeof( struct berval ) +
2046                               num * LDAP_PVT_CSNSTR_BUFSIZE, op->o_tmpmemctx );
2047           csns[0].bv_val = (char *)(csns + num);
2048 
2049           ldap_pvt_thread_rdwr_rlock( &sl->sl_mutex );
2050           /* Make a copy of the relevant UUIDs. Put the Deletes up front
2051            * and everything else at the end. Do this first so we can
2052            * let the write side manage the sessionlog again.
2053            */
2054           assert( sl->sl_entries );
2055 
2056           /* Find first relevant log entry. If greater than mincsn, backtrack one entry */
2057           {
2058                     slog_entry te = {0};
2059                     te.se_csn = *mincsn;
2060                     entry = ldap_tavl_find3( sl->sl_entries, &te, syncprov_sessionlog_cmp, &ndel );
2061           }
2062           if ( ndel > 0 && entry )
2063                     entry = ldap_tavl_next( entry, TAVL_DIR_LEFT );
2064           /* if none, just start at beginning */
2065           if ( !entry )
2066                     entry = ldap_tavl_end( sl->sl_entries, TAVL_DIR_LEFT );
2067 
2068           do {
2069                     char uuidstr[40] = {};
2070                     slog_entry *se = entry->avl_data;
2071                     int k;
2072 
2073                     /* Make sure writes can still make progress */
2074                     ldap_pvt_thread_rdwr_runlock( &sl->sl_mutex );
2075                     ndel = 1;
2076                     for ( k=0; k<srs->sr_state.numcsns; k++ ) {
2077                               if ( se->se_sid == srs->sr_state.sids[k] ) {
2078                                         ndel = ber_bvcmp( &se->se_csn, &srs->sr_state.ctxcsn[k] );
2079                                         break;
2080                               }
2081                     }
2082                     if ( ndel <= 0 ) {
2083                               ldap_pvt_thread_rdwr_rlock( &sl->sl_mutex );
2084                               continue;
2085                     }
2086                     ndel = 0;
2087                     for ( k=0; k<numcsns; k++ ) {
2088                               if ( se->se_sid == sids[k] ) {
2089                                         ndel = ber_bvcmp( &se->se_csn, &ctxcsn[k] );
2090                                         break;
2091                               }
2092                     }
2093                     if ( ndel > 0 ) {
2094                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_play_sessionlog: "
2095                                         "cmp %d, csn %s too new, we're finished\n",
2096                                         op->o_log_prefix, ndel, se->se_csn.bv_val );
2097                               ldap_pvt_thread_rdwr_rlock( &sl->sl_mutex );
2098                               break;
2099                     }
2100                     if ( se->se_tag == LDAP_REQ_DELETE ) {
2101                               j = i;
2102                               i++;
2103                     } else {
2104                               if ( se->se_tag == LDAP_REQ_ADD ) {
2105                                         ldap_pvt_thread_rdwr_rlock( &sl->sl_mutex );
2106                                         continue;
2107                               }
2108                               nmods++;
2109                               j = num - nmods;
2110                     }
2111                     uuids[j].bv_val = uuids[0].bv_val + (j * UUID_LEN);
2112                     AC_MEMCPY(uuids[j].bv_val, se->se_uuid.bv_val, UUID_LEN);
2113                     uuids[j].bv_len = UUID_LEN;
2114 
2115                     csns[j].bv_val = csns[0].bv_val + (j * LDAP_PVT_CSNSTR_BUFSIZE);
2116                     AC_MEMCPY(csns[j].bv_val, se->se_csn.bv_val, se->se_csn.bv_len);
2117                     csns[j].bv_len = se->se_csn.bv_len;
2118                     /* We're printing it */
2119                     csns[j].bv_val[csns[j].bv_len] = '\0';
2120 
2121                     if ( LogTest( LDAP_DEBUG_SYNC ) ) {
2122                               lutil_uuidstr_from_normalized( uuids[j].bv_val, uuids[j].bv_len,
2123                                                   uuidstr, 40 );
2124                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_play_sessionlog: "
2125                                         "picking a %s entry uuid=%s cookie=%s\n",
2126                                         op->o_log_prefix, se->se_tag == LDAP_REQ_DELETE ? "deleted" : "modified",
2127                                         uuidstr, csns[j].bv_val );
2128                     }
2129                     ldap_pvt_thread_rdwr_rlock( &sl->sl_mutex );
2130           } while ( (entry = ldap_tavl_next( entry, TAVL_DIR_RIGHT )) != NULL );
2131           ldap_pvt_thread_rdwr_runlock( &sl->sl_mutex );
2132           ldap_pvt_thread_rdwr_wlock( &sl->sl_mutex );
2133           sl->sl_playing--;
2134           ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
2135 
2136           ndel = i;
2137 
2138           /* Zero out unused slots */
2139           for ( i=ndel; i < num - nmods; i++ )
2140                     uuids[i].bv_len = 0;
2141 
2142           /* Mods must be validated to see if they belong in this delete set.
2143            */
2144 
2145           mmods = nmods;
2146           /* Strip any duplicates */
2147           for ( i=0; i<nmods; i++ ) {
2148                     for ( j=0; j<ndel; j++ ) {
2149                               if ( bvmatch( &uuids[j], &uuids[num - 1 - i] )) {
2150                                         uuids[num - 1 - i].bv_len = 0;
2151                                         mmods --;
2152                                         break;
2153                               }
2154                     }
2155                     if ( uuids[num - 1 - i].bv_len == 0 ) continue;
2156                     for ( j=0; j<i; j++ ) {
2157                               if ( bvmatch( &uuids[num - 1 - j], &uuids[num - 1 - i] )) {
2158                                         uuids[num - 1 - i].bv_len = 0;
2159                                         mmods --;
2160                                         break;
2161                               }
2162                     }
2163           }
2164 
2165           /* Check mods now */
2166           if ( mmods ) {
2167                     check_uuidlist_presence( op, uuids, num, nmods );
2168           }
2169 
2170           /* ITS#8768 Send entries sorted by CSN order */
2171           i = j = 0;
2172           while ( i < ndel || j < nmods ) {
2173                     struct berval cookie;
2174                     int index;
2175 
2176                     /* Skip over duplicate mods */
2177                     if ( j < nmods && BER_BVISEMPTY( &uuids[ num - 1 - j ] ) ) {
2178                               j++;
2179                               continue;
2180                     }
2181                     index = num - 1 - j;
2182 
2183                     if ( i >= ndel ) {
2184                               j++;
2185                     } else if ( j >= nmods ) {
2186                               index = i++;
2187                     /* Take the oldest by CSN order */
2188                     } else if ( ber_bvcmp( &csns[index], &csns[i] ) < 0 ) {
2189                               j++;
2190                     } else {
2191                               index = i++;
2192                     }
2193 
2194                     uuid[0] = uuids[index];
2195                     csn[0] = csns[index];
2196 
2197                     slap_compose_sync_cookie( op, &cookie, srs->sr_state.ctxcsn,
2198                                         srs->sr_state.rid, slap_serverID ? slap_serverID : -1, csn );
2199                     if ( LogTest( LDAP_DEBUG_SYNC ) ) {
2200                               char uuidstr[40];
2201                               lutil_uuidstr_from_normalized( uuid[0].bv_val, uuid[0].bv_len,
2202                                                   uuidstr, 40 );
2203                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_play_sessionlog: "
2204                                                   "sending a new disappearing entry uuid=%s cookie=%s\n",
2205                                                   op->o_log_prefix, uuidstr, cookie.bv_val );
2206                     }
2207 
2208                     /* TODO: we might batch those that share the same CSN (think present
2209                      * phase), but would have to limit how many we send out at once */
2210                     syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, &cookie, 0, uuid, 1 );
2211           }
2212           op->o_tmpfree( uuids, op->o_tmpmemctx );
2213           op->o_tmpfree( csns, op->o_tmpmemctx );
2214 
2215           return LDAP_SUCCESS;
2216 }
2217 
2218 static int
syncprov_play_accesslog(Operation * op,SlapReply * rs,sync_control * srs,BerVarray ctxcsn,int numcsns,int * sids,struct berval * mincsn,int minsid)2219 syncprov_play_accesslog( Operation *op, SlapReply *rs, sync_control *srs,
2220                     BerVarray ctxcsn, int numcsns, int *sids,
2221                     struct berval *mincsn, int minsid )
2222 {
2223           slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
2224           syncprov_info_t *si = on->on_bi.bi_private;
2225           Operation fop;
2226           SlapReply frs = { REP_RESULT };
2227           slap_callback cb = {};
2228           Filter *f;
2229           syncprov_accesslog_deletes uuid_progress = {
2230                     .op = op,
2231                     .rs = rs,
2232                     .srs = srs,
2233                     .ctxcsn = ctxcsn,
2234                     .numcsns = numcsns,
2235                     .sids = sids,
2236           };
2237           struct berval oldestcsn = BER_BVNULL, newestcsn = ctxcsn[0],
2238           basedn, filterpattern = BER_BVC(
2239                               "(&"
2240                                         "(entryCSN>=%s)"
2241                                         "(entryCSN<=%s)"
2242                                         "(reqResult=0)"
2243                                         "(|"
2244                                                   "(reqDN:dnSubtreeMatch:=%s)"
2245                                                   "(reqNewDN:dnSubtreeMatch:=%s)"
2246                                         ")"
2247                                         "(|"
2248                                                   "(objectclass=auditWriteObject)"
2249                                                   "(objectclass=auditExtended)"
2250                               "))" );
2251           BackendDB *db;
2252           Entry *e;
2253           Attribute *a;
2254           int i, rc = -1;
2255 
2256           assert( !BER_BVISNULL( &si->si_logbase ) );
2257 
2258           for ( i=1; i < numcsns; i++ ) {
2259                     if ( ber_bvcmp( &newestcsn, &ctxcsn[i] ) < 0 ) {
2260                               newestcsn = ctxcsn[i];
2261                     }
2262           }
2263 
2264           db = select_backend( &si->si_logbase, 0 );
2265           if ( !db ) {
2266                     Debug( LDAP_DEBUG_ANY, "%s syncprov_play_accesslog: "
2267                                         "No database configured to hold accesslog dn=%s\n",
2268                                         op->o_log_prefix, si->si_logbase.bv_val );
2269                     return LDAP_NO_SUCH_OBJECT;
2270           }
2271 
2272           fop = *op;
2273           fop.o_sync_mode = 0;
2274           fop.o_bd = db;
2275           rc = be_entry_get_rw( &fop, &si->si_logbase, NULL, ad_minCSN, 0, &e );
2276           if ( rc ) {
2277                     return rc;
2278           }
2279 
2280           a = attr_find( e->e_attrs, ad_minCSN );
2281           if ( !a ) {
2282                     be_entry_release_rw( &fop, e, 0 );
2283                     return LDAP_NO_SUCH_ATTRIBUTE;
2284           }
2285           for ( i=0; i < a->a_numvals; i++ ) {
2286                     if ( BER_BVISEMPTY( &oldestcsn ) ||
2287                                         ber_bvcmp( &oldestcsn, &a->a_nvals[i] ) > 0 ) {
2288                               oldestcsn = a->a_nvals[i];
2289                     }
2290           }
2291 
2292           filter_escape_value_x( &op->o_req_ndn, &basedn, fop.o_tmpmemctx );
2293           /* filter_escape_value_x sets output to BVNULL if input value is empty,
2294            * supply our own copy */
2295           if ( BER_BVISEMPTY( &basedn ) ) {
2296                     basedn.bv_val = "";
2297           }
2298           fop.o_req_ndn = fop.o_req_dn = si->si_logbase;
2299           fop.ors_filterstr.bv_val = fop.o_tmpalloc(
2300                               filterpattern.bv_len +
2301                               oldestcsn.bv_len + newestcsn.bv_len + 2 * basedn.bv_len,
2302                               fop.o_tmpmemctx );
2303           fop.ors_filterstr.bv_len = sprintf( fop.ors_filterstr.bv_val,
2304                               filterpattern.bv_val,
2305                               oldestcsn.bv_val, newestcsn.bv_val, basedn.bv_val, basedn.bv_val );
2306           Debug( LDAP_DEBUG_SYNC, "%s syncprov_play_accesslog: "
2307                               "prepared filter '%s', base='%s'\n",
2308                               op->o_log_prefix, fop.ors_filterstr.bv_val, si->si_logbase.bv_val );
2309           f = str2filter_x( &fop, fop.ors_filterstr.bv_val );
2310           assert( f != NULL );
2311           fop.ors_filter = f;
2312 
2313           if ( !BER_BVISEMPTY( &basedn ) ) {
2314                     fop.o_tmpfree( basedn.bv_val, fop.o_tmpmemctx );
2315           }
2316           be_entry_release_rw( &fop, e, 0 );
2317 
2318           /*
2319            * Allocate memory for list_len uuids for use by the callback, populate
2320            * with entries that we have sent or checked still match the filter.
2321            * A disappearing entry gets its uuid sent as a delete.
2322            *
2323            * in the callback, we need:
2324            * - original op and rs so we can send the message
2325            * - sync_control
2326            * - the uuid buffer and list and their length
2327            * - number of uuids we already have in the list
2328            * - the lookup structure so we don't have to check/send a uuid twice
2329            *   (AVL?)
2330            */
2331           uuid_progress.list_len = SLAP_SYNCUUID_SET_SIZE;
2332           uuid_progress.uuid_list = fop.o_tmpalloc( (uuid_progress.list_len) * sizeof(struct berval), fop.o_tmpmemctx );
2333           uuid_progress.uuid_buf = fop.o_tmpalloc( (uuid_progress.list_len) * UUID_LEN, fop.o_tmpmemctx );
2334 
2335           cb.sc_private = &uuid_progress;
2336           cb.sc_response = syncprov_accesslog_uuid_cb;
2337 
2338           fop.o_callback = &cb;
2339 
2340           rc = fop.o_bd->be_search( &fop, &frs );
2341 
2342           fop.o_tmpfree( uuid_progress.uuid_buf, fop.o_tmpmemctx );
2343           fop.o_tmpfree( uuid_progress.uuid_list, fop.o_tmpmemctx );
2344           fop.o_tmpfree( fop.ors_filterstr.bv_val, fop.o_tmpmemctx );
2345           filter_free_x( &fop, f, 1 );
2346 
2347           return rc;
2348 }
2349 
2350 static int
syncprov_new_ctxcsn(opcookie * opc,syncprov_info_t * si,int csn_changed,int numvals,BerVarray vals)2351 syncprov_new_ctxcsn( opcookie *opc, syncprov_info_t *si, int csn_changed, int numvals, BerVarray vals )
2352 {
2353           unsigned i;
2354           int j, sid;
2355 
2356           for ( i=0; i<numvals; i++ ) {
2357                     sid = slap_parse_csn_sid( &vals[i] );
2358                     for ( j=0; j<si->si_numcsns; j++ ) {
2359                               if ( sid < si->si_sids[j] )
2360                                         break;
2361                               if ( sid == si->si_sids[j] ) {
2362                                         if ( ber_bvcmp( &vals[i], &si->si_ctxcsn[j] ) > 0 ) {
2363                                                   ber_bvreplace( &si->si_ctxcsn[j], &vals[i] );
2364                                                   csn_changed = 1;
2365                                         }
2366                                         break;
2367                               }
2368                     }
2369 
2370                     if ( j == si->si_numcsns || sid != si->si_sids[j] ) {
2371                               slap_insert_csn_sids( (struct sync_cookie *)&si->si_ctxcsn,
2372                                         j, sid, &vals[i] );
2373                               csn_changed = 1;
2374                     }
2375           }
2376           if ( csn_changed )
2377                     si->si_dirty = 0;
2378           ldap_pvt_thread_rdwr_wunlock( &si->si_csn_rwlock );
2379 
2380           if ( csn_changed ) {
2381                     syncops *ss;
2382                     ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
2383                     for ( ss = si->si_ops; ss; ss = ss->s_next ) {
2384                               if ( ss->s_op->o_abandon )
2385                                         continue;
2386                               /* Send the updated csn to all syncrepl consumers,
2387                                * including the server from which it originated.
2388                                * The syncrepl consumer and syncprov provider on
2389                                * the originating server may be configured to store
2390                                * their csn values in different entries.
2391                                */
2392                               syncprov_qresp( opc, ss, LDAP_SYNC_NEW_COOKIE );
2393                     }
2394                     ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
2395           }
2396           return csn_changed;
2397 }
2398 
2399 static int
syncprov_op_response(Operation * op,SlapReply * rs)2400 syncprov_op_response( Operation *op, SlapReply *rs )
2401 {
2402           opcookie *opc = op->o_callback->sc_private;
2403           slap_overinst *on = opc->son;
2404           syncprov_info_t               *si = on->on_bi.bi_private;
2405           syncmatches *sm;
2406 
2407           if ( rs->sr_err == LDAP_SUCCESS )
2408           {
2409                     struct berval maxcsn;
2410                     char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
2411                     int do_check = 0, have_psearches, foundit, csn_changed = 0;
2412 
2413                     ldap_pvt_thread_mutex_lock( &si->si_resp_mutex );
2414 
2415                     /* Update our context CSN */
2416                     cbuf[0] = '\0';
2417                     maxcsn.bv_val = cbuf;
2418                     maxcsn.bv_len = sizeof(cbuf);
2419                     ldap_pvt_thread_rdwr_wlock( &si->si_csn_rwlock );
2420 
2421                     slap_get_commit_csn( op, &maxcsn, &foundit );
2422                     if ( BER_BVISEMPTY( &maxcsn ) && SLAP_GLUE_SUBORDINATE( op->o_bd )) {
2423                               /* syncrepl queues the CSN values in the db where
2424                                * it is configured , not where the changes are made.
2425                                * So look for a value in the glue db if we didn't
2426                                * find any in this db.
2427                                */
2428                               BackendDB *be = op->o_bd;
2429                               op->o_bd = select_backend( &be->be_nsuffix[0], 1);
2430                               maxcsn.bv_val = cbuf;
2431                               maxcsn.bv_len = sizeof(cbuf);
2432                               slap_get_commit_csn( op, &maxcsn, &foundit );
2433                               op->o_bd = be;
2434                     }
2435                     if ( !BER_BVISEMPTY( &maxcsn ) ) {
2436                               int i, sid;
2437 #ifdef CHECK_CSN
2438                               Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
2439                               assert( !syn->ssyn_validate( syn, &maxcsn ));
2440 #endif
2441                               sid = slap_parse_csn_sid( &maxcsn );
2442                               for ( i=0; i<si->si_numcsns; i++ ) {
2443                                         if ( sid < si->si_sids[i] )
2444                                                   break;
2445                                         if ( sid == si->si_sids[i] ) {
2446                                                   if ( ber_bvcmp( &maxcsn, &si->si_ctxcsn[i] ) > 0 ) {
2447                                                             ber_bvreplace( &si->si_ctxcsn[i], &maxcsn );
2448                                                             csn_changed = 1;
2449                                                   }
2450                                                   break;
2451                                         }
2452                               }
2453                               /* It's a new SID for us */
2454                               if ( i == si->si_numcsns || sid != si->si_sids[i] ) {
2455                                         slap_insert_csn_sids((struct sync_cookie *)&(si->si_ctxcsn),
2456                                                   i, sid, &maxcsn );
2457                                         csn_changed = 1;
2458                               }
2459                     }
2460 
2461                     /* Don't do any processing for consumer contextCSN updates */
2462                     if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) &&
2463                               op->o_tag == LDAP_REQ_MODIFY &&
2464                               op->orm_modlist &&
2465                               op->orm_modlist->sml_op == LDAP_MOD_REPLACE &&
2466                               op->orm_modlist->sml_desc == slap_schema.si_ad_contextCSN ) {
2467                               /* Catch contextCSN updates from syncrepl. We have to look at
2468                                * all the attribute values, as there may be more than one csn
2469                                * that changed, and only one can be passed in the csn queue.
2470                                */
2471                               csn_changed = syncprov_new_ctxcsn( opc, si, csn_changed,
2472                                         op->orm_modlist->sml_numvals, op->orm_modlist->sml_values );
2473                               if ( csn_changed )
2474                                         si->si_numops++;
2475                               goto leave;
2476                     }
2477                     if ( op->o_dont_replicate ) {
2478                               if ( csn_changed )
2479                                         si->si_numops++;
2480                               ldap_pvt_thread_rdwr_wunlock( &si->si_csn_rwlock );
2481                               goto leave;
2482                     }
2483 
2484                     /* If we're adding the context entry, parse all of its contextCSNs */
2485                     if ( op->o_tag == LDAP_REQ_ADD &&
2486                               dn_match( &op->o_req_ndn, &si->si_contextdn )) {
2487                               Attribute *a = attr_find( op->ora_e->e_attrs, slap_schema.si_ad_contextCSN );
2488                               if ( a ) {
2489                                         csn_changed = syncprov_new_ctxcsn( opc, si, csn_changed, a->a_numvals, a->a_vals );
2490                                         if ( csn_changed )
2491                                                   si->si_numops++;
2492                                         goto added;
2493                               }
2494                     }
2495 
2496                     if ( csn_changed )
2497                               si->si_numops++;
2498                     if ( si->si_chkops || si->si_chktime ) {
2499                               /* Never checkpoint adding the context entry,
2500                                * it will deadlock
2501                                */
2502                               if ( op->o_tag != LDAP_REQ_ADD ||
2503                                         !dn_match( &op->o_req_ndn, &si->si_contextdn )) {
2504                                         if ( si->si_chkops && si->si_numops >= si->si_chkops ) {
2505                                                   do_check = 1;
2506                                                   si->si_numops = 0;
2507                                         }
2508                                         if ( si->si_chktime &&
2509                                                   (op->o_time - si->si_chklast >= si->si_chktime )) {
2510                                                   if ( si->si_chklast ) {
2511                                                             do_check = 1;
2512                                                             si->si_chklast = op->o_time;
2513                                                   } else {
2514                                                             si->si_chklast = 1;
2515                                                   }
2516                                         }
2517                               }
2518                     }
2519                     si->si_dirty = !csn_changed;
2520                     ldap_pvt_thread_rdwr_wunlock( &si->si_csn_rwlock );
2521 
2522 added:
2523                     if ( do_check ) {
2524                               ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
2525                               syncprov_checkpoint( op, on );
2526                               ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
2527                     }
2528 
2529                     /* only update consumer ctx if this is a newer csn */
2530                     if ( csn_changed ) {
2531                               opc->sctxcsn = maxcsn;
2532                     }
2533 
2534                     /* Handle any persistent searches */
2535                     ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
2536                     have_psearches = ( si->si_ops != NULL );
2537                     ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
2538                     if ( have_psearches ) {
2539                               switch(op->o_tag) {
2540                               case LDAP_REQ_ADD:
2541                               case LDAP_REQ_MODIFY:
2542                               case LDAP_REQ_MODRDN:
2543                               case LDAP_REQ_EXTENDED:
2544                                         syncprov_matchops( op, opc, 0 );
2545                                         break;
2546                               case LDAP_REQ_DELETE:
2547                                         /* for each match in opc->smatches:
2548                                          *   send DELETE msg
2549                                          */
2550                                         for ( sm = opc->smatches; sm; sm=sm->sm_next ) {
2551                                                   if ( sm->sm_op->s_op->o_abandon )
2552                                                             continue;
2553                                                   syncprov_qresp( opc, sm->sm_op, LDAP_SYNC_DELETE );
2554                                         }
2555                                         if ( opc->ssres.s_info )
2556                                                   free_resinfo( &opc->ssres );
2557                                         break;
2558                               }
2559                     }
2560 
2561                     /* Add any log records */
2562                     if ( si->si_logs ) {
2563                               syncprov_add_slog( op );
2564                     }
2565 leave:              ldap_pvt_thread_mutex_unlock( &si->si_resp_mutex );
2566           }
2567           return SLAP_CB_CONTINUE;
2568 }
2569 
2570 /* We don't use a subentry to store the context CSN any more.
2571  * We expose the current context CSN as an operational attribute
2572  * of the suffix entry.
2573  */
2574 static int
syncprov_op_compare(Operation * op,SlapReply * rs)2575 syncprov_op_compare( Operation *op, SlapReply *rs )
2576 {
2577           slap_overinst                 *on = (slap_overinst *)op->o_bd->bd_info;
2578           syncprov_info_t               *si = on->on_bi.bi_private;
2579           int rc = SLAP_CB_CONTINUE;
2580 
2581           if ( dn_match( &op->o_req_ndn, &si->si_contextdn ) &&
2582                     op->oq_compare.rs_ava->aa_desc == slap_schema.si_ad_contextCSN )
2583           {
2584                     Entry e = {0};
2585                     Attribute a = {0};
2586 
2587                     e.e_name = si->si_contextdn;
2588                     e.e_nname = si->si_contextdn;
2589                     e.e_attrs = &a;
2590 
2591                     a.a_desc = slap_schema.si_ad_contextCSN;
2592 
2593                     ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
2594 
2595                     a.a_vals = si->si_ctxcsn;
2596                     a.a_nvals = a.a_vals;
2597                     a.a_numvals = si->si_numcsns;
2598 
2599                     rs->sr_err = access_allowed( op, &e, op->oq_compare.rs_ava->aa_desc,
2600                               &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
2601                     if ( ! rs->sr_err ) {
2602                               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2603                               goto return_results;
2604                     }
2605 
2606                     if ( get_assert( op ) &&
2607                               ( test_filter( op, &e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) )
2608                     {
2609                               rs->sr_err = LDAP_ASSERTION_FAILED;
2610                               goto return_results;
2611                     }
2612 
2613 
2614                     rs->sr_err = LDAP_COMPARE_FALSE;
2615 
2616                     if ( attr_valfind( &a,
2617                               SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
2618                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
2619                                         &op->oq_compare.rs_ava->aa_value, NULL, op->o_tmpmemctx ) == 0 )
2620                     {
2621                               rs->sr_err = LDAP_COMPARE_TRUE;
2622                     }
2623 
2624 return_results:;
2625 
2626                     ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
2627 
2628                     send_ldap_result( op, rs );
2629 
2630                     if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
2631                               rs->sr_err = LDAP_SUCCESS;
2632                     }
2633                     rc = rs->sr_err;
2634           }
2635 
2636           return rc;
2637 }
2638 
2639 static int
syncprov_op_mod(Operation * op,SlapReply * rs)2640 syncprov_op_mod( Operation *op, SlapReply *rs )
2641 {
2642           slap_overinst                 *on = (slap_overinst *)op->o_bd->bd_info;
2643           syncprov_info_t               *si = on->on_bi.bi_private;
2644           slap_callback *cb;
2645           opcookie *opc;
2646           int have_psearches, cbsize;
2647 
2648           ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
2649           have_psearches = ( si->si_ops != NULL );
2650           si->si_active++;
2651           ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
2652 
2653           cbsize = sizeof(slap_callback) + sizeof(opcookie) +
2654                     (have_psearches ? sizeof(modinst) : 0 );
2655 
2656           cb = op->o_tmpcalloc(1, cbsize, op->o_tmpmemctx);
2657           opc = (opcookie *)(cb+1);
2658           opc->son = on;
2659           cb->sc_response = syncprov_op_response;
2660           cb->sc_cleanup = syncprov_op_cleanup;
2661           cb->sc_private = opc;
2662           cb->sc_next = op->o_callback;
2663           op->o_callback = cb;
2664 
2665           opc->osid = -1;
2666           opc->rsid = -1;
2667           if ( op->o_csn.bv_val ) {
2668                     opc->osid = slap_parse_csn_sid( &op->o_csn );
2669           }
2670           if ( op->o_controls ) {
2671                     struct sync_cookie *scook =
2672                     op->o_controls[slap_cids.sc_LDAPsync];
2673                     if ( scook )
2674                               opc->rsid = scook->sid;
2675           }
2676 
2677           if ( op->o_dont_replicate )
2678                     return SLAP_CB_CONTINUE;
2679 
2680           /* If there are active persistent searches, lock this operation.
2681            * See seqmod.c for the locking logic on its own.
2682            */
2683           if ( have_psearches ) {
2684                     modtarget *mt, mtdummy;
2685                     modinst *mi;
2686 
2687                     mi = (modinst *)(opc+1);
2688                     mi->mi_op = op;
2689 
2690                     /* See if we're already modifying this entry... */
2691                     mtdummy.mt_dn = op->o_req_ndn;
2692 retry:
2693                     ldap_pvt_thread_mutex_lock( &si->si_mods_mutex );
2694                     mt = ldap_avl_find( si->si_mods, &mtdummy, sp_avl_cmp );
2695                     if ( mt ) {
2696                               ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
2697                               if ( mt->mt_mods == NULL ) {
2698                                         /* Cannot reuse this mt, as another thread is about
2699                                          * to release it in syncprov_op_cleanup. Wait for them
2700                                          * to finish; our own insert is required to succeed.
2701                                          */
2702                                         ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
2703                                         ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
2704                                         ldap_pvt_thread_yield();
2705                                         goto retry;
2706                               }
2707                     }
2708                     if ( mt ) {
2709                               mt->mt_tail->mi_next = mi;
2710                               mt->mt_tail = mi;
2711                               ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
2712                               /* wait for this op to get to head of list */
2713                               while ( mt->mt_mods != mi ) {
2714                                         modinst *m2;
2715                                         /* don't wait on other mods from the same thread */
2716                                         for ( m2 = mt->mt_mods; m2; m2 = m2->mi_next ) {
2717                                                   if ( m2->mi_op->o_threadctx == op->o_threadctx ) {
2718                                                             break;
2719                                                   }
2720                                         }
2721                                         if ( m2 )
2722                                                   break;
2723 
2724                                         ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
2725                                         /* FIXME: if dynamic config can delete overlays or
2726                                          * databases we'll have to check for cleanup here.
2727                                          * Currently it's not an issue because there are
2728                                          * no dynamic config deletes...
2729                                          */
2730                                         if ( slapd_shutdown )
2731                                                   return SLAPD_ABANDON;
2732 
2733                                         if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
2734                                                   ldap_pvt_thread_yield();
2735                                         ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
2736 
2737                                         /* clean up if the caller is giving up */
2738                                         if ( op->o_abandon ) {
2739                                                   modinst **m2;
2740                                                   slap_callback **sc;
2741                                                   for (m2 = &mt->mt_mods; ; m2 = &(*m2)->mi_next) {
2742                                                             if ( *m2 == mi ) {
2743                                                                       *m2 = mi->mi_next;
2744                                                                       if ( mt->mt_tail == mi )
2745                                                                                 mt->mt_tail = ( m2 == &mt->mt_mods ) ? NULL : (modinst *)m2;
2746                                                                       break;
2747                                                             }
2748                                                   }
2749                                                   for (sc = &op->o_callback; ; sc = &(*sc)->sc_next) {
2750                                                             if ( *sc == cb ) {
2751                                                                       *sc = cb->sc_next;
2752                                                                       break;
2753                                                             }
2754                                                   }
2755                                                   op->o_tmpfree( cb, op->o_tmpmemctx );
2756                                                   ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
2757                                                   return SLAPD_ABANDON;
2758                                         }
2759                               }
2760                               ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
2761                     } else {
2762                               /* Record that we're modifying this entry now */
2763                               mt = ch_malloc( sizeof(modtarget) );
2764                               mt->mt_mods = mi;
2765                               mt->mt_tail = mi;
2766                               ber_dupbv( &mt->mt_dn, &mi->mi_op->o_req_ndn );
2767                               ldap_pvt_thread_mutex_init( &mt->mt_mutex );
2768                               ldap_avl_insert( &si->si_mods, mt, sp_avl_cmp, ldap_avl_dup_error );
2769                               ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
2770                     }
2771                     opc->smt = mt;
2772           }
2773 
2774           if (( have_psearches || si->si_logs ) && op->o_tag != LDAP_REQ_ADD )
2775                     syncprov_matchops( op, opc, 1 );
2776 
2777           return SLAP_CB_CONTINUE;
2778 }
2779 
2780 static int
syncprov_op_extended(Operation * op,SlapReply * rs)2781 syncprov_op_extended( Operation *op, SlapReply *rs )
2782 {
2783           if ( exop_is_write( op ))
2784                     return syncprov_op_mod( op, rs );
2785 
2786           return SLAP_CB_CONTINUE;
2787 }
2788 
2789 typedef struct searchstate {
2790           slap_overinst *ss_on;
2791           syncops *ss_so;
2792           BerVarray ss_ctxcsn;
2793           int *ss_sids;
2794           int ss_numcsns;
2795 #define   SS_PRESENT          0x01
2796 #define   SS_CHANGED          0x02
2797           int ss_flags;
2798 } searchstate;
2799 
2800 typedef struct SyncOperationBuffer {
2801           Operation           sob_op;
2802           Opheader            sob_hdr;
2803           OpExtra                       sob_oe;
2804           AttributeName       sob_extra;          /* not always present */
2805           /* Further data allocated here */
2806 } SyncOperationBuffer;
2807 
2808 static void
syncprov_detach_op(Operation * op,syncops * so,slap_overinst * on)2809 syncprov_detach_op( Operation *op, syncops *so, slap_overinst *on )
2810 {
2811           SyncOperationBuffer *sopbuf2;
2812           Operation *op2;
2813           int i, alen = 0;
2814           size_t size;
2815           char *ptr;
2816           GroupAssertion *g1, *g2;
2817 
2818           /* count the search attrs */
2819           for (i=0; op->ors_attrs && !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++) {
2820                     alen += op->ors_attrs[i].an_name.bv_len + 1;
2821           }
2822           /* Make a new copy of the operation */
2823           size = offsetof( SyncOperationBuffer, sob_extra ) +
2824                     (i ? ( (i+1) * sizeof(AttributeName) + alen) : 0) +
2825                     op->o_req_dn.bv_len + 1 +
2826                     op->o_req_ndn.bv_len + 1 +
2827                     op->o_ndn.bv_len + 1 +
2828                     so->s_filterstr.bv_len + 1;
2829           sopbuf2 = ch_calloc( 1, size );
2830           op2 = &sopbuf2->sob_op;
2831           op2->o_hdr = &sopbuf2->sob_hdr;
2832           LDAP_SLIST_FIRST(&op2->o_extra) = &sopbuf2->sob_oe;
2833 
2834           /* Copy the fields we care about explicitly, leave the rest alone */
2835           *op2->o_hdr = *op->o_hdr;
2836           op2->o_tag = op->o_tag;
2837           op2->o_time = op->o_time;
2838           op2->o_bd = on->on_info->oi_origdb;
2839           op2->o_request = op->o_request;
2840           op2->o_managedsait = op->o_managedsait;
2841           LDAP_SLIST_FIRST(&op2->o_extra)->oe_key = on;
2842           LDAP_SLIST_NEXT(LDAP_SLIST_FIRST(&op2->o_extra), oe_next) = NULL;
2843 
2844           ptr = (char *) sopbuf2 + offsetof( SyncOperationBuffer, sob_extra );
2845           if ( i ) {
2846                     op2->ors_attrs = (AttributeName *) ptr;
2847                     ptr = (char *) &op2->ors_attrs[i+1];
2848                     for (i=0; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++) {
2849                               op2->ors_attrs[i] = op->ors_attrs[i];
2850                               op2->ors_attrs[i].an_name.bv_val = ptr;
2851                               ptr = lutil_strcopy( ptr, op->ors_attrs[i].an_name.bv_val ) + 1;
2852                     }
2853                     BER_BVZERO( &op2->ors_attrs[i].an_name );
2854           }
2855 
2856           op2->o_authz = op->o_authz;
2857           op2->o_ndn.bv_val = ptr;
2858           ptr = lutil_strcopy(ptr, op->o_ndn.bv_val) + 1;
2859           op2->o_dn = op2->o_ndn;
2860           op2->o_req_dn.bv_len = op->o_req_dn.bv_len;
2861           op2->o_req_dn.bv_val = ptr;
2862           ptr = lutil_strcopy(ptr, op->o_req_dn.bv_val) + 1;
2863           op2->o_req_ndn.bv_len = op->o_req_ndn.bv_len;
2864           op2->o_req_ndn.bv_val = ptr;
2865           ptr = lutil_strcopy(ptr, op->o_req_ndn.bv_val) + 1;
2866           op2->ors_filterstr.bv_val = ptr;
2867           strcpy( ptr, so->s_filterstr.bv_val );
2868           op2->ors_filterstr.bv_len = so->s_filterstr.bv_len;
2869 
2870           /* Skip the AND/GE clause that we stuck on in front */
2871           if ( so->s_flags & PS_FIX_FILTER ) {
2872                     op2->ors_filter = op->ors_filter->f_and->f_next;
2873                     so->s_flags ^= PS_FIX_FILTER;
2874           } else {
2875                     op2->ors_filter = op->ors_filter;
2876           }
2877           op2->ors_filter = filter_dup( op2->ors_filter, NULL );
2878           so->s_op = op2;
2879 
2880           /* Copy any cached group ACLs individually */
2881           op2->o_groups = NULL;
2882           for ( g1=op->o_groups; g1; g1=g1->ga_next ) {
2883                     g2 = ch_malloc( sizeof(GroupAssertion) + g1->ga_len );
2884                     *g2 = *g1;
2885                     strcpy( g2->ga_ndn, g1->ga_ndn );
2886                     g2->ga_next = op2->o_groups;
2887                     op2->o_groups = g2;
2888           }
2889           /* Don't allow any further group caching */
2890           op2->o_do_not_cache = 1;
2891 
2892           /* Add op2 to conn so abandon will find us */
2893           op->o_conn->c_n_ops_executing++;
2894           op->o_conn->c_n_ops_completed--;
2895           LDAP_STAILQ_INSERT_TAIL( &op->o_conn->c_ops, op2, o_next );
2896           so->s_flags |= PS_IS_DETACHED;
2897 
2898           /* Prevent anyone else from trying to send a result for this op */
2899           op->o_abandon = 1;
2900 }
2901 
2902 static int
syncprov_search_response(Operation * op,SlapReply * rs)2903 syncprov_search_response( Operation *op, SlapReply *rs )
2904 {
2905           searchstate *ss = op->o_callback->sc_private;
2906           slap_overinst *on = ss->ss_on;
2907           syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
2908           sync_control *srs = op->o_controls[slap_cids.sc_LDAPsync];
2909 
2910           if ( rs->sr_type == REP_SEARCH || rs->sr_type == REP_SEARCHREF ) {
2911                     Attribute *a;
2912                     /* If we got a referral without a referral object, there's
2913                      * something missing that we cannot replicate. Just ignore it.
2914                      * The consumer will abort because we didn't send the expected
2915                      * control.
2916                      */
2917                     if ( !rs->sr_entry ) {
2918                               assert( rs->sr_entry != NULL );
2919                               Debug( LDAP_DEBUG_ANY, "%s syncprov_search_response: "
2920                                         "bogus referral in context\n", op->o_log_prefix );
2921                               return SLAP_CB_CONTINUE;
2922                     }
2923                     a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryCSN );
2924                     if ( a == NULL && rs->sr_operational_attrs != NULL ) {
2925                               a = attr_find( rs->sr_operational_attrs, slap_schema.si_ad_entryCSN );
2926                     }
2927                     if ( a ) {
2928                               int i, sid;
2929                               sid = slap_parse_csn_sid( &a->a_nvals[0] );
2930 
2931                               /* If not a persistent search */
2932                               if ( !ss->ss_so ) {
2933                                         /* Make sure entry is less than the snapshot'd contextCSN */
2934                                         for ( i=0; i<ss->ss_numcsns; i++ ) {
2935                                                   if ( sid == ss->ss_sids[i] && ber_bvcmp( &a->a_nvals[0],
2936                                                             &ss->ss_ctxcsn[i] ) > 0 ) {
2937                                                             Debug( LDAP_DEBUG_SYNC, "%s syncprov_search_response: "
2938                                                                       "Entry %s CSN %s greater than snapshot %s\n",
2939                                                                       op->o_log_prefix,
2940                                                                       rs->sr_entry->e_name.bv_val,
2941                                                                       a->a_nvals[0].bv_val,
2942                                                                       ss->ss_ctxcsn[i].bv_val );
2943                                                             return LDAP_SUCCESS;
2944                                                   }
2945                                         }
2946                               }
2947 
2948                               /* Don't send old entries twice */
2949                               if ( srs->sr_state.ctxcsn ) {
2950                                         for ( i=0; i<srs->sr_state.numcsns; i++ ) {
2951                                                   if ( sid == srs->sr_state.sids[i] &&
2952                                                             ber_bvcmp( &a->a_nvals[0],
2953                                                                       &srs->sr_state.ctxcsn[i] )<= 0 ) {
2954                                                             Debug( LDAP_DEBUG_SYNC, "%s syncprov_search_response: "
2955                                                                       "Entry %s CSN %s older or equal to ctx %s\n",
2956                                                                       op->o_log_prefix,
2957                                                                       rs->sr_entry->e_name.bv_val,
2958                                                                       a->a_nvals[0].bv_val,
2959                                                                       srs->sr_state.ctxcsn[i].bv_val );
2960                                                             return LDAP_SUCCESS;
2961                                                   }
2962                                         }
2963                               }
2964                     }
2965                     rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
2966                               op->o_tmpmemctx );
2967                     rs->sr_ctrls[1] = NULL;
2968                     rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
2969                     /* If we're in delta-sync mode, always send a cookie */
2970                     if ( si->si_nopres && si->si_usehint && a ) {
2971                               struct berval cookie;
2972                               slap_compose_sync_cookie( op, &cookie, a->a_nvals, srs->sr_state.rid,
2973                                                   slap_serverID ? slap_serverID : -1, NULL );
2974                               rs->sr_err = syncprov_state_ctrl( op, rs, rs->sr_entry,
2975                                         LDAP_SYNC_ADD, rs->sr_ctrls, 0, 1, &cookie );
2976                               op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
2977                     } else {
2978                               rs->sr_err = syncprov_state_ctrl( op, rs, rs->sr_entry,
2979                                         LDAP_SYNC_ADD, rs->sr_ctrls, 0, 0, NULL );
2980                     }
2981           } else if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS ) {
2982                     struct berval cookie = BER_BVNULL;
2983 
2984                     if ( ( ss->ss_flags & SS_CHANGED ) &&
2985                               ss->ss_ctxcsn && !BER_BVISNULL( &ss->ss_ctxcsn[0] )) {
2986                               slap_compose_sync_cookie( op, &cookie, ss->ss_ctxcsn,
2987                                         srs->sr_state.rid,
2988                                         slap_serverID ? slap_serverID : -1, NULL );
2989 
2990                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_search_response: cookie=%s\n",
2991                                         op->o_log_prefix, cookie.bv_val );
2992                     }
2993 
2994                     /* Is this a regular refresh?
2995                      * Note: refresh never gets here if there were no changes
2996                      */
2997                     if ( !ss->ss_so ) {
2998                               rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
2999                                         op->o_tmpmemctx );
3000                               rs->sr_ctrls[1] = NULL;
3001                               rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
3002                               rs->sr_err = syncprov_done_ctrl( op, rs, rs->sr_ctrls,
3003                                         0, 1, &cookie, ( ss->ss_flags & SS_PRESENT ) ?  LDAP_SYNC_REFRESH_PRESENTS :
3004                                                   LDAP_SYNC_REFRESH_DELETES );
3005                               op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
3006                     } else {
3007                     /* It's RefreshAndPersist, transition to Persist phase */
3008                               syncprov_sendinfo( op, rs, ( ss->ss_flags & SS_PRESENT ) ?
3009                                         LDAP_TAG_SYNC_REFRESH_PRESENT : LDAP_TAG_SYNC_REFRESH_DELETE,
3010                                         ( ss->ss_flags & SS_CHANGED ) ? &cookie : NULL,
3011                                         1, NULL, 0 );
3012                               if ( !BER_BVISNULL( &cookie ))
3013                                         op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
3014 
3015                               /* Detach this Op from frontend control */
3016                               ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
3017 
3018                               /* But not if this connection was closed along the way */
3019                               if ( op->o_abandon ) {
3020                                         ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
3021                                         /* syncprov_ab_cleanup will free this syncop */
3022                                         return SLAPD_ABANDON;
3023 
3024                               } else {
3025                                         ldap_pvt_thread_mutex_lock( &ss->ss_so->s_mutex );
3026                                         /* Turn off the refreshing flag */
3027                                         ss->ss_so->s_flags ^= PS_IS_REFRESHING;
3028 
3029                                         Debug( LDAP_DEBUG_SYNC, "%s syncprov_search_response: "
3030                                                   "detaching op\n", op->o_log_prefix );
3031                                         syncprov_detach_op( op, ss->ss_so, on );
3032 
3033                                         ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
3034 
3035                                         /* If there are queued responses, fire them off */
3036                                         if ( ss->ss_so->s_res )
3037                                                   syncprov_qstart( ss->ss_so );
3038                                         ldap_pvt_thread_mutex_unlock( &ss->ss_so->s_mutex );
3039                               }
3040 
3041                               return LDAP_SUCCESS;
3042                     }
3043           }
3044 
3045           return SLAP_CB_CONTINUE;
3046 }
3047 
3048 static int
syncprov_op_search(Operation * op,SlapReply * rs)3049 syncprov_op_search( Operation *op, SlapReply *rs )
3050 {
3051           slap_overinst                 *on = (slap_overinst *)op->o_bd->bd_info;
3052           syncprov_info_t               *si = (syncprov_info_t *)on->on_bi.bi_private;
3053           slap_callback       *cb;
3054           int gotstate = 0, changed = 0, do_present = 0;
3055           syncops *sop = NULL;
3056           searchstate *ss;
3057           sync_control *srs;
3058           BerVarray ctxcsn;
3059           int i, *sids, numcsns;
3060           struct berval mincsn, maxcsn;
3061           int minsid, maxsid;
3062           int dirty = 0;
3063 
3064           if ( !(op->o_sync_mode & SLAP_SYNC_REFRESH) ) return SLAP_CB_CONTINUE;
3065 
3066           if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
3067                     send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "illegal value for derefAliases" );
3068                     return rs->sr_err;
3069           }
3070 
3071           srs = op->o_controls[slap_cids.sc_LDAPsync];
3072           Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
3073                     "got a %ssearch with a cookie=%s\n",
3074                     op->o_log_prefix,
3075                     op->o_sync_mode & SLAP_SYNC_PERSIST ? "persistent ": "",
3076                     srs->sr_state.octet_str.bv_val );
3077 
3078           /* If this is a persistent search, set it up right away */
3079           if ( op->o_sync_mode & SLAP_SYNC_PERSIST ) {
3080                     syncops so = {0};
3081                     fbase_cookie fc;
3082                     opcookie opc;
3083                     slap_callback sc = {0};
3084 
3085                     fc.fss = &so;
3086                     fc.fbase = 0;
3087                     so.s_eid = NOID;
3088                     so.s_op = op;
3089                     so.s_flags = PS_IS_REFRESHING | PS_FIND_BASE;
3090                     /* syncprov_findbase expects to be called as a callback... */
3091                     sc.sc_private = &opc;
3092                     opc.son = on;
3093                     ldap_pvt_thread_mutex_init( &so.s_mutex );
3094                     cb = op->o_callback;
3095                     op->o_callback = &sc;
3096                     rs->sr_err = syncprov_findbase( op, &fc );
3097                     op->o_callback = cb;
3098                     ldap_pvt_thread_mutex_destroy( &so.s_mutex );
3099 
3100                     if ( rs->sr_err != LDAP_SUCCESS ) {
3101                               send_ldap_result( op, rs );
3102                               return rs->sr_err;
3103                     }
3104                     sop = ch_malloc( sizeof( syncops ));
3105                     *sop = so;
3106                     sop->s_rid = srs->sr_state.rid;
3107                     sop->s_sid = srs->sr_state.sid;
3108                     /* set refcount=2 to prevent being freed out from under us
3109                      * by abandons that occur while we're running here
3110                      */
3111                     sop->s_inuse = 2;
3112 
3113                     ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
3114                     while ( si->si_active ) {
3115                               /* Wait for active mods to finish before proceeding, as they
3116                                * may already have inspected the si_ops list looking for
3117                                * consumers to replicate the change to.  Using the log
3118                                * doesn't help, as we may finish playing it before the
3119                                * active mods gets added to it.
3120                                */
3121                               ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
3122                               if ( slapd_shutdown ) {
3123                                         ch_free( sop );
3124                                         return SLAPD_ABANDON;
3125                               }
3126                               if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
3127                                         ldap_pvt_thread_yield();
3128                               ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
3129                     }
3130                     if ( op->o_abandon ) {
3131                               ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
3132                               ch_free( sop );
3133                               return SLAPD_ABANDON;
3134                     }
3135                     ldap_pvt_thread_mutex_init( &sop->s_mutex );
3136                     sop->s_next = si->si_ops;
3137                     sop->s_si = si;
3138                     si->si_ops = sop;
3139                     ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
3140                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
3141                               "registered persistent search\n", op->o_log_prefix );
3142           }
3143 
3144           /* snapshot the ctxcsn
3145            * Note: this must not be done before the psearch setup. (ITS#8365)
3146            */
3147           ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
3148           numcsns = si->si_numcsns;
3149           if ( numcsns ) {
3150                     ber_bvarray_dup_x( &ctxcsn, si->si_ctxcsn, op->o_tmpmemctx );
3151                     sids = op->o_tmpalloc( numcsns * sizeof(int), op->o_tmpmemctx );
3152                     for ( i=0; i<numcsns; i++ )
3153                               sids[i] = si->si_sids[i];
3154           } else {
3155                     ctxcsn = NULL;
3156                     sids = NULL;
3157           }
3158           dirty = si->si_dirty;
3159           ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
3160 
3161           /* If we have a cookie, handle the PRESENT lookups */
3162           if ( srs->sr_state.ctxcsn ) {
3163                     sessionlog *sl;
3164                     int i, j;
3165 
3166                     /* If we don't have any CSN of our own yet, bail out.
3167                      */
3168                     if ( !numcsns ) {
3169                               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
3170                               rs->sr_text = "consumer has state info but provider doesn't!";
3171                               goto bailout;
3172                     }
3173 
3174                     if ( !si->si_nopres )
3175                               do_present = SS_PRESENT;
3176 
3177                     /* If there are SIDs we don't recognize in the cookie, drop them */
3178                     for (i=0; i<srs->sr_state.numcsns; ) {
3179                               for (j=i; j<numcsns; j++) {
3180                                         if ( srs->sr_state.sids[i] <= sids[j] ) {
3181                                                   break;
3182                                         }
3183                               }
3184                               /* not found */
3185                               if ( j == numcsns || srs->sr_state.sids[i] != sids[j] ) {
3186                                         char *tmp = srs->sr_state.ctxcsn[i].bv_val;
3187                                         srs->sr_state.numcsns--;
3188                                         for ( j=i; j<srs->sr_state.numcsns; j++ ) {
3189                                                   srs->sr_state.ctxcsn[j] = srs->sr_state.ctxcsn[j+1];
3190                                                   srs->sr_state.sids[j] = srs->sr_state.sids[j+1];
3191                                         }
3192                                         srs->sr_state.ctxcsn[j].bv_val = tmp;
3193                                         srs->sr_state.ctxcsn[j].bv_len = 0;
3194                                         continue;
3195                               }
3196                               i++;
3197                     }
3198 
3199                     if (srs->sr_state.numcsns != numcsns) {
3200                               /* consumer doesn't have the right number of CSNs */
3201                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
3202                                         "consumer cookie is missing a csn we track\n",
3203                                         op->o_log_prefix );
3204                               changed = SS_CHANGED;
3205                               if ( srs->sr_state.ctxcsn ) {
3206                                         ber_bvarray_free_x( srs->sr_state.ctxcsn, op->o_tmpmemctx );
3207                                         srs->sr_state.ctxcsn = NULL;
3208                               }
3209                               if ( srs->sr_state.sids ) {
3210                                         slap_sl_free( srs->sr_state.sids, op->o_tmpmemctx );
3211                                         srs->sr_state.sids = NULL;
3212                               }
3213                               srs->sr_state.numcsns = 0;
3214                               goto shortcut;
3215                     }
3216 
3217                     /* Find the smallest CSN which differs from contextCSN */
3218                     mincsn.bv_len = 0;
3219                     maxcsn.bv_len = 0;
3220                     for ( i=0,j=0; i<srs->sr_state.numcsns; i++ ) {
3221                               int newer;
3222                               while ( srs->sr_state.sids[i] != sids[j] ) j++;
3223                               if ( BER_BVISEMPTY( &maxcsn ) || ber_bvcmp( &maxcsn,
3224                                         &srs->sr_state.ctxcsn[i] ) < 0 ) {
3225                                         maxcsn = srs->sr_state.ctxcsn[i];
3226                                         maxsid = sids[j];
3227                               }
3228                               newer = ber_bvcmp( &srs->sr_state.ctxcsn[i], &ctxcsn[j] );
3229                               /* If our state is newer, tell consumer about changes */
3230                               if ( newer < 0) {
3231                                         changed = SS_CHANGED;
3232                                         if ( BER_BVISEMPTY( &mincsn ) || ber_bvcmp( &mincsn,
3233                                                   &srs->sr_state.ctxcsn[i] ) > 0 ) {
3234                                                   mincsn = srs->sr_state.ctxcsn[i];
3235                                                   minsid = sids[j];
3236                                         }
3237                               } else if ( newer > 0 && sids[j] == slap_serverID ) {
3238                               /* our state is older, complain to consumer */
3239                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
3240                                         rs->sr_text = "consumer state is newer than provider!";
3241                                         Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
3242                                                   "consumer %d state %s is newer than provider %d state %s\n",
3243                                                   op->o_log_prefix, sids[i], srs->sr_state.ctxcsn[i].bv_val,
3244                                                   sids[j], /* == slap_serverID */
3245                                                   ctxcsn[j].bv_val);
3246 bailout:
3247                                         if ( sop ) {
3248                                                   syncops **sp = &si->si_ops;
3249 
3250                                                   ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
3251                                                   while ( *sp != sop )
3252                                                             sp = &(*sp)->s_next;
3253                                                   *sp = sop->s_next;
3254                                                   ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
3255                                                   ch_free( sop );
3256                                         }
3257                                         rs->sr_ctrls = NULL;
3258                                         send_ldap_result( op, rs );
3259                                         return rs->sr_err;
3260                               }
3261                     }
3262                     if ( BER_BVISEMPTY( &mincsn )) {
3263                               mincsn = maxcsn;
3264                               minsid = maxsid;
3265                     }
3266 
3267                     /* If nothing has changed, shortcut it */
3268                     if ( !changed && !dirty ) {
3269                               do_present = 0;
3270 no_change:          if ( !(op->o_sync_mode & SLAP_SYNC_PERSIST) ) {
3271                                         LDAPControl         *ctrls[2];
3272 
3273                                         ctrls[0] = NULL;
3274                                         ctrls[1] = NULL;
3275                                         syncprov_done_ctrl( op, rs, ctrls, 0, 0,
3276                                                   NULL, LDAP_SYNC_REFRESH_DELETES );
3277                                         rs->sr_ctrls = ctrls;
3278                                         rs->sr_err = LDAP_SUCCESS;
3279                                         send_ldap_result( op, rs );
3280                                         rs->sr_ctrls = NULL;
3281                                         return rs->sr_err;
3282                               }
3283                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
3284                                         "no change, skipping log replay\n",
3285                                         op->o_log_prefix );
3286                               goto shortcut;
3287                     }
3288 
3289                     if ( !BER_BVISNULL( &si->si_logbase ) ) {
3290                               do_present = 0;
3291                               if ( syncprov_play_accesslog( op, rs, srs, ctxcsn,
3292                                                   numcsns, sids, &mincsn, minsid ) ) {
3293                                         do_present = SS_PRESENT;
3294                               }
3295                     } else if ( si->si_logs ) {
3296                               do_present = 0;
3297                               if ( syncprov_play_sessionlog( op, rs, srs, ctxcsn,
3298                                                   numcsns, sids, &mincsn, minsid ) ) {
3299                                         do_present = SS_PRESENT;
3300                               }
3301                     }
3302                     /*
3303                      * If sessionlog wasn't useful, see if we can find at least one entry
3304                      * that hasn't changed based on the cookie.
3305                      *
3306                      * TODO: Using mincsn only (rather than the whole cookie) will
3307                      * under-approximate the set of entries that haven't changed, but we
3308                      * can't look up CSNs by serverid with the current indexing support.
3309                      *
3310                      * As a result, dormant serverids in the cluster become mincsns and
3311                      * more likely to make syncprov_findcsn(,FIND_CSN,) fail -> triggering
3312                      * an expensive refresh...
3313                      */
3314                     if ( !do_present ) {
3315                               gotstate = 1;
3316                     } else if ( syncprov_findcsn( op, FIND_CSN, &mincsn ) != LDAP_SUCCESS ) {
3317                               /* No, so a reload is required */
3318                               /* the 2.2 consumer doesn't send this hint */
3319                               if ( si->si_usehint && srs->sr_rhint == 0 ) {
3320                                         if ( ctxcsn )
3321                                                   ber_bvarray_free_x( ctxcsn, op->o_tmpmemctx );
3322                                         if ( sids )
3323                                                   op->o_tmpfree( sids, op->o_tmpmemctx );
3324                                         rs->sr_err = LDAP_SYNC_REFRESH_REQUIRED;
3325                                         rs->sr_text = "sync cookie is stale";
3326                                         goto bailout;
3327                               }
3328                               Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
3329                                         "failed to find entry with csn=%s, ignoring cookie\n",
3330                                         op->o_log_prefix, mincsn.bv_val );
3331                               if ( srs->sr_state.ctxcsn ) {
3332                                         ber_bvarray_free_x( srs->sr_state.ctxcsn, op->o_tmpmemctx );
3333                                         srs->sr_state.ctxcsn = NULL;
3334                               }
3335                               if ( srs->sr_state.sids ) {
3336                                         slap_sl_free( srs->sr_state.sids, op->o_tmpmemctx );
3337                                         srs->sr_state.sids = NULL;
3338                               }
3339                               srs->sr_state.numcsns = 0;
3340                     } else {
3341                               gotstate = 1;
3342                               /* If changed and doing Present lookup, send Present UUIDs */
3343                               if ( syncprov_findcsn( op, FIND_PRESENT, 0 ) != LDAP_SUCCESS ) {
3344                                         if ( ctxcsn )
3345                                                   ber_bvarray_free_x( ctxcsn, op->o_tmpmemctx );
3346                                         if ( sids )
3347                                                   op->o_tmpfree( sids, op->o_tmpmemctx );
3348                                         goto bailout;
3349                               }
3350                     }
3351           } else {
3352                     /* The consumer knows nothing, we know nothing. OK. */
3353                     if (!numcsns)
3354                               goto no_change;
3355                     /* No consumer state, assume something has changed */
3356                     changed = SS_CHANGED;
3357           }
3358 
3359 shortcut:
3360           /* Append CSN range to search filter, save original filter
3361            * for persistent search evaluation
3362            */
3363           if ( sop ) {
3364                     ldap_pvt_thread_mutex_lock( &sop->s_mutex );
3365                     sop->s_filterstr = op->ors_filterstr;
3366                     /* correct the refcount that was set to 2 before */
3367                     sop->s_inuse--;
3368           }
3369 
3370           /* If something changed, find the changes */
3371           if ( gotstate && ( changed || dirty ) ) {
3372                     Filter *fand, *fava;
3373 
3374                     fand = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
3375                     fand->f_choice = LDAP_FILTER_AND;
3376                     fand->f_next = NULL;
3377                     fava = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
3378                     fand->f_and = fava;
3379                     fava->f_choice = LDAP_FILTER_GE;
3380                     fava->f_ava = op->o_tmpalloc( sizeof(AttributeAssertion), op->o_tmpmemctx );
3381                     fava->f_ava->aa_desc = slap_schema.si_ad_entryCSN;
3382 #ifdef LDAP_COMP_MATCH
3383                     fava->f_ava->aa_cf = NULL;
3384 #endif
3385                     ber_dupbv_x( &fava->f_ava->aa_value, &mincsn, op->o_tmpmemctx );
3386                     fava->f_next = op->ors_filter;
3387                     op->ors_filter = fand;
3388                     filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
3389                     if ( sop ) {
3390                               sop->s_flags |= PS_FIX_FILTER;
3391                     }
3392           }
3393           if ( sop ) {
3394                     ldap_pvt_thread_mutex_unlock( &sop->s_mutex );
3395           }
3396 
3397           /* Let our callback add needed info to returned entries */
3398           cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(searchstate), op->o_tmpmemctx);
3399           ss = (searchstate *)(cb+1);
3400           ss->ss_on = on;
3401           ss->ss_so = sop;
3402           ss->ss_flags = do_present | changed;
3403           ss->ss_ctxcsn = ctxcsn;
3404           ss->ss_numcsns = numcsns;
3405           ss->ss_sids = sids;
3406           cb->sc_response = syncprov_search_response;
3407           cb->sc_private = ss;
3408           cb->sc_next = op->o_callback;
3409           op->o_callback = cb;
3410 
3411           /* If this is a persistent search and no changes were reported during
3412            * the refresh phase, just invoke the response callback to transition
3413            * us into persist phase
3414            */
3415           if ( !changed && !dirty ) {
3416                     Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
3417                               "nothing changed, finishing up initial search early\n",
3418                               op->o_log_prefix );
3419                     rs->sr_err = LDAP_SUCCESS;
3420                     rs->sr_nentries = 0;
3421                     send_ldap_result( op, rs );
3422                     return rs->sr_err;
3423           }
3424           return SLAP_CB_CONTINUE;
3425 }
3426 
3427 static int
syncprov_operational(Operation * op,SlapReply * rs)3428 syncprov_operational(
3429           Operation *op,
3430           SlapReply *rs )
3431 {
3432           slap_overinst                 *on = (slap_overinst *)op->o_bd->bd_info;
3433           syncprov_info_t               *si = (syncprov_info_t *)on->on_bi.bi_private;
3434 
3435           /* This prevents generating unnecessarily; frontend will strip
3436            * any statically stored copy.
3437            */
3438           if ( op->o_sync != SLAP_CONTROL_NONE )
3439                     return SLAP_CB_CONTINUE;
3440 
3441           if ( rs->sr_entry &&
3442                     dn_match( &rs->sr_entry->e_nname, &si->si_contextdn )) {
3443 
3444                     if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
3445                               ad_inlist( slap_schema.si_ad_contextCSN, rs->sr_attrs )) {
3446                               Attribute *a, **ap = NULL;
3447 
3448                               for ( a=rs->sr_entry->e_attrs; a; a=a->a_next ) {
3449                                         if ( a->a_desc == slap_schema.si_ad_contextCSN )
3450                                                   break;
3451                               }
3452 
3453                               ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
3454                               if ( si->si_ctxcsn ) {
3455                                         if ( !a ) {
3456                                                   for ( ap = &rs->sr_operational_attrs; *ap;
3457                                                             ap=&(*ap)->a_next );
3458 
3459                                                   a = attr_alloc( slap_schema.si_ad_contextCSN );
3460                                                   *ap = a;
3461                                         }
3462 
3463                                         if ( !ap ) {
3464                                                   if ( rs_entry2modifiable( op, rs, on )) {
3465                                                             a = attr_find( rs->sr_entry->e_attrs,
3466                                                                       slap_schema.si_ad_contextCSN );
3467                                                   }
3468                                                   if ( a->a_nvals != a->a_vals ) {
3469                                                             ber_bvarray_free( a->a_nvals );
3470                                                   }
3471                                                   a->a_nvals = NULL;
3472                                                   ber_bvarray_free( a->a_vals );
3473                                                   a->a_vals = NULL;
3474                                                   a->a_numvals = 0;
3475                                         }
3476                                         attr_valadd( a, si->si_ctxcsn, si->si_ctxcsn, si->si_numcsns );
3477                               }
3478                               ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
3479                     }
3480           }
3481           return SLAP_CB_CONTINUE;
3482 }
3483 
3484 static int
syncprov_setup_accesslog(void)3485 syncprov_setup_accesslog(void)
3486 {
3487           const char *text;
3488           int rc = -1;
3489 
3490           if ( !ad_reqType ) {
3491                     if ( slap_str2ad( "reqType", &ad_reqType, &text ) ) {
3492                               Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
3493                                                   "couldn't get definition for attribute reqType, "
3494                                                   "is accessslog configured?\n" );
3495                               return rc;
3496                     }
3497           }
3498 
3499           if ( !ad_reqResult ) {
3500                     if ( slap_str2ad( "reqResult", &ad_reqResult, &text ) ) {
3501                               Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
3502                                                   "couldn't get definition for attribute reqResult, "
3503                                                   "is accessslog configured?\n" );
3504                               return rc;
3505                     }
3506           }
3507 
3508           if ( !ad_reqDN ) {
3509                     if ( slap_str2ad( "reqDN", &ad_reqDN, &text ) ) {
3510                               Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
3511                                                   "couldn't get definition for attribute reqDN, "
3512                                                   "is accessslog configured?\n" );
3513                               return rc;
3514                     }
3515           }
3516 
3517           if ( !ad_reqEntryUUID ) {
3518                     if ( slap_str2ad( "reqEntryUUID", &ad_reqEntryUUID, &text ) ) {
3519                               Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
3520                                                   "couldn't get definition for attribute reqEntryUUID, "
3521                                                   "is accessslog configured?\n" );
3522                               return rc;
3523                     }
3524           }
3525 
3526           if ( !ad_reqNewDN ) {
3527                     if ( slap_str2ad( "reqNewDN", &ad_reqNewDN, &text ) ) {
3528                               Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
3529                                                   "couldn't get definition for attribute reqNewDN, "
3530                                                   "is accessslog configured?\n" );
3531                               return rc;
3532                     }
3533           }
3534 
3535           if ( !ad_minCSN ) {
3536                     if ( slap_str2ad( "minCSN", &ad_minCSN, &text ) ) {
3537                               Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
3538                                                   "couldn't get definition for attribute minCSN, "
3539                                                   "is accessslog configured?\n" );
3540                               return rc;
3541                     }
3542           }
3543 
3544           return LDAP_SUCCESS;
3545 }
3546 
3547 enum {
3548           SP_CHKPT = 1,
3549           SP_SESSL,
3550           SP_NOPRES,
3551           SP_USEHINT,
3552           SP_LOGDB
3553 };
3554 
3555 static ConfigDriver sp_cf_gen;
3556 
3557 static ConfigTable spcfg[] = {
3558           { "syncprov-checkpoint", "ops> <minutes", 3, 3, 0, ARG_MAGIC|SP_CHKPT,
3559                     sp_cf_gen, "( OLcfgOvAt:1.1 NAME 'olcSpCheckpoint' "
3560                               "DESC 'ContextCSN checkpoint interval in ops and minutes' "
3561                               "EQUALITY caseIgnoreMatch "
3562                               "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
3563           { "syncprov-sessionlog", "ops", 2, 2, 0, ARG_INT|ARG_MAGIC|SP_SESSL,
3564                     sp_cf_gen, "( OLcfgOvAt:1.2 NAME 'olcSpSessionlog' "
3565                               "DESC 'Session log size in ops' "
3566                               "EQUALITY integerMatch "
3567                               "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
3568           { "syncprov-nopresent", NULL, 2, 2, 0, ARG_ON_OFF|ARG_MAGIC|SP_NOPRES,
3569                     sp_cf_gen, "( OLcfgOvAt:1.3 NAME 'olcSpNoPresent' "
3570                               "DESC 'Omit Present phase processing' "
3571                               "EQUALITY booleanMatch "
3572                               "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
3573           { "syncprov-reloadhint", NULL, 2, 2, 0, ARG_ON_OFF|ARG_MAGIC|SP_USEHINT,
3574                     sp_cf_gen, "( OLcfgOvAt:1.4 NAME 'olcSpReloadHint' "
3575                               "DESC 'Observe Reload Hint in Request control' "
3576                               "EQUALITY booleanMatch "
3577                               "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
3578           { "syncprov-sessionlog-source", NULL, 2, 2, 0, ARG_DN|ARG_QUOTE|ARG_MAGIC|SP_LOGDB,
3579                     sp_cf_gen, "( OLcfgOvAt:1.5 NAME 'olcSpSessionlogSource' "
3580                               "DESC 'On startup, try loading sessionlog from this subtree' "
3581                               "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
3582           { NULL, NULL, 0, 0, 0, ARG_IGNORED }
3583 };
3584 
3585 static ConfigOCs spocs[] = {
3586           { "( OLcfgOvOc:1.1 "
3587                     "NAME 'olcSyncProvConfig' "
3588                     "DESC 'SyncRepl Provider configuration' "
3589                     "SUP olcOverlayConfig "
3590                     "MAY ( olcSpCheckpoint "
3591                               "$ olcSpSessionlog "
3592                               "$ olcSpNoPresent "
3593                               "$ olcSpReloadHint "
3594                               "$ olcSpSessionlogSource "
3595                     ") )",
3596                               Cft_Overlay, spcfg },
3597           { NULL, 0, NULL }
3598 };
3599 
3600 static int
sp_cf_gen(ConfigArgs * c)3601 sp_cf_gen(ConfigArgs *c)
3602 {
3603           slap_overinst                 *on = (slap_overinst *)c->bi;
3604           syncprov_info_t               *si = (syncprov_info_t *)on->on_bi.bi_private;
3605           int rc = 0;
3606 
3607           if ( c->op == SLAP_CONFIG_EMIT ) {
3608                     switch ( c->type ) {
3609                     case SP_CHKPT:
3610                               if ( si->si_chkops || si->si_chktime ) {
3611                                         struct berval bv;
3612                                         /* we assume si_chktime is a multiple of 60
3613                                          * because the parsed value was originally
3614                                          * multiplied by 60 */
3615                                         bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
3616                                                   "%d %d", si->si_chkops, si->si_chktime/60 );
3617                                         if ( bv.bv_len >= sizeof( c->cr_msg ) ) {
3618                                                   rc = 1;
3619                                         } else {
3620                                                   bv.bv_val = c->cr_msg;
3621                                                   value_add_one( &c->rvalue_vals, &bv );
3622                                         }
3623                               } else {
3624                                         rc = 1;
3625                               }
3626                               break;
3627                     case SP_SESSL:
3628                               if ( si->si_logs ) {
3629                                         c->value_int = si->si_logs->sl_size;
3630                               } else {
3631                                         rc = 1;
3632                               }
3633                               break;
3634                     case SP_NOPRES:
3635                               if ( si->si_nopres ) {
3636                                         c->value_int = 1;
3637                               } else {
3638                                         rc = 1;
3639                               }
3640                               break;
3641                     case SP_USEHINT:
3642                               if ( si->si_usehint ) {
3643                                         c->value_int = 1;
3644                               } else {
3645                                         rc = 1;
3646                               }
3647                               break;
3648                     case SP_LOGDB:
3649                               if ( BER_BVISEMPTY( &si->si_logbase ) ) {
3650                                         rc = 1;
3651                               } else {
3652                                         value_add_one( &c->rvalue_vals, &si->si_logbase );
3653                                         value_add_one( &c->rvalue_nvals, &si->si_logbase );
3654                               }
3655                               break;
3656                     }
3657                     return rc;
3658           } else if ( c->op == LDAP_MOD_DELETE ) {
3659                     switch ( c->type ) {
3660                     case SP_CHKPT:
3661                               si->si_chkops = 0;
3662                               si->si_chktime = 0;
3663                               break;
3664                     case SP_SESSL:
3665                               if ( si->si_logs )
3666                                         si->si_logs->sl_size = 0;
3667                               break;
3668                     case SP_NOPRES:
3669                               si->si_nopres = 0;
3670                               break;
3671                     case SP_USEHINT:
3672                               si->si_usehint = 0;
3673                               break;
3674                     case SP_LOGDB:
3675                               if ( !BER_BVISNULL( &si->si_logbase ) ) {
3676                                         ch_free( si->si_logbase.bv_val );
3677                                         BER_BVZERO( &si->si_logbase );
3678                               }
3679                               break;
3680                     }
3681                     return rc;
3682           }
3683           switch ( c->type ) {
3684           case SP_CHKPT:
3685                     if ( lutil_atoi( &si->si_chkops, c->argv[1] ) != 0 ) {
3686                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s unable to parse checkpoint ops # \"%s\"",
3687                                         c->argv[0], c->argv[1] );
3688                               Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
3689                                         "%s: %s\n", c->log, c->cr_msg );
3690                               return ARG_BAD_CONF;
3691                     }
3692                     if ( si->si_chkops <= 0 ) {
3693                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid checkpoint ops # \"%d\"",
3694                                         c->argv[0], si->si_chkops );
3695                               Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
3696                                         "%s: %s\n", c->log, c->cr_msg );
3697                               return ARG_BAD_CONF;
3698                     }
3699                     if ( lutil_atoi( &si->si_chktime, c->argv[2] ) != 0 ) {
3700                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s unable to parse checkpoint time \"%s\"",
3701                                         c->argv[0], c->argv[1] );
3702                               Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
3703                                         "%s: %s\n", c->log, c->cr_msg );
3704                               return ARG_BAD_CONF;
3705                     }
3706                     if ( si->si_chktime <= 0 ) {
3707                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid checkpoint time \"%d\"",
3708                                         c->argv[0], si->si_chkops );
3709                               Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
3710                                         "%s: %s\n", c->log, c->cr_msg );
3711                               return ARG_BAD_CONF;
3712                     }
3713                     si->si_chktime *= 60;
3714                     break;
3715           case SP_SESSL: {
3716                     sessionlog *sl;
3717                     int size = c->value_int;
3718 
3719                     if ( size < 0 ) {
3720                               snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s size %d is negative",
3721                                         c->argv[0], size );
3722                               Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
3723                                         "%s: %s\n", c->log, c->cr_msg );
3724                               return ARG_BAD_CONF;
3725                     }
3726                     if ( size && !BER_BVISNULL( &si->si_logbase ) ) {
3727                               Debug( LDAP_DEBUG_ANY, "syncprov_config: while configuring "
3728                                                   "internal sessionlog, accesslog source has already been "
3729                                                   "configured, this results in wasteful operation\n" );
3730                     }
3731                     sl = si->si_logs;
3732                     if ( !sl ) {
3733                               if ( !size ) break;
3734                               sl = ch_calloc( 1, sizeof( sessionlog ));
3735                               ldap_pvt_thread_rdwr_init( &sl->sl_mutex );
3736                               si->si_logs = sl;
3737                     }
3738                     sl->sl_size = size;
3739                     }
3740                     break;
3741           case SP_NOPRES:
3742                     si->si_nopres = c->value_int;
3743                     break;
3744           case SP_USEHINT:
3745                     si->si_usehint = c->value_int;
3746                     break;
3747           case SP_LOGDB:
3748                     if ( si->si_logs ) {
3749                               Debug( LDAP_DEBUG_ANY, "syncprov_config: while configuring "
3750                                                   "accesslog source, internal sessionlog has already been "
3751                                                   "configured, this results in wasteful operation\n" );
3752                     }
3753                     if ( CONFIG_ONLINE_ADD( c ) ) {
3754                               if ( !select_backend( &c->value_ndn, 0 ) ) {
3755                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
3756                                                   "<%s> no matching backend found for suffix",
3757                                                   c->argv[0] );
3758                                         Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
3759                                                   c->log, c->cr_msg, c->value_dn.bv_val );
3760                                         rc = 1;
3761                                         break;
3762                               }
3763                               ch_free( c->value_ndn.bv_val );
3764                     }
3765                     si->si_logbase = c->value_ndn;
3766                     rc = syncprov_setup_accesslog();
3767                     ch_free( c->value_dn.bv_val );
3768                     break;
3769           }
3770           return rc;
3771 }
3772 
3773 /* ITS#3456 we cannot run this search on the main thread, must use a
3774  * child thread in order to insure we have a big enough stack.
3775  */
3776 static void *
syncprov_db_otask(void * ptr)3777 syncprov_db_otask(
3778           void *ptr
3779 )
3780 {
3781           syncprov_findcsn( ptr, FIND_MAXCSN, 0 );
3782           return NULL;
3783 }
3784 
3785 static int
syncprov_db_ocallback(Operation * op,SlapReply * rs)3786 syncprov_db_ocallback(
3787           Operation *op,
3788           SlapReply *rs
3789 )
3790 {
3791           if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
3792                     if ( rs->sr_entry->e_name.bv_len )
3793                               op->o_callback->sc_private = (void *)1;
3794           }
3795           return LDAP_SUCCESS;
3796 }
3797 
3798 /* ITS#9015 see if the DB is really empty */
3799 static void *
syncprov_db_otask2(void * ptr)3800 syncprov_db_otask2(
3801           void *ptr
3802 )
3803 {
3804           Operation *op = ptr;
3805           SlapReply rs = {REP_RESULT};
3806           slap_callback cb = {0};
3807           int rc;
3808 
3809           cb.sc_response = syncprov_db_ocallback;
3810 
3811           op->o_managedsait = SLAP_CONTROL_CRITICAL;
3812           op->o_callback = &cb;
3813           op->o_tag = LDAP_REQ_SEARCH;
3814           op->ors_scope = LDAP_SCOPE_SUBTREE;
3815           op->ors_limit = NULL;
3816           op->ors_slimit = 1;
3817           op->ors_tlimit = SLAP_NO_LIMIT;
3818           op->ors_attrs = slap_anlist_no_attrs;
3819           op->ors_attrsonly = 1;
3820           op->ors_deref = LDAP_DEREF_NEVER;
3821           op->ors_filter = &generic_filter;
3822           op->ors_filterstr = generic_filterstr;
3823           rc = op->o_bd->be_search( op, &rs );
3824           if ( rc == LDAP_SIZELIMIT_EXCEEDED || cb.sc_private )
3825                     op->ors_slimit = 2;
3826           return NULL;
3827 }
3828 
3829 /* Read any existing contextCSN from the underlying db.
3830  * Then search for any entries newer than that. If no value exists,
3831  * just generate it. Cache whatever result.
3832  */
3833 static int
syncprov_db_open(BackendDB * be,ConfigReply * cr)3834 syncprov_db_open(
3835           BackendDB *be,
3836           ConfigReply *cr
3837 )
3838 {
3839           slap_overinst   *on = (slap_overinst *) be->bd_info;
3840           syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
3841 
3842           Connection conn = { 0 };
3843           OperationBuffer opbuf;
3844           Operation *op;
3845           Entry *e = NULL;
3846           Attribute *a;
3847           int rc;
3848           void *thrctx = NULL;
3849 
3850           if ( !SLAP_LASTMOD( be )) {
3851                     Debug( LDAP_DEBUG_ANY,
3852                               "syncprov_db_open: invalid config, lastmod must be enabled\n" );
3853                     return -1;
3854           }
3855 
3856           if ( slapMode & SLAP_TOOL_MODE ) {
3857                     return 0;
3858           }
3859 
3860           rc = overlay_register_control( be, LDAP_CONTROL_SYNC );
3861           if ( rc ) {
3862                     return rc;
3863           }
3864 
3865           Debug( LDAP_DEBUG_SYNC, "syncprov_db_open: "
3866                     "starting syncprov for suffix %s\n",
3867                     be->be_suffix[0].bv_val );
3868 
3869           thrctx = ldap_pvt_thread_pool_context();
3870           connection_fake_init2( &conn, &opbuf, thrctx, 0 );
3871           op = &opbuf.ob_op;
3872           op->o_bd = be;
3873           op->o_dn = be->be_rootdn;
3874           op->o_ndn = be->be_rootndn;
3875 
3876           if ( SLAP_SYNC_SUBENTRY( be )) {
3877                     build_new_dn( &si->si_contextdn, be->be_nsuffix,
3878                               (struct berval *)&slap_ldapsync_cn_bv, NULL );
3879           } else {
3880                     si->si_contextdn = be->be_nsuffix[0];
3881           }
3882           rc = overlay_entry_get_ov( op, &si->si_contextdn, NULL,
3883                     slap_schema.si_ad_contextCSN, 0, &e, on );
3884 
3885           if ( e ) {
3886                     ldap_pvt_thread_t tid;
3887 
3888                     a = attr_find( e->e_attrs, slap_schema.si_ad_contextCSN );
3889                     if ( a ) {
3890                               ber_bvarray_dup_x( &si->si_ctxcsn, a->a_vals, NULL );
3891                               si->si_numcsns = a->a_numvals;
3892                               si->si_sids = slap_parse_csn_sids( si->si_ctxcsn, a->a_numvals, NULL );
3893                               slap_sort_csn_sids( si->si_ctxcsn, si->si_sids, si->si_numcsns, NULL );
3894                     }
3895                     overlay_entry_release_ov( op, e, 0, on );
3896                     if ( si->si_ctxcsn && !SLAP_DBCLEAN( be )) {
3897                               op->o_tag = LDAP_REQ_SEARCH;
3898                               op->o_req_dn = be->be_suffix[0];
3899                               op->o_req_ndn = be->be_nsuffix[0];
3900                               op->ors_scope = LDAP_SCOPE_SUBTREE;
3901                               ldap_pvt_thread_create( &tid, 0, syncprov_db_otask, op );
3902                               ldap_pvt_thread_join( tid, NULL );
3903                     }
3904           }
3905 
3906           /* Didn't find a contextCSN, should we generate one? */
3907           if ( !si->si_ctxcsn ) {
3908                     char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
3909                     struct berval csn;
3910 
3911                     if ( SLAP_SINGLE_SHADOW( op->o_bd ) ) {
3912                               /* Not in charge of this serverID, don't generate anything. */
3913                               goto out;
3914                     }
3915                     if ( !SLAP_SYNC_SUBENTRY( be ) && rc != LDAP_SUCCESS
3916                                         && rc != LDAP_NO_SUCH_ATTRIBUTE ) {
3917                               /* If the DB is genuinely empty, don't generate one either. */
3918                               goto out;
3919                     }
3920                     if ( !si->si_contextdn.bv_len ) {
3921                               ldap_pvt_thread_t tid;
3922                               /* a glue entry here with no contextCSN might mean an empty DB.
3923                                * we need to search for children, to be sure.
3924                                */
3925                               op->o_req_dn = be->be_suffix[0];
3926                               op->o_req_ndn = be->be_nsuffix[0];
3927                               op->o_bd->bd_info = (BackendInfo *)on->on_info;
3928                               ldap_pvt_thread_create( &tid, 0, syncprov_db_otask2, op );
3929                               ldap_pvt_thread_join( tid, NULL );
3930                               if ( op->ors_slimit == 1 )
3931                                         goto out;
3932                     }
3933 
3934                     csn.bv_val = csnbuf;
3935                     csn.bv_len = sizeof( csnbuf );
3936                     slap_get_csn( op, &csn, 0 );
3937                     value_add_one( &si->si_ctxcsn, &csn );
3938                     si->si_numcsns = 1;
3939                     si->si_sids = ch_malloc( sizeof(int) );
3940                     si->si_sids[0] = slap_serverID;
3941                     Debug( LDAP_DEBUG_SYNC, "syncprov_db_open: "
3942                               "generated a new ctxcsn=%s for suffix %s\n",
3943                               csn.bv_val, be->be_suffix[0].bv_val );
3944 
3945                     /* make sure we do a checkpoint on close */
3946                     si->si_numops++;
3947           }
3948 
3949           /* Initialize the sessionlog mincsn */
3950           if ( si->si_logs && si->si_numcsns ) {
3951                     sessionlog *sl = si->si_logs;
3952                     int i;
3953                     ber_bvarray_dup_x( &sl->sl_mincsn, si->si_ctxcsn, NULL );
3954                     sl->sl_numcsns = si->si_numcsns;
3955                     sl->sl_sids = ch_malloc( si->si_numcsns * sizeof(int) );
3956                     for ( i=0; i < si->si_numcsns; i++ )
3957                               sl->sl_sids[i] = si->si_sids[i];
3958           }
3959 
3960           if ( !BER_BVISNULL( &si->si_logbase ) ) {
3961                     BackendDB *db = select_backend( &si->si_logbase, 0 );
3962                     if ( !db ) {
3963                               Debug( LDAP_DEBUG_ANY, "syncprov_db_open: "
3964                                                   "configured accesslog database dn='%s' not present\n",
3965                                                   si->si_logbase.bv_val );
3966                               return -1;
3967                     }
3968           }
3969 
3970 out:
3971           op->o_bd->bd_info = (BackendInfo *)on;
3972           return 0;
3973 }
3974 
3975 /* Write the current contextCSN into the underlying db.
3976  */
3977 static int
syncprov_db_close(BackendDB * be,ConfigReply * cr)3978 syncprov_db_close(
3979           BackendDB *be,
3980           ConfigReply *cr
3981 )
3982 {
3983           slap_overinst   *on = (slap_overinst *) be->bd_info;
3984           syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
3985 #ifdef SLAP_CONFIG_DELETE
3986           syncops *so, *sonext;
3987 #endif /* SLAP_CONFIG_DELETE */
3988 
3989           if ( slapMode & SLAP_TOOL_MODE ) {
3990                     return 0;
3991           }
3992           if ( si->si_numops ) {
3993                     Connection conn = {0};
3994                     OperationBuffer opbuf;
3995                     Operation *op;
3996                     void *thrctx;
3997 
3998                     thrctx = ldap_pvt_thread_pool_context();
3999                     connection_fake_init2( &conn, &opbuf, thrctx, 0 );
4000                     op = &opbuf.ob_op;
4001                     op->o_bd = be;
4002                     op->o_dn = be->be_rootdn;
4003                     op->o_ndn = be->be_rootndn;
4004                     syncprov_checkpoint( op, on );
4005           }
4006 
4007 #ifdef SLAP_CONFIG_DELETE
4008           if ( !slapd_shutdown ) {
4009                     ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
4010                     for ( so=si->si_ops, sonext=so;  so; so=sonext  ) {
4011                               SlapReply rs = {REP_RESULT};
4012                               rs.sr_err = LDAP_UNAVAILABLE;
4013                               ldap_pvt_thread_mutex_lock( &so->s_mutex );
4014                               send_ldap_result( so->s_op, &rs );
4015                               sonext=so->s_next;
4016                               if ( so->s_flags & PS_TASK_QUEUED )
4017                                         ldap_pvt_thread_pool_retract( so->s_pool_cookie );
4018                               ldap_pvt_thread_mutex_unlock( &so->s_mutex );
4019                               if ( !syncprov_drop_psearch( so, 0 ))
4020                                         so->s_si = NULL;
4021                     }
4022                     si->si_ops=NULL;
4023                     ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
4024           }
4025           overlay_unregister_control( be, LDAP_CONTROL_SYNC );
4026 #endif /* SLAP_CONFIG_DELETE */
4027 
4028           return 0;
4029 }
4030 
4031 static int
syncprov_db_init(BackendDB * be,ConfigReply * cr)4032 syncprov_db_init(
4033           BackendDB *be,
4034           ConfigReply *cr
4035 )
4036 {
4037           slap_overinst       *on = (slap_overinst *)be->bd_info;
4038           syncprov_info_t     *si;
4039 
4040           if ( SLAP_ISGLOBALOVERLAY( be ) ) {
4041                     Debug( LDAP_DEBUG_ANY,
4042                               "syncprov must be instantiated within a database.\n" );
4043                     return 1;
4044           }
4045 
4046           si = ch_calloc(1, sizeof(syncprov_info_t));
4047           on->on_bi.bi_private = si;
4048           ldap_pvt_thread_rdwr_init( &si->si_csn_rwlock );
4049           ldap_pvt_thread_mutex_init( &si->si_ops_mutex );
4050           ldap_pvt_thread_mutex_init( &si->si_mods_mutex );
4051           ldap_pvt_thread_mutex_init( &si->si_resp_mutex );
4052 
4053           csn_anlist[0].an_desc = slap_schema.si_ad_entryCSN;
4054           csn_anlist[0].an_name = slap_schema.si_ad_entryCSN->ad_cname;
4055           csn_anlist[1].an_desc = slap_schema.si_ad_entryUUID;
4056           csn_anlist[1].an_name = slap_schema.si_ad_entryUUID->ad_cname;
4057 
4058           uuid_anlist[0].an_desc = slap_schema.si_ad_entryUUID;
4059           uuid_anlist[0].an_name = slap_schema.si_ad_entryUUID->ad_cname;
4060 
4061           return 0;
4062 }
4063 
4064 static int
syncprov_db_destroy(BackendDB * be,ConfigReply * cr)4065 syncprov_db_destroy(
4066           BackendDB *be,
4067           ConfigReply *cr
4068 )
4069 {
4070           slap_overinst       *on = (slap_overinst *)be->bd_info;
4071           syncprov_info_t     *si = (syncprov_info_t *)on->on_bi.bi_private;
4072 
4073           if ( si ) {
4074                     if ( si->si_logs ) {
4075                               sessionlog *sl = si->si_logs;
4076 
4077                               ldap_tavl_free( sl->sl_entries, (AVL_FREE)ch_free );
4078                               if ( sl->sl_mincsn )
4079                                         ber_bvarray_free( sl->sl_mincsn );
4080                               if ( sl->sl_sids )
4081                                         ch_free( sl->sl_sids );
4082 
4083                               ldap_pvt_thread_rdwr_destroy(&si->si_logs->sl_mutex);
4084                               ch_free( si->si_logs );
4085                     }
4086                     if ( si->si_ctxcsn )
4087                               ber_bvarray_free( si->si_ctxcsn );
4088                     if ( si->si_sids )
4089                               ch_free( si->si_sids );
4090                     ldap_pvt_thread_mutex_destroy( &si->si_resp_mutex );
4091                     ldap_pvt_thread_mutex_destroy( &si->si_mods_mutex );
4092                     ldap_pvt_thread_mutex_destroy( &si->si_ops_mutex );
4093                     ldap_pvt_thread_rdwr_destroy( &si->si_csn_rwlock );
4094                     ch_free( si );
4095           }
4096 
4097           return 0;
4098 }
4099 
syncprov_parseCtrl(Operation * op,SlapReply * rs,LDAPControl * ctrl)4100 static int syncprov_parseCtrl (
4101           Operation *op,
4102           SlapReply *rs,
4103           LDAPControl *ctrl )
4104 {
4105           ber_tag_t tag;
4106           BerElementBuffer berbuf;
4107           BerElement *ber = (BerElement *)&berbuf;
4108           ber_int_t mode;
4109           ber_len_t len;
4110           struct berval cookie = BER_BVNULL;
4111           sync_control *sr;
4112           int rhint = 0;
4113 
4114           if ( op->o_sync != SLAP_CONTROL_NONE ) {
4115                     rs->sr_text = "Sync control specified multiple times";
4116                     return LDAP_PROTOCOL_ERROR;
4117           }
4118 
4119           if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
4120                     rs->sr_text = "Sync control specified with pagedResults control";
4121                     return LDAP_PROTOCOL_ERROR;
4122           }
4123 
4124           if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
4125                     rs->sr_text = "Sync control value is absent";
4126                     return LDAP_PROTOCOL_ERROR;
4127           }
4128 
4129           if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
4130                     rs->sr_text = "Sync control value is empty";
4131                     return LDAP_PROTOCOL_ERROR;
4132           }
4133 
4134           /* Parse the control value
4135            *      syncRequestValue ::= SEQUENCE {
4136            *              mode   ENUMERATED {
4137            *                      -- 0 unused
4138            *                      refreshOnly               (1),
4139            *                      -- 2 reserved
4140            *                      refreshAndPersist         (3)
4141            *              },
4142            *              cookie  syncCookie OPTIONAL
4143            *      }
4144            */
4145 
4146           ber_init2( ber, &ctrl->ldctl_value, 0 );
4147 
4148           if ( (tag = ber_scanf( ber, "{i" /*}*/, &mode )) == LBER_ERROR ) {
4149                     rs->sr_text = "Sync control : mode decoding error";
4150                     return LDAP_PROTOCOL_ERROR;
4151           }
4152 
4153           switch( mode ) {
4154           case LDAP_SYNC_REFRESH_ONLY:
4155                     mode = SLAP_SYNC_REFRESH;
4156                     break;
4157           case LDAP_SYNC_REFRESH_AND_PERSIST:
4158                     mode = SLAP_SYNC_REFRESH_AND_PERSIST;
4159                     break;
4160           default:
4161                     rs->sr_text = "Sync control : unknown update mode";
4162                     return LDAP_PROTOCOL_ERROR;
4163           }
4164 
4165           tag = ber_peek_tag( ber, &len );
4166 
4167           if ( tag == LDAP_TAG_SYNC_COOKIE ) {
4168                     if (( ber_scanf( ber, /*{*/ "m", &cookie )) == LBER_ERROR ) {
4169                               rs->sr_text = "Sync control : cookie decoding error";
4170                               return LDAP_PROTOCOL_ERROR;
4171                     }
4172                     tag = ber_peek_tag( ber, &len );
4173           }
4174           if ( tag == LDAP_TAG_RELOAD_HINT ) {
4175                     if (( ber_scanf( ber, /*{*/ "b", &rhint )) == LBER_ERROR ) {
4176                               rs->sr_text = "Sync control : rhint decoding error";
4177                               return LDAP_PROTOCOL_ERROR;
4178                     }
4179           }
4180           if (( ber_scanf( ber, /*{*/ "}")) == LBER_ERROR ) {
4181                               rs->sr_text = "Sync control : decoding error";
4182                               return LDAP_PROTOCOL_ERROR;
4183           }
4184           sr = op->o_tmpcalloc( 1, sizeof(struct sync_control), op->o_tmpmemctx );
4185           sr->sr_rhint = rhint;
4186           if (!BER_BVISNULL(&cookie)) {
4187                     ber_dupbv_x( &sr->sr_state.octet_str, &cookie, op->o_tmpmemctx );
4188                     /* If parse fails, pretend no cookie was sent */
4189                     if ( slap_parse_sync_cookie( &sr->sr_state, op->o_tmpmemctx ) ||
4190                               sr->sr_state.rid == -1 ) {
4191                               if ( sr->sr_state.ctxcsn ) {
4192                                         ber_bvarray_free_x( sr->sr_state.ctxcsn, op->o_tmpmemctx );
4193                                         sr->sr_state.ctxcsn = NULL;
4194                               }
4195                               sr->sr_state.numcsns = 0;
4196                     }
4197           }
4198 
4199           op->o_controls[slap_cids.sc_LDAPsync] = sr;
4200 
4201           op->o_sync = ctrl->ldctl_iscritical
4202                     ? SLAP_CONTROL_CRITICAL
4203                     : SLAP_CONTROL_NONCRITICAL;
4204 
4205           op->o_sync_mode |= mode;      /* o_sync_mode shares o_sync */
4206 
4207           return LDAP_SUCCESS;
4208 }
4209 
4210 /* This overlay is set up for dynamic loading via moduleload. For static
4211  * configuration, you'll need to arrange for the slap_overinst to be
4212  * initialized and registered by some other function inside slapd.
4213  */
4214 
4215 static slap_overinst                    syncprov;
4216 
4217 int
syncprov_initialize()4218 syncprov_initialize()
4219 {
4220           int rc;
4221 
4222           rc = register_supported_control( LDAP_CONTROL_SYNC,
4223                     SLAP_CTRL_SEARCH, NULL,
4224                     syncprov_parseCtrl, &slap_cids.sc_LDAPsync );
4225           if ( rc != LDAP_SUCCESS ) {
4226                     Debug( LDAP_DEBUG_ANY,
4227                               "syncprov_init: Failed to register control %d\n", rc );
4228                     return rc;
4229           }
4230 
4231           syncprov.on_bi.bi_type = "syncprov";
4232           syncprov.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
4233           syncprov.on_bi.bi_db_init = syncprov_db_init;
4234           syncprov.on_bi.bi_db_destroy = syncprov_db_destroy;
4235           syncprov.on_bi.bi_db_open = syncprov_db_open;
4236           syncprov.on_bi.bi_db_close = syncprov_db_close;
4237 
4238           syncprov.on_bi.bi_op_abandon = syncprov_op_abandon;
4239           syncprov.on_bi.bi_op_cancel = syncprov_op_abandon;
4240 
4241           syncprov.on_bi.bi_op_add = syncprov_op_mod;
4242           syncprov.on_bi.bi_op_compare = syncprov_op_compare;
4243           syncprov.on_bi.bi_op_delete = syncprov_op_mod;
4244           syncprov.on_bi.bi_op_modify = syncprov_op_mod;
4245           syncprov.on_bi.bi_op_modrdn = syncprov_op_mod;
4246           syncprov.on_bi.bi_op_search = syncprov_op_search;
4247           syncprov.on_bi.bi_extended = syncprov_op_extended;
4248           syncprov.on_bi.bi_operational = syncprov_operational;
4249 
4250           syncprov.on_bi.bi_cf_ocs = spocs;
4251 
4252           generic_filter.f_desc = slap_schema.si_ad_objectClass;
4253 
4254           rc = config_register_schema( spcfg, spocs );
4255           if ( rc ) return rc;
4256 
4257           return overlay_register( &syncprov );
4258 }
4259 
4260 #if SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC
4261 int
init_module(int argc,char * argv[])4262 init_module( int argc, char *argv[] )
4263 {
4264           return syncprov_initialize();
4265 }
4266 #endif /* SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC */
4267 
4268 #endif /* defined(SLAPD_OVER_SYNCPROV) */
4269