1 /* tools.c - tools for slap tools */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2000-2005 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
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>.
20 #include <ac/string.h>
26 static DBC *cursor = NULL;
29 typedef struct dn_id {
34 #define HOLE_SIZE 4096
35 static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
36 static unsigned nhmax = HOLE_SIZE;
37 static unsigned nholes;
39 static int index_nattrs;
41 #define bdb_tool_idl_cmp BDB_SYMBOL(tool_idl_cmp)
42 #define bdb_tool_idl_flush_one BDB_SYMBOL(tool_idl_flush_one)
43 #define bdb_tool_idl_flush BDB_SYMBOL(tool_idl_flush)
45 static int bdb_tool_idl_flush( BackendDB *be );
49 static ID *bdb_tool_idls;
50 static int bdb_tool_idl_next;
52 int bdb_tool_entry_open(
53 BackendDB *be, int mode )
55 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
57 /* initialize key and data thangs */
60 key.flags = DB_DBT_REALLOC;
61 data.flags = DB_DBT_REALLOC;
64 int rc = bdb->bi_id2entry->bdi_db->cursor(
65 bdb->bi_id2entry->bdi_db, NULL, &cursor,
72 /* Get a block of memory for the IDL cache */
73 if ( !(slapMode & SLAP_TOOL_READONLY ) && bdb->bi_idl_cache_max_size )
74 bdb_tool_idls = ch_malloc( bdb->bi_idl_cache_max_size *
75 sizeof( ID ) * IDBLOCK );
80 int bdb_tool_entry_close(
95 cursor->c_close( cursor );
99 bdb_tool_idl_flush( be );
101 free( bdb_tool_idls );
102 bdb_tool_idls = NULL;
103 bdb_tool_idl_next = 0;
107 fprintf( stderr, "Error, entries missing!\n");
108 for (i=0; i<nholes; i++) {
109 fprintf(stderr, " entry %ld: %s\n",
110 holes[i].id, holes[i].dn.bv_val);
118 ID bdb_tool_entry_next(
123 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
125 assert( be != NULL );
126 assert( slapMode & SLAP_TOOL_MODE );
127 assert( bdb != NULL );
129 rc = cursor->c_get( cursor, &key, &data, DB_NEXT );
132 /* If we're doing linear indexing and there are more attrs to
133 * index, and we're at the end of the database, start over.
135 if ( index_nattrs && rc == DB_NOTFOUND ) {
136 /* optional - do a checkpoint here? */
137 bdb_attr_info_free( bdb->bi_attrs[0] );
138 bdb->bi_attrs[0] = bdb->bi_attrs[index_nattrs];
140 rc = cursor->c_get( cursor, &key, &data, DB_FIRST );
149 if( data.data == NULL ) {
153 BDB_DISK2ID( key.data, &id );
157 ID bdb_tool_dn2id_get(
164 EntryInfo *ei = NULL;
167 if ( BER_BVISEMPTY(dn) )
172 op.o_tmpmemctx = NULL;
173 op.o_tmpmfuncs = &ch_mfuncs;
175 rc = bdb_cache_find_ndn( &op, NULL, dn, &ei );
176 if ( ei ) bdb_cache_entryinfo_unlock( ei );
177 if ( rc == DB_NOTFOUND )
183 int bdb_tool_id2entry_get(
189 int rc = bdb_id2entry( be, NULL, 0, id, e );
191 if ( rc == DB_NOTFOUND && id == 0 ) {
192 Entry *dummy = ch_calloc( 1, sizeof(Entry) );
193 struct berval gluebv = BER_BVC("glue");
194 dummy->e_name.bv_val = ch_strdup( "" );
195 dummy->e_nname.bv_val = ch_strdup( "" );
196 attr_merge_one( dummy, slap_schema.si_ad_objectClass, &gluebv, NULL );
197 attr_merge_one( dummy, slap_schema.si_ad_structuralObjectClass,
205 Entry* bdb_tool_entry_get( BackendDB *be, ID id )
211 assert( be != NULL );
212 assert( slapMode & SLAP_TOOL_MODE );
213 assert( data.data != NULL );
215 DBT2bv( &data, &bv );
217 #ifdef SLAP_ZONE_ALLOC
218 /* FIXME: will add ctx later */
219 rc = entry_decode( &bv, &e, NULL );
221 rc = entry_decode( &bv, &e );
224 if( rc == LDAP_SUCCESS ) {
228 if ( slapMode & SLAP_TOOL_READONLY ) {
229 EntryInfo *ei = NULL;
235 op.o_tmpmemctx = NULL;
236 op.o_tmpmfuncs = &ch_mfuncs;
238 rc = bdb_cache_find_parent( &op, NULL, cursor->locker, id, &ei );
239 if ( rc == LDAP_SUCCESS ) {
240 bdb_cache_entryinfo_unlock( ei );
252 static int bdb_tool_next_id(
259 struct berval dn = e->e_name;
260 struct berval ndn = e->e_nname;
261 struct berval pdn, npdn;
262 EntryInfo *ei = NULL, eidummy;
265 if (ndn.bv_len == 0) {
270 rc = bdb_cache_find_ndn( op, tid, &ndn, &ei );
271 if ( ei ) bdb_cache_entryinfo_unlock( ei );
272 if ( rc == DB_NOTFOUND ) {
273 if ( !be_issuffix( op->o_bd, &ndn ) ) {
275 dnParent( &dn, &pdn );
276 dnParent( &ndn, &npdn );
279 rc = bdb_tool_next_id( op, tid, e, text, 1 );
285 /* If parent didn't exist, it was created just now
286 * and its ID is now in e->e_id. Make sure the current
287 * entry gets added under the new parent ID.
289 if ( eid != e->e_id ) {
290 eidummy.bei_id = e->e_id;
294 rc = bdb_next_id( op->o_bd, tid, &e->e_id );
296 snprintf( text->bv_val, text->bv_len,
297 "next_id failed: %s (%d)",
298 db_strerror(rc), rc );
299 Debug( LDAP_DEBUG_ANY,
300 "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
303 rc = bdb_dn2id_add( op, tid, ei, e );
305 snprintf( text->bv_val, text->bv_len,
306 "dn2id_add failed: %s (%d)",
307 db_strerror(rc), rc );
308 Debug( LDAP_DEBUG_ANY,
309 "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
311 if ( nholes == nhmax - 1 ) {
312 if ( holes == hbuf ) {
313 holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
314 AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
316 holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
320 ber_dupbv( &holes[nholes].dn, &ndn );
321 holes[nholes++].id = e->e_id;
323 } else if ( !hole ) {
326 e->e_id = ei->bei_id;
328 for ( i=0; i<nholes; i++) {
329 if ( holes[i].id == e->e_id ) {
331 free(holes[i].dn.bv_val);
332 for (j=i;j<nholes;j++) holes[j] = holes[j+1];
336 } else if ( holes[i].id > e->e_id ) {
344 ID bdb_tool_entry_put(
347 struct berval *text )
350 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
355 assert( be != NULL );
356 assert( slapMode & SLAP_TOOL_MODE );
358 assert( text != NULL );
359 assert( text->bv_val != NULL );
360 assert( text->bv_val[0] == '\0' ); /* overconservative? */
362 Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(bdb_tool_entry_put)
363 "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
365 if (! (slapMode & SLAP_TOOL_QUICK)) {
366 rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid,
367 bdb->bi_db_opflags );
369 snprintf( text->bv_val, text->bv_len,
370 "txn_begin failed: %s (%d)",
371 db_strerror(rc), rc );
372 Debug( LDAP_DEBUG_ANY,
373 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
374 text->bv_val, 0, 0 );
381 op.o_tmpmemctx = NULL;
382 op.o_tmpmfuncs = &ch_mfuncs;
384 /* add dn2id indices */
385 rc = bdb_tool_next_id( &op, tid, e, text, 0 );
391 rc = bdb_id2entry_add( be, tid, e );
393 snprintf( text->bv_val, text->bv_len,
394 "id2entry_add failed: %s (%d)",
395 db_strerror(rc), rc );
396 Debug( LDAP_DEBUG_ANY,
397 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
398 text->bv_val, 0, 0 );
402 if ( !bdb->bi_linear_index )
403 rc = bdb_index_entry_add( &op, tid, e );
405 snprintf( text->bv_val, text->bv_len,
406 "index_entry_add failed: %s (%d)",
407 db_strerror(rc), rc );
408 Debug( LDAP_DEBUG_ANY,
409 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
410 text->bv_val, 0, 0 );
416 if ( !( slapMode & SLAP_TOOL_QUICK )) {
417 rc = TXN_COMMIT( tid, 0 );
419 snprintf( text->bv_val, text->bv_len,
420 "txn_commit failed: %s (%d)",
421 db_strerror(rc), rc );
422 Debug( LDAP_DEBUG_ANY,
423 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
424 text->bv_val, 0, 0 );
430 if ( !( slapMode & SLAP_TOOL_QUICK )) {
432 snprintf( text->bv_val, text->bv_len,
433 "txn_aborted! %s (%d)",
434 db_strerror(rc), rc );
435 Debug( LDAP_DEBUG_ANY,
436 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
437 text->bv_val, 0, 0 );
445 int bdb_tool_entry_reindex(
449 struct bdb_info *bi = (struct bdb_info *) be->be_private;
456 Debug( LDAP_DEBUG_ARGS,
457 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld )\n",
460 /* No indexes configured, nothing to do. Could return an
461 * error here to shortcut things.
467 /* Get the first attribute to index */
468 if (bi->bi_linear_index && !index_nattrs) {
469 index_nattrs = bi->bi_nattrs - 1;
473 e = bdb_tool_entry_get( be, id );
476 Debug( LDAP_DEBUG_ANY,
477 LDAP_XSTRING(bdb_tool_entry_reindex)
478 ": could not locate id=%ld\n",
483 if (! (slapMode & SLAP_TOOL_QUICK)) {
484 rc = TXN_BEGIN( bi->bi_dbenv, NULL, &tid, bi->bi_db_opflags );
486 Debug( LDAP_DEBUG_ANY,
487 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) ": "
488 "txn_begin failed: %s (%d)\n",
489 db_strerror(rc), rc, 0 );
495 * just (re)add them for now
496 * assume that some other routine (not yet implemented)
497 * will zap index databases
501 Debug( LDAP_DEBUG_TRACE,
502 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld, \"%s\" )\n",
503 (long) id, e->e_dn, 0 );
507 op.o_tmpmemctx = NULL;
508 op.o_tmpmfuncs = &ch_mfuncs;
510 rc = bdb_index_entry_add( &op, tid, e );
514 if (! (slapMode & SLAP_TOOL_QUICK)) {
515 rc = TXN_COMMIT( tid, 0 );
517 Debug( LDAP_DEBUG_ANY,
518 "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
519 ": txn_commit failed: %s (%d)\n",
520 db_strerror(rc), rc, 0 );
526 if (! (slapMode & SLAP_TOOL_QUICK)) {
528 Debug( LDAP_DEBUG_ANY,
529 "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
530 ": txn_aborted! %s (%d)\n",
531 db_strerror(rc), rc, 0 );
535 bdb_entry_release( &op, e, 0 );
540 ID bdb_tool_entry_modify(
543 struct berval *text )
546 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
551 assert( be != NULL );
552 assert( slapMode & SLAP_TOOL_MODE );
554 assert( text != NULL );
555 assert( text->bv_val != NULL );
556 assert( text->bv_val[0] == '\0' ); /* overconservative? */
558 assert ( e->e_id != NOID );
560 Debug( LDAP_DEBUG_TRACE,
561 "=> " LDAP_XSTRING(bdb_tool_entry_modify) "( %ld, \"%s\" )\n",
562 (long) e->e_id, e->e_dn, 0 );
564 if (! (slapMode & SLAP_TOOL_QUICK)) {
565 rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid,
566 bdb->bi_db_opflags );
568 snprintf( text->bv_val, text->bv_len,
569 "txn_begin failed: %s (%d)",
570 db_strerror(rc), rc );
571 Debug( LDAP_DEBUG_ANY,
572 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
573 text->bv_val, 0, 0 );
580 op.o_tmpmemctx = NULL;
581 op.o_tmpmfuncs = &ch_mfuncs;
584 rc = bdb_id2entry_update( be, tid, e );
586 snprintf( text->bv_val, text->bv_len,
587 "id2entry_add failed: %s (%d)",
588 db_strerror(rc), rc );
589 Debug( LDAP_DEBUG_ANY,
590 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
591 text->bv_val, 0, 0 );
596 /* FIXME: this is bogus, we don't have the old values to delete
597 * from the index because the given entry has already been modified.
599 rc = bdb_index_entry_del( &op, tid, e );
601 snprintf( text->bv_val, text->bv_len,
602 "index_entry_del failed: %s (%d)",
603 db_strerror(rc), rc );
604 Debug( LDAP_DEBUG_ANY,
605 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
606 text->bv_val, 0, 0 );
611 rc = bdb_index_entry_add( &op, tid, e );
613 snprintf( text->bv_val, text->bv_len,
614 "index_entry_add failed: %s (%d)",
615 db_strerror(rc), rc );
616 Debug( LDAP_DEBUG_ANY,
617 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
618 text->bv_val, 0, 0 );
624 if (! (slapMode & SLAP_TOOL_QUICK)) {
625 rc = TXN_COMMIT( tid, 0 );
627 snprintf( text->bv_val, text->bv_len,
628 "txn_commit failed: %s (%d)",
629 db_strerror(rc), rc );
630 Debug( LDAP_DEBUG_ANY,
631 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": "
632 "%s\n", text->bv_val, 0, 0 );
638 if (! (slapMode & SLAP_TOOL_QUICK)) {
640 snprintf( text->bv_val, text->bv_len,
641 "txn_aborted! %s (%d)",
642 db_strerror(rc), rc );
643 Debug( LDAP_DEBUG_ANY,
644 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
645 text->bv_val, 0, 0 );
654 typedef struct bdb_tool_idl_cache {
657 int idls[BDB_IDL_DB_SIZE / IDBLOCK];
659 } bdb_tool_idl_cache;
662 bdb_tool_idl_cmp( const void *v1, const void *v2 )
664 const bdb_tool_idl_cache *c1 = v1, *c2 = v2;
667 if (( rc = c1->kstr.bv_len - c2->kstr.bv_len )) return rc;
668 return memcmp( c1->kstr.bv_val, c2->kstr.bv_val, c1->kstr.bv_len );
672 bdb_tool_idl_flush_one( void *v1, void *arg )
674 bdb_tool_idl_cache *ic = v1;
686 rc = db->cursor( db, NULL, &curs, 0 );
693 bv2DBT( &ic->kstr, &key );
695 data.size = data.ulen = sizeof( ID );
696 data.flags = DB_DBT_USERMEM;
699 rc = curs->c_get( curs, &key, &data, DB_SET );
700 /* If key already exists and we're writing a range... */
701 if ( rc == 0 && ic->count > BDB_IDL_DB_SIZE ) {
702 /* If it's not currently a range, must delete old info */
705 while ( curs->c_get( curs, &key, &data, DB_NEXT_DUP ) == 0 )
706 curs->c_del( curs, 0 );
709 /* Store range marker */
710 curs->c_put( curs, &key, &data, DB_KEYFIRST );
714 rc = curs->c_get( curs, &key, &data, DB_NEXT_DUP );
717 rc = curs->c_get( curs, &key, &data, DB_NEXT_DUP );
720 curs->c_del( curs, 0 );
722 BDB_ID2DISK( ic->last, &nid );
723 curs->c_put( curs, &key, &data, DB_KEYLAST );
725 } else if ( rc && rc != DB_NOTFOUND ) {
727 } else if ( ic->count > BDB_IDL_DB_SIZE ) {
728 /* range, didn't exist before */
730 rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
732 BDB_ID2DISK( ic->first, &nid );
733 rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
735 BDB_ID2DISK( ic->last, &nid );
736 rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
745 /* Just a normal write */
747 for ( n=0; n<=ic->idn; n++ ) {
748 ID *ids = bdb_tool_idls + ic->idls[n];
753 end = ic->count & (IDBLOCK-1);
757 for ( i=0; i<end; i++ ) {
758 if ( !ids[i] ) continue;
759 BDB_ID2DISK( ids[i], &nid );
760 rc = curs->c_put( curs, &key, &data, DB_NODUPDATA );
762 if ( rc == DB_KEYEXIST ) {
777 curs->c_close( curs );
782 bdb_tool_idl_flush( BackendDB *be )
784 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
789 for ( i=BDB_NDB; i < bdb->bi_ndatabases; i++ ) {
790 db = bdb->bi_databases[i]->bdi_db;
791 if ( !db->app_private ) continue;
792 rc = avl_apply( db->app_private, bdb_tool_idl_flush_one, db, -1,
794 avl_free( db->app_private, NULL );
795 db->app_private = NULL;
801 bdb->bi_idl_cache_size = 0;
802 bdb_tool_idl_next = 0;
807 int bdb_tool_idl_add(
814 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
815 bdb_tool_idl_cache *ic, itmp;
820 if ( !bdb->bi_idl_cache_max_size )
821 return bdb_idl_insert_key( be, db, txn, key, id );
823 DBT2bv( key, &itmp.kstr );
826 root = db->app_private;
828 ic = avl_find( root, &itmp, bdb_tool_idl_cmp );
830 /* No entry yet, create one */
837 ic = ch_malloc( sizeof( bdb_tool_idl_cache ) + itmp.kstr.bv_len );
838 ic->kstr.bv_len = itmp.kstr.bv_len;
839 ic->kstr.bv_val = (char *)(ic+1);
840 AC_MEMCPY( ic->kstr.bv_val, itmp.kstr.bv_val, ic->kstr.bv_len );
844 avl_insert( &root, ic, bdb_tool_idl_cmp, avl_dup_error );
845 db->app_private = root;
847 /* load existing key count here */
848 rc = db->cursor( db, NULL, &curs, 0 );
851 data.ulen = sizeof( ID );
852 data.flags = DB_DBT_USERMEM;
854 rc = curs->c_get( curs, key, &data, DB_SET );
857 ic->count = BDB_IDL_DB_SIZE+1;
861 curs->c_count( curs, &count, 0 );
863 BDB_DISK2ID( &nid, &ic->first );
866 curs->c_close( curs );
868 /* are we a range already? */
869 if ( ic->count > BDB_IDL_DB_SIZE ) {
872 /* Are we at the limit, and converting to a range? */
873 } else if ( ic->count == BDB_IDL_DB_SIZE ) {
879 /* No free block, create that too */
880 if ( ic->idn == -1 || ( ic->count & (IDBLOCK-1)) == 0) {
881 bdb->bi_idl_cache_size++;
882 if ( bdb->bi_idl_cache_size > bdb->bi_idl_cache_max_size ) {
883 rc = bdb_tool_idl_flush( be );
888 ic->idls[++ic->idn] = bdb_tool_idl_next;
889 ids = bdb_tool_idls + bdb_tool_idl_next;
890 bdb_tool_idl_next += IDBLOCK;
892 memset( ids, 0, IDBLOCK * sizeof( ID ));
896 ids = bdb_tool_idls + ic->idls[ic->idn];
897 ids[ ic->count & (IDBLOCK-1) ] = id;