1 /* tools.cpp - tools for slap tools */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2008-2021 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Howard Chu for inclusion
18  * in OpenLDAP Software. This work was sponsored by MySQL.
19  */
20 
21 #include "portable.h"
22 
23 #include <stdio.h>
24 #include <ac/string.h>
25 #include <ac/errno.h>
26 
27 #include "lutil.h"
28 
29 #include "back-ndb.h"
30 
31 typedef struct dn_id {
32           ID id;
33           struct berval dn;
34 } dn_id;
35 
36 #define   HOLE_SIZE 4096
37 static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
38 static unsigned nhmax = HOLE_SIZE;
39 static unsigned nholes;
40 static Avlnode *myParents;
41 
42 static Ndb *myNdb;
43 static NdbTransaction *myScanTxn;
44 static NdbIndexScanOperation *myScanOp;
45 
46 static NdbRecAttr *myScanID, *myScanOC;
47 static NdbRecAttr *myScanDN[NDB_MAX_RDNS];
48 static char myDNbuf[2048];
49 static char myIdbuf[2*sizeof(ID)];
50 static char myOcbuf[NDB_OC_BUFLEN];
51 static NdbRdns myRdns;
52 
53 static NdbTransaction *myPutTxn;
54 static int myPutCnt;
55 
56 static struct berval *myOcList;
57 static struct berval myDn;
58 
59 extern "C"
ndb_tool_entry_open(BackendDB * be,int mode)60 int ndb_tool_entry_open(
61           BackendDB *be, int mode )
62 {
63           struct ndb_info *ni = (struct ndb_info *) be->be_private;
64 
65           myNdb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
66           return myNdb->init(1024);
67 }
68 
69 extern "C"
ndb_tool_entry_close(BackendDB * be)70 int ndb_tool_entry_close(
71           BackendDB *be )
72 {
73           if ( myPutTxn ) {
74                     int rc = myPutTxn->execute(NdbTransaction::Commit);
75                     if( rc != 0 ) {
76                               char text[1024];
77                               snprintf( text, sizeof(text),
78                                                   "txn_commit failed: %s (%d)",
79                                                   myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
80                               Debug( LDAP_DEBUG_ANY,
81                                         "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
82                                         text, 0, 0 );
83                     }
84                     myPutTxn->close();
85                     myPutTxn = NULL;
86           }
87           myPutCnt = 0;
88 
89           if( nholes ) {
90                     unsigned i;
91                     fprintf( stderr, "Error, entries missing!\n");
92                     for (i=0; i<nholes; i++) {
93                               fprintf(stderr, "  entry %ld: %s\n",
94                                         holes[i].id, holes[i].dn.bv_val);
95                     }
96                     return -1;
97           }
98 
99           return 0;
100 }
101 
102 extern "C"
ndb_tool_entry_next(BackendDB * be)103 ID ndb_tool_entry_next(
104           BackendDB *be )
105 {
106           struct ndb_info *ni = (struct ndb_info *) be->be_private;
107           char *ptr;
108           ID id;
109           int i;
110 
111           assert( be != NULL );
112           assert( slapMode & SLAP_TOOL_MODE );
113 
114           if ( myScanOp->nextResult() ) {
115                     myScanOp->close();
116                     myScanOp = NULL;
117                     myScanTxn->close();
118                     myScanTxn = NULL;
119                     return NOID;
120           }
121           id = myScanID->u_64_value();
122 
123           if ( myOcList ) {
124                     ber_bvarray_free( myOcList );
125           }
126           myOcList = ndb_ref2oclist( myOcbuf, NULL );
127           for ( i=0; i<NDB_MAX_RDNS; i++ ) {
128                     if ( myScanDN[i]->isNULL() || !myRdns.nr_buf[i][0] )
129                               break;
130           }
131           myRdns.nr_num = i;
132           ptr = myDNbuf;
133           for ( --i; i>=0; i-- ) {
134                     char *buf;
135                     int len;
136                     buf = myRdns.nr_buf[i];
137                     len = *buf++;
138                     ptr = lutil_strncopy( ptr, buf, len );
139                     if ( i )
140                               *ptr++ = ',';
141           }
142           *ptr = '\0';
143           myDn.bv_val = myDNbuf;
144           myDn.bv_len = ptr - myDNbuf;
145 
146           return id;
147 }
148 
149 extern "C"
ndb_tool_entry_first(BackendDB * be)150 ID ndb_tool_entry_first(
151           BackendDB *be )
152 {
153           struct ndb_info *ni = (struct ndb_info *) be->be_private;
154           int i;
155 
156           myScanTxn = myNdb->startTransaction();
157           if ( !myScanTxn )
158                     return NOID;
159 
160           myScanOp = myScanTxn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
161           if ( !myScanOp )
162                     return NOID;
163 
164           if ( myScanOp->readTuples( NdbOperation::LM_CommittedRead, NdbScanOperation::SF_KeyInfo ))
165                     return NOID;
166 
167           myScanID = myScanOp->getValue( EID_COLUMN, myIdbuf );
168           myScanOC = myScanOp->getValue( OCS_COLUMN, myOcbuf );
169           for ( i=0; i<NDB_MAX_RDNS; i++ ) {
170                     myScanDN[i] = myScanOp->getValue( i+RDN_COLUMN, myRdns.nr_buf[i] );
171           }
172           if ( myScanTxn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 ))
173                     return NOID;
174 
175           return ndb_tool_entry_next( be );
176 }
177 
178 extern "C"
ndb_tool_dn2id_get(Backend * be,struct berval * dn)179 ID ndb_tool_dn2id_get(
180           Backend *be,
181           struct berval *dn
182 )
183 {
184           struct ndb_info *ni = (struct ndb_info *) be->be_private;
185           NdbArgs NA;
186           NdbRdns rdns;
187           Entry e;
188           char text[1024];
189           Operation op = {0};
190           Opheader ohdr = {0};
191           int rc;
192 
193           if ( BER_BVISEMPTY(dn) )
194                     return 0;
195 
196           NA.ndb = myNdb;
197           NA.txn = myNdb->startTransaction();
198           if ( !NA.txn ) {
199                     snprintf( text, sizeof(text),
200                               "startTransaction failed: %s (%d)",
201                               myNdb->getNdbError().message, myNdb->getNdbError().code );
202                     Debug( LDAP_DEBUG_ANY,
203                               "=> " LDAP_XSTRING(ndb_tool_dn2id_get) ": %s\n",
204                                text, 0, 0 );
205                     return NOID;
206           }
207           if ( myOcList ) {
208                     ber_bvarray_free( myOcList );
209                     myOcList = NULL;
210           }
211           op.o_hdr = &ohdr;
212           op.o_bd = be;
213           op.o_tmpmemctx = NULL;
214           op.o_tmpmfuncs = &ch_mfuncs;
215 
216           NA.e = &e;
217           e.e_name = *dn;
218           NA.rdns = &rdns;
219           NA.ocs = NULL;
220           rc = ndb_entry_get_info( &op, &NA, 0, NULL );
221           myOcList = NA.ocs;
222           NA.txn->close();
223           if ( rc )
224                     return NOID;
225 
226           myDn = *dn;
227 
228           return e.e_id;
229 }
230 
231 extern "C"
ndb_tool_entry_get(BackendDB * be,ID id)232 Entry* ndb_tool_entry_get( BackendDB *be, ID id )
233 {
234           NdbArgs NA;
235           int rc;
236           char text[1024];
237           Operation op = {0};
238           Opheader ohdr = {0};
239 
240           assert( be != NULL );
241           assert( slapMode & SLAP_TOOL_MODE );
242 
243           NA.txn = myNdb->startTransaction();
244           if ( !NA.txn ) {
245                     snprintf( text, sizeof(text),
246                               "start_transaction failed: %s (%d)",
247                               myNdb->getNdbError().message, myNdb->getNdbError().code );
248                     Debug( LDAP_DEBUG_ANY,
249                               "=> " LDAP_XSTRING(ndb_tool_entry_get) ": %s\n",
250                                text, 0, 0 );
251                     return NULL;
252           }
253 
254           NA.e = entry_alloc();
255           NA.e->e_id = id;
256           ber_dupbv( &NA.e->e_name, &myDn );
257           dnNormalize( 0, NULL, NULL, &NA.e->e_name, &NA.e->e_nname, NULL );
258 
259           op.o_hdr = &ohdr;
260           op.o_bd = be;
261           op.o_tmpmemctx = NULL;
262           op.o_tmpmfuncs = &ch_mfuncs;
263 
264           NA.ndb = myNdb;
265           NA.ocs = myOcList;
266           rc = ndb_entry_get_data( &op, &NA, 0 );
267 
268           if ( rc ) {
269                     entry_free( NA.e );
270                     NA.e = NULL;
271           }
272           NA.txn->close();
273 
274           return NA.e;
275 }
276 
277 static struct berval glueval[] = {
278           BER_BVC("glue"),
279           BER_BVNULL
280 };
281 
ndb_dnid_cmp(const void * v1,const void * v2)282 static int ndb_dnid_cmp( const void *v1, const void *v2 )
283 {
284           struct dn_id *dn1 = (struct dn_id *)v1,
285                     *dn2 = (struct dn_id *)v2;
286           return ber_bvcmp( &dn1->dn, &dn2->dn );
287 }
288 
ndb_tool_next_id(Operation * op,NdbArgs * NA,struct berval * text,int hole)289 static int ndb_tool_next_id(
290           Operation *op,
291           NdbArgs *NA,
292           struct berval *text,
293           int hole )
294 {
295           struct berval ndn = NA->e->e_nname;
296           int rc;
297 
298           if (ndn.bv_len == 0) {
299                     NA->e->e_id = 0;
300                     return 0;
301           }
302 
303           rc = ndb_entry_get_info( op, NA, 0, NULL );
304           if ( rc ) {
305                     Attribute *a, tmp = {0};
306                     if ( !be_issuffix( op->o_bd, &ndn ) ) {
307                               struct dn_id *dptr;
308                               struct berval npdn;
309                               dnParent( &ndn, &npdn );
310                               NA->e->e_nname = npdn;
311                               NA->rdns->nr_num--;
312                               rc = ndb_tool_next_id( op, NA, text, 1 );
313                               NA->e->e_nname = ndn;
314                               NA->rdns->nr_num++;
315                               if ( rc ) {
316                                         return rc;
317                               }
318                               /* If parent didn't exist, it was created just now
319                                * and its ID is now in e->e_id.
320                                */
321                               dptr = (struct dn_id *)ch_malloc( sizeof( struct dn_id ) + npdn.bv_len + 1);
322                               dptr->id = NA->e->e_id;
323                               dptr->dn.bv_val = (char *)(dptr+1);
324                               strcpy(dptr->dn.bv_val, npdn.bv_val );
325                               dptr->dn.bv_len = npdn.bv_len;
326                               if ( avl_insert( &myParents, dptr, ndb_dnid_cmp, avl_dup_error )) {
327                                         ch_free( dptr );
328                               }
329                     }
330                     rc = ndb_next_id( op->o_bd, myNdb, &NA->e->e_id );
331                     if ( rc ) {
332                               snprintf( text->bv_val, text->bv_len,
333                                         "next_id failed: %s (%d)",
334                                         myNdb->getNdbError().message, myNdb->getNdbError().code );
335                               Debug( LDAP_DEBUG_ANY,
336                                         "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
337                               return rc;
338                     }
339                     if ( hole ) {
340                               a = NA->e->e_attrs;
341                               NA->e->e_attrs = &tmp;
342                               tmp.a_desc = slap_schema.si_ad_objectClass;
343                               tmp.a_vals = glueval;
344                               tmp.a_nvals = tmp.a_vals;
345                               tmp.a_numvals = 1;
346                     }
347                     rc = ndb_entry_put_info( op->o_bd, NA, 0 );
348                     if ( hole ) {
349                               NA->e->e_attrs = a;
350                     }
351                     if ( rc ) {
352                               snprintf( text->bv_val, text->bv_len,
353                                         "ndb_entry_put_info failed: %s (%d)",
354                                         myNdb->getNdbError().message, myNdb->getNdbError().code );
355                     Debug( LDAP_DEBUG_ANY,
356                               "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
357                     } else if ( hole ) {
358                               if ( nholes == nhmax - 1 ) {
359                                         if ( holes == hbuf ) {
360                                                   holes = (dn_id *)ch_malloc( nhmax * sizeof(dn_id) * 2 );
361                                                   AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
362                                         } else {
363                                                   holes = (dn_id *)ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
364                                         }
365                                         nhmax *= 2;
366                               }
367                               ber_dupbv( &holes[nholes].dn, &ndn );
368                               holes[nholes++].id = NA->e->e_id;
369                     }
370           } else if ( !hole ) {
371                     unsigned i;
372 
373                     for ( i=0; i<nholes; i++) {
374                               if ( holes[i].id == NA->e->e_id ) {
375                                         int j;
376                                         free(holes[i].dn.bv_val);
377                                         for (j=i;j<nholes;j++) holes[j] = holes[j+1];
378                                         holes[j].id = 0;
379                                         nholes--;
380                                         rc = ndb_entry_put_info( op->o_bd, NA, 1 );
381                                         break;
382                               } else if ( holes[i].id > NA->e->e_id ) {
383                                         break;
384                               }
385                     }
386           }
387           return rc;
388 }
389 
390 extern "C"
ndb_tool_entry_put(BackendDB * be,Entry * e,struct berval * text)391 ID ndb_tool_entry_put(
392           BackendDB *be,
393           Entry *e,
394           struct berval *text )
395 {
396           struct ndb_info *ni = (struct ndb_info *) be->be_private;
397           struct dn_id dtmp, *dptr;
398           NdbArgs NA;
399           NdbRdns rdns;
400           int rc, slow = 0;
401           Operation op = {0};
402           Opheader ohdr = {0};
403 
404           assert( be != NULL );
405           assert( slapMode & SLAP_TOOL_MODE );
406 
407           assert( text != NULL );
408           assert( text->bv_val != NULL );
409           assert( text->bv_val[0] == '\0' );      /* overconservative? */
410 
411           Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(ndb_tool_entry_put)
412                     "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
413 
414           if ( !be_issuffix( be, &e->e_nname )) {
415                     dnParent( &e->e_nname, &dtmp.dn );
416                     dptr = (struct dn_id *)avl_find( myParents, &dtmp, ndb_dnid_cmp );
417                     if ( !dptr )
418                               slow = 1;
419           }
420 
421           rdns.nr_num = 0;
422 
423           op.o_hdr = &ohdr;
424           op.o_bd = be;
425           op.o_tmpmemctx = NULL;
426           op.o_tmpmfuncs = &ch_mfuncs;
427 
428           if ( !slow ) {
429                     rc = ndb_next_id( be, myNdb, &e->e_id );
430                     if ( rc ) {
431                               snprintf( text->bv_val, text->bv_len,
432                                         "next_id failed: %s (%d)",
433                                         myNdb->getNdbError().message, myNdb->getNdbError().code );
434                               Debug( LDAP_DEBUG_ANY,
435                                         "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
436                               return rc;
437                     }
438           }
439 
440           if ( !myPutTxn )
441                     myPutTxn = myNdb->startTransaction();
442           if ( !myPutTxn ) {
443                     snprintf( text->bv_val, text->bv_len,
444                               "start_transaction failed: %s (%d)",
445                               myNdb->getNdbError().message, myNdb->getNdbError().code );
446                     Debug( LDAP_DEBUG_ANY,
447                               "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
448                                text->bv_val, 0, 0 );
449                     return NOID;
450           }
451 
452           /* add dn2id indices */
453           ndb_dn2rdns( &e->e_name, &rdns );
454           NA.rdns = &rdns;
455           NA.e = e;
456           NA.ndb = myNdb;
457           NA.txn = myPutTxn;
458           if ( slow ) {
459                     rc = ndb_tool_next_id( &op, &NA, text, 0 );
460                     if( rc != 0 ) {
461                               goto done;
462                     }
463           } else {
464                     rc = ndb_entry_put_info( be, &NA, 0 );
465                     if ( rc != 0 ) {
466                               goto done;
467                     }
468           }
469 
470           /* id2entry index */
471           rc = ndb_entry_put_data( be, &NA );
472           if( rc != 0 ) {
473                     snprintf( text->bv_val, text->bv_len,
474                                         "ndb_entry_put_data failed: %s (%d)",
475                                         myNdb->getNdbError().message, myNdb->getNdbError().code );
476                     Debug( LDAP_DEBUG_ANY,
477                               "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
478                               text->bv_val, 0, 0 );
479                     goto done;
480           }
481 
482 done:
483           if( rc == 0 ) {
484                     myPutCnt++;
485                     if ( !( myPutCnt & 0x0f )) {
486                               rc = myPutTxn->execute(NdbTransaction::Commit);
487                               if( rc != 0 ) {
488                                         snprintf( text->bv_val, text->bv_len,
489                                                   "txn_commit failed: %s (%d)",
490                                                   myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
491                                         Debug( LDAP_DEBUG_ANY,
492                                                   "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
493                                                   text->bv_val, 0, 0 );
494                                         e->e_id = NOID;
495                               }
496                               myPutTxn->close();
497                               myPutTxn = NULL;
498                     }
499           } else {
500                     snprintf( text->bv_val, text->bv_len,
501                               "txn_aborted! %s (%d)",
502                               myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
503                     Debug( LDAP_DEBUG_ANY,
504                               "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
505                               text->bv_val, 0, 0 );
506                     e->e_id = NOID;
507                     myPutTxn->close();
508           }
509 
510           return e->e_id;
511 }
512 
513 extern "C"
ndb_tool_entry_reindex(BackendDB * be,ID id,AttributeDescription ** adv)514 int ndb_tool_entry_reindex(
515           BackendDB *be,
516           ID id,
517           AttributeDescription **adv )
518 {
519           struct ndb_info *ni = (struct ndb_info *) be->be_private;
520 
521           Debug( LDAP_DEBUG_ARGS,
522                     "=> " LDAP_XSTRING(ndb_tool_entry_reindex) "( %ld )\n",
523                     (long) id, 0, 0 );
524 
525           return 0;
526 }
527 
528 extern "C"
ndb_tool_entry_modify(BackendDB * be,Entry * e,struct berval * text)529 ID ndb_tool_entry_modify(
530           BackendDB *be,
531           Entry *e,
532           struct berval *text )
533 {
534           struct ndb_info *ni = (struct ndb_info *) be->be_private;
535           int rc;
536 
537           Debug( LDAP_DEBUG_TRACE,
538                     "=> " LDAP_XSTRING(ndb_tool_entry_modify) "( %ld, \"%s\" )\n",
539                     (long) e->e_id, e->e_dn, 0 );
540 
541 done:
542           return e->e_id;
543 }
544 
545