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