/* tools.cpp - tools for slap tools */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 2008-2020 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ /* ACKNOWLEDGEMENTS: * This work was initially developed by Howard Chu for inclusion * in OpenLDAP Software. This work was sponsored by MySQL. */ #include "portable.h" #include #include #include #include "lutil.h" #include "back-ndb.h" typedef struct dn_id { ID id; struct berval dn; } dn_id; #define HOLE_SIZE 4096 static dn_id hbuf[HOLE_SIZE], *holes = hbuf; static unsigned nhmax = HOLE_SIZE; static unsigned nholes; static Avlnode *myParents; static Ndb *myNdb; static NdbTransaction *myScanTxn; static NdbIndexScanOperation *myScanOp; static NdbRecAttr *myScanID, *myScanOC; static NdbRecAttr *myScanDN[NDB_MAX_RDNS]; static char myDNbuf[2048]; static char myIdbuf[2*sizeof(ID)]; static char myOcbuf[NDB_OC_BUFLEN]; static NdbRdns myRdns; static NdbTransaction *myPutTxn; static int myPutCnt; static struct berval *myOcList; static struct berval myDn; extern "C" int ndb_tool_entry_open( BackendDB *be, int mode ) { struct ndb_info *ni = (struct ndb_info *) be->be_private; myNdb = new Ndb( ni->ni_cluster[0], ni->ni_dbname ); return myNdb->init(1024); } extern "C" int ndb_tool_entry_close( BackendDB *be ) { if ( myPutTxn ) { int rc = myPutTxn->execute(NdbTransaction::Commit); if( rc != 0 ) { char text[1024]; snprintf( text, sizeof(text), "txn_commit failed: %s (%d)", myPutTxn->getNdbError().message, myPutTxn->getNdbError().code ); Debug( LDAP_DEBUG_ANY, "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n", text, 0, 0 ); } myPutTxn->close(); myPutTxn = NULL; } myPutCnt = 0; if( nholes ) { unsigned i; fprintf( stderr, "Error, entries missing!\n"); for (i=0; ibe_private; char *ptr; ID id; int i; assert( be != NULL ); assert( slapMode & SLAP_TOOL_MODE ); if ( myScanOp->nextResult() ) { myScanOp->close(); myScanOp = NULL; myScanTxn->close(); myScanTxn = NULL; return NOID; } id = myScanID->u_64_value(); if ( myOcList ) { ber_bvarray_free( myOcList ); } myOcList = ndb_ref2oclist( myOcbuf, NULL ); for ( i=0; iisNULL() || !myRdns.nr_buf[i][0] ) break; } myRdns.nr_num = i; ptr = myDNbuf; for ( --i; i>=0; i-- ) { char *buf; int len; buf = myRdns.nr_buf[i]; len = *buf++; ptr = lutil_strncopy( ptr, buf, len ); if ( i ) *ptr++ = ','; } *ptr = '\0'; myDn.bv_val = myDNbuf; myDn.bv_len = ptr - myDNbuf; return id; } extern "C" ID ndb_tool_entry_first( BackendDB *be ) { struct ndb_info *ni = (struct ndb_info *) be->be_private; int i; myScanTxn = myNdb->startTransaction(); if ( !myScanTxn ) return NOID; myScanOp = myScanTxn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE ); if ( !myScanOp ) return NOID; if ( myScanOp->readTuples( NdbOperation::LM_CommittedRead, NdbScanOperation::SF_KeyInfo )) return NOID; myScanID = myScanOp->getValue( EID_COLUMN, myIdbuf ); myScanOC = myScanOp->getValue( OCS_COLUMN, myOcbuf ); for ( i=0; igetValue( i+RDN_COLUMN, myRdns.nr_buf[i] ); } if ( myScanTxn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) return NOID; return ndb_tool_entry_next( be ); } extern "C" ID ndb_tool_dn2id_get( Backend *be, struct berval *dn ) { struct ndb_info *ni = (struct ndb_info *) be->be_private; NdbArgs NA; NdbRdns rdns; Entry e; char text[1024]; Operation op = {0}; Opheader ohdr = {0}; int rc; if ( BER_BVISEMPTY(dn) ) return 0; NA.ndb = myNdb; NA.txn = myNdb->startTransaction(); if ( !NA.txn ) { snprintf( text, sizeof(text), "startTransaction failed: %s (%d)", myNdb->getNdbError().message, myNdb->getNdbError().code ); Debug( LDAP_DEBUG_ANY, "=> " LDAP_XSTRING(ndb_tool_dn2id_get) ": %s\n", text, 0, 0 ); return NOID; } if ( myOcList ) { ber_bvarray_free( myOcList ); myOcList = NULL; } op.o_hdr = &ohdr; op.o_bd = be; op.o_tmpmemctx = NULL; op.o_tmpmfuncs = &ch_mfuncs; NA.e = &e; e.e_name = *dn; NA.rdns = &rdns; NA.ocs = NULL; rc = ndb_entry_get_info( &op, &NA, 0, NULL ); myOcList = NA.ocs; NA.txn->close(); if ( rc ) return NOID; myDn = *dn; return e.e_id; } extern "C" Entry* ndb_tool_entry_get( BackendDB *be, ID id ) { NdbArgs NA; int rc; char text[1024]; Operation op = {0}; Opheader ohdr = {0}; assert( be != NULL ); assert( slapMode & SLAP_TOOL_MODE ); NA.txn = myNdb->startTransaction(); if ( !NA.txn ) { snprintf( text, sizeof(text), "start_transaction failed: %s (%d)", myNdb->getNdbError().message, myNdb->getNdbError().code ); Debug( LDAP_DEBUG_ANY, "=> " LDAP_XSTRING(ndb_tool_entry_get) ": %s\n", text, 0, 0 ); return NULL; } NA.e = entry_alloc(); NA.e->e_id = id; ber_dupbv( &NA.e->e_name, &myDn ); dnNormalize( 0, NULL, NULL, &NA.e->e_name, &NA.e->e_nname, NULL ); op.o_hdr = &ohdr; op.o_bd = be; op.o_tmpmemctx = NULL; op.o_tmpmfuncs = &ch_mfuncs; NA.ndb = myNdb; NA.ocs = myOcList; rc = ndb_entry_get_data( &op, &NA, 0 ); if ( rc ) { entry_free( NA.e ); NA.e = NULL; } NA.txn->close(); return NA.e; } static struct berval glueval[] = { BER_BVC("glue"), BER_BVNULL }; static int ndb_dnid_cmp( const void *v1, const void *v2 ) { struct dn_id *dn1 = (struct dn_id *)v1, *dn2 = (struct dn_id *)v2; return ber_bvcmp( &dn1->dn, &dn2->dn ); } static int ndb_tool_next_id( Operation *op, NdbArgs *NA, struct berval *text, int hole ) { struct berval ndn = NA->e->e_nname; int rc; if (ndn.bv_len == 0) { NA->e->e_id = 0; return 0; } rc = ndb_entry_get_info( op, NA, 0, NULL ); if ( rc ) { Attribute *a, tmp = {0}; if ( !be_issuffix( op->o_bd, &ndn ) ) { struct dn_id *dptr; struct berval npdn; dnParent( &ndn, &npdn ); NA->e->e_nname = npdn; NA->rdns->nr_num--; rc = ndb_tool_next_id( op, NA, text, 1 ); NA->e->e_nname = ndn; NA->rdns->nr_num++; if ( rc ) { return rc; } /* If parent didn't exist, it was created just now * and its ID is now in e->e_id. */ dptr = (struct dn_id *)ch_malloc( sizeof( struct dn_id ) + npdn.bv_len + 1); dptr->id = NA->e->e_id; dptr->dn.bv_val = (char *)(dptr+1); strcpy(dptr->dn.bv_val, npdn.bv_val ); dptr->dn.bv_len = npdn.bv_len; if ( avl_insert( &myParents, dptr, ndb_dnid_cmp, avl_dup_error )) { ch_free( dptr ); } } rc = ndb_next_id( op->o_bd, myNdb, &NA->e->e_id ); if ( rc ) { snprintf( text->bv_val, text->bv_len, "next_id failed: %s (%d)", myNdb->getNdbError().message, myNdb->getNdbError().code ); Debug( LDAP_DEBUG_ANY, "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 ); return rc; } if ( hole ) { a = NA->e->e_attrs; NA->e->e_attrs = &tmp; tmp.a_desc = slap_schema.si_ad_objectClass; tmp.a_vals = glueval; tmp.a_nvals = tmp.a_vals; tmp.a_numvals = 1; } rc = ndb_entry_put_info( op->o_bd, NA, 0 ); if ( hole ) { NA->e->e_attrs = a; } if ( rc ) { snprintf( text->bv_val, text->bv_len, "ndb_entry_put_info failed: %s (%d)", myNdb->getNdbError().message, myNdb->getNdbError().code ); Debug( LDAP_DEBUG_ANY, "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 ); } else if ( hole ) { if ( nholes == nhmax - 1 ) { if ( holes == hbuf ) { holes = (dn_id *)ch_malloc( nhmax * sizeof(dn_id) * 2 ); AC_MEMCPY( holes, hbuf, sizeof(hbuf) ); } else { holes = (dn_id *)ch_realloc( holes, nhmax * sizeof(dn_id) * 2 ); } nhmax *= 2; } ber_dupbv( &holes[nholes].dn, &ndn ); holes[nholes++].id = NA->e->e_id; } } else if ( !hole ) { unsigned i; for ( i=0; ie->e_id ) { int j; free(holes[i].dn.bv_val); for (j=i;jo_bd, NA, 1 ); break; } else if ( holes[i].id > NA->e->e_id ) { break; } } } return rc; } extern "C" ID ndb_tool_entry_put( BackendDB *be, Entry *e, struct berval *text ) { struct ndb_info *ni = (struct ndb_info *) be->be_private; struct dn_id dtmp, *dptr; NdbArgs NA; NdbRdns rdns; int rc, slow = 0; Operation op = {0}; Opheader ohdr = {0}; assert( be != NULL ); assert( slapMode & SLAP_TOOL_MODE ); assert( text != NULL ); assert( text->bv_val != NULL ); assert( text->bv_val[0] == '\0' ); /* overconservative? */ Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(ndb_tool_entry_put) "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 ); if ( !be_issuffix( be, &e->e_nname )) { dnParent( &e->e_nname, &dtmp.dn ); dptr = (struct dn_id *)avl_find( myParents, &dtmp, ndb_dnid_cmp ); if ( !dptr ) slow = 1; } rdns.nr_num = 0; op.o_hdr = &ohdr; op.o_bd = be; op.o_tmpmemctx = NULL; op.o_tmpmfuncs = &ch_mfuncs; if ( !slow ) { rc = ndb_next_id( be, myNdb, &e->e_id ); if ( rc ) { snprintf( text->bv_val, text->bv_len, "next_id failed: %s (%d)", myNdb->getNdbError().message, myNdb->getNdbError().code ); Debug( LDAP_DEBUG_ANY, "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 ); return rc; } } if ( !myPutTxn ) myPutTxn = myNdb->startTransaction(); if ( !myPutTxn ) { snprintf( text->bv_val, text->bv_len, "start_transaction failed: %s (%d)", myNdb->getNdbError().message, myNdb->getNdbError().code ); Debug( LDAP_DEBUG_ANY, "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n", text->bv_val, 0, 0 ); return NOID; } /* add dn2id indices */ ndb_dn2rdns( &e->e_name, &rdns ); NA.rdns = &rdns; NA.e = e; NA.ndb = myNdb; NA.txn = myPutTxn; if ( slow ) { rc = ndb_tool_next_id( &op, &NA, text, 0 ); if( rc != 0 ) { goto done; } } else { rc = ndb_entry_put_info( be, &NA, 0 ); if ( rc != 0 ) { goto done; } } /* id2entry index */ rc = ndb_entry_put_data( be, &NA ); if( rc != 0 ) { snprintf( text->bv_val, text->bv_len, "ndb_entry_put_data failed: %s (%d)", myNdb->getNdbError().message, myNdb->getNdbError().code ); Debug( LDAP_DEBUG_ANY, "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n", text->bv_val, 0, 0 ); goto done; } done: if( rc == 0 ) { myPutCnt++; if ( !( myPutCnt & 0x0f )) { rc = myPutTxn->execute(NdbTransaction::Commit); if( rc != 0 ) { snprintf( text->bv_val, text->bv_len, "txn_commit failed: %s (%d)", myPutTxn->getNdbError().message, myPutTxn->getNdbError().code ); Debug( LDAP_DEBUG_ANY, "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n", text->bv_val, 0, 0 ); e->e_id = NOID; } myPutTxn->close(); myPutTxn = NULL; } } else { snprintf( text->bv_val, text->bv_len, "txn_aborted! %s (%d)", myPutTxn->getNdbError().message, myPutTxn->getNdbError().code ); Debug( LDAP_DEBUG_ANY, "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n", text->bv_val, 0, 0 ); e->e_id = NOID; myPutTxn->close(); } return e->e_id; } extern "C" int ndb_tool_entry_reindex( BackendDB *be, ID id, AttributeDescription **adv ) { struct ndb_info *ni = (struct ndb_info *) be->be_private; Debug( LDAP_DEBUG_ARGS, "=> " LDAP_XSTRING(ndb_tool_entry_reindex) "( %ld )\n", (long) id, 0, 0 ); return 0; } extern "C" ID ndb_tool_entry_modify( BackendDB *be, Entry *e, struct berval *text ) { struct ndb_info *ni = (struct ndb_info *) be->be_private; int rc; Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(ndb_tool_entry_modify) "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 ); done: return e->e_id; }