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