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 Avlnode *index_attrs, index_dummy;
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 );
47 int bdb_tool_entry_open(
48 BackendDB *be, int mode )
50 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
52 /* initialize key and data thangs */
55 key.flags = DB_DBT_REALLOC;
56 data.flags = DB_DBT_REALLOC;
59 int rc = bdb->bi_id2entry->bdi_db->cursor(
60 bdb->bi_id2entry->bdi_db, NULL, &cursor,
70 int bdb_tool_entry_close(
85 cursor->c_close( cursor );
89 bdb_tool_idl_flush( be );
93 fprintf( stderr, "Error, entries missing!\n");
94 for (i=0; i<nholes; i++) {
95 fprintf(stderr, " entry %ld: %s\n",
96 holes[i].id, holes[i].dn.bv_val);
104 static int bdb_reindex_cmp(const void *a, const void *b) { return 0; }
106 ID bdb_tool_entry_next(
111 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
113 assert( be != NULL );
114 assert( slapMode & SLAP_TOOL_MODE );
115 assert( bdb != NULL );
117 rc = cursor->c_get( cursor, &key, &data, DB_NEXT );
120 /* If we're doing linear indexing and there are more attrs to
121 * index, and we're at the end of the database, start over.
123 if ( bdb->bi_attrs == &index_dummy ) {
124 if ( index_attrs && rc == DB_NOTFOUND ) {
125 /* optional - do a checkpoint here? */
126 index_dummy.avl_data = avl_delete(&index_attrs, NULL, bdb_reindex_cmp);
127 rc = cursor->c_get( cursor, &key, &data, DB_FIRST );
130 bdb->bi_attrs = NULL;
138 if( data.data == NULL ) {
142 BDB_DISK2ID( key.data, &id );
146 ID bdb_tool_dn2id_get(
153 EntryInfo *ei = NULL;
156 if ( BER_BVISEMPTY(dn) )
161 op.o_tmpmemctx = NULL;
162 op.o_tmpmfuncs = &ch_mfuncs;
164 rc = bdb_cache_find_ndn( &op, NULL, dn, &ei );
165 if ( ei ) bdb_cache_entryinfo_unlock( ei );
166 if ( rc == DB_NOTFOUND )
172 int bdb_tool_id2entry_get(
178 int rc = bdb_id2entry( be, NULL, 0, id, e );
180 if ( rc == DB_NOTFOUND && id == 0 ) {
181 Entry *dummy = ch_calloc( 1, sizeof(Entry) );
182 struct berval gluebv = BER_BVC("glue");
183 dummy->e_name.bv_val = ch_strdup( "" );
184 dummy->e_nname.bv_val = ch_strdup( "" );
185 attr_merge_one( dummy, slap_schema.si_ad_objectClass, &gluebv, NULL );
186 attr_merge_one( dummy, slap_schema.si_ad_structuralObjectClass,
194 Entry* bdb_tool_entry_get( BackendDB *be, ID id )
200 assert( be != NULL );
201 assert( slapMode & SLAP_TOOL_MODE );
202 assert( data.data != NULL );
204 DBT2bv( &data, &bv );
206 #ifdef SLAP_ZONE_ALLOC
207 /* FIXME: will add ctx later */
208 rc = entry_decode( &bv, &e, NULL );
210 rc = entry_decode( &bv, &e );
213 if( rc == LDAP_SUCCESS ) {
217 if ( slapMode & SLAP_TOOL_READONLY ) {
218 EntryInfo *ei = NULL;
224 op.o_tmpmemctx = NULL;
225 op.o_tmpmfuncs = &ch_mfuncs;
227 rc = bdb_cache_find_parent( &op, NULL, cursor->locker, id, &ei );
228 if ( rc == LDAP_SUCCESS ) {
229 bdb_cache_entryinfo_unlock( ei );
241 static int bdb_tool_next_id(
248 struct berval dn = e->e_name;
249 struct berval ndn = e->e_nname;
250 struct berval pdn, npdn;
251 EntryInfo *ei = NULL, eidummy;
254 if (ndn.bv_len == 0) {
259 rc = bdb_cache_find_ndn( op, tid, &ndn, &ei );
260 if ( ei ) bdb_cache_entryinfo_unlock( ei );
261 if ( rc == DB_NOTFOUND ) {
262 if ( !be_issuffix( op->o_bd, &ndn ) ) {
264 dnParent( &dn, &pdn );
265 dnParent( &ndn, &npdn );
268 rc = bdb_tool_next_id( op, tid, e, text, 1 );
274 /* If parent didn't exist, it was created just now
275 * and its ID is now in e->e_id. Make sure the current
276 * entry gets added under the new parent ID.
278 if ( eid != e->e_id ) {
279 eidummy.bei_id = e->e_id;
283 rc = bdb_next_id( op->o_bd, tid, &e->e_id );
285 snprintf( text->bv_val, text->bv_len,
286 "next_id failed: %s (%d)",
287 db_strerror(rc), rc );
288 Debug( LDAP_DEBUG_ANY,
289 "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
292 rc = bdb_dn2id_add( op, tid, ei, e );
294 snprintf( text->bv_val, text->bv_len,
295 "dn2id_add failed: %s (%d)",
296 db_strerror(rc), rc );
297 Debug( LDAP_DEBUG_ANY,
298 "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
300 if ( nholes == nhmax - 1 ) {
301 if ( holes == hbuf ) {
302 holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
303 AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
305 holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
309 ber_dupbv( &holes[nholes].dn, &ndn );
310 holes[nholes++].id = e->e_id;
312 } else if ( !hole ) {
315 e->e_id = ei->bei_id;
317 for ( i=0; i<nholes; i++) {
318 if ( holes[i].id == e->e_id ) {
320 free(holes[i].dn.bv_val);
321 for (j=i;j<nholes;j++) holes[j] = holes[j+1];
325 } else if ( holes[i].id > e->e_id ) {
333 ID bdb_tool_entry_put(
336 struct berval *text )
339 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
344 assert( be != NULL );
345 assert( slapMode & SLAP_TOOL_MODE );
347 assert( text != NULL );
348 assert( text->bv_val != NULL );
349 assert( text->bv_val[0] == '\0' ); /* overconservative? */
351 Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(bdb_tool_entry_put)
352 "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
354 if (! (slapMode & SLAP_TOOL_QUICK)) {
355 rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid,
356 bdb->bi_db_opflags );
358 snprintf( text->bv_val, text->bv_len,
359 "txn_begin failed: %s (%d)",
360 db_strerror(rc), rc );
361 Debug( LDAP_DEBUG_ANY,
362 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
363 text->bv_val, 0, 0 );
370 op.o_tmpmemctx = NULL;
371 op.o_tmpmfuncs = &ch_mfuncs;
373 /* add dn2id indices */
374 rc = bdb_tool_next_id( &op, tid, e, text, 0 );
380 rc = bdb_id2entry_add( be, tid, e );
382 snprintf( text->bv_val, text->bv_len,
383 "id2entry_add failed: %s (%d)",
384 db_strerror(rc), rc );
385 Debug( LDAP_DEBUG_ANY,
386 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
387 text->bv_val, 0, 0 );
391 if ( !bdb->bi_linear_index )
392 rc = bdb_index_entry_add( &op, tid, e );
394 snprintf( text->bv_val, text->bv_len,
395 "index_entry_add failed: %s (%d)",
396 db_strerror(rc), rc );
397 Debug( LDAP_DEBUG_ANY,
398 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
399 text->bv_val, 0, 0 );
405 if ( !( slapMode & SLAP_TOOL_QUICK )) {
406 rc = TXN_COMMIT( tid, 0 );
408 snprintf( text->bv_val, text->bv_len,
409 "txn_commit failed: %s (%d)",
410 db_strerror(rc), rc );
411 Debug( LDAP_DEBUG_ANY,
412 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
413 text->bv_val, 0, 0 );
419 if ( !( slapMode & SLAP_TOOL_QUICK )) {
421 snprintf( text->bv_val, text->bv_len,
422 "txn_aborted! %s (%d)",
423 db_strerror(rc), rc );
424 Debug( LDAP_DEBUG_ANY,
425 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
426 text->bv_val, 0, 0 );
434 int bdb_tool_entry_reindex(
438 struct bdb_info *bi = (struct bdb_info *) be->be_private;
445 Debug( LDAP_DEBUG_ARGS,
446 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld )\n",
449 /* No indexes configured, nothing to do. Could return an
450 * error here to shortcut things.
456 /* Get the first attribute to index */
457 if (bi->bi_linear_index && !index_attrs && bi->bi_attrs != &index_dummy) {
458 index_attrs = bi->bi_attrs;
459 bi->bi_attrs = &index_dummy;
460 index_dummy.avl_data = avl_delete(&index_attrs, NULL, bdb_reindex_cmp);
463 e = bdb_tool_entry_get( be, id );
466 Debug( LDAP_DEBUG_ANY,
467 LDAP_XSTRING(bdb_tool_entry_reindex)
468 ": could not locate id=%ld\n",
473 if (! (slapMode & SLAP_TOOL_QUICK)) {
474 rc = TXN_BEGIN( bi->bi_dbenv, NULL, &tid, bi->bi_db_opflags );
476 Debug( LDAP_DEBUG_ANY,
477 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) ": "
478 "txn_begin failed: %s (%d)\n",
479 db_strerror(rc), rc, 0 );
485 * just (re)add them for now
486 * assume that some other routine (not yet implemented)
487 * will zap index databases
491 Debug( LDAP_DEBUG_TRACE,
492 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld, \"%s\" )\n",
493 (long) id, e->e_dn, 0 );
497 op.o_tmpmemctx = NULL;
498 op.o_tmpmfuncs = &ch_mfuncs;
500 rc = bdb_index_entry_add( &op, tid, e );
504 if (! (slapMode & SLAP_TOOL_QUICK)) {
505 rc = TXN_COMMIT( tid, 0 );
507 Debug( LDAP_DEBUG_ANY,
508 "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
509 ": txn_commit failed: %s (%d)\n",
510 db_strerror(rc), rc, 0 );
516 if (! (slapMode & SLAP_TOOL_QUICK)) {
518 Debug( LDAP_DEBUG_ANY,
519 "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
520 ": txn_aborted! %s (%d)\n",
521 db_strerror(rc), rc, 0 );
525 bdb_entry_release( &op, e, 0 );
530 ID bdb_tool_entry_modify(
533 struct berval *text )
536 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
541 assert( be != NULL );
542 assert( slapMode & SLAP_TOOL_MODE );
544 assert( text != NULL );
545 assert( text->bv_val != NULL );
546 assert( text->bv_val[0] == '\0' ); /* overconservative? */
548 assert ( e->e_id != NOID );
550 Debug( LDAP_DEBUG_TRACE,
551 "=> " LDAP_XSTRING(bdb_tool_entry_modify) "( %ld, \"%s\" )\n",
552 (long) e->e_id, e->e_dn, 0 );
554 if (! (slapMode & SLAP_TOOL_QUICK)) {
555 rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid,
556 bdb->bi_db_opflags );
558 snprintf( text->bv_val, text->bv_len,
559 "txn_begin failed: %s (%d)",
560 db_strerror(rc), rc );
561 Debug( LDAP_DEBUG_ANY,
562 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
563 text->bv_val, 0, 0 );
570 op.o_tmpmemctx = NULL;
571 op.o_tmpmfuncs = &ch_mfuncs;
574 rc = bdb_id2entry_update( be, tid, e );
576 snprintf( text->bv_val, text->bv_len,
577 "id2entry_add failed: %s (%d)",
578 db_strerror(rc), rc );
579 Debug( LDAP_DEBUG_ANY,
580 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
581 text->bv_val, 0, 0 );
586 /* FIXME: this is bogus, we don't have the old values to delete
587 * from the index because the given entry has already been modified.
589 rc = bdb_index_entry_del( &op, tid, e );
591 snprintf( text->bv_val, text->bv_len,
592 "index_entry_del failed: %s (%d)",
593 db_strerror(rc), rc );
594 Debug( LDAP_DEBUG_ANY,
595 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
596 text->bv_val, 0, 0 );
601 rc = bdb_index_entry_add( &op, tid, e );
603 snprintf( text->bv_val, text->bv_len,
604 "index_entry_add failed: %s (%d)",
605 db_strerror(rc), rc );
606 Debug( LDAP_DEBUG_ANY,
607 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
608 text->bv_val, 0, 0 );
614 if (! (slapMode & SLAP_TOOL_QUICK)) {
615 rc = TXN_COMMIT( tid, 0 );
617 snprintf( text->bv_val, text->bv_len,
618 "txn_commit failed: %s (%d)",
619 db_strerror(rc), rc );
620 Debug( LDAP_DEBUG_ANY,
621 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": "
622 "%s\n", text->bv_val, 0, 0 );
628 if (! (slapMode & SLAP_TOOL_QUICK)) {
630 snprintf( text->bv_val, text->bv_len,
631 "txn_aborted! %s (%d)",
632 db_strerror(rc), rc );
633 Debug( LDAP_DEBUG_ANY,
634 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
635 text->bv_val, 0, 0 );
645 typedef struct bdb_tool_idl_cache_entry {
646 struct bdb_tool_idl_cache_entry *next;
648 } bdb_tool_idl_cache_entry;
650 typedef struct bdb_tool_idl_cache {
652 bdb_tool_idl_cache_entry *head, *tail;
655 } bdb_tool_idl_cache;
657 static bdb_tool_idl_cache_entry *bdb_tool_idl_free_list;
660 bdb_tool_idl_cmp( const void *v1, const void *v2 )
662 const bdb_tool_idl_cache *c1 = v1, *c2 = v2;
665 if (( rc = c1->kstr.bv_len - c2->kstr.bv_len )) return rc;
666 return memcmp( c1->kstr.bv_val, c2->kstr.bv_val, c1->kstr.bv_len );
670 bdb_tool_idl_flush_one( void *v1, void *arg )
672 bdb_tool_idl_cache *ic = v1;
674 bdb_tool_idl_cache_entry *ice;
680 rc = db->cursor( db, NULL, &curs, 0 );
687 bv2DBT( &ic->kstr, &key );
689 data.size = data.ulen = sizeof( ID );
690 data.flags = DB_DBT_USERMEM;
693 rc = curs->c_get( curs, &key, &data, DB_SET );
694 /* If key already exists and we're writing a range... */
695 if ( rc == 0 && ic->head == NULL ) {
696 /* If it's not currently a range, must delete old info */
699 while ( curs->c_get( curs, &key, &data, DB_NEXT_DUP ) == 0 )
700 curs->c_del( curs, 0 );
703 /* Store range marker */
704 curs->c_put( curs, &key, &data, DB_KEYFIRST );
708 rc = curs->c_get( curs, &key, &data, DB_NEXT_DUP );
711 rc = curs->c_get( curs, &key, &data, DB_NEXT_DUP );
714 curs->c_del( curs, 0 );
716 BDB_ID2DISK( ic->last, &nid );
717 curs->c_put( curs, &key, &data, DB_KEYLAST );
719 } else if ( rc && rc != DB_NOTFOUND ) {
721 } else if ( ic->head == NULL ) {
722 /* range, didn't exist before */
724 rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
726 BDB_ID2DISK( ic->first, &nid );
727 rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
729 BDB_ID2DISK( ic->last, &nid );
730 rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
737 /* Just a normal write */
739 for ( ice = ic->head; ice; ice = ic->head ) {
740 int end = ice->next ? IDBLOCK : (ic->count & (IDBLOCK-1));
741 ic->head = ice->next;
742 for ( i=0; i<end; i++ ) {
743 if ( !ice->ids[i] ) continue;
744 BDB_ID2DISK( ice->ids[i], &nid );
745 rc = curs->c_put( curs, &key, &data, DB_NODUPDATA );
747 if ( rc == DB_KEYEXIST ) {
755 ice->next = bdb_tool_idl_free_list;
756 bdb_tool_idl_free_list = ice;
764 curs->c_close( curs );
769 bdb_tool_idl_flush( BackendDB *be )
771 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
776 for ( i=BDB_NDB; i < bdb->bi_ndatabases; i++ ) {
777 db = bdb->bi_databases[i]->bdi_db;
778 if ( !db->app_private ) continue;
779 rc = avl_apply( db->app_private, bdb_tool_idl_flush_one, db, -1,
781 avl_free( db->app_private, NULL );
782 db->app_private = NULL;
783 if ( rc == -1 ) break;
787 bdb->bi_idl_cache_size = 0;
791 int bdb_tool_idl_add(
798 struct bdb_info *bdb = (struct bdb_info *) be->be_private;
799 bdb_tool_idl_cache *ic, itmp;
800 bdb_tool_idl_cache_entry *ice;
804 if ( !bdb->bi_idl_cache_max_size )
805 return bdb_idl_insert_key( be, db, txn, key, id );
807 root = db->app_private;
809 DBT2bv( key, &itmp.kstr );
811 ic = avl_find( root, &itmp, bdb_tool_idl_cmp );
813 /* No entry yet, create one */
820 ic = ch_malloc( sizeof( bdb_tool_idl_cache ) + itmp.kstr.bv_len );
821 ic->kstr.bv_len = itmp.kstr.bv_len;
822 ic->kstr.bv_val = (char *)(ic+1);
823 AC_MEMCPY( ic->kstr.bv_val, itmp.kstr.bv_val, ic->kstr.bv_len );
824 ic->head = ic->tail = NULL;
827 avl_insert( &root, ic, bdb_tool_idl_cmp, avl_dup_error );
828 db->app_private = root;
830 /* load existing key count here */
831 rc = db->cursor( db, NULL, &curs, 0 );
834 data.ulen = sizeof( ID );
835 data.flags = DB_DBT_USERMEM;
837 rc = curs->c_get( curs, key, &data, DB_SET );
840 ic->count = BDB_IDL_DB_SIZE+1;
844 curs->c_count( curs, &count, 0 );
846 BDB_DISK2ID( &nid, &ic->first );
849 curs->c_close( curs );
851 /* are we a range already? */
852 if ( ic->count > BDB_IDL_DB_SIZE ) {
855 /* Are we at the limit, and converting to a range? */
856 } else if ( ic->count == BDB_IDL_DB_SIZE ) {
857 for (ice = ic->head; ice; ice = ice->next ) {
858 bdb->bi_idl_cache_size--;
860 ic->tail->next = bdb_tool_idl_free_list;
861 bdb_tool_idl_free_list = ic->head;
862 ic->head = ic->tail = NULL;
867 /* No free block, create that too */
868 if ( !ic->tail || ( ic->count & (IDBLOCK-1)) == 0) {
869 if ( bdb_tool_idl_free_list ) {
870 ice = bdb_tool_idl_free_list;
871 bdb_tool_idl_free_list = ice->next;
873 ice = ch_malloc( sizeof( bdb_tool_idl_cache_entry ));
875 memset( ice, 0, sizeof( *ice ));
879 ic->tail->next = ice;
882 bdb->bi_idl_cache_size++;
887 ice->ids[ ic->count & (IDBLOCK-1) ] = id;
891 /* Check for flush */
892 if ( bdb->bi_idl_cache_size > bdb->bi_idl_cache_max_size ) {
893 rc = bdb_tool_idl_flush( be );