Home | History | Annotate | Line # | Download | only in back-mdb
tools.c revision 1.2
      1 /*	$NetBSD: tools.c,v 1.2 2020/08/11 13:15:40 christos Exp $	*/
      2 
      3 /* tools.c - tools for slap tools */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2011-2020 The OpenLDAP Foundation.
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted only as authorized by the OpenLDAP
     12  * Public License.
     13  *
     14  * A copy of this license is available in the file LICENSE in the
     15  * top-level directory of the distribution or, alternatively, at
     16  * <http://www.OpenLDAP.org/license.html>.
     17  */
     18 
     19 #include <sys/cdefs.h>
     20 __RCSID("$NetBSD: tools.c,v 1.2 2020/08/11 13:15:40 christos Exp $");
     21 
     22 #include "portable.h"
     23 
     24 #include <stdio.h>
     25 #include <ac/string.h>
     26 #include <ac/errno.h>
     27 
     28 #define AVL_INTERNAL
     29 #include "back-mdb.h"
     30 #include "idl.h"
     31 
     32 #ifdef MDB_TOOL_IDL_CACHING
     33 static int mdb_tool_idl_flush( BackendDB *be, MDB_txn *txn );
     34 
     35 #define	IDBLOCK	1024
     36 
     37 typedef struct mdb_tool_idl_cache_entry {
     38 	struct mdb_tool_idl_cache_entry *next;
     39 	ID ids[IDBLOCK];
     40 } mdb_tool_idl_cache_entry;
     41 
     42 typedef struct mdb_tool_idl_cache {
     43 	struct berval kstr;
     44 	mdb_tool_idl_cache_entry *head, *tail;
     45 	ID first, last;
     46 	int count;
     47 	short offset;
     48 	short flags;
     49 } mdb_tool_idl_cache;
     50 #define WAS_FOUND	0x01
     51 #define WAS_RANGE	0x02
     52 
     53 #define MDB_TOOL_IDL_FLUSH(be, txn)	mdb_tool_idl_flush(be, txn)
     54 #else
     55 #define MDB_TOOL_IDL_FLUSH(be, txn)
     56 #endif /* MDB_TOOL_IDL_CACHING */
     57 
     58 MDB_txn *mdb_tool_txn = NULL;
     59 
     60 static MDB_txn *txi = NULL;
     61 static MDB_cursor *cursor = NULL, *idcursor = NULL;
     62 static MDB_cursor *mcp = NULL, *mcd = NULL;
     63 static MDB_val key, data;
     64 static ID previd = NOID;
     65 
     66 typedef struct dn_id {
     67 	ID id;
     68 	struct berval dn;
     69 } dn_id;
     70 
     71 #define	HOLE_SIZE	4096
     72 static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
     73 static unsigned nhmax = HOLE_SIZE;
     74 static unsigned nholes;
     75 
     76 static struct berval	*tool_base;
     77 static int		tool_scope;
     78 static Filter		*tool_filter;
     79 static Entry		*tool_next_entry;
     80 
     81 static ID mdb_tool_ix_id;
     82 static Operation *mdb_tool_ix_op;
     83 static MDB_txn *mdb_tool_ix_txn;
     84 static int mdb_tool_index_tcount, mdb_tool_threads;
     85 static IndexRec *mdb_tool_index_rec;
     86 static struct mdb_info *mdb_tool_info;
     87 static ldap_pvt_thread_mutex_t mdb_tool_index_mutex;
     88 static ldap_pvt_thread_cond_t mdb_tool_index_cond_main;
     89 static ldap_pvt_thread_cond_t mdb_tool_index_cond_work;
     90 static void * mdb_tool_index_task( void *ctx, void *ptr );
     91 
     92 static int	mdb_writes, mdb_writes_per_commit;
     93 
     94 /* Number of ops per commit in Quick mode.
     95  * Batching speeds writes overall, but too large a
     96  * batch will fail with MDB_TXN_FULL.
     97  */
     98 #ifndef MDB_WRITES_PER_COMMIT
     99 #define MDB_WRITES_PER_COMMIT	500
    100 #endif
    101 
    102 static int
    103 mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep );
    104 
    105 int mdb_tool_entry_open(
    106 	BackendDB *be, int mode )
    107 {
    108 	/* In Quick mode, commit once per 500 entries */
    109 	mdb_writes = 0;
    110 	if ( slapMode & SLAP_TOOL_QUICK )
    111 		mdb_writes_per_commit = MDB_WRITES_PER_COMMIT;
    112 	else
    113 		mdb_writes_per_commit = 1;
    114 
    115 	/* Set up for threaded slapindex */
    116 	if (( slapMode & (SLAP_TOOL_QUICK|SLAP_TOOL_READONLY)) == SLAP_TOOL_QUICK ) {
    117 		if ( !mdb_tool_info ) {
    118 			struct mdb_info *mdb = (struct mdb_info *) be->be_private;
    119 			ldap_pvt_thread_mutex_init( &mdb_tool_index_mutex );
    120 			ldap_pvt_thread_cond_init( &mdb_tool_index_cond_main );
    121 			ldap_pvt_thread_cond_init( &mdb_tool_index_cond_work );
    122 			if ( mdb->mi_nattrs ) {
    123 				int i;
    124 #if 0			/* threaded indexing has no performance advantage */
    125 				mdb_tool_threads = slap_tool_thread_max - 1;
    126 #endif
    127 				if ( mdb_tool_threads > 1 ) {
    128 					mdb_tool_index_rec = ch_calloc( mdb->mi_nattrs, sizeof( IndexRec ));
    129 					mdb_tool_index_tcount = mdb_tool_threads - 1;
    130 					for (i=1; i<mdb_tool_threads; i++) {
    131 						int *ptr = ch_malloc( sizeof( int ));
    132 						*ptr = i;
    133 						ldap_pvt_thread_pool_submit( &connection_pool,
    134 							mdb_tool_index_task, ptr );
    135 					}
    136 					mdb_tool_info = mdb;
    137 				}
    138 			}
    139 		}
    140 	}
    141 
    142 	return 0;
    143 }
    144 
    145 int mdb_tool_entry_close(
    146 	BackendDB *be )
    147 {
    148 	if ( mdb_tool_info ) {
    149 		slapd_shutdown = 1;
    150 		ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
    151 
    152 		/* There might still be some threads starting */
    153 		while ( mdb_tool_index_tcount > 0 ) {
    154 			ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
    155 					&mdb_tool_index_mutex );
    156 		}
    157 
    158 		mdb_tool_index_tcount = mdb_tool_threads - 1;
    159 		ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
    160 
    161 		/* Make sure all threads are stopped */
    162 		while ( mdb_tool_index_tcount > 0 ) {
    163 			ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
    164 				&mdb_tool_index_mutex );
    165 		}
    166 		ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
    167 
    168 		mdb_tool_info = NULL;
    169 		slapd_shutdown = 0;
    170 		ch_free( mdb_tool_index_rec );
    171 		mdb_tool_index_tcount = mdb_tool_threads - 1;
    172 	}
    173 
    174 	if( idcursor ) {
    175 		mdb_cursor_close( idcursor );
    176 		idcursor = NULL;
    177 	}
    178 	if( cursor ) {
    179 		mdb_cursor_close( cursor );
    180 		cursor = NULL;
    181 	}
    182 	{
    183 		struct mdb_info *mdb = be->be_private;
    184 		if ( mdb ) {
    185 			int i;
    186 			for (i=0; i<mdb->mi_nattrs; i++)
    187 				mdb->mi_attrs[i]->ai_cursor = NULL;
    188 		}
    189 	}
    190 	if( mdb_tool_txn ) {
    191 		int rc;
    192 		MDB_TOOL_IDL_FLUSH( be, mdb_tool_txn );
    193 		if (( rc = mdb_txn_commit( mdb_tool_txn ))) {
    194 			Debug( LDAP_DEBUG_ANY,
    195 				LDAP_XSTRING(mdb_tool_entry_close) ": database %s: "
    196 				"txn_commit failed: %s (%d)\n",
    197 				be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    198 			return -1;
    199 		}
    200 		mdb_tool_txn = NULL;
    201 	}
    202 	if( txi ) {
    203 		int rc;
    204 		if (( rc = mdb_txn_commit( txi ))) {
    205 			Debug( LDAP_DEBUG_ANY,
    206 				LDAP_XSTRING(mdb_tool_entry_close) ": database %s: "
    207 				"txn_commit failed: %s (%d)\n",
    208 				be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    209 			return -1;
    210 		}
    211 		txi = NULL;
    212 	}
    213 
    214 	if( nholes ) {
    215 		unsigned i;
    216 		fprintf( stderr, "Error, entries missing!\n");
    217 		for (i=0; i<nholes; i++) {
    218 			fprintf(stderr, "  entry %ld: %s\n",
    219 				holes[i].id, holes[i].dn.bv_val);
    220 		}
    221 		nholes = 0;
    222 		return -1;
    223 	}
    224 
    225 	return 0;
    226 }
    227 
    228 ID
    229 mdb_tool_entry_first_x(
    230 	BackendDB *be,
    231 	struct berval *base,
    232 	int scope,
    233 	Filter *f )
    234 {
    235 	tool_base = base;
    236 	tool_scope = scope;
    237 	tool_filter = f;
    238 
    239 	return mdb_tool_entry_next( be );
    240 }
    241 
    242 ID mdb_tool_entry_next(
    243 	BackendDB *be )
    244 {
    245 	int rc;
    246 	ID id;
    247 	struct mdb_info *mdb;
    248 
    249 	assert( be != NULL );
    250 	assert( slapMode & SLAP_TOOL_MODE );
    251 
    252 	mdb = (struct mdb_info *) be->be_private;
    253 	assert( mdb != NULL );
    254 
    255 	if ( !mdb_tool_txn ) {
    256 		rc = mdb_txn_begin( mdb->mi_dbenv, NULL, MDB_RDONLY, &mdb_tool_txn );
    257 		if ( rc )
    258 			return NOID;
    259 		rc = mdb_cursor_open( mdb_tool_txn, mdb->mi_id2entry, &cursor );
    260 		if ( rc ) {
    261 			mdb_txn_abort( mdb_tool_txn );
    262 			return NOID;
    263 		}
    264 	}
    265 
    266 next:;
    267 	rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT );
    268 
    269 	if( rc ) {
    270 		return NOID;
    271 	}
    272 
    273 	previd = *(ID *)key.mv_data;
    274 	id = previd;
    275 
    276 	if ( !data.mv_size )
    277 		goto next;
    278 
    279 	if ( tool_filter || tool_base ) {
    280 		static Operation op = {0};
    281 		static Opheader ohdr = {0};
    282 
    283 		op.o_hdr = &ohdr;
    284 		op.o_bd = be;
    285 		op.o_tmpmemctx = NULL;
    286 		op.o_tmpmfuncs = &ch_mfuncs;
    287 
    288 		if ( tool_next_entry ) {
    289 			mdb_entry_release( &op, tool_next_entry, 0 );
    290 			tool_next_entry = NULL;
    291 		}
    292 
    293 		rc = mdb_tool_entry_get_int( be, id, &tool_next_entry );
    294 		if ( rc == LDAP_NO_SUCH_OBJECT ) {
    295 			goto next;
    296 		}
    297 
    298 		assert( tool_next_entry != NULL );
    299 
    300 		if ( tool_filter && test_filter( NULL, tool_next_entry, tool_filter ) != LDAP_COMPARE_TRUE )
    301 		{
    302 			mdb_entry_release( &op, tool_next_entry, 0 );
    303 			tool_next_entry = NULL;
    304 			goto next;
    305 		}
    306 	}
    307 
    308 	return id;
    309 }
    310 
    311 ID mdb_tool_dn2id_get(
    312 	Backend *be,
    313 	struct berval *dn
    314 )
    315 {
    316 	struct mdb_info *mdb;
    317 	Operation op = {0};
    318 	Opheader ohdr = {0};
    319 	ID id;
    320 	int rc;
    321 
    322 	if ( BER_BVISEMPTY(dn) )
    323 		return 0;
    324 
    325 	mdb = (struct mdb_info *) be->be_private;
    326 
    327 	if ( !mdb_tool_txn ) {
    328 		rc = mdb_txn_begin( mdb->mi_dbenv, NULL, (slapMode & SLAP_TOOL_READONLY) != 0 ?
    329 			MDB_RDONLY : 0, &mdb_tool_txn );
    330 		if ( rc )
    331 			return NOID;
    332 	}
    333 
    334 	op.o_hdr = &ohdr;
    335 	op.o_bd = be;
    336 	op.o_tmpmemctx = NULL;
    337 	op.o_tmpmfuncs = &ch_mfuncs;
    338 
    339 	rc = mdb_dn2id( &op, mdb_tool_txn, NULL, dn, &id, NULL, NULL, NULL );
    340 	if ( rc == MDB_NOTFOUND )
    341 		return NOID;
    342 
    343 	return id;
    344 }
    345 
    346 static int
    347 mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep )
    348 {
    349 	Operation op = {0};
    350 	Opheader ohdr = {0};
    351 
    352 	Entry *e = NULL;
    353 	struct berval dn = BER_BVNULL, ndn = BER_BVNULL;
    354 	int rc;
    355 
    356 	assert( be != NULL );
    357 	assert( slapMode & SLAP_TOOL_MODE );
    358 
    359 	if ( ( tool_filter || tool_base ) && id == previd && tool_next_entry != NULL ) {
    360 		*ep = tool_next_entry;
    361 		tool_next_entry = NULL;
    362 		return LDAP_SUCCESS;
    363 	}
    364 
    365 	if ( id != previd ) {
    366 		key.mv_size = sizeof(ID);
    367 		key.mv_data = &id;
    368 		rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
    369 		if ( rc ) {
    370 			rc = LDAP_OTHER;
    371 			goto done;
    372 		}
    373 	}
    374 	if ( !data.mv_size ) {
    375 		rc = LDAP_NO_SUCH_OBJECT;
    376 		goto done;
    377 	}
    378 
    379 	op.o_hdr = &ohdr;
    380 	op.o_bd = be;
    381 	op.o_tmpmemctx = NULL;
    382 	op.o_tmpmfuncs = &ch_mfuncs;
    383 	if ( slapMode & SLAP_TOOL_READONLY ) {
    384 		rc = mdb_id2name( &op, mdb_tool_txn, &idcursor, id, &dn, &ndn );
    385 		if ( rc  ) {
    386 			rc = LDAP_OTHER;
    387 			goto done;
    388 		}
    389 		if ( tool_base != NULL ) {
    390 			if ( !dnIsSuffixScope( &ndn, tool_base, tool_scope ) ) {
    391 				ch_free( dn.bv_val );
    392 				ch_free( ndn.bv_val );
    393 				rc = LDAP_NO_SUCH_OBJECT;
    394 				goto done;
    395 			}
    396 		}
    397 	}
    398 	rc = mdb_entry_decode( &op, mdb_tool_txn, &data, &e );
    399 	e->e_id = id;
    400 	if ( !BER_BVISNULL( &dn )) {
    401 		e->e_name = dn;
    402 		e->e_nname = ndn;
    403 	} else {
    404 		e->e_name.bv_val = NULL;
    405 		e->e_nname.bv_val = NULL;
    406 	}
    407 
    408 done:
    409 	if ( e != NULL ) {
    410 		*ep = e;
    411 	}
    412 
    413 	return rc;
    414 }
    415 
    416 Entry*
    417 mdb_tool_entry_get( BackendDB *be, ID id )
    418 {
    419 	Entry *e = NULL;
    420 	int rc;
    421 
    422 	if ( !mdb_tool_txn ) {
    423 		struct mdb_info *mdb = (struct mdb_info *) be->be_private;
    424 		rc = mdb_txn_begin( mdb->mi_dbenv, NULL,
    425 			(slapMode & SLAP_TOOL_READONLY) ? MDB_RDONLY : 0, &mdb_tool_txn );
    426 		if ( rc )
    427 			return NULL;
    428 	}
    429 	if ( !cursor ) {
    430 		struct mdb_info *mdb = (struct mdb_info *) be->be_private;
    431 		rc = mdb_cursor_open( mdb_tool_txn, mdb->mi_id2entry, &cursor );
    432 		if ( rc ) {
    433 			mdb_txn_abort( mdb_tool_txn );
    434 			mdb_tool_txn = NULL;
    435 			return NULL;
    436 		}
    437 	}
    438 	(void)mdb_tool_entry_get_int( be, id, &e );
    439 	return e;
    440 }
    441 
    442 static int mdb_tool_next_id(
    443 	Operation *op,
    444 	MDB_txn *tid,
    445 	Entry *e,
    446 	struct berval *text,
    447 	int hole )
    448 {
    449 	struct berval dn = e->e_name;
    450 	struct berval ndn = e->e_nname;
    451 	struct berval pdn, npdn, nmatched;
    452 	ID id, pid = 0;
    453 	int rc;
    454 
    455 	if (ndn.bv_len == 0) {
    456 		e->e_id = 0;
    457 		return 0;
    458 	}
    459 
    460 	rc = mdb_dn2id( op, tid, mcp, &ndn, &id, NULL, NULL, &nmatched );
    461 	if ( rc == MDB_NOTFOUND ) {
    462 		if ( !be_issuffix( op->o_bd, &ndn ) ) {
    463 			ID eid = e->e_id;
    464 			dnParent( &ndn, &npdn );
    465 			if ( nmatched.bv_len != npdn.bv_len ) {
    466 				dnParent( &dn, &pdn );
    467 				e->e_name = pdn;
    468 				e->e_nname = npdn;
    469 				rc = mdb_tool_next_id( op, tid, e, text, 1 );
    470 				e->e_name = dn;
    471 				e->e_nname = ndn;
    472 				if ( rc ) {
    473 					return rc;
    474 				}
    475 				/* If parent didn't exist, it was created just now
    476 				 * and its ID is now in e->e_id. Make sure the current
    477 				 * entry gets added under the new parent ID.
    478 				 */
    479 				if ( eid != e->e_id ) {
    480 					pid = e->e_id;
    481 				}
    482 			} else {
    483 				pid = id;
    484 			}
    485 		}
    486 		rc = mdb_next_id( op->o_bd, idcursor, &e->e_id );
    487 		if ( rc ) {
    488 			snprintf( text->bv_val, text->bv_len,
    489 				"next_id failed: %s (%d)",
    490 				mdb_strerror(rc), rc );
    491 		Debug( LDAP_DEBUG_ANY,
    492 			"=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
    493 			return rc;
    494 		}
    495 		rc = mdb_dn2id_add( op, mcp, mcd, pid, 1, 1, e );
    496 		if ( rc ) {
    497 			snprintf( text->bv_val, text->bv_len,
    498 				"dn2id_add failed: %s (%d)",
    499 				mdb_strerror(rc), rc );
    500 			Debug( LDAP_DEBUG_ANY,
    501 				"=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
    502 		} else if ( hole ) {
    503 			MDB_val key, data;
    504 			if ( nholes == nhmax - 1 ) {
    505 				if ( holes == hbuf ) {
    506 					holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
    507 					AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
    508 				} else {
    509 					holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
    510 				}
    511 				nhmax *= 2;
    512 			}
    513 			ber_dupbv( &holes[nholes].dn, &ndn );
    514 			holes[nholes++].id = e->e_id;
    515 			key.mv_size = sizeof(ID);
    516 			key.mv_data = &e->e_id;
    517 			data.mv_size = 0;
    518 			data.mv_data = NULL;
    519 			rc = mdb_cursor_put( idcursor, &key, &data, MDB_NOOVERWRITE );
    520 			if ( rc == MDB_KEYEXIST )
    521 				rc = 0;
    522 			if ( rc ) {
    523 				snprintf( text->bv_val, text->bv_len,
    524 					"dummy id2entry add failed: %s (%d)",
    525 					mdb_strerror(rc), rc );
    526 				Debug( LDAP_DEBUG_ANY,
    527 					"=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
    528 			}
    529 		}
    530 	} else if ( !hole ) {
    531 		unsigned i, j;
    532 
    533 		e->e_id = id;
    534 
    535 		for ( i=0; i<nholes; i++) {
    536 			if ( holes[i].id == e->e_id ) {
    537 				free(holes[i].dn.bv_val);
    538 				for (j=i;j<nholes;j++) holes[j] = holes[j+1];
    539 				holes[j].id = 0;
    540 				nholes--;
    541 				break;
    542 			} else if ( holes[i].id > e->e_id ) {
    543 				break;
    544 			}
    545 		}
    546 	}
    547 	return rc;
    548 }
    549 
    550 static int
    551 mdb_tool_index_add(
    552 	Operation *op,
    553 	MDB_txn *txn,
    554 	Entry *e )
    555 {
    556 	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
    557 
    558 	if ( !mdb->mi_nattrs )
    559 		return 0;
    560 
    561 	if ( mdb_tool_threads > 1 ) {
    562 		IndexRec *ir;
    563 		int i, rc;
    564 		Attribute *a;
    565 
    566 		ir = mdb_tool_index_rec;
    567 		for (i=0; i<mdb->mi_nattrs; i++)
    568 			ir[i].ir_attrs = NULL;
    569 
    570 		for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
    571 			rc = mdb_index_recset( mdb, a, a->a_desc->ad_type,
    572 				&a->a_desc->ad_tags, ir );
    573 			if ( rc )
    574 				return rc;
    575 		}
    576 		for (i=0; i<mdb->mi_nattrs; i++) {
    577 			if ( !ir[i].ir_ai )
    578 				break;
    579 			rc = mdb_cursor_open( txn, ir[i].ir_ai->ai_dbi,
    580 				 &ir[i].ir_ai->ai_cursor );
    581 			if ( rc )
    582 				return rc;
    583 		}
    584 		mdb_tool_ix_id = e->e_id;
    585 		mdb_tool_ix_op = op;
    586 		mdb_tool_ix_txn = txn;
    587 		ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
    588 		/* Wait for all threads to be ready */
    589 		while ( mdb_tool_index_tcount ) {
    590 			ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
    591 				&mdb_tool_index_mutex );
    592 		}
    593 
    594 		for ( i=1; i<mdb_tool_threads; i++ )
    595 			mdb_tool_index_rec[i].ir_i = LDAP_BUSY;
    596 		mdb_tool_index_tcount = mdb_tool_threads - 1;
    597 		ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
    598 		ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
    599 
    600 		rc = mdb_index_recrun( op, txn, mdb, ir, e->e_id, 0 );
    601 		if ( rc )
    602 			return rc;
    603 		ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
    604 		for ( i=1; i<mdb_tool_threads; i++ ) {
    605 			if ( mdb_tool_index_rec[i].ir_i == LDAP_BUSY ) {
    606 				ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
    607 					&mdb_tool_index_mutex );
    608 				i--;
    609 				continue;
    610 			}
    611 			if ( mdb_tool_index_rec[i].ir_i ) {
    612 				rc = mdb_tool_index_rec[i].ir_i;
    613 				break;
    614 			}
    615 		}
    616 		ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
    617 		return rc;
    618 	} else
    619 	{
    620 		return mdb_index_entry_add( op, txn, e );
    621 	}
    622 }
    623 
    624 ID mdb_tool_entry_put(
    625 	BackendDB *be,
    626 	Entry *e,
    627 	struct berval *text )
    628 {
    629 	int rc;
    630 	struct mdb_info *mdb;
    631 	Operation op = {0};
    632 	Opheader ohdr = {0};
    633 
    634 	assert( be != NULL );
    635 	assert( slapMode & SLAP_TOOL_MODE );
    636 
    637 	assert( text != NULL );
    638 	assert( text->bv_val != NULL );
    639 	assert( text->bv_val[0] == '\0' );	/* overconservative? */
    640 
    641 	Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_tool_entry_put)
    642 		"( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
    643 
    644 	mdb = (struct mdb_info *) be->be_private;
    645 
    646 	if ( !mdb_tool_txn ) {
    647 		rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &mdb_tool_txn );
    648 		if( rc != 0 ) {
    649 			snprintf( text->bv_val, text->bv_len,
    650 				"txn_begin failed: %s (%d)",
    651 				mdb_strerror(rc), rc );
    652 			Debug( LDAP_DEBUG_ANY,
    653 				"=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
    654 				 text->bv_val, 0, 0 );
    655 			return NOID;
    656 		}
    657 		rc = mdb_cursor_open( mdb_tool_txn, mdb->mi_id2entry, &idcursor );
    658 		if( rc != 0 ) {
    659 			snprintf( text->bv_val, text->bv_len,
    660 				"cursor_open failed: %s (%d)",
    661 				mdb_strerror(rc), rc );
    662 			Debug( LDAP_DEBUG_ANY,
    663 				"=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
    664 				 text->bv_val, 0, 0 );
    665 			return NOID;
    666 		}
    667 		if ( !mdb->mi_nextid ) {
    668 			ID dummy;
    669 			mdb_next_id( be, idcursor, &dummy );
    670 		}
    671 		rc = mdb_cursor_open( mdb_tool_txn, mdb->mi_dn2id, &mcp );
    672 		if( rc != 0 ) {
    673 			snprintf( text->bv_val, text->bv_len,
    674 				"cursor_open failed: %s (%d)",
    675 				mdb_strerror(rc), rc );
    676 			Debug( LDAP_DEBUG_ANY,
    677 				"=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
    678 				 text->bv_val, 0, 0 );
    679 			return NOID;
    680 		}
    681 		rc = mdb_cursor_open( mdb_tool_txn, mdb->mi_dn2id, &mcd );
    682 		if( rc != 0 ) {
    683 			snprintf( text->bv_val, text->bv_len,
    684 				"cursor_open failed: %s (%d)",
    685 				mdb_strerror(rc), rc );
    686 			Debug( LDAP_DEBUG_ANY,
    687 				"=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
    688 				 text->bv_val, 0, 0 );
    689 			return NOID;
    690 		}
    691 	}
    692 
    693 	op.o_hdr = &ohdr;
    694 	op.o_bd = be;
    695 	op.o_tmpmemctx = NULL;
    696 	op.o_tmpmfuncs = &ch_mfuncs;
    697 
    698 	/* add dn2id indices */
    699 	rc = mdb_tool_next_id( &op, mdb_tool_txn, e, text, 0 );
    700 	if( rc != 0 ) {
    701 		goto done;
    702 	}
    703 
    704 	rc = mdb_tool_index_add( &op, mdb_tool_txn, e );
    705 	if( rc != 0 ) {
    706 		snprintf( text->bv_val, text->bv_len,
    707 				"index_entry_add failed: err=%d", rc );
    708 		Debug( LDAP_DEBUG_ANY,
    709 			"=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
    710 			text->bv_val, 0, 0 );
    711 		goto done;
    712 	}
    713 
    714 
    715 	/* id2entry index */
    716 	rc = mdb_id2entry_add( &op, mdb_tool_txn, idcursor, e );
    717 	if( rc != 0 ) {
    718 		snprintf( text->bv_val, text->bv_len,
    719 				"id2entry_add failed: err=%d", rc );
    720 		Debug( LDAP_DEBUG_ANY,
    721 			"=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
    722 			text->bv_val, 0, 0 );
    723 		goto done;
    724 	}
    725 
    726 done:
    727 	if( rc == 0 ) {
    728 		mdb_writes++;
    729 		if ( mdb_writes >= mdb_writes_per_commit ) {
    730 			unsigned i;
    731 			MDB_TOOL_IDL_FLUSH( be, mdb_tool_txn );
    732 			rc = mdb_txn_commit( mdb_tool_txn );
    733 			for ( i=0; i<mdb->mi_nattrs; i++ )
    734 				mdb->mi_attrs[i]->ai_cursor = NULL;
    735 			mdb_writes = 0;
    736 			mdb_tool_txn = NULL;
    737 			idcursor = NULL;
    738 			if( rc != 0 ) {
    739 				mdb->mi_numads = 0;
    740 				snprintf( text->bv_val, text->bv_len,
    741 						"txn_commit failed: %s (%d)",
    742 						mdb_strerror(rc), rc );
    743 				Debug( LDAP_DEBUG_ANY,
    744 					"=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
    745 					text->bv_val, 0, 0 );
    746 				e->e_id = NOID;
    747 			}
    748 		}
    749 
    750 	} else {
    751 		unsigned i;
    752 		mdb_txn_abort( mdb_tool_txn );
    753 		mdb_tool_txn = NULL;
    754 		idcursor = NULL;
    755 		for ( i=0; i<mdb->mi_nattrs; i++ )
    756 			mdb->mi_attrs[i]->ai_cursor = NULL;
    757 		mdb_writes = 0;
    758 		snprintf( text->bv_val, text->bv_len,
    759 			"txn_aborted! %s (%d)",
    760 			rc == LDAP_OTHER ? "Internal error" :
    761 			mdb_strerror(rc), rc );
    762 		Debug( LDAP_DEBUG_ANY,
    763 			"=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
    764 			text->bv_val, 0, 0 );
    765 		e->e_id = NOID;
    766 	}
    767 
    768 	return e->e_id;
    769 }
    770 
    771 static int mdb_dn2id_upgrade( BackendDB *be );
    772 
    773 int mdb_tool_entry_reindex(
    774 	BackendDB *be,
    775 	ID id,
    776 	AttributeDescription **adv )
    777 {
    778 	struct mdb_info *mi = (struct mdb_info *) be->be_private;
    779 	int rc;
    780 	Entry *e;
    781 	Operation op = {0};
    782 	Opheader ohdr = {0};
    783 
    784 	Debug( LDAP_DEBUG_ARGS,
    785 		"=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld )\n",
    786 		(long) id, 0, 0 );
    787 	assert( tool_base == NULL );
    788 	assert( tool_filter == NULL );
    789 
    790 	/* Special: do a dn2id upgrade */
    791 	if ( adv && adv[0] == slap_schema.si_ad_entryDN ) {
    792 		/* short-circuit tool_entry_next() */
    793 		mdb_cursor_get( cursor, &key, &data, MDB_LAST );
    794 		return mdb_dn2id_upgrade( be );
    795 	}
    796 
    797 	/* No indexes configured, nothing to do. Could return an
    798 	 * error here to shortcut things.
    799 	 */
    800 	if (!mi->mi_attrs) {
    801 		return 0;
    802 	}
    803 
    804 	/* Check for explicit list of attrs to index */
    805 	if ( adv ) {
    806 		int i, j, n;
    807 
    808 		if ( mi->mi_attrs[0]->ai_desc != adv[0] ) {
    809 			/* count */
    810 			for ( n = 0; adv[n]; n++ ) ;
    811 
    812 			/* insertion sort */
    813 			for ( i = 0; i < n; i++ ) {
    814 				AttributeDescription *ad = adv[i];
    815 				for ( j = i-1; j>=0; j--) {
    816 					if ( SLAP_PTRCMP( adv[j], ad ) <= 0 ) break;
    817 					adv[j+1] = adv[j];
    818 				}
    819 				adv[j+1] = ad;
    820 			}
    821 		}
    822 
    823 		for ( i = 0; adv[i]; i++ ) {
    824 			if ( mi->mi_attrs[i]->ai_desc != adv[i] ) {
    825 				for ( j = i+1; j < mi->mi_nattrs; j++ ) {
    826 					if ( mi->mi_attrs[j]->ai_desc == adv[i] ) {
    827 						AttrInfo *ai = mi->mi_attrs[i];
    828 						mi->mi_attrs[i] = mi->mi_attrs[j];
    829 						mi->mi_attrs[j] = ai;
    830 						break;
    831 					}
    832 				}
    833 				if ( j == mi->mi_nattrs ) {
    834 					Debug( LDAP_DEBUG_ANY,
    835 						LDAP_XSTRING(mdb_tool_entry_reindex)
    836 						": no index configured for %s\n",
    837 						adv[i]->ad_cname.bv_val, 0, 0 );
    838 					return -1;
    839 				}
    840 			}
    841 		}
    842 		mi->mi_nattrs = i;
    843 	}
    844 
    845 	e = mdb_tool_entry_get( be, id );
    846 
    847 	if( e == NULL ) {
    848 		Debug( LDAP_DEBUG_ANY,
    849 			LDAP_XSTRING(mdb_tool_entry_reindex)
    850 			": could not locate id=%ld\n",
    851 			(long) id, 0, 0 );
    852 		return -1;
    853 	}
    854 
    855 	if ( !txi ) {
    856 		rc = mdb_txn_begin( mi->mi_dbenv, NULL, 0, &txi );
    857 		if( rc != 0 ) {
    858 			Debug( LDAP_DEBUG_ANY,
    859 				"=> " LDAP_XSTRING(mdb_tool_entry_reindex) ": "
    860 				"txn_begin failed: %s (%d)\n",
    861 				mdb_strerror(rc), rc, 0 );
    862 			goto done;
    863 		}
    864 	}
    865 
    866 	if ( slapMode & SLAP_TRUNCATE_MODE ) {
    867 		int i;
    868 		for ( i=0; i < mi->mi_nattrs; i++ ) {
    869 			rc = mdb_drop( txi, mi->mi_attrs[i]->ai_dbi, 0 );
    870 			if ( rc ) {
    871 				Debug( LDAP_DEBUG_ANY,
    872 					LDAP_XSTRING(mdb_tool_entry_reindex)
    873 					": (Truncate) mdb_drop(%s) failed: %s (%d)\n",
    874 					mi->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
    875 					mdb_strerror(rc), rc );
    876 				return -1;
    877 			}
    878 		}
    879 		slapMode ^= SLAP_TRUNCATE_MODE;
    880 	}
    881 
    882 	/*
    883 	 * just (re)add them for now
    884 	 * Use truncate mode to empty/reset index databases
    885 	 */
    886 
    887 	Debug( LDAP_DEBUG_TRACE,
    888 		"=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld )\n",
    889 		(long) id, 0, 0 );
    890 
    891 	op.o_hdr = &ohdr;
    892 	op.o_bd = be;
    893 	op.o_tmpmemctx = NULL;
    894 	op.o_tmpmfuncs = &ch_mfuncs;
    895 
    896 	rc = mdb_tool_index_add( &op, txi, e );
    897 
    898 done:
    899 	if( rc == 0 ) {
    900 		mdb_writes++;
    901 		if ( mdb_writes >= mdb_writes_per_commit ) {
    902 			MDB_val key;
    903 			unsigned i;
    904 			MDB_TOOL_IDL_FLUSH( be, txi );
    905 			rc = mdb_txn_commit( txi );
    906 			mdb_writes = 0;
    907 			for ( i=0; i<mi->mi_nattrs; i++ )
    908 				mi->mi_attrs[i]->ai_cursor = NULL;
    909 			if( rc != 0 ) {
    910 				Debug( LDAP_DEBUG_ANY,
    911 					"=> " LDAP_XSTRING(mdb_tool_entry_reindex)
    912 					": txn_commit failed: %s (%d)\n",
    913 					mdb_strerror(rc), rc, 0 );
    914 				e->e_id = NOID;
    915 			}
    916 			mdb_cursor_close( cursor );
    917 			txi = NULL;
    918 			/* Must close the read txn to allow old pages to be reclaimed. */
    919 			mdb_txn_abort( mdb_tool_txn );
    920 			/* and then reopen it so that tool_entry_next still works. */
    921 			mdb_txn_begin( mi->mi_dbenv, NULL, MDB_RDONLY, &mdb_tool_txn );
    922 			mdb_cursor_open( mdb_tool_txn, mi->mi_id2entry, &cursor );
    923 			key.mv_data = &id;
    924 			key.mv_size = sizeof(ID);
    925 			mdb_cursor_get( cursor, &key, NULL, MDB_SET );
    926 		}
    927 
    928 	} else {
    929 		unsigned i;
    930 		mdb_writes = 0;
    931 		mdb_cursor_close( cursor );
    932 		cursor = NULL;
    933 		mdb_txn_abort( txi );
    934 		for ( i=0; i<mi->mi_nattrs; i++ )
    935 			mi->mi_attrs[i]->ai_cursor = NULL;
    936 		Debug( LDAP_DEBUG_ANY,
    937 			"=> " LDAP_XSTRING(mdb_tool_entry_reindex)
    938 			": txn_aborted! err=%d\n",
    939 			rc, 0, 0 );
    940 		e->e_id = NOID;
    941 		txi = NULL;
    942 	}
    943 	mdb_entry_release( &op, e, 0 );
    944 
    945 	return rc;
    946 }
    947 
    948 ID mdb_tool_entry_modify(
    949 	BackendDB *be,
    950 	Entry *e,
    951 	struct berval *text )
    952 {
    953 	int rc;
    954 	struct mdb_info *mdb;
    955 	Operation op = {0};
    956 	Opheader ohdr = {0};
    957 
    958 	assert( be != NULL );
    959 	assert( slapMode & SLAP_TOOL_MODE );
    960 
    961 	assert( text != NULL );
    962 	assert( text->bv_val != NULL );
    963 	assert( text->bv_val[0] == '\0' );	/* overconservative? */
    964 
    965 	assert ( e->e_id != NOID );
    966 
    967 	Debug( LDAP_DEBUG_TRACE,
    968 		"=> " LDAP_XSTRING(mdb_tool_entry_modify) "( %ld, \"%s\" )\n",
    969 		(long) e->e_id, e->e_dn, 0 );
    970 
    971 	mdb = (struct mdb_info *) be->be_private;
    972 
    973 	if( cursor ) {
    974 		mdb_cursor_close( cursor );
    975 		cursor = NULL;
    976 	}
    977 	if ( !mdb_tool_txn ) {
    978 		rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &mdb_tool_txn );
    979 		if( rc != 0 ) {
    980 			snprintf( text->bv_val, text->bv_len,
    981 				"txn_begin failed: %s (%d)",
    982 				mdb_strerror(rc), rc );
    983 			Debug( LDAP_DEBUG_ANY,
    984 				"=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
    985 				 text->bv_val, 0, 0 );
    986 			return NOID;
    987 		}
    988 	}
    989 
    990 	op.o_hdr = &ohdr;
    991 	op.o_bd = be;
    992 	op.o_tmpmemctx = NULL;
    993 	op.o_tmpmfuncs = &ch_mfuncs;
    994 
    995 	/* id2entry index */
    996 	rc = mdb_id2entry_update( &op, mdb_tool_txn, NULL, e );
    997 	if( rc != 0 ) {
    998 		snprintf( text->bv_val, text->bv_len,
    999 				"id2entry_update failed: err=%d", rc );
   1000 		Debug( LDAP_DEBUG_ANY,
   1001 			"=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
   1002 			text->bv_val, 0, 0 );
   1003 		goto done;
   1004 	}
   1005 
   1006 done:
   1007 	if( rc == 0 ) {
   1008 		rc = mdb_txn_commit( mdb_tool_txn );
   1009 		if( rc != 0 ) {
   1010 			mdb->mi_numads = 0;
   1011 			snprintf( text->bv_val, text->bv_len,
   1012 					"txn_commit failed: %s (%d)",
   1013 					mdb_strerror(rc), rc );
   1014 			Debug( LDAP_DEBUG_ANY,
   1015 				"=> " LDAP_XSTRING(mdb_tool_entry_modify) ": "
   1016 				"%s\n", text->bv_val, 0, 0 );
   1017 			e->e_id = NOID;
   1018 		}
   1019 
   1020 	} else {
   1021 		mdb_txn_abort( mdb_tool_txn );
   1022 		snprintf( text->bv_val, text->bv_len,
   1023 			"txn_aborted! %s (%d)",
   1024 			mdb_strerror(rc), rc );
   1025 		Debug( LDAP_DEBUG_ANY,
   1026 			"=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
   1027 			text->bv_val, 0, 0 );
   1028 		e->e_id = NOID;
   1029 	}
   1030 	mdb_tool_txn = NULL;
   1031 	idcursor = NULL;
   1032 
   1033 	return e->e_id;
   1034 }
   1035 
   1036 static void *
   1037 mdb_tool_index_task( void *ctx, void *ptr )
   1038 {
   1039 	int base = *(int *)ptr;
   1040 
   1041 	free( ptr );
   1042 	while ( 1 ) {
   1043 		ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
   1044 		mdb_tool_index_tcount--;
   1045 		if ( !mdb_tool_index_tcount )
   1046 			ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
   1047 		ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_work,
   1048 			&mdb_tool_index_mutex );
   1049 		if ( slapd_shutdown ) {
   1050 			mdb_tool_index_tcount--;
   1051 			if ( !mdb_tool_index_tcount )
   1052 				ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
   1053 			ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
   1054 			break;
   1055 		}
   1056 		ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
   1057 		mdb_tool_index_rec[base].ir_i = mdb_index_recrun( mdb_tool_ix_op,
   1058 			mdb_tool_ix_txn,
   1059 			mdb_tool_info, mdb_tool_index_rec, mdb_tool_ix_id, base );
   1060 	}
   1061 
   1062 	return NULL;
   1063 }
   1064 
   1065 #ifdef MDB_TOOL_IDL_CACHING
   1066 static int
   1067 mdb_tool_idl_cmp( const void *v1, const void *v2 )
   1068 {
   1069 	const mdb_tool_idl_cache *c1 = v1, *c2 = v2;
   1070 	int rc;
   1071 
   1072 	if (( rc = c1->kstr.bv_len - c2->kstr.bv_len )) return rc;
   1073 	return memcmp( c1->kstr.bv_val, c2->kstr.bv_val, c1->kstr.bv_len );
   1074 }
   1075 
   1076 static int
   1077 mdb_tool_idl_flush_one( MDB_cursor *mc, AttrInfo *ai, mdb_tool_idl_cache *ic )
   1078 {
   1079 	mdb_tool_idl_cache_entry *ice;
   1080 	MDB_val key, data[2];
   1081 	int i, rc;
   1082 	ID id, nid;
   1083 
   1084 	/* Freshly allocated, ignore it */
   1085 	if ( !ic->head && ic->count <= MDB_IDL_DB_SIZE ) {
   1086 		return 0;
   1087 	}
   1088 
   1089 	key.mv_data = ic->kstr.bv_val;
   1090 	key.mv_size = ic->kstr.bv_len;
   1091 
   1092 	if ( ic->count > MDB_IDL_DB_SIZE ) {
   1093 		while ( ic->flags & WAS_FOUND ) {
   1094 			rc = mdb_cursor_get( mc, &key, data, MDB_SET );
   1095 			if ( rc ) {
   1096 				/* FIXME: find out why this happens */
   1097 				ic->flags = 0;
   1098 				break;
   1099 			}
   1100 			if ( ic->flags & WAS_RANGE ) {
   1101 				/* Skip lo */
   1102 				rc = mdb_cursor_get( mc, &key, data, MDB_NEXT_DUP );
   1103 
   1104 				/* Get hi */
   1105 				rc = mdb_cursor_get( mc, &key, data, MDB_NEXT_DUP );
   1106 
   1107 				/* Store range hi */
   1108 				data[0].mv_data = &ic->last;
   1109 				rc = mdb_cursor_put( mc, &key, data, MDB_CURRENT );
   1110 			} else {
   1111 				/* Delete old data, replace with range */
   1112 				ic->first = *(ID *)data[0].mv_data;
   1113 				mdb_cursor_del( mc, MDB_NODUPDATA );
   1114 			}
   1115 			break;
   1116 		}
   1117 		if ( !(ic->flags & WAS_RANGE)) {
   1118 			/* range, didn't exist before */
   1119 			nid = 0;
   1120 			data[0].mv_size = sizeof(ID);
   1121 			data[0].mv_data = &nid;
   1122 			rc = mdb_cursor_put( mc, &key, data, 0 );
   1123 			if ( rc == 0 ) {
   1124 				data[0].mv_data = &ic->first;
   1125 				rc = mdb_cursor_put( mc, &key, data, 0 );
   1126 				if ( rc == 0 ) {
   1127 					data[0].mv_data = &ic->last;
   1128 					rc = mdb_cursor_put( mc, &key, data, 0 );
   1129 				}
   1130 			}
   1131 			if ( rc ) {
   1132 				rc = -1;
   1133 			}
   1134 		}
   1135 	} else {
   1136 		/* Normal write */
   1137 		int n;
   1138 
   1139 		data[0].mv_size = sizeof(ID);
   1140 		rc = 0;
   1141 		i = ic->offset;
   1142 		for ( ice = ic->head, n=0; ice; ice = ice->next, n++ ) {
   1143 			int end;
   1144 			if ( ice->next ) {
   1145 				end = IDBLOCK;
   1146 			} else {
   1147 				end = ic->count & (IDBLOCK-1);
   1148 				if ( !end )
   1149 					end = IDBLOCK;
   1150 			}
   1151 			data[1].mv_size = end - i;
   1152 			data[0].mv_data = &ice->ids[i];
   1153 			i = 0;
   1154 			rc = mdb_cursor_put( mc, &key, data, MDB_NODUPDATA|MDB_APPEND|MDB_MULTIPLE );
   1155 			if ( rc ) {
   1156 				if ( rc == MDB_KEYEXIST ) {
   1157 					rc = 0;
   1158 					continue;
   1159 				}
   1160 				rc = -1;
   1161 				break;
   1162 			}
   1163 		}
   1164 		if ( ic->head ) {
   1165 			ic->tail->next = ai->ai_flist;
   1166 			ai->ai_flist = ic->head;
   1167 		}
   1168 	}
   1169 	ic->head = ai->ai_clist;
   1170 	ai->ai_clist = ic;
   1171 	return rc;
   1172 }
   1173 
   1174 static int
   1175 mdb_tool_idl_flush_db( MDB_txn *txn, AttrInfo *ai )
   1176 {
   1177 	MDB_cursor *mc;
   1178 	Avlnode *root;
   1179 	int rc;
   1180 
   1181 	mdb_cursor_open( txn, ai->ai_dbi, &mc );
   1182 	root = tavl_end( ai->ai_root, TAVL_DIR_LEFT );
   1183 	do {
   1184 		rc = mdb_tool_idl_flush_one( mc, ai, root->avl_data );
   1185 		if ( rc != -1 )
   1186 			rc = 0;
   1187 	} while ((root = tavl_next(root, TAVL_DIR_RIGHT)));
   1188 	mdb_cursor_close( mc );
   1189 
   1190 	return rc;
   1191 }
   1192 
   1193 static int
   1194 mdb_tool_idl_flush( BackendDB *be, MDB_txn *txn )
   1195 {
   1196 	struct mdb_info *mdb = (struct mdb_info *) be->be_private;
   1197 	int rc = 0;
   1198 	unsigned int i, dbi;
   1199 
   1200 	for ( i=0; i < mdb->mi_nattrs; i++ ) {
   1201 		if ( !mdb->mi_attrs[i]->ai_root ) continue;
   1202 		rc = mdb_tool_idl_flush_db( txn, mdb->mi_attrs[i] );
   1203 		tavl_free(mdb->mi_attrs[i]->ai_root, NULL);
   1204 		mdb->mi_attrs[i]->ai_root = NULL;
   1205 		if ( rc )
   1206 			break;
   1207 	}
   1208 	return rc;
   1209 }
   1210 
   1211 int mdb_tool_idl_add(
   1212 	BackendDB *be,
   1213 	MDB_cursor *mc,
   1214 	struct berval *keys,
   1215 	ID id )
   1216 {
   1217 	MDB_dbi dbi;
   1218 	mdb_tool_idl_cache *ic, itmp;
   1219 	mdb_tool_idl_cache_entry *ice;
   1220 	int i, rc, lcount;
   1221 	AttrInfo *ai = (AttrInfo *)mc;
   1222 	mc = ai->ai_cursor;
   1223 
   1224 	dbi = ai->ai_dbi;
   1225 	for (i=0; keys[i].bv_val; i++) {
   1226 	itmp.kstr = keys[i];
   1227 	ic = tavl_find( (Avlnode *)ai->ai_root, &itmp, mdb_tool_idl_cmp );
   1228 
   1229 	/* No entry yet, create one */
   1230 	if ( !ic ) {
   1231 		MDB_val key, data;
   1232 		ID nid;
   1233 		int rc;
   1234 
   1235 		if ( ai->ai_clist ) {
   1236 			ic = ai->ai_clist;
   1237 			ai->ai_clist = ic->head;
   1238 		} else {
   1239 			ic = ch_malloc( sizeof( mdb_tool_idl_cache ) + itmp.kstr.bv_len + 4 );
   1240 		}
   1241 		ic->kstr.bv_len = itmp.kstr.bv_len;
   1242 		ic->kstr.bv_val = (char *)(ic+1);
   1243 		memcpy( ic->kstr.bv_val, itmp.kstr.bv_val, ic->kstr.bv_len );
   1244 		ic->head = ic->tail = NULL;
   1245 		ic->last = 0;
   1246 		ic->count = 0;
   1247 		ic->offset = 0;
   1248 		ic->flags = 0;
   1249 		tavl_insert( (Avlnode **)&ai->ai_root, ic, mdb_tool_idl_cmp,
   1250 			avl_dup_error );
   1251 
   1252 		/* load existing key count here */
   1253 		key.mv_size = keys[i].bv_len;
   1254 		key.mv_data = keys[i].bv_val;
   1255 		rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
   1256 		if ( rc == 0 ) {
   1257 			ic->flags |= WAS_FOUND;
   1258 			nid = *(ID *)data.mv_data;
   1259 			if ( nid == 0 ) {
   1260 				ic->count = MDB_IDL_DB_SIZE+1;
   1261 				ic->flags |= WAS_RANGE;
   1262 			} else {
   1263 				size_t count;
   1264 
   1265 				mdb_cursor_count( mc, &count );
   1266 				ic->count = count;
   1267 				ic->first = nid;
   1268 				ic->offset = count & (IDBLOCK-1);
   1269 			}
   1270 		}
   1271 	}
   1272 	/* are we a range already? */
   1273 	if ( ic->count > MDB_IDL_DB_SIZE ) {
   1274 		ic->last = id;
   1275 		continue;
   1276 	/* Are we at the limit, and converting to a range? */
   1277 	} else if ( ic->count == MDB_IDL_DB_SIZE ) {
   1278 		if ( ic->head ) {
   1279 			ic->tail->next = ai->ai_flist;
   1280 			ai->ai_flist = ic->head;
   1281 		}
   1282 		ic->head = ic->tail = NULL;
   1283 		ic->last = id;
   1284 		ic->count++;
   1285 		continue;
   1286 	}
   1287 	/* No free block, create that too */
   1288 	lcount = ic->count & (IDBLOCK-1);
   1289 	if ( !ic->tail || lcount == 0) {
   1290 		if ( ai->ai_flist ) {
   1291 			ice = ai->ai_flist;
   1292 			ai->ai_flist = ice->next;
   1293 		} else {
   1294 			ice = ch_malloc( sizeof( mdb_tool_idl_cache_entry ));
   1295 		}
   1296 		ice->next = NULL;
   1297 		if ( !ic->head ) {
   1298 			ic->head = ice;
   1299 		} else {
   1300 			ic->tail->next = ice;
   1301 		}
   1302 		ic->tail = ice;
   1303 		if ( lcount )
   1304 			ice->ids[lcount-1] = 0;
   1305 		if ( !ic->count )
   1306 			ic->first = id;
   1307 	}
   1308 	ice = ic->tail;
   1309 	if (!lcount || ice->ids[lcount-1] != id)
   1310 		ice->ids[lcount] = id;
   1311 	ic->count++;
   1312 	}
   1313 
   1314 	return 0;
   1315 }
   1316 #endif /* MDB_TOOL_IDL_CACHING */
   1317 
   1318 /* Upgrade from pre 2.4.34 dn2id format */
   1319 
   1320 #include <ac/unistd.h>
   1321 #include <lutil_meter.h>
   1322 
   1323 #define STACKSIZ	2048
   1324 
   1325 typedef struct rec {
   1326 	ID id;
   1327 	size_t len;
   1328 	char rdn[512];
   1329 } rec;
   1330 
   1331 static int
   1332 mdb_dn2id_upgrade( BackendDB *be ) {
   1333 	struct mdb_info *mi = (struct mdb_info *) be->be_private;
   1334 	MDB_txn *mt;
   1335 	MDB_cursor *mc = NULL;
   1336 	MDB_val key, data;
   1337 	int rc, writes=0, depth=0;
   1338 	int enable_meter = 0;
   1339 	ID id = 0, *num, count = 0;
   1340 	rec *stack;
   1341 	lutil_meter_t meter;
   1342 
   1343 	if (!(mi->mi_flags & MDB_NEED_UPGRADE)) {
   1344 		Debug( LDAP_DEBUG_ANY, "database %s: No upgrade needed.\n",
   1345 			be->be_suffix[0].bv_val, 0, 0 );
   1346 		return 0;
   1347 	}
   1348 
   1349 	{
   1350 		MDB_stat st;
   1351 
   1352 		mdb_stat(mdb_cursor_txn(cursor), mi->mi_dbis[MDB_ID2ENTRY], &st);
   1353 		if (!st.ms_entries) {
   1354 			/* Empty DB, nothing to upgrade? */
   1355 			return 0;
   1356 		}
   1357 		if (isatty(2))
   1358 			enable_meter = !lutil_meter_open(&meter,
   1359 				&lutil_meter_text_display,
   1360 				&lutil_meter_linear_estimator,
   1361 				st.ms_entries);
   1362 	}
   1363 
   1364 	num = ch_malloc(STACKSIZ * (sizeof(ID) + sizeof(rec)));
   1365 	stack = (rec *)(num + STACKSIZ);
   1366 
   1367 	rc = mdb_txn_begin(mi->mi_dbenv, NULL, 0, &mt);
   1368 	if (rc) {
   1369 		Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_txn_begin failed, %s (%d)\n",
   1370 			mdb_strerror(rc), rc, 0 );
   1371 		goto leave;
   1372 	}
   1373 	rc = mdb_cursor_open(mt, mi->mi_dbis[MDB_DN2ID], &mc);
   1374 	if (rc) {
   1375 		Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_open failed, %s (%d)\n",
   1376 			mdb_strerror(rc), rc, 0 );
   1377 		goto leave;
   1378 	}
   1379 
   1380 	key.mv_size = sizeof(ID);
   1381 	/* post-order depth-first update */
   1382 	for(;;) {
   1383 		size_t dkids;
   1384 		unsigned char *ptr;
   1385 
   1386 		/* visit */
   1387 		key.mv_data = &id;
   1388 		stack[depth].id = id;
   1389 		rc = mdb_cursor_get(mc, &key, &data, MDB_SET);
   1390 		if (rc) {
   1391 			Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_get failed, %s (%d)\n",
   1392 				mdb_strerror(rc), rc, 0 );
   1393 			goto leave;
   1394 		}
   1395 		num[depth] = 1;
   1396 
   1397 		rc = mdb_cursor_count(mc, &dkids);
   1398 		if (rc) {
   1399 			Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_count failed, %s (%d)\n",
   1400 				mdb_strerror(rc), rc, 0 );
   1401 			goto leave;
   1402 		}
   1403 		if (dkids > 1) {
   1404 			rc = mdb_cursor_get(mc, &key, &data, MDB_NEXT_DUP);
   1405 down:
   1406 			ptr = (unsigned char *)data.mv_data + data.mv_size - sizeof(ID);
   1407 			memcpy(&id, ptr, sizeof(ID));
   1408 			depth++;
   1409 			memcpy(stack[depth].rdn, data.mv_data, data.mv_size);
   1410 			stack[depth].len = data.mv_size;
   1411 			continue;
   1412 		}
   1413 
   1414 
   1415 		/* pop: write updated count, advance to next node */
   1416 pop:
   1417 		/* update superior counts */
   1418 		if (depth)
   1419 			num[depth-1] += num[depth];
   1420 
   1421 		key.mv_data = &id;
   1422 		id = stack[depth-1].id;
   1423 		data.mv_data = stack[depth].rdn;
   1424 		data.mv_size = stack[depth].len;
   1425 		rc = mdb_cursor_get(mc, &key, &data, MDB_GET_BOTH);
   1426 		if (rc) {
   1427 			Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_get(BOTH) failed, %s (%d)\n",
   1428 				mdb_strerror(rc), rc, 0 );
   1429 			goto leave;
   1430 		}
   1431 		data.mv_data = stack[depth].rdn;
   1432 		ptr = (unsigned char *)data.mv_data + data.mv_size;
   1433 		memcpy(ptr, &num[depth], sizeof(ID));
   1434 		data.mv_size += sizeof(ID);
   1435 		rc = mdb_cursor_del(mc, 0);
   1436 		if (rc) {
   1437 			Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_del failed, %s (%d)\n",
   1438 				mdb_strerror(rc), rc, 0 );
   1439 			goto leave;
   1440 		}
   1441 		rc = mdb_cursor_put(mc, &key, &data, 0);
   1442 		if (rc) {
   1443 			Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_put failed, %s (%d)\n",
   1444 				mdb_strerror(rc), rc, 0 );
   1445 			goto leave;
   1446 		}
   1447 		count++;
   1448 #if 1
   1449 		if (enable_meter)
   1450 			lutil_meter_update(&meter, count, 0);
   1451 #else
   1452 		{
   1453 			int len;
   1454 			ptr = data.mv_data;
   1455 			len = (ptr[0] & 0x7f) << 8 | ptr[1];
   1456 			printf("ID: %zu, %zu, %.*s\n", stack[depth].id, num[depth], len, ptr+2);
   1457 		}
   1458 #endif
   1459 		writes++;
   1460 		if (writes == 1000) {
   1461 			mdb_cursor_close(mc);
   1462 			rc = mdb_txn_commit(mt);
   1463 			if (rc) {
   1464 				Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_txn_commit failed, %s (%d)\n",
   1465 					mdb_strerror(rc), rc, 0 );
   1466 				goto leave;
   1467 			}
   1468 			rc = mdb_txn_begin(mi->mi_dbenv, NULL, 0, &mt);
   1469 			if (rc) {
   1470 				Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_txn_begin(2) failed, %s (%d)\n",
   1471 					mdb_strerror(rc), rc, 0 );
   1472 				goto leave;
   1473 			}
   1474 			rc = mdb_cursor_open(mt, mi->mi_dbis[MDB_DN2ID], &mc);
   1475 			if (rc) {
   1476 				Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_open(2) failed, %s (%d)\n",
   1477 					mdb_strerror(rc), rc, 0 );
   1478 				goto leave;
   1479 			}
   1480 			rc = mdb_cursor_get(mc, &key, &data, MDB_GET_BOTH);
   1481 			if (rc) {
   1482 				Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_get(2) failed, %s (%d)\n",
   1483 					mdb_strerror(rc), rc, 0 );
   1484 				goto leave;
   1485 			}
   1486 			writes = 0;
   1487 		}
   1488 		depth--;
   1489 
   1490 		rc = mdb_cursor_get(mc, &key, &data, MDB_NEXT_DUP);
   1491 		if (rc == 0)
   1492 			goto down;
   1493 		rc = 0;
   1494 		if (depth)
   1495 			goto pop;
   1496 		else
   1497 			break;
   1498 	}
   1499 leave:
   1500 	mdb_cursor_close(mc);
   1501 	if (mt) {
   1502 		int r2;
   1503 		r2 = mdb_txn_commit(mt);
   1504 		if (r2) {
   1505 			Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_txn_commit(2) failed, %s (%d)\n",
   1506 				mdb_strerror(r2), r2, 0 );
   1507 			if (!rc)
   1508 				rc = r2;
   1509 		}
   1510 	}
   1511 	ch_free(num);
   1512 	if (enable_meter) {
   1513 		lutil_meter_update(&meter, count, 1);
   1514 		lutil_meter_close(&meter);
   1515 	}
   1516 	return rc;
   1517 }
   1518