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 31typedef struct dn_id { 32 ID id; 33 struct berval dn; 34} dn_id; 35 36#define HOLE_SIZE 4096 37static dn_id hbuf[HOLE_SIZE], *holes = hbuf; 38static unsigned nhmax = HOLE_SIZE; 39static unsigned nholes; 40static Avlnode *myParents; 41 42static Ndb *myNdb; 43static NdbTransaction *myScanTxn; 44static NdbIndexScanOperation *myScanOp; 45 46static NdbRecAttr *myScanID, *myScanOC; 47static NdbRecAttr *myScanDN[NDB_MAX_RDNS]; 48static char myDNbuf[2048]; 49static char myIdbuf[2*sizeof(ID)]; 50static char myOcbuf[NDB_OC_BUFLEN]; 51static NdbRdns myRdns; 52 53static NdbTransaction *myPutTxn; 54static int myPutCnt; 55 56static struct berval *myOcList; 57static struct berval myDn; 58 59extern "C" 60int 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 69extern "C" 70int 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 102extern "C" 103ID 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 149extern "C" 150ID 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 178extern "C" 179ID 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 231extern "C" 232Entry* 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 277static struct berval glueval[] = { 278 BER_BVC("glue"), 279 BER_BVNULL 280}; 281 282static 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 289static 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 390extern "C" 391ID 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 482done: 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 513extern "C" 514int 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 528extern "C" 529ID 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 541done: 542 return e->e_id; 543} 544 545