1 /*        $NetBSD: backglue.c,v 1.3 2021/08/14 16:14:58 christos Exp $          */
2 
3 /* backglue.c - backend glue */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2001-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 
19 /*
20  * Functions to glue a bunch of other backends into a single tree.
21  * All of the glued backends must share a common suffix. E.g., you
22  * can glue o=foo and ou=bar,o=foo but you can't glue o=foo and o=bar.
23  *
24  * The purpose of these functions is to allow you to split a single database
25  * into pieces (for load balancing purposes, whatever) but still be able
26  * to treat it as a single database after it's been split. As such, each
27  * of the glued backends should have identical rootdn.
28  *  -- Howard Chu
29  */
30 
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: backglue.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
33 
34 #include "portable.h"
35 
36 #include <stdio.h>
37 
38 #include <ac/string.h>
39 #include <ac/socket.h>
40 
41 #define SLAPD_TOOLS
42 #include "slap.h"
43 #include "lutil.h"
44 #include "slap-config.h"
45 
46 typedef struct gluenode {
47           BackendDB *gn_be;
48           struct berval gn_pdn;
49 } gluenode;
50 
51 typedef struct glueinfo {
52           int gi_nodes;
53           struct berval gi_pdn;
54           gluenode gi_n[1];
55 } glueinfo;
56 
57 static slap_overinst          glue;
58 
59 static int glueMode;
60 static BackendDB *glueBack;
61 static BackendDB glueBackDone;
62 #define GLUEBACK_DONE (&glueBackDone)
63 
64 static slap_overinst * glue_tool_inst( BackendInfo *bi);
65 
66 static slap_response glue_op_response;
67 
68 /* Just like select_backend, but only for our backends */
69 static BackendDB *
glue_back_select(BackendDB * be,struct berval * dn)70 glue_back_select (
71           BackendDB *be,
72           struct berval *dn
73 )
74 {
75           slap_overinst       *on = (slap_overinst *)be->bd_info;
76           glueinfo            *gi = (glueinfo *)on->on_bi.bi_private;
77           int i;
78 
79           for (i = gi->gi_nodes-1; i >= 0; i--) {
80                     assert( gi->gi_n[i].gn_be->be_nsuffix != NULL );
81 
82                     if (dnIsSuffix(dn, &gi->gi_n[i].gn_be->be_nsuffix[0])) {
83                               return gi->gi_n[i].gn_be;
84                     }
85           }
86           be->bd_info = on->on_info->oi_orig;
87           return be;
88 }
89 
90 
91 typedef struct glue_state {
92           char *matched;
93           BerVarray refs;
94           LDAPControl **ctrls;
95           int err;
96           int matchlen;
97           int nrefs;
98           int nctrls;
99 } glue_state;
100 
101 static int
glue_op_cleanup(Operation * op,SlapReply * rs)102 glue_op_cleanup( Operation *op, SlapReply *rs )
103 {
104           /* This is not a final result */
105           if (rs->sr_type == REP_RESULT )
106                     rs->sr_type = REP_GLUE_RESULT;
107           return SLAP_CB_CONTINUE;
108 }
109 
110 static int
glue_op_response(Operation * op,SlapReply * rs)111 glue_op_response ( Operation *op, SlapReply *rs )
112 {
113           glue_state *gs = op->o_callback->sc_private;
114 
115           switch(rs->sr_type) {
116           case REP_SEARCH:
117           case REP_SEARCHREF:
118           case REP_INTERMEDIATE:
119                     return SLAP_CB_CONTINUE;
120 
121           default:
122                     if (rs->sr_err == LDAP_SUCCESS ||
123                               rs->sr_err == LDAP_SIZELIMIT_EXCEEDED ||
124                               rs->sr_err == LDAP_TIMELIMIT_EXCEEDED ||
125                               rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ||
126                               rs->sr_err == LDAP_NO_SUCH_OBJECT ||
127                               gs->err != LDAP_SUCCESS)
128                               gs->err = rs->sr_err;
129                     if (gs->err == LDAP_SUCCESS && gs->matched) {
130                               ch_free (gs->matched);
131                               gs->matched = NULL;
132                               gs->matchlen = 0;
133                     }
134                     if (gs->err != LDAP_SUCCESS && rs->sr_matched) {
135                               int len;
136                               len = strlen (rs->sr_matched);
137                               if (len > gs->matchlen) {
138                                         if (gs->matched)
139                                                   ch_free (gs->matched);
140                                         gs->matched = ch_strdup (rs->sr_matched);
141                                         gs->matchlen = len;
142                               }
143                     }
144                     if (rs->sr_ref) {
145                               int i, j, k;
146                               BerVarray new;
147 
148                               for (i=0; rs->sr_ref[i].bv_val; i++);
149 
150                               j = gs->nrefs;
151                               if (!j) {
152                                         new = ch_malloc ((i+1)*sizeof(struct berval));
153                               } else {
154                                         new = ch_realloc(gs->refs,
155                                                   (j+i+1)*sizeof(struct berval));
156                               }
157                               for (k=0; k<i; j++,k++) {
158                                         ber_dupbv( &new[j], &rs->sr_ref[k] );
159                               }
160                               new[j].bv_val = NULL;
161                               gs->nrefs = j;
162                               gs->refs = new;
163                     }
164                     if (rs->sr_ctrls) {
165                               int i, j, k;
166                               LDAPControl **newctrls;
167 
168                               for (i=0; rs->sr_ctrls[i]; i++);
169 
170                               j = gs->nctrls;
171                               if (!j) {
172                                         newctrls = op->o_tmpalloc((i+1)*sizeof(LDAPControl *),
173                                                   op->o_tmpmemctx);
174                               } else {
175                                         /* Forget old pagedResults response if we're sending
176                                          * a new one now
177                                          */
178                                         if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
179                                                   int newpage = 0;
180                                                   for ( k=0; k<i; k++ ) {
181                                                             if ( !strcmp(rs->sr_ctrls[k]->ldctl_oid,
182                                                                       LDAP_CONTROL_PAGEDRESULTS )) {
183                                                                       newpage = 1;
184                                                                       break;
185                                                             }
186                                                   }
187                                                   if ( newpage ) {
188                                                             for ( k=0; k<j; k++ ) {
189                                                                       if ( !strcmp(gs->ctrls[k]->ldctl_oid,
190                                                                                 LDAP_CONTROL_PAGEDRESULTS ))
191                                                                       {
192                                                                                 op->o_tmpfree(gs->ctrls[k], op->o_tmpmemctx);
193                                                                                 gs->ctrls[k] = gs->ctrls[--j];
194                                                                                 gs->ctrls[j] = NULL;
195                                                                                 break;
196                                                                       }
197                                                             }
198                                                   }
199                                         }
200                                         newctrls = op->o_tmprealloc(gs->ctrls,
201                                                   (j+i+1)*sizeof(LDAPControl *), op->o_tmpmemctx);
202                               }
203                               for (k=0; k<i; j++,k++) {
204                                         ber_len_t oidlen = strlen( rs->sr_ctrls[k]->ldctl_oid );
205                                         newctrls[j] = op->o_tmpalloc(sizeof(LDAPControl) + oidlen + 1 + rs->sr_ctrls[k]->ldctl_value.bv_len + 1,
206                                                   op->o_tmpmemctx);
207                                         newctrls[j]->ldctl_iscritical = rs->sr_ctrls[k]->ldctl_iscritical;
208                                         newctrls[j]->ldctl_oid = (char *)&newctrls[j][1];
209                                         lutil_strcopy( newctrls[j]->ldctl_oid, rs->sr_ctrls[k]->ldctl_oid );
210                                         if ( !BER_BVISNULL( &rs->sr_ctrls[k]->ldctl_value ) ) {
211                                                   newctrls[j]->ldctl_value.bv_val = &newctrls[j]->ldctl_oid[oidlen + 1];
212                                                   newctrls[j]->ldctl_value.bv_len = rs->sr_ctrls[k]->ldctl_value.bv_len;
213                                                   lutil_memcopy( newctrls[j]->ldctl_value.bv_val,
214                                                             rs->sr_ctrls[k]->ldctl_value.bv_val,
215                                                             rs->sr_ctrls[k]->ldctl_value.bv_len + 1 );
216                                         } else {
217                                                   BER_BVZERO( &newctrls[j]->ldctl_value );
218                                         }
219                               }
220                               newctrls[j] = NULL;
221                               gs->nctrls = j;
222                               gs->ctrls = newctrls;
223                     }
224           }
225           return 0;
226 }
227 
228 static int
glue_op_func(Operation * op,SlapReply * rs)229 glue_op_func ( Operation *op, SlapReply *rs )
230 {
231           slap_overinst       *on = (slap_overinst *)op->o_bd->bd_info;
232           BackendDB *b0 = op->o_bd;
233           BackendInfo *bi0 = op->o_bd->bd_info, *bi1;
234           slap_operation_t which = op_bind;
235           int rc;
236 
237           op->o_bd = glue_back_select (b0, &op->o_req_ndn);
238 
239           /* If we're on the primary backend, let overlay framework handle it */
240           if ( op->o_bd == b0 )
241                     return SLAP_CB_CONTINUE;
242 
243           b0->bd_info = on->on_info->oi_orig;
244 
245           switch(op->o_tag) {
246           case LDAP_REQ_ADD: which = op_add; break;
247           case LDAP_REQ_DELETE: which = op_delete; break;
248           case LDAP_REQ_MODIFY: which = op_modify; break;
249           case LDAP_REQ_MODRDN: which = op_modrdn; break;
250           case LDAP_REQ_EXTENDED: which = op_extended; break;
251           default: assert( 0 ); break;
252           }
253 
254           bi1 = op->o_bd->bd_info;
255           rc = (&bi1->bi_op_bind)[ which ] ?
256                     (&bi1->bi_op_bind)[ which ]( op, rs ) : SLAP_CB_BYPASS;
257 
258           op->o_bd = b0;
259           op->o_bd->bd_info = bi0;
260           return rc;
261 }
262 
263 static int
glue_op_abandon(Operation * op,SlapReply * rs)264 glue_op_abandon( Operation *op, SlapReply *rs )
265 {
266           slap_overinst       *on = (slap_overinst *)op->o_bd->bd_info;
267           glueinfo            *gi = (glueinfo *)on->on_bi.bi_private;
268           BackendDB *b0 = op->o_bd;
269           BackendInfo *bi0 = op->o_bd->bd_info;
270           int i;
271 
272           b0->bd_info = on->on_info->oi_orig;
273 
274           for (i = gi->gi_nodes-1; i >= 0; i--) {
275                     assert( gi->gi_n[i].gn_be->be_nsuffix != NULL );
276                     op->o_bd = gi->gi_n[i].gn_be;
277                     if ( op->o_bd == b0 )
278                               continue;
279                     if ( op->o_bd->bd_info->bi_op_abandon )
280                               op->o_bd->bd_info->bi_op_abandon( op, rs );
281           }
282           op->o_bd = b0;
283           op->o_bd->bd_info = bi0;
284           return SLAP_CB_CONTINUE;
285 }
286 
287 static int
glue_response(Operation * op,SlapReply * rs)288 glue_response ( Operation *op, SlapReply *rs )
289 {
290           BackendDB *be = op->o_bd;
291           be = glue_back_select (op->o_bd, &op->o_req_ndn);
292 
293           /* If we're on the primary backend, let overlay framework handle it.
294            * Otherwise, bail out.
295            */
296           return ( op->o_bd == be ) ? SLAP_CB_CONTINUE : SLAP_CB_BYPASS;
297 }
298 
299 static int
glue_chk_referrals(Operation * op,SlapReply * rs)300 glue_chk_referrals ( Operation *op, SlapReply *rs )
301 {
302           slap_overinst       *on = (slap_overinst *)op->o_bd->bd_info;
303           BackendDB *b0 = op->o_bd;
304           BackendInfo *bi0 = op->o_bd->bd_info;
305           int rc;
306 
307           op->o_bd = glue_back_select (b0, &op->o_req_ndn);
308           if ( op->o_bd == b0 )
309                     return SLAP_CB_CONTINUE;
310 
311           b0->bd_info = on->on_info->oi_orig;
312 
313           if ( op->o_bd->bd_info->bi_chk_referrals )
314                     rc = ( *op->o_bd->bd_info->bi_chk_referrals )( op, rs );
315           else
316                     rc = SLAP_CB_CONTINUE;
317 
318           op->o_bd = b0;
319           op->o_bd->bd_info = bi0;
320           return rc;
321 }
322 
323 static int
glue_chk_controls(Operation * op,SlapReply * rs)324 glue_chk_controls ( Operation *op, SlapReply *rs )
325 {
326           slap_overinst       *on = (slap_overinst *)op->o_bd->bd_info;
327           BackendDB *b0 = op->o_bd;
328           BackendInfo *bi0 = op->o_bd->bd_info;
329           int rc = SLAP_CB_CONTINUE;
330 
331           op->o_bd = glue_back_select (b0, &op->o_req_ndn);
332           if ( op->o_bd == b0 )
333                     return SLAP_CB_CONTINUE;
334 
335           b0->bd_info = on->on_info->oi_orig;
336 
337           /* if the subordinate database has overlays, the bi_chk_controls()
338            * hook is actually over_aux_chk_controls(); in case it actually
339            * wraps a missing hok, we need to mimic the behavior
340            * of the frontend applied to that database */
341           if ( op->o_bd->bd_info->bi_chk_controls ) {
342                     rc = ( *op->o_bd->bd_info->bi_chk_controls )( op, rs );
343           }
344 
345 
346           if ( rc == SLAP_CB_CONTINUE ) {
347                     rc = backend_check_controls( op, rs );
348           }
349 
350           op->o_bd = b0;
351           op->o_bd->bd_info = bi0;
352           return rc;
353 }
354 
355 /* ITS#4615 - overlays configured above the glue overlay should be
356  * invoked for the entire glued tree. Overlays configured below the
357  * glue overlay should only be invoked on the primary backend.
358  * So, if we're searching on any subordinates, we need to force the
359  * current overlay chain to stop processing, without stopping the
360  * overall callback flow.
361  */
362 static int
glue_sub_search(Operation * op,SlapReply * rs,BackendDB * b0,slap_overinst * on)363 glue_sub_search( Operation *op, SlapReply *rs, BackendDB *b0,
364           slap_overinst *on )
365 {
366           /* Process any overlays on the primary backend */
367           if ( op->o_bd == b0 && on->on_next ) {
368                     BackendInfo *bi = op->o_bd->bd_info;
369                     int rc = SLAP_CB_CONTINUE;
370                     for ( on=on->on_next; on; on=on->on_next ) {
371                               op->o_bd->bd_info = (BackendInfo *)on;
372                               if ( on->on_bi.bi_op_search ) {
373                                         rc = on->on_bi.bi_op_search( op, rs );
374                                         if ( rc != SLAP_CB_CONTINUE )
375                                                   break;
376                               }
377                     }
378                     op->o_bd->bd_info = bi;
379                     if ( rc != SLAP_CB_CONTINUE )
380                               return rc;
381           }
382           return op->o_bd->be_search( op, rs );
383 }
384 
385 static const ID glueID = NOID;
386 static const struct berval gluecookie = { sizeof( glueID ), (char *)&glueID };
387 
388 static int
glue_op_search(Operation * op,SlapReply * rs)389 glue_op_search ( Operation *op, SlapReply *rs )
390 {
391           slap_overinst       *on = (slap_overinst *)op->o_bd->bd_info;
392           glueinfo            *gi = (glueinfo *)on->on_bi.bi_private;
393           BackendDB *b0 = op->o_bd;
394           BackendDB *b1 = NULL, *btmp;
395           BackendInfo *bi0 = op->o_bd->bd_info;
396           int i;
397           long stoptime = 0, starttime;
398           glue_state gs = {NULL, NULL, NULL, 0, 0, 0, 0};
399           slap_callback cb = { NULL, glue_op_response, glue_op_cleanup, NULL };
400           int scope0, tlimit0;
401           struct berval dn, ndn, *pdn;
402 
403           cb.sc_private = &gs;
404 
405           cb.sc_next = op->o_callback;
406 
407           starttime = op->o_time;
408           stoptime = slap_get_time () + op->ors_tlimit;
409 
410           /* reset dummy cookie used to keep paged results going across databases */
411           if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED
412                     && bvmatch( &((PagedResultsState *)op->o_pagedresults_state)->ps_cookieval, &gluecookie ) )
413           {
414                     PagedResultsState *ps = op->o_pagedresults_state;
415                     BerElementBuffer berbuf;
416                     BerElement *ber = (BerElement *)&berbuf;
417                     struct berval cookie = BER_BVC(""), value;
418                     int c;
419 
420                     for (c = 0; op->o_ctrls[c] != NULL; c++) {
421                               if (strcmp(op->o_ctrls[c]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS) == 0)
422                                         break;
423                     }
424 
425                     assert( op->o_ctrls[c] != NULL );
426 
427                     ber_init2( ber, NULL, LBER_USE_DER );
428                     ber_printf( ber, "{iO}", ps->ps_size, &cookie );
429                     ber_flatten2( ber, &value, 0 );
430                     assert( op->o_ctrls[c]->ldctl_value.bv_len >= value.bv_len );
431                     op->o_ctrls[c]->ldctl_value.bv_len = value.bv_len;
432                     lutil_memcopy( op->o_ctrls[c]->ldctl_value.bv_val,
433                               value.bv_val, value.bv_len );
434                     ber_free_buf( ber );
435 
436                     ps->ps_cookie = (PagedResultsCookie)0;
437                     BER_BVZERO( &ps->ps_cookieval );
438           }
439 
440           op->o_bd = glue_back_select (b0, &op->o_req_ndn);
441           b0->bd_info = on->on_info->oi_orig;
442 
443           switch (op->ors_scope) {
444           case LDAP_SCOPE_BASE:
445                     if ( op->o_bd == b0 )
446                               return SLAP_CB_CONTINUE;
447 
448                     if (op->o_bd && op->o_bd->be_search) {
449                               rs->sr_err = op->o_bd->be_search( op, rs );
450                     } else {
451                               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
452                     }
453                     return rs->sr_err;
454 
455           case LDAP_SCOPE_ONELEVEL:
456           case LDAP_SCOPE_SUBTREE:
457           case LDAP_SCOPE_SUBORDINATE: /* FIXME */
458                     op->o_callback = &cb;
459                     rs->sr_err = gs.err = LDAP_UNWILLING_TO_PERFORM;
460                     scope0 = op->ors_scope;
461                     tlimit0 = op->ors_tlimit;
462                     dn = op->o_req_dn;
463                     ndn = op->o_req_ndn;
464                     b1 = op->o_bd;
465 
466                     /*
467                      * Execute in reverse order, most specific first
468                      */
469                     for (i = gi->gi_nodes; i >= 0; i--) {
470                               if ( i == gi->gi_nodes ) {
471                                         btmp = b0;
472                                         pdn = &gi->gi_pdn;
473                               } else {
474                                         btmp = gi->gi_n[i].gn_be;
475                                         pdn = &gi->gi_n[i].gn_pdn;
476                               }
477                               if (!btmp || !btmp->be_search)
478                                         continue;
479                               if (!dnIsSuffix(&btmp->be_nsuffix[0], &b1->be_nsuffix[0]))
480                                         continue;
481                               if (get_no_subordinate_glue(op) && btmp != b1)
482                                         continue;
483                               /* If we remembered which backend we were on before,
484                                * skip down to it now
485                                */
486                               if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED &&
487                                         op->o_conn->c_pagedresults_state.ps_be &&
488                                         op->o_conn->c_pagedresults_state.ps_be != btmp )
489                                         continue;
490 
491                               if (tlimit0 != SLAP_NO_LIMIT) {
492                                         op->o_time = slap_get_time();
493                                         op->ors_tlimit = stoptime - op->o_time;
494                                         if (op->ors_tlimit <= 0) {
495                                                   rs->sr_err = gs.err = LDAP_TIMELIMIT_EXCEEDED;
496                                                   break;
497                                         }
498                               }
499                               rs->sr_err = 0;
500                               /*
501                                * check for abandon
502                                */
503                               if (op->o_abandon) {
504                                         goto end_of_loop;
505                               }
506                               op->o_bd = btmp;
507 
508                               assert( op->o_bd->be_suffix != NULL );
509                               assert( op->o_bd->be_nsuffix != NULL );
510 
511                               if (scope0 == LDAP_SCOPE_ONELEVEL &&
512                                         dn_match(pdn, &ndn))
513                               {
514                                         struct berval mdn, mndn;
515                                         op->ors_scope = LDAP_SCOPE_BASE;
516                                         mdn = op->o_req_dn = op->o_bd->be_suffix[0];
517                                         mndn = op->o_req_ndn = op->o_bd->be_nsuffix[0];
518                                         rs->sr_err = op->o_bd->be_search(op, rs);
519                                         if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
520                                                   gs.err = LDAP_SUCCESS;
521                                         }
522                                         op->ors_scope = LDAP_SCOPE_ONELEVEL;
523                                         if ( op->o_req_dn.bv_val == mdn.bv_val )
524                                                   op->o_req_dn = dn;
525                                         if ( op->o_req_ndn.bv_val == mndn.bv_val )
526                                                   op->o_req_ndn = ndn;
527 
528                               } else if (scope0 == LDAP_SCOPE_SUBTREE &&
529                                         dn_match(&op->o_bd->be_nsuffix[0], &ndn))
530                               {
531                                         rs->sr_err = glue_sub_search( op, rs, b0, on );
532 
533                               } else if (scope0 == LDAP_SCOPE_SUBTREE &&
534                                         dnIsSuffix(&op->o_bd->be_nsuffix[0], &ndn))
535                               {
536                                         struct berval mdn, mndn;
537                                         mdn = op->o_req_dn = op->o_bd->be_suffix[0];
538                                         mndn = op->o_req_ndn = op->o_bd->be_nsuffix[0];
539                                         rs->sr_err = glue_sub_search( op, rs, b0, on );
540                                         if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
541                                                   gs.err = LDAP_SUCCESS;
542                                         }
543                                         if ( op->o_req_dn.bv_val == mdn.bv_val )
544                                                   op->o_req_dn = dn;
545                                         if ( op->o_req_ndn.bv_val == mndn.bv_val )
546                                                   op->o_req_ndn = ndn;
547 
548                               } else if (dnIsSuffix(&ndn, &op->o_bd->be_nsuffix[0])) {
549                                         rs->sr_err = glue_sub_search( op, rs, b0, on );
550                               }
551 
552                               switch ( gs.err ) {
553 
554                               /*
555                                * Add errors that should result in dropping
556                                * the search
557                                */
558                               case LDAP_SIZELIMIT_EXCEEDED:
559                               case LDAP_TIMELIMIT_EXCEEDED:
560                               case LDAP_ADMINLIMIT_EXCEEDED:
561                               case LDAP_NO_SUCH_OBJECT:
562 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
563                               case LDAP_X_CANNOT_CHAIN:
564 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
565                                         goto end_of_loop;
566 
567                               case LDAP_SUCCESS:
568                                         if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
569                                                   PagedResultsState *ps = op->o_pagedresults_state;
570 
571                                                   /* Assume this backend can be forgotten now */
572                                                   op->o_conn->c_pagedresults_state.ps_be = NULL;
573 
574                                                   /* If we have a full page, exit the loop. We may
575                                                    * need to remember this backend so we can continue
576                                                    * from here on a subsequent request.
577                                                    */
578                                                   if ( rs->sr_nentries >= ps->ps_size ) {
579                                                             PagedResultsState *cps = &op->o_conn->c_pagedresults_state;
580 
581                                                             /* Don't bother to remember the first backend.
582                                                              * Only remember the last one if there's more state left.
583                                                              */
584                                                             if ( op->o_bd != b0 &&
585                                                                       ( cps->ps_cookie != NOID
586                                                                                 || !BER_BVISNULL( &cps->ps_cookieval )
587                                                                                 || op->o_bd != gi->gi_n[0].gn_be ) )
588                                                             {
589                                                                       op->o_conn->c_pagedresults_state.ps_be = op->o_bd;
590                                                             }
591 
592                                                             /* Check whether the cookie is empty,
593                                                              * and give remaining databases a chance
594                                                              */
595                                                             if ( op->o_bd != gi->gi_n[0].gn_be || cps->ps_cookie == NOID ) {
596                                                                       int                 c;
597 
598                                                                       for ( c = 0; gs.ctrls[c] != NULL; c++ ) {
599                                                                                 if ( strcmp( gs.ctrls[c]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 ) {
600                                                                                           break;
601                                                                                 }
602                                                                       }
603 
604                                                                       if ( gs.ctrls[c] != NULL ) {
605                                                                                 BerElementBuffer berbuf;
606                                                                                 BerElement          *ber = (BerElement *)&berbuf;
607                                                                                 ber_tag_t tag;
608                                                                                 ber_int_t size;
609                                                                                 struct berval       cookie, value;
610 
611                                                                                 ber_init2( ber, &gs.ctrls[c]->ldctl_value, LBER_USE_DER );
612 
613                                                                                 tag = ber_scanf( ber, "{im}", &size, &cookie );
614                                                                                 assert( tag != LBER_ERROR );
615 
616                                                                                 if ( BER_BVISEMPTY( &cookie ) && op->o_bd != gi->gi_n[0].gn_be ) {
617                                                                                           /* delete old, create new cookie with NOID */
618                                                                                           PagedResultsCookie respcookie = (PagedResultsCookie)NOID;
619                                                                                           ber_len_t oidlen = strlen( gs.ctrls[c]->ldctl_oid );
620                                                                                           LDAPControl *newctrl;
621 
622                                                                                           /* it's next database's turn */
623                                                                                           if ( btmp == b0 ) {
624                                                                                                     op->o_conn->c_pagedresults_state.ps_be = gi->gi_n[gi->gi_nodes - 1].gn_be;
625 
626                                                                                           } else {
627                                                                                                     op->o_conn->c_pagedresults_state.ps_be = gi->gi_n[(i > 0 ? i - 1: 0)].gn_be;
628                                                                                           }
629 
630                                                                                           cookie.bv_val = (char *)&respcookie;
631                                                                                           cookie.bv_len = sizeof( PagedResultsCookie );
632 
633                                                                                           ber_init2( ber, NULL, LBER_USE_DER );
634                                                                                           ber_printf( ber, "{iO}", 0, &cookie );
635                                                                                           ber_flatten2( ber, &value, 0 );
636 
637                                                                                           newctrl = op->o_tmprealloc( gs.ctrls[c],
638                                                                                                     sizeof(LDAPControl) + oidlen + 1 + value.bv_len + 1,
639                                                                                                     op->o_tmpmemctx);
640                                                                                           newctrl->ldctl_iscritical = gs.ctrls[c]->ldctl_iscritical;
641                                                                                           newctrl->ldctl_oid = (char *)&newctrl[1];
642                                                                                           lutil_strcopy( newctrl->ldctl_oid, gs.ctrls[c]->ldctl_oid );
643                                                                                           newctrl->ldctl_value.bv_len = value.bv_len;
644                                                                                           lutil_memcopy( newctrl->ldctl_value.bv_val,
645                                                                                                     value.bv_val, value.bv_len );
646 
647                                                                                           gs.ctrls[c] = newctrl;
648 
649                                                                                           ber_free_buf( ber );
650 
651                                                                                 } else if ( !BER_BVISEMPTY( &cookie ) && op->o_bd != b0 ) {
652                                                                                           /* if cookie not empty, it's again this database's turn */
653                                                                                           op->o_conn->c_pagedresults_state.ps_be = op->o_bd;
654                                                                                 }
655                                                                       }
656                                                             }
657 
658                                                             goto end_of_loop;
659                                                   }
660 
661                                                   /* This backend has run out of entries, but more responses
662                                                    * can fit in the page. Fake a reset of the state so the
663                                                    * next backend will start up properly. Only back-[bh]db
664                                                    * and back-sql look at this state info.
665                                                    */
666                                                   ps->ps_cookie = (PagedResultsCookie)0;
667                                                   BER_BVZERO( &ps->ps_cookieval );
668 
669                                                   {
670                                                             /* change the size of the page in the request
671                                                              * that will be propagated, and reset the cookie */
672                                                             BerElementBuffer berbuf;
673                                                             BerElement *ber = (BerElement *)&berbuf;
674                                                             int size = ps->ps_size - rs->sr_nentries;
675                                                             struct berval cookie = BER_BVC(""), value;
676                                                             int c;
677 
678                                                             for (c = 0; op->o_ctrls[c] != NULL; c++) {
679                                                                       if (strcmp(op->o_ctrls[c]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS) == 0)
680                                                                                 break;
681                                                             }
682 
683                                                             assert( op->o_ctrls[c] != NULL );
684 
685                                                             ber_init2( ber, NULL, LBER_USE_DER );
686                                                             ber_printf( ber, "{iO}", size, &cookie );
687                                                             ber_flatten2( ber, &value, 0 );
688                                                             assert( op->o_ctrls[c]->ldctl_value.bv_len >= value.bv_len );
689                                                             op->o_ctrls[c]->ldctl_value.bv_len = value.bv_len;
690                                                             lutil_memcopy( op->o_ctrls[c]->ldctl_value.bv_val,
691                                                                       value.bv_val, value.bv_len );
692                                                             ber_free_buf( ber );
693                                                   }
694                                         }
695 
696                               default:
697                                         break;
698                               }
699                     }
700 end_of_loop:;
701                     op->ors_scope = scope0;
702                     op->ors_tlimit = tlimit0;
703                     op->o_time = starttime;
704 
705                     break;
706           }
707 
708           op->o_callback = cb.sc_next;
709           if ( op->o_abandon ) {
710                     rs->sr_err = SLAPD_ABANDON;
711           } else {
712                     rs->sr_err = gs.err;
713                     rs->sr_matched = gs.matched;
714                     rs->sr_ref = gs.refs;
715           }
716           rs->sr_ctrls = gs.ctrls;
717 
718           send_ldap_result( op, rs );
719 
720           op->o_bd = b0;
721           op->o_bd->bd_info = bi0;
722           if (gs.matched)
723                     free (gs.matched);
724           if (gs.refs)
725                     ber_bvarray_free(gs.refs);
726           if (gs.ctrls) {
727                     for (i = gs.nctrls; --i >= 0; ) {
728                               op->o_tmpfree(gs.ctrls[i], op->o_tmpmemctx);
729                     }
730                     op->o_tmpfree(gs.ctrls, op->o_tmpmemctx);
731           }
732           return rs->sr_err;
733 }
734 
735 static BackendDB toolDB;
736 
737 static int
glue_tool_entry_open(BackendDB * b0,int mode)738 glue_tool_entry_open (
739           BackendDB *b0,
740           int mode
741 )
742 {
743           slap_overinfo       *oi = (slap_overinfo *)b0->bd_info;
744 
745           /* We don't know which backend to talk to yet, so just
746            * remember the mode and move on...
747            */
748 
749           glueMode = mode;
750           glueBack = NULL;
751           toolDB = *b0;
752           toolDB.bd_info = oi->oi_orig;
753 
754           /* Sanity checks */
755           {
756                     slap_overinst *on = glue_tool_inst( b0->bd_info );
757                     glueinfo  *gi = on->on_bi.bi_private;
758 
759                     int i;
760                     for (i = 0; i < gi->gi_nodes; i++) {
761                               BackendDB *bd;
762                               struct berval pdn;
763 
764                               dnParent( &gi->gi_n[i].gn_be->be_nsuffix[0], &pdn );
765                               bd = select_backend( &pdn, 0 );
766                               if ( bd ) {
767                                         ID id;
768                                         BackendDB db;
769 
770                                         if ( overlay_is_over( bd ) ) {
771                                                   slap_overinfo *oi = (slap_overinfo *)bd->bd_info;
772                                                   db = *bd;
773                                                   db.bd_info = oi->oi_orig;
774                                                   bd = &db;
775                                         }
776 
777                                         if ( !bd->bd_info->bi_tool_dn2id_get
778                                                   || !bd->bd_info->bi_tool_entry_open
779                                                   || !bd->bd_info->bi_tool_entry_close )
780                                         {
781                                                   continue;
782                                         }
783 
784                                         bd->bd_info->bi_tool_entry_open( bd, 0 );
785                                         id = bd->bd_info->bi_tool_dn2id_get( bd, &gi->gi_n[i].gn_be->be_nsuffix[0] );
786                                         bd->bd_info->bi_tool_entry_close( bd );
787                                         if ( id != NOID ) {
788                                                   Debug( LDAP_DEBUG_ANY,
789                                                             "glue_tool_entry_open: subordinate database suffix entry DN=\"%s\" also present in superior database rooted at DN=\"%s\"\n",
790                                                             gi->gi_n[i].gn_be->be_suffix[0].bv_val, bd->be_suffix[0].bv_val );
791                                                   return LDAP_OTHER;
792                                         }
793                               }
794                     }
795           }
796 
797           return 0;
798 }
799 
800 static int
glue_tool_entry_close(BackendDB * b0)801 glue_tool_entry_close (
802           BackendDB *b0
803 )
804 {
805           int rc = 0;
806 
807           if (glueBack && glueBack != GLUEBACK_DONE) {
808                     if (!glueBack->be_entry_close)
809                               return 0;
810                     rc = glueBack->be_entry_close (glueBack);
811           }
812           return rc;
813 }
814 
815 static slap_overinst *
glue_tool_inst(BackendInfo * bi)816 glue_tool_inst(
817           BackendInfo *bi
818 )
819 {
820           slap_overinfo       *oi = (slap_overinfo *)bi;
821           slap_overinst       *on;
822 
823           for ( on = oi->oi_list; on; on=on->on_next ) {
824                     if ( !strcmp( on->on_bi.bi_type, glue.on_bi.bi_type ))
825                               return on;
826           }
827           return NULL;
828 }
829 
830 /* This function will only be called in tool mode */
831 static int
glue_open(BackendInfo * bi)832 glue_open (
833           BackendInfo *bi
834 )
835 {
836           slap_overinst *on = glue_tool_inst( bi );
837           glueinfo            *gi = on->on_bi.bi_private;
838           static int glueOpened = 0;
839           int i, j, same, bsame = 0, rc = 0;
840           ConfigReply cr = {0};
841 
842           if (glueOpened) return 0;
843 
844           glueOpened = 1;
845 
846           /* If we were invoked in tool mode, open all the underlying backends */
847           if (slapMode & SLAP_TOOL_MODE) {
848                     for (i = 0; i<gi->gi_nodes; i++) {
849                               same = 0;
850                               /* Same bi_open as our main backend? */
851                               if ( gi->gi_n[i].gn_be->bd_info->bi_open ==
852                                         on->on_info->oi_orig->bi_open )
853                                         bsame = 1;
854 
855                               /* Loop thru the bd_info's and make sure we only
856                                * invoke their bi_open functions once each.
857                                */
858                               for ( j = 0; j<i; j++ ) {
859                                         if ( gi->gi_n[i].gn_be->bd_info->bi_open ==
860                                                   gi->gi_n[j].gn_be->bd_info->bi_open ) {
861                                                   same = 1;
862                                                   break;
863                                         }
864                               }
865                               /* OK, it's unique and non-NULL, call it. */
866                               if ( !same && gi->gi_n[i].gn_be->bd_info->bi_open )
867                                         rc = gi->gi_n[i].gn_be->bd_info->bi_open(
868                                                   gi->gi_n[i].gn_be->bd_info );
869                               /* Let backend.c take care of the rest of startup */
870                               if ( !rc )
871                                         rc = backend_startup_one( gi->gi_n[i].gn_be, &cr );
872                               if ( rc ) break;
873                     }
874                     if ( !rc && !bsame && on->on_info->oi_orig->bi_open )
875                               rc = on->on_info->oi_orig->bi_open( on->on_info->oi_orig );
876 
877           } /* other case is impossible */
878           return rc;
879 }
880 
881 /* This function will only be called in tool mode */
882 static int
glue_close(BackendInfo * bi)883 glue_close (
884           BackendInfo *bi
885 )
886 {
887           static int glueClosed = 0;
888           int rc = 0;
889 
890           if (glueClosed) return 0;
891 
892           glueClosed = 1;
893 
894           if (slapMode & SLAP_TOOL_MODE) {
895                     rc = backend_shutdown( NULL );
896           }
897           return rc;
898 }
899 
900 static int
glue_entry_get_rw(Operation * op,struct berval * dn,ObjectClass * oc,AttributeDescription * ad,int rw,Entry ** e)901 glue_entry_get_rw (
902           Operation           *op,
903           struct berval       *dn,
904           ObjectClass                   *oc,
905           AttributeDescription          *ad,
906           int       rw,
907           Entry     **e )
908 {
909           int rc;
910           BackendDB *b0 = op->o_bd;
911           op->o_bd = glue_back_select( b0, dn );
912 
913           if ( op->o_bd->be_fetch ) {
914                     rc = op->o_bd->be_fetch( op, dn, oc, ad, rw, e );
915           } else {
916                     rc = LDAP_UNWILLING_TO_PERFORM;
917           }
918           op->o_bd =b0;
919           return rc;
920 }
921 
922 static int
glue_entry_release_rw(Operation * op,Entry * e,int rw)923 glue_entry_release_rw (
924           Operation *op,
925           Entry *e,
926           int rw
927 )
928 {
929           BackendDB *b0 = op->o_bd;
930           int rc = -1;
931 
932           op->o_bd = glue_back_select (b0, &e->e_nname);
933 
934           if ( op->o_bd->be_release ) {
935                     rc = op->o_bd->be_release( op, e, rw );
936 
937           } else {
938                     /* FIXME: mimic be_entry_release_rw
939                      * when no be_release() available */
940                     /* free entry */
941                     entry_free( e );
942                     rc = 0;
943           }
944           op->o_bd = b0;
945           return rc;
946 }
947 
948 static struct berval *glue_base;
949 static int glue_scope;
950 static Filter *glue_filter;
951 
952 static ID
glue_tool_entry_first(BackendDB * b0)953 glue_tool_entry_first (
954           BackendDB *b0
955 )
956 {
957           slap_overinst       *on = glue_tool_inst( b0->bd_info );
958           glueinfo            *gi = on->on_bi.bi_private;
959           int i;
960           ID rc;
961 
962           /* If we're starting from scratch, start at the most general */
963           if (!glueBack) {
964                     if ( toolDB.be_entry_open && toolDB.be_entry_first ) {
965                               glueBack = &toolDB;
966                     } else {
967                               for (i = gi->gi_nodes-1; i >= 0; i--) {
968                                         if (gi->gi_n[i].gn_be->be_entry_open &&
969                                                   gi->gi_n[i].gn_be->be_entry_first) {
970                                                             glueBack = gi->gi_n[i].gn_be;
971                                                   break;
972                                         }
973                               }
974                     }
975           }
976           if (!glueBack || !glueBack->be_entry_open || !glueBack->be_entry_first ||
977                     glueBack->be_entry_open (glueBack, glueMode) != 0)
978                     return NOID;
979 
980           rc = glueBack->be_entry_first (glueBack);
981           while ( rc == NOID ) {
982                     if ( glueBack && glueBack->be_entry_close )
983                               glueBack->be_entry_close (glueBack);
984                     for (i=0; i<gi->gi_nodes; i++) {
985                               if (gi->gi_n[i].gn_be == glueBack)
986                                         break;
987                     }
988                     if (i == 0) {
989                               glueBack = GLUEBACK_DONE;
990                               break;
991                     } else {
992                               glueBack = gi->gi_n[i-1].gn_be;
993                               rc = glue_tool_entry_first (b0);
994                               if ( glueBack == GLUEBACK_DONE ) {
995                                         break;
996                               }
997                     }
998           }
999           return rc;
1000 }
1001 
1002 static ID
glue_tool_entry_first_x(BackendDB * b0,struct berval * base,int scope,Filter * f)1003 glue_tool_entry_first_x (
1004           BackendDB *b0,
1005           struct berval *base,
1006           int scope,
1007           Filter *f
1008 )
1009 {
1010           slap_overinst       *on = glue_tool_inst( b0->bd_info );
1011           glueinfo            *gi = on->on_bi.bi_private;
1012           int i;
1013           ID rc;
1014 
1015           glue_base = base;
1016           glue_scope = scope;
1017           glue_filter = f;
1018 
1019           /* If we're starting from scratch, start at the most general */
1020           if (!glueBack) {
1021                     if ( toolDB.be_entry_open && toolDB.be_entry_first_x ) {
1022                               glueBack = &toolDB;
1023                     } else {
1024                               for (i = gi->gi_nodes-1; i >= 0; i--) {
1025                                         if (gi->gi_n[i].gn_be->be_entry_open &&
1026                                                   gi->gi_n[i].gn_be->be_entry_first_x)
1027                                         {
1028                                                   glueBack = gi->gi_n[i].gn_be;
1029                                                   break;
1030                                         }
1031                               }
1032                     }
1033           }
1034           if (!glueBack || !glueBack->be_entry_open || !glueBack->be_entry_first_x ||
1035                     glueBack->be_entry_open (glueBack, glueMode) != 0)
1036                     return NOID;
1037 
1038           rc = glueBack->be_entry_first_x (glueBack,
1039                     glue_base, glue_scope, glue_filter);
1040           while ( rc == NOID ) {
1041                     if ( glueBack && glueBack->be_entry_close )
1042                               glueBack->be_entry_close (glueBack);
1043                     for (i=0; i<gi->gi_nodes; i++) {
1044                               if (gi->gi_n[i].gn_be == glueBack)
1045                                         break;
1046                     }
1047                     if (i == 0) {
1048                               glueBack = GLUEBACK_DONE;
1049                               break;
1050                     } else {
1051                               glueBack = gi->gi_n[i-1].gn_be;
1052                               rc = glue_tool_entry_first_x (b0,
1053                                         glue_base, glue_scope, glue_filter);
1054                               if ( glueBack == GLUEBACK_DONE ) {
1055                                         break;
1056                               }
1057                     }
1058           }
1059           return rc;
1060 }
1061 
1062 static ID
glue_tool_entry_next(BackendDB * b0)1063 glue_tool_entry_next (
1064           BackendDB *b0
1065 )
1066 {
1067           slap_overinst       *on = glue_tool_inst( b0->bd_info );
1068           glueinfo            *gi = on->on_bi.bi_private;
1069           int i;
1070           ID rc;
1071 
1072           if (!glueBack || !glueBack->be_entry_next)
1073                     return NOID;
1074 
1075           rc = glueBack->be_entry_next (glueBack);
1076 
1077           /* If we ran out of entries in one database, move on to the next */
1078           while (rc == NOID) {
1079                     if ( glueBack && glueBack->be_entry_close )
1080                               glueBack->be_entry_close (glueBack);
1081                     for (i=0; i<gi->gi_nodes; i++) {
1082                               if (gi->gi_n[i].gn_be == glueBack)
1083                                         break;
1084                     }
1085                     if (i == 0) {
1086                               glueBack = GLUEBACK_DONE;
1087                               break;
1088                     } else {
1089                               glueBack = gi->gi_n[i-1].gn_be;
1090                               if ( glue_base || glue_filter ) {
1091                                         /* using entry_first_x() */
1092                                         rc = glue_tool_entry_first_x (b0,
1093                                                   glue_base, glue_scope, glue_filter);
1094 
1095                               } else {
1096                                         /* using entry_first() */
1097                                         rc = glue_tool_entry_first (b0);
1098                               }
1099                               if ( glueBack == GLUEBACK_DONE ) {
1100                                         break;
1101                               }
1102                     }
1103           }
1104           return rc;
1105 }
1106 
1107 static ID
glue_tool_dn2id_get(BackendDB * b0,struct berval * dn)1108 glue_tool_dn2id_get (
1109           BackendDB *b0,
1110           struct berval *dn
1111 )
1112 {
1113           BackendDB *be, b2;
1114           int rc = -1;
1115 
1116           b2 = *b0;
1117           b2.bd_info = (BackendInfo *)glue_tool_inst( b0->bd_info );
1118           be = glue_back_select (&b2, dn);
1119           if ( be == &b2 ) be = &toolDB;
1120 
1121           if (!be->be_dn2id_get)
1122                     return NOID;
1123 
1124           if (!glueBack) {
1125                     if ( be->be_entry_open ) {
1126                               rc = be->be_entry_open (be, glueMode);
1127                     }
1128                     if (rc != 0) {
1129                               return NOID;
1130                     }
1131           } else if (be != glueBack) {
1132                     /* If this entry belongs in a different branch than the
1133                      * previous one, close the current database and open the
1134                      * new one.
1135                      */
1136                     if ( glueBack->be_entry_close ) {
1137                               glueBack->be_entry_close (glueBack);
1138                     }
1139                     if ( be->be_entry_open ) {
1140                               rc = be->be_entry_open (be, glueMode);
1141                     }
1142                     if (rc != 0) {
1143                               return NOID;
1144                     }
1145           }
1146           glueBack = be;
1147           return be->be_dn2id_get (be, dn);
1148 }
1149 
1150 static Entry *
glue_tool_entry_get(BackendDB * b0,ID id)1151 glue_tool_entry_get (
1152           BackendDB *b0,
1153           ID id
1154 )
1155 {
1156           if (!glueBack || !glueBack->be_entry_get)
1157                     return NULL;
1158 
1159           return glueBack->be_entry_get (glueBack, id);
1160 }
1161 
1162 static ID
glue_tool_entry_put(BackendDB * b0,Entry * e,struct berval * text)1163 glue_tool_entry_put (
1164           BackendDB *b0,
1165           Entry *e,
1166           struct berval *text
1167 )
1168 {
1169           BackendDB *be, b2;
1170           int rc = -1;
1171 
1172           b2 = *b0;
1173           b2.bd_info = (BackendInfo *)glue_tool_inst( b0->bd_info );
1174           be = glue_back_select (&b2, &e->e_nname);
1175           if ( be == &b2 ) be = &toolDB;
1176 
1177           if (!be->be_entry_put)
1178                     return NOID;
1179 
1180           if (!glueBack) {
1181                     if ( be->be_entry_open ) {
1182                               rc = be->be_entry_open (be, glueMode);
1183                     }
1184                     if (rc != 0) {
1185                               return NOID;
1186                     }
1187           } else if (be != glueBack) {
1188                     /* If this entry belongs in a different branch than the
1189                      * previous one, close the current database and open the
1190                      * new one.
1191                      */
1192                     if ( glueBack->be_entry_close ) {
1193                               glueBack->be_entry_close (glueBack);
1194                     }
1195                     if ( be->be_entry_open ) {
1196                               rc = be->be_entry_open (be, glueMode);
1197                     }
1198                     if (rc != 0) {
1199                               return NOID;
1200                     }
1201           }
1202           glueBack = be;
1203           return be->be_entry_put (be, e, text);
1204 }
1205 
1206 static ID
glue_tool_entry_modify(BackendDB * b0,Entry * e,struct berval * text)1207 glue_tool_entry_modify (
1208           BackendDB *b0,
1209           Entry *e,
1210           struct berval *text
1211 )
1212 {
1213           if (!glueBack || !glueBack->be_entry_modify)
1214                     return NOID;
1215 
1216           return glueBack->be_entry_modify (glueBack, e, text);
1217 }
1218 
1219 static int
glue_tool_entry_reindex(BackendDB * b0,ID id,AttributeDescription ** adv)1220 glue_tool_entry_reindex (
1221           BackendDB *b0,
1222           ID id,
1223           AttributeDescription **adv
1224 )
1225 {
1226           if (!glueBack || !glueBack->be_entry_reindex)
1227                     return -1;
1228 
1229           return glueBack->be_entry_reindex (glueBack, id, adv);
1230 }
1231 
1232 static int
glue_tool_sync(BackendDB * b0)1233 glue_tool_sync (
1234           BackendDB *b0
1235 )
1236 {
1237           slap_overinst       *on = glue_tool_inst( b0->bd_info );
1238           glueinfo            *gi = on->on_bi.bi_private;
1239           BackendInfo                   *bi = b0->bd_info;
1240           int i;
1241 
1242           /* just sync everyone */
1243           for (i = 0; i<gi->gi_nodes; i++)
1244                     if (gi->gi_n[i].gn_be->be_sync)
1245                               gi->gi_n[i].gn_be->be_sync (gi->gi_n[i].gn_be);
1246           b0->bd_info = on->on_info->oi_orig;
1247           if ( b0->be_sync )
1248                     b0->be_sync( b0 );
1249           b0->bd_info = bi;
1250           return 0;
1251 }
1252 
1253 typedef struct glue_Addrec {
1254           struct glue_Addrec *ga_next;
1255           BackendDB *ga_be;
1256 } glue_Addrec;
1257 
1258 /* List of added subordinates */
1259 static glue_Addrec *ga_list;
1260 static int ga_adding;
1261 
1262 static int
glue_db_init(BackendDB * be,ConfigReply * cr)1263 glue_db_init(
1264           BackendDB *be,
1265           ConfigReply *cr
1266 )
1267 {
1268           slap_overinst       *on = (slap_overinst *)be->bd_info;
1269           slap_overinfo       *oi = on->on_info;
1270           BackendInfo         *bi = oi->oi_orig;
1271           glueinfo *gi;
1272 
1273           if ( SLAP_GLUE_SUBORDINATE( be )) {
1274                     Debug( LDAP_DEBUG_ANY, "glue: backend %s is already subordinate, "
1275                               "cannot have glue overlay!\n",
1276                               be->be_suffix[0].bv_val );
1277                     return LDAP_OTHER;
1278           }
1279 
1280           gi = ch_calloc( 1, sizeof(glueinfo));
1281           on->on_bi.bi_private = gi;
1282           dnParent( be->be_nsuffix, &gi->gi_pdn );
1283 
1284           /* Currently the overlay framework doesn't handle these entry points
1285            * but we need them....
1286            */
1287           oi->oi_bi.bi_open = glue_open;
1288           oi->oi_bi.bi_close = glue_close;
1289 
1290           /* Only advertise these if the root DB supports them */
1291           if ( bi->bi_tool_entry_open )
1292                     oi->oi_bi.bi_tool_entry_open = glue_tool_entry_open;
1293           if ( bi->bi_tool_entry_close )
1294                     oi->oi_bi.bi_tool_entry_close = glue_tool_entry_close;
1295           if ( bi->bi_tool_entry_first )
1296                     oi->oi_bi.bi_tool_entry_first = glue_tool_entry_first;
1297           /* FIXME: check whether all support bi_tool_entry_first_x() ? */
1298           if ( bi->bi_tool_entry_first_x )
1299                     oi->oi_bi.bi_tool_entry_first_x = glue_tool_entry_first_x;
1300           if ( bi->bi_tool_entry_next )
1301                     oi->oi_bi.bi_tool_entry_next = glue_tool_entry_next;
1302           if ( bi->bi_tool_entry_get )
1303                     oi->oi_bi.bi_tool_entry_get = glue_tool_entry_get;
1304           if ( bi->bi_tool_dn2id_get )
1305                     oi->oi_bi.bi_tool_dn2id_get = glue_tool_dn2id_get;
1306           if ( bi->bi_tool_entry_put )
1307                     oi->oi_bi.bi_tool_entry_put = glue_tool_entry_put;
1308           if ( bi->bi_tool_entry_reindex )
1309                     oi->oi_bi.bi_tool_entry_reindex = glue_tool_entry_reindex;
1310           if ( bi->bi_tool_entry_modify )
1311                     oi->oi_bi.bi_tool_entry_modify = glue_tool_entry_modify;
1312           if ( bi->bi_tool_sync )
1313                     oi->oi_bi.bi_tool_sync = glue_tool_sync;
1314 
1315           SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_GLUE_INSTANCE;
1316 
1317           if ( ga_list && ( slapMode & SLAP_SERVER_MODE ) ) {
1318                     be->bd_info = (BackendInfo *)oi;
1319                     glue_sub_attach( 1 );
1320           }
1321 
1322           return 0;
1323 }
1324 
1325 static int
glue_db_destroy(BackendDB * be,ConfigReply * cr)1326 glue_db_destroy (
1327           BackendDB *be,
1328           ConfigReply *cr
1329 )
1330 {
1331           slap_overinst       *on = (slap_overinst *)be->bd_info;
1332           glueinfo            *gi = (glueinfo *)on->on_bi.bi_private;
1333 
1334           free (gi);
1335           return 0;
1336 }
1337 
1338 static int
glue_db_close(BackendDB * be,ConfigReply * cr)1339 glue_db_close(
1340           BackendDB *be,
1341           ConfigReply *cr
1342 )
1343 {
1344           slap_overinst       *on = (slap_overinst *)be->bd_info;
1345 
1346           on->on_info->oi_bi.bi_db_close = 0;
1347           return 0;
1348 }
1349 
1350 int
glue_sub_del(BackendDB * b0)1351 glue_sub_del( BackendDB *b0 )
1352 {
1353           BackendDB *be;
1354           int rc = 0;
1355 
1356           /* Find the top backend for this subordinate */
1357           be = b0;
1358           while ( (be=LDAP_STAILQ_NEXT( be, be_next )) != NULL ) {
1359                     slap_overinfo *oi;
1360                     slap_overinst *on;
1361                     glueinfo *gi;
1362                     int i;
1363 
1364                     if ( SLAP_GLUE_SUBORDINATE( be ))
1365                               continue;
1366                     if ( !SLAP_GLUE_INSTANCE( be ))
1367                               continue;
1368                     if ( !dnIsSuffix( &b0->be_nsuffix[0], &be->be_nsuffix[0] ))
1369                               continue;
1370 
1371                     /* OK, got the right backend, find the overlay */
1372                     oi = (slap_overinfo *)be->bd_info;
1373                     for ( on=oi->oi_list; on; on=on->on_next ) {
1374                               if ( on->on_bi.bi_type == glue.on_bi.bi_type )
1375                                         break;
1376                     }
1377                     assert( on != NULL );
1378                     gi = on->on_bi.bi_private;
1379                     for ( i=0; i < gi->gi_nodes; i++ ) {
1380                               if ( gi->gi_n[i].gn_be == b0 ) {
1381                                         int j;
1382 
1383                                         for (j=i+1; j < gi->gi_nodes; j++)
1384                                                   gi->gi_n[j-1] = gi->gi_n[j];
1385 
1386                                         gi->gi_nodes--;
1387                               }
1388                     }
1389           }
1390           if ( be == NULL )
1391                     rc = LDAP_NO_SUCH_OBJECT;
1392 
1393           return rc;
1394 }
1395 
1396 
1397 /* Attach all the subordinate backends to their superior */
1398 int
glue_sub_attach(int online)1399 glue_sub_attach( int online )
1400 {
1401           glue_Addrec *ga, *gnext = NULL;
1402           int rc = 0;
1403 
1404           if ( ga_adding )
1405                     return 0;
1406 
1407           ga_adding = 1;
1408 
1409           /* For all the subordinate backends */
1410           for ( ga=ga_list; ga != NULL; ga = gnext ) {
1411                     BackendDB *be;
1412 
1413                     gnext = ga->ga_next;
1414 
1415                     /* Find the top backend for this subordinate */
1416                     be = ga->ga_be;
1417                     while ( (be=LDAP_STAILQ_NEXT( be, be_next )) != NULL ) {
1418                               slap_overinfo *oi;
1419                               slap_overinst *on;
1420                               glueinfo *gi;
1421 
1422                               if ( SLAP_GLUE_SUBORDINATE( be ))
1423                                         continue;
1424                               if ( !dnIsSuffix( &ga->ga_be->be_nsuffix[0], &be->be_nsuffix[0] ))
1425                                         continue;
1426 
1427                               /* If it's not already configured, set up the overlay */
1428                               if ( !SLAP_GLUE_INSTANCE( be )) {
1429                                         rc = overlay_config( be, glue.on_bi.bi_type, -1, NULL, NULL);
1430                                         if ( rc )
1431                                                   break;
1432                               }
1433                               /* Find the overlay instance */
1434                               oi = (slap_overinfo *)be->bd_info;
1435                               for ( on=oi->oi_list; on; on=on->on_next ) {
1436                                         if ( on->on_bi.bi_type == glue.on_bi.bi_type )
1437                                                   break;
1438                               }
1439                               assert( on != NULL );
1440                               gi = on->on_bi.bi_private;
1441                               gi = (glueinfo *)ch_realloc( gi, sizeof(glueinfo) +
1442                                         gi->gi_nodes * sizeof(gluenode));
1443                               gi->gi_n[gi->gi_nodes].gn_be = ga->ga_be;
1444                               dnParent( &ga->ga_be->be_nsuffix[0],
1445                                         &gi->gi_n[gi->gi_nodes].gn_pdn );
1446                               gi->gi_nodes++;
1447                               on->on_bi.bi_private = gi;
1448                               ga->ga_be->be_flags |= SLAP_DBFLAG_GLUE_LINKED;
1449                               break;
1450                     }
1451                     if ( !be ) {
1452                               Debug( LDAP_DEBUG_ANY, "glue: no superior found for sub %s!\n",
1453                                         ga->ga_be->be_suffix[0].bv_val );
1454                               /* allow this for now, assume a superior will
1455                                * be added later
1456                                */
1457                               if ( online ) {
1458                                         rc = 0;
1459                                         gnext = ga_list;
1460                                         break;
1461                               }
1462                               rc = LDAP_NO_SUCH_OBJECT;
1463                     }
1464                     ch_free( ga );
1465                     if ( rc ) break;
1466           }
1467 
1468           ga_list = gnext;
1469 
1470           ga_adding = 0;
1471 
1472           return rc;
1473 }
1474 
1475 int
glue_sub_add(BackendDB * be,int advert,int online)1476 glue_sub_add( BackendDB *be, int advert, int online )
1477 {
1478           glue_Addrec *ga;
1479           int rc = 0;
1480 
1481           if ( overlay_is_inst( be, "glue" )) {
1482                     Debug( LDAP_DEBUG_ANY, "glue: backend %s already has glue overlay, "
1483                               "cannot be a subordinate!\n",
1484                               be->be_suffix[0].bv_val );
1485                     return LDAP_OTHER;
1486           }
1487           SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_GLUE_SUBORDINATE;
1488           if ( advert )
1489                     SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_GLUE_ADVERTISE;
1490 
1491           ga = ch_malloc( sizeof( glue_Addrec ));
1492           ga->ga_next = ga_list;
1493           ga->ga_be = be;
1494           ga_list = ga;
1495 
1496           if ( online )
1497                     rc = glue_sub_attach( online );
1498 
1499           return rc;
1500 }
1501 
1502 static int
glue_access_allowed(Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,slap_access_t access,AccessControlState * state,slap_mask_t * maskp)1503 glue_access_allowed(
1504           Operation           *op,
1505           Entry                         *e,
1506           AttributeDescription          *desc,
1507           struct berval                 *val,
1508           slap_access_t                 access,
1509           AccessControlState  *state,
1510           slap_mask_t                   *maskp )
1511 {
1512           BackendDB *b0, *be = glue_back_select( op->o_bd, &e->e_nname );
1513           int rc;
1514 
1515           if ( be == NULL || be == op->o_bd || be->bd_info->bi_access_allowed == NULL )
1516                     return SLAP_CB_CONTINUE;
1517 
1518           b0 = op->o_bd;
1519           op->o_bd = be;
1520           rc = be->bd_info->bi_access_allowed ( op, e, desc, val, access, state, maskp );
1521           op->o_bd = b0;
1522           return rc;
1523 }
1524 
1525 int
glue_sub_init()1526 glue_sub_init()
1527 {
1528           glue.on_bi.bi_type = "glue";
1529 
1530           glue.on_bi.bi_db_init = glue_db_init;
1531           glue.on_bi.bi_db_close = glue_db_close;
1532           glue.on_bi.bi_db_destroy = glue_db_destroy;
1533 
1534           glue.on_bi.bi_op_search = glue_op_search;
1535           glue.on_bi.bi_op_modify = glue_op_func;
1536           glue.on_bi.bi_op_modrdn = glue_op_func;
1537           glue.on_bi.bi_op_add = glue_op_func;
1538           glue.on_bi.bi_op_delete = glue_op_func;
1539           glue.on_bi.bi_op_abandon = glue_op_abandon;
1540           glue.on_bi.bi_extended = glue_op_func;
1541 
1542           glue.on_bi.bi_chk_referrals = glue_chk_referrals;
1543           glue.on_bi.bi_chk_controls = glue_chk_controls;
1544           glue.on_bi.bi_entry_get_rw = glue_entry_get_rw;
1545           glue.on_bi.bi_entry_release_rw = glue_entry_release_rw;
1546           glue.on_bi.bi_access_allowed = glue_access_allowed;
1547 
1548           glue.on_response = glue_response;
1549 
1550           return overlay_register( &glue );
1551 }
1552