]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/tools.c
6aae331484f76c33499849bf2ebf493667d09300
[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-2012 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 Operation *mdb_tool_ix_op;
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 struct mdb_info *mdb_tool_info;
80 static ldap_pvt_thread_mutex_t mdb_tool_index_mutex;
81 static ldap_pvt_thread_cond_t mdb_tool_index_cond_main;
82 static ldap_pvt_thread_cond_t mdb_tool_index_cond_work;
83 static void * mdb_tool_index_task( void *ctx, void *ptr );
84
85 static int      mdb_writes, mdb_writes_per_commit;
86
87 static int
88 mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep );
89
90 int mdb_tool_entry_open(
91         BackendDB *be, int mode )
92 {
93         /* In Quick mode, commit once per 1000 entries */
94         mdb_writes = 0;
95         if ( slapMode & SLAP_TOOL_QUICK )
96                 mdb_writes_per_commit = 1000;
97         else
98                 mdb_writes_per_commit = 1;
99
100         /* Set up for threaded slapindex */
101         if (( slapMode & (SLAP_TOOL_QUICK|SLAP_TOOL_READONLY)) == SLAP_TOOL_QUICK ) {
102                 if ( !mdb_tool_info ) {
103                         struct mdb_info *mdb = (struct mdb_info *) be->be_private;
104                         ldap_pvt_thread_mutex_init( &mdb_tool_index_mutex );
105                         ldap_pvt_thread_cond_init( &mdb_tool_index_cond_main );
106                         ldap_pvt_thread_cond_init( &mdb_tool_index_cond_work );
107                         if ( mdb->mi_nattrs ) {
108                                 int i;
109                                 mdb_tool_threads = slap_tool_thread_max - 1;
110                                 if ( mdb_tool_threads > 1 ) {
111                                         mdb_tool_index_rec = ch_calloc( mdb->mi_nattrs, sizeof( IndexRec ));
112                                         mdb_tool_index_tcount = mdb_tool_threads - 1;
113                                         for (i=1; i<mdb_tool_threads; i++) {
114                                                 int *ptr = ch_malloc( sizeof( int ));
115                                                 *ptr = i;
116                                                 ldap_pvt_thread_pool_submit( &connection_pool,
117                                                         mdb_tool_index_task, ptr );
118                                         }
119                                         mdb_tool_info = mdb;
120                                 }
121                         }
122                 }
123         }
124
125         return 0;
126 }
127
128 int mdb_tool_entry_close(
129         BackendDB *be )
130 {
131         if ( mdb_tool_info ) {
132                 slapd_shutdown = 1;
133                 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
134
135                 /* There might still be some threads starting */
136                 while ( mdb_tool_index_tcount > 0 ) {
137                         ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
138                                         &mdb_tool_index_mutex );
139                 }
140
141                 mdb_tool_index_tcount = mdb_tool_threads - 1;
142                 ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
143
144                 /* Make sure all threads are stopped */
145                 while ( mdb_tool_index_tcount > 0 ) {
146                         ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
147                                 &mdb_tool_index_mutex );
148                 }
149                 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
150
151                 mdb_tool_info = NULL;
152                 slapd_shutdown = 0;
153                 ch_free( mdb_tool_index_rec );
154                 mdb_tool_index_tcount = mdb_tool_threads - 1;
155         }
156
157         if( idcursor ) {
158                 mdb_cursor_close( idcursor );
159                 idcursor = NULL;
160         }
161         if( cursor ) {
162                 mdb_cursor_close( cursor );
163                 cursor = NULL;
164         }
165         if( txn ) {
166                 MDB_TOOL_IDL_FLUSH( be, txn );
167                 if ( mdb_txn_commit( txn ))
168                         return -1;
169                 txn = NULL;
170         }
171
172         if( nholes ) {
173                 unsigned i;
174                 fprintf( stderr, "Error, entries missing!\n");
175                 for (i=0; i<nholes; i++) {
176                         fprintf(stderr, "  entry %ld: %s\n",
177                                 holes[i].id, holes[i].dn.bv_val);
178                 }
179                 nholes = 0;
180                 return -1;
181         }
182
183         return 0;
184 }
185
186 ID
187 mdb_tool_entry_first_x(
188         BackendDB *be,
189         struct berval *base,
190         int scope,
191         Filter *f )
192 {
193         tool_base = base;
194         tool_scope = scope;
195         tool_filter = f;
196
197         return mdb_tool_entry_next( be );
198 }
199
200 ID mdb_tool_entry_next(
201         BackendDB *be )
202 {
203         int rc;
204         ID id;
205         struct mdb_info *mdb;
206
207         assert( be != NULL );
208         assert( slapMode & SLAP_TOOL_MODE );
209
210         mdb = (struct mdb_info *) be->be_private;
211         assert( mdb != NULL );
212
213         if ( !txn ) {
214                 rc = mdb_txn_begin( mdb->mi_dbenv, NULL, MDB_RDONLY, &txn );
215                 if ( rc )
216                         return NOID;
217                 rc = mdb_cursor_open( txn, mdb->mi_id2entry, &cursor );
218                 if ( rc ) {
219                         mdb_txn_abort( txn );
220                         return NOID;
221                 }
222         }
223
224 next:;
225         rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT );
226
227         if( rc ) {
228                 return NOID;
229         }
230
231         previd = *(ID *)key.mv_data;
232         id = previd;
233
234         if ( !data.mv_size )
235                 goto next;
236
237         if ( tool_filter || tool_base ) {
238                 static Operation op = {0};
239                 static Opheader ohdr = {0};
240
241                 op.o_hdr = &ohdr;
242                 op.o_bd = be;
243                 op.o_tmpmemctx = NULL;
244                 op.o_tmpmfuncs = &ch_mfuncs;
245
246                 if ( tool_next_entry ) {
247                         mdb_entry_release( &op, tool_next_entry, 0 );
248                         tool_next_entry = NULL;
249                 }
250
251                 rc = mdb_tool_entry_get_int( be, id, &tool_next_entry );
252                 if ( rc == LDAP_NO_SUCH_OBJECT ) {
253                         goto next;
254                 }
255
256                 assert( tool_next_entry != NULL );
257
258                 if ( tool_filter && test_filter( NULL, tool_next_entry, tool_filter ) != LDAP_COMPARE_TRUE )
259                 {
260                         mdb_entry_release( &op, tool_next_entry, 0 );
261                         tool_next_entry = NULL;
262                         goto next;
263                 }
264         }
265
266         return id;
267 }
268
269 ID mdb_tool_dn2id_get(
270         Backend *be,
271         struct berval *dn
272 )
273 {
274         struct mdb_info *mdb;
275         Operation op = {0};
276         Opheader ohdr = {0};
277         ID id;
278         int rc;
279
280         if ( BER_BVISEMPTY(dn) )
281                 return 0;
282
283         mdb = (struct mdb_info *) be->be_private;
284
285         if ( !txn ) {
286                 rc = mdb_txn_begin( mdb->mi_dbenv, NULL, (slapMode & SLAP_TOOL_READONLY) != 0 ?
287                         MDB_RDONLY : 0, &txn );
288                 if ( rc )
289                         return NOID;
290         }
291
292         op.o_hdr = &ohdr;
293         op.o_bd = be;
294         op.o_tmpmemctx = NULL;
295         op.o_tmpmfuncs = &ch_mfuncs;
296
297         rc = mdb_dn2id( &op, txn, NULL, dn, &id, NULL, NULL );
298         if ( rc == MDB_NOTFOUND )
299                 return NOID;
300
301         return id;
302 }
303
304 static int
305 mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep )
306 {
307         Operation op = {0};
308         Opheader ohdr = {0};
309
310         Entry *e = NULL;
311         struct berval dn = BER_BVNULL, ndn = BER_BVNULL;
312         int rc;
313
314         assert( be != NULL );
315         assert( slapMode & SLAP_TOOL_MODE );
316
317         if ( ( tool_filter || tool_base ) && id == previd && tool_next_entry != NULL ) {
318                 *ep = tool_next_entry;
319                 tool_next_entry = NULL;
320                 return LDAP_SUCCESS;
321         }
322
323         if ( id != previd ) {
324                 key.mv_size = sizeof(ID);
325                 key.mv_data = &id;
326                 rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
327                 if ( rc ) {
328                         rc = LDAP_OTHER;
329                         goto done;
330                 }
331         }
332         if ( !data.mv_size ) {
333                 rc = LDAP_NO_SUCH_OBJECT;
334                 goto done;
335         }
336
337         op.o_hdr = &ohdr;
338         op.o_bd = be;
339         op.o_tmpmemctx = NULL;
340         op.o_tmpmfuncs = &ch_mfuncs;
341         if ( slapMode & SLAP_TOOL_READONLY ) {
342                 rc = mdb_id2name( &op, txn, &idcursor, id, &dn, &ndn );
343                 if ( rc  ) {
344                         rc = LDAP_OTHER;
345                         if ( e ) {
346                                 mdb_entry_return( &op, e );
347                                 e = NULL;
348                         }
349                         goto done;
350                 }
351                 if ( tool_base != NULL ) {
352                         if ( !dnIsSuffixScope( &ndn, tool_base, tool_scope ) ) {
353                                 ch_free( dn.bv_val );
354                                 ch_free( ndn.bv_val );
355                                 rc = LDAP_NO_SUCH_OBJECT;
356                                 goto done;
357                         }
358                 }
359         }
360         rc = mdb_entry_decode( &op, &data, &e );
361         e->e_id = id;
362         if ( !BER_BVISNULL( &dn )) {
363                 e->e_name = dn;
364                 e->e_nname = ndn;
365         } else {
366                 e->e_name.bv_val = NULL;
367                 e->e_nname.bv_val = NULL;
368         }
369
370 done:
371         if ( e != NULL ) {
372                 *ep = e;
373         }
374
375         return rc;
376 }
377
378 Entry*
379 mdb_tool_entry_get( BackendDB *be, ID id )
380 {
381         Entry *e = NULL;
382         int rc;
383
384         if ( !txn ) {
385                 struct mdb_info *mdb = (struct mdb_info *) be->be_private;
386                 rc = mdb_txn_begin( mdb->mi_dbenv, NULL,
387                         (slapMode & SLAP_TOOL_READONLY) ? MDB_RDONLY : 0, &txn );
388                 if ( rc )
389                         return NULL;
390         }
391         if ( !cursor ) {
392                 struct mdb_info *mdb = (struct mdb_info *) be->be_private;
393                 rc = mdb_cursor_open( txn, mdb->mi_id2entry, &cursor );
394                 if ( rc ) {
395                         mdb_txn_abort( txn );
396                         txn = NULL;
397                         return NULL;
398                 }
399         }
400         (void)mdb_tool_entry_get_int( be, id, &e );
401         return e;
402 }
403
404 static int mdb_tool_next_id(
405         Operation *op,
406         MDB_txn *tid,
407         Entry *e,
408         struct berval *text,
409         int hole )
410 {
411         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
412         struct berval dn = e->e_name;
413         struct berval ndn = e->e_nname;
414         struct berval pdn, npdn, nmatched;
415         ID id, pid = 0;
416         int rc;
417
418         if (ndn.bv_len == 0) {
419                 e->e_id = 0;
420                 return 0;
421         }
422
423         rc = mdb_dn2id( op, tid, mcp, &ndn, &id, NULL, &nmatched );
424         if ( rc == MDB_NOTFOUND ) {
425                 if ( !be_issuffix( op->o_bd, &ndn ) ) {
426                         ID eid = e->e_id;
427                         dnParent( &ndn, &npdn );
428                         if ( nmatched.bv_len != npdn.bv_len ) {
429                                 dnParent( &dn, &pdn );
430                                 e->e_name = pdn;
431                                 e->e_nname = npdn;
432                                 rc = mdb_tool_next_id( op, tid, e, text, 1 );
433                                 e->e_name = dn;
434                                 e->e_nname = ndn;
435                                 if ( rc ) {
436                                         return rc;
437                                 }
438                                 /* If parent didn't exist, it was created just now
439                                  * and its ID is now in e->e_id. Make sure the current
440                                  * entry gets added under the new parent ID.
441                                  */
442                                 if ( eid != e->e_id ) {
443                                         pid = e->e_id;
444                                 }
445                         } else {
446                                 pid = id;
447                         }
448                 }
449                 rc = mdb_next_id( op->o_bd, idcursor, &e->e_id );
450                 if ( rc ) {
451                         snprintf( text->bv_val, text->bv_len,
452                                 "next_id failed: %s (%d)",
453                                 mdb_strerror(rc), rc );
454                 Debug( LDAP_DEBUG_ANY,
455                         "=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
456                         return rc;
457                 }
458                 rc = mdb_dn2id_add( op, mcp, mcd, pid, e );
459                 if ( rc ) {
460                         snprintf( text->bv_val, text->bv_len,
461                                 "dn2id_add failed: %s (%d)",
462                                 mdb_strerror(rc), rc );
463                         Debug( LDAP_DEBUG_ANY,
464                                 "=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
465                 } else if ( hole ) {
466                         MDB_val key, data;
467                         if ( nholes == nhmax - 1 ) {
468                                 if ( holes == hbuf ) {
469                                         holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
470                                         AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
471                                 } else {
472                                         holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
473                                 }
474                                 nhmax *= 2;
475                         }
476                         ber_dupbv( &holes[nholes].dn, &ndn );
477                         holes[nholes++].id = e->e_id;
478                         key.mv_size = sizeof(ID);
479                         key.mv_data = &e->e_id;
480                         data.mv_size = 0;
481                         data.mv_data = NULL;
482                         rc = mdb_cursor_put( idcursor, &key, &data, MDB_NOOVERWRITE );
483                         if ( rc == MDB_KEYEXIST )
484                                 rc = 0;
485                         if ( rc ) {
486                                 snprintf( text->bv_val, text->bv_len,
487                                         "dummy id2entry add failed: %s (%d)",
488                                         mdb_strerror(rc), rc );
489                                 Debug( LDAP_DEBUG_ANY,
490                                         "=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
491                         }
492                 }
493         } else if ( !hole ) {
494                 unsigned i, j;
495
496                 e->e_id = id;
497
498                 for ( i=0; i<nholes; i++) {
499                         if ( holes[i].id == e->e_id ) {
500                                 free(holes[i].dn.bv_val);
501                                 for (j=i;j<nholes;j++) holes[j] = holes[j+1];
502                                 holes[j].id = 0;
503                                 nholes--;
504                                 break;
505                         } else if ( holes[i].id > e->e_id ) {
506                                 break;
507                         }
508                 }
509         }
510         return rc;
511 }
512
513 static int
514 mdb_tool_index_add(
515         Operation *op,
516         MDB_txn *txn,
517         Entry *e )
518 {
519         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
520
521         if ( !mdb->mi_nattrs )
522                 return 0;
523
524         if ( mdb_tool_threads > 1 ) {
525                 IndexRec *ir;
526                 int i, rc;
527                 Attribute *a;
528
529                 ir = mdb_tool_index_rec;
530                 for (i=0; i<mdb->mi_nattrs; i++)
531                         ir[i].ir_attrs = NULL;
532
533                 for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
534                         rc = mdb_index_recset( mdb, a, a->a_desc->ad_type,
535                                 &a->a_desc->ad_tags, ir );
536                         if ( rc )
537                                 return rc;
538                 }
539                 for (i=0; i<mdb->mi_nattrs; i++) {
540                         if ( !ir[i].ir_ai )
541                                 break;
542                         rc = mdb_cursor_open( txn, ir[i].ir_ai->ai_dbi,
543                                  &ir[i].ir_ai->ai_cursor );
544                         if ( rc )
545                                 return rc;
546                 }
547                 mdb_tool_ix_id = e->e_id;
548                 mdb_tool_ix_op = op;
549                 mdb_tool_ix_txn = txn;
550                 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
551                 /* Wait for all threads to be ready */
552                 while ( mdb_tool_index_tcount ) {
553                         ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
554                                 &mdb_tool_index_mutex );
555                 }
556
557                 for ( i=1; i<mdb_tool_threads; i++ )
558                         mdb_tool_index_rec[i].ir_i = LDAP_BUSY;
559                 mdb_tool_index_tcount = mdb_tool_threads - 1;
560                 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
561                 ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
562
563                 rc = mdb_index_recrun( op, txn, mdb, ir, e->e_id, 0 );
564                 if ( rc )
565                         return rc;
566                 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
567                 for ( i=1; i<mdb_tool_threads; i++ ) {
568                         if ( mdb_tool_index_rec[i].ir_i == LDAP_BUSY ) {
569                                 ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
570                                         &mdb_tool_index_mutex );
571                                 i--;
572                                 continue;
573                         }
574                         if ( mdb_tool_index_rec[i].ir_i ) {
575                                 rc = mdb_tool_index_rec[i].ir_i;
576                                 break;
577                         }
578                 }
579                 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
580                 return rc;
581         } else
582         {
583                 return mdb_index_entry_add( op, txn, e );
584         }
585 }
586
587 ID mdb_tool_entry_put(
588         BackendDB *be,
589         Entry *e,
590         struct berval *text )
591 {
592         int rc;
593         struct mdb_info *mdb;
594         Operation op = {0};
595         Opheader ohdr = {0};
596
597         assert( be != NULL );
598         assert( slapMode & SLAP_TOOL_MODE );
599
600         assert( text != NULL );
601         assert( text->bv_val != NULL );
602         assert( text->bv_val[0] == '\0' );      /* overconservative? */
603
604         Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_tool_entry_put)
605                 "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
606
607         mdb = (struct mdb_info *) be->be_private;
608
609         if ( !txn ) {
610                 rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn );
611                 if( rc != 0 ) {
612                         snprintf( text->bv_val, text->bv_len,
613                                 "txn_begin failed: %s (%d)",
614                                 mdb_strerror(rc), rc );
615                         Debug( LDAP_DEBUG_ANY,
616                                 "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
617                                  text->bv_val, 0, 0 );
618                         return NOID;
619                 }
620                 rc = mdb_cursor_open( txn, mdb->mi_id2entry, &idcursor );
621                 if( rc != 0 ) {
622                         snprintf( text->bv_val, text->bv_len,
623                                 "cursor_open failed: %s (%d)",
624                                 mdb_strerror(rc), rc );
625                         Debug( LDAP_DEBUG_ANY,
626                                 "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
627                                  text->bv_val, 0, 0 );
628                         return NOID;
629                 }
630                 if ( !mdb->mi_nextid ) {
631                         ID dummy;
632                         mdb_next_id( be, idcursor, &dummy );
633                 }
634                 rc = mdb_cursor_open( txn, mdb->mi_dn2id, &mcp );
635                 if( rc != 0 ) {
636                         snprintf( text->bv_val, text->bv_len,
637                                 "cursor_open failed: %s (%d)",
638                                 mdb_strerror(rc), rc );
639                         Debug( LDAP_DEBUG_ANY,
640                                 "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
641                                  text->bv_val, 0, 0 );
642                         return NOID;
643                 }
644                 rc = mdb_cursor_open( txn, mdb->mi_dn2id, &mcd );
645                 if( rc != 0 ) {
646                         snprintf( text->bv_val, text->bv_len,
647                                 "cursor_open failed: %s (%d)",
648                                 mdb_strerror(rc), rc );
649                         Debug( LDAP_DEBUG_ANY,
650                                 "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
651                                  text->bv_val, 0, 0 );
652                         return NOID;
653                 }
654         }
655
656         op.o_hdr = &ohdr;
657         op.o_bd = be;
658         op.o_tmpmemctx = NULL;
659         op.o_tmpmfuncs = &ch_mfuncs;
660
661         /* add dn2id indices */
662         rc = mdb_tool_next_id( &op, txn, e, text, 0 );
663         if( rc != 0 ) {
664                 goto done;
665         }
666
667         rc = mdb_tool_index_add( &op, txn, e );
668         if( rc != 0 ) {
669                 snprintf( text->bv_val, text->bv_len,
670                                 "index_entry_add failed: err=%d", rc );
671                 Debug( LDAP_DEBUG_ANY,
672                         "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
673                         text->bv_val, 0, 0 );
674                 goto done;
675         }
676
677
678         /* id2entry index */
679         rc = mdb_id2entry_add( &op, txn, idcursor, e );
680         if( rc != 0 ) {
681                 snprintf( text->bv_val, text->bv_len,
682                                 "id2entry_add failed: err=%d", rc );
683                 Debug( LDAP_DEBUG_ANY,
684                         "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
685                         text->bv_val, 0, 0 );
686                 goto done;
687         }
688
689 done:
690         if( rc == 0 ) {
691                 mdb_writes++;
692                 if ( mdb_writes >= mdb_writes_per_commit ) {
693                         unsigned i;
694                         MDB_TOOL_IDL_FLUSH( be, txn );
695                         rc = mdb_txn_commit( txn );
696                         for ( i=0; i<mdb->mi_nattrs; i++ )
697                                 mdb->mi_attrs[i]->ai_cursor = NULL;
698                         mdb_writes = 0;
699                         txn = NULL;
700                         idcursor = NULL;
701                         if( rc != 0 ) {
702                                 snprintf( text->bv_val, text->bv_len,
703                                                 "txn_commit failed: %s (%d)",
704                                                 mdb_strerror(rc), rc );
705                                 Debug( LDAP_DEBUG_ANY,
706                                         "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
707                                         text->bv_val, 0, 0 );
708                                 e->e_id = NOID;
709                         }
710                 }
711
712         } else {
713                 unsigned i;
714                 mdb_txn_abort( txn );
715                 txn = NULL;
716                 idcursor = NULL;
717                 for ( i=0; i<mdb->mi_nattrs; i++ )
718                         mdb->mi_attrs[i]->ai_cursor = NULL;
719                 mdb_writes = 0;
720                 snprintf( text->bv_val, text->bv_len,
721                         "txn_aborted! %s (%d)",
722                         rc == LDAP_OTHER ? "Internal error" :
723                         mdb_strerror(rc), rc );
724                 Debug( LDAP_DEBUG_ANY,
725                         "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
726                         text->bv_val, 0, 0 );
727                 e->e_id = NOID;
728         }
729
730         return e->e_id;
731 }
732
733 int mdb_tool_entry_reindex(
734         BackendDB *be,
735         ID id,
736         AttributeDescription **adv )
737 {
738         struct mdb_info *mi = (struct mdb_info *) be->be_private;
739         int rc;
740         Entry *e;
741         Operation op = {0};
742         Opheader ohdr = {0};
743
744         Debug( LDAP_DEBUG_ARGS,
745                 "=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld )\n",
746                 (long) id, 0, 0 );
747         assert( tool_base == NULL );
748         assert( tool_filter == NULL );
749
750         /* No indexes configured, nothing to do. Could return an
751          * error here to shortcut things.
752          */
753         if (!mi->mi_attrs) {
754                 return 0;
755         }
756
757         /* Check for explicit list of attrs to index */
758         if ( adv ) {
759                 int i, j, n;
760
761                 if ( mi->mi_attrs[0]->ai_desc != adv[0] ) {
762                         /* count */
763                         for ( n = 0; adv[n]; n++ ) ;
764
765                         /* insertion sort */
766                         for ( i = 0; i < n; i++ ) {
767                                 AttributeDescription *ad = adv[i];
768                                 for ( j = i-1; j>=0; j--) {
769                                         if ( SLAP_PTRCMP( adv[j], ad ) <= 0 ) break;
770                                         adv[j+1] = adv[j];
771                                 }
772                                 adv[j+1] = ad;
773                         }
774                 }
775
776                 for ( i = 0; adv[i]; i++ ) {
777                         if ( mi->mi_attrs[i]->ai_desc != adv[i] ) {
778                                 for ( j = i+1; j < mi->mi_nattrs; j++ ) {
779                                         if ( mi->mi_attrs[j]->ai_desc == adv[i] ) {
780                                                 AttrInfo *ai = mi->mi_attrs[i];
781                                                 mi->mi_attrs[i] = mi->mi_attrs[j];
782                                                 mi->mi_attrs[j] = ai;
783                                                 break;
784                                         }
785                                 }
786                                 if ( j == mi->mi_nattrs ) {
787                                         Debug( LDAP_DEBUG_ANY,
788                                                 LDAP_XSTRING(mdb_tool_entry_reindex)
789                                                 ": no index configured for %s\n",
790                                                 adv[i]->ad_cname.bv_val, 0, 0 );
791                                         return -1;
792                                 }
793                         }
794                 }
795                 mi->mi_nattrs = i;
796         }
797
798         e = mdb_tool_entry_get( be, id );
799
800         if( e == NULL ) {
801                 Debug( LDAP_DEBUG_ANY,
802                         LDAP_XSTRING(mdb_tool_entry_reindex)
803                         ": could not locate id=%ld\n",
804                         (long) id, 0, 0 );
805                 return -1;
806         }
807
808         if ( !txi ) {
809                 rc = mdb_txn_begin( mi->mi_dbenv, NULL, 0, &txi );
810                 if( rc != 0 ) {
811                         Debug( LDAP_DEBUG_ANY,
812                                 "=> " LDAP_XSTRING(mdb_tool_entry_reindex) ": "
813                                 "txn_begin failed: %s (%d)\n",
814                                 mdb_strerror(rc), rc, 0 );
815                         goto done;
816                 }
817         }
818
819         if ( slapMode & SLAP_TRUNCATE_MODE ) {
820                 int i;
821                 for ( i=0; i < mi->mi_nattrs; i++ ) {
822                         rc = mdb_drop( txi, mi->mi_attrs[i]->ai_dbi, 0 );
823                         if ( rc ) {
824                                 Debug( LDAP_DEBUG_ANY,
825                                         LDAP_XSTRING(mdb_tool_entry_reindex)
826                                         ": (Truncate) mdb_drop(%s) failed: %s (%d)\n",
827                                         mi->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
828                                         mdb_strerror(rc), rc );
829                                 return -1;
830                         }
831                 }
832                 slapMode ^= SLAP_TRUNCATE_MODE;
833         }
834
835         /*
836          * just (re)add them for now
837          * Use truncate mode to empty/reset index databases
838          */
839
840         Debug( LDAP_DEBUG_TRACE,
841                 "=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld )\n",
842                 (long) id, 0, 0 );
843
844         op.o_hdr = &ohdr;
845         op.o_bd = be;
846         op.o_tmpmemctx = NULL;
847         op.o_tmpmfuncs = &ch_mfuncs;
848
849         rc = mdb_tool_index_add( &op, txi, e );
850
851 done:
852         if( rc == 0 ) {
853                 mdb_writes++;
854                 if ( mdb_writes >= mdb_writes_per_commit ) {
855                         MDB_val key;
856                         unsigned i;
857                         MDB_TOOL_IDL_FLUSH( be, txi );
858                         rc = mdb_txn_commit( txi );
859                         mdb_writes = 0;
860                         for ( i=0; i<mi->mi_nattrs; i++ )
861                                 mi->mi_attrs[i]->ai_cursor = NULL;
862                         if( rc != 0 ) {
863                                 Debug( LDAP_DEBUG_ANY,
864                                         "=> " LDAP_XSTRING(mdb_tool_entry_reindex)
865                                         ": txn_commit failed: %s (%d)\n",
866                                         mdb_strerror(rc), rc, 0 );
867                                 e->e_id = NOID;
868                         }
869                         mdb_cursor_close( cursor );
870                         txi = NULL;
871                         /* Must close the read txn to allow old pages to be reclaimed. */
872                         mdb_txn_abort( txn );
873                         /* and then reopen it so that tool_entry_next still works. */
874                         mdb_txn_begin( mi->mi_dbenv, NULL, MDB_RDONLY, &txn );
875                         mdb_cursor_open( txn, mi->mi_id2entry, &cursor );
876                         key.mv_data = &id;
877                         key.mv_size = sizeof(ID);
878                         mdb_cursor_get( cursor, &key, NULL, MDB_SET );
879                 }
880
881         } else {
882                 unsigned i;
883                 mdb_writes = 0;
884                 mdb_cursor_close( cursor );
885                 cursor = NULL;
886                 mdb_txn_abort( txi );
887                 for ( i=0; i<mi->mi_nattrs; i++ )
888                         mi->mi_attrs[i]->ai_cursor = NULL;
889                 Debug( LDAP_DEBUG_ANY,
890                         "=> " LDAP_XSTRING(mdb_tool_entry_reindex)
891                         ": txn_aborted! err=%d\n",
892                         rc, 0, 0 );
893                 e->e_id = NOID;
894                 txi = NULL;
895         }
896         mdb_entry_release( &op, e, 0 );
897
898         return rc;
899 }
900
901 ID mdb_tool_entry_modify(
902         BackendDB *be,
903         Entry *e,
904         struct berval *text )
905 {
906         int rc;
907         struct mdb_info *mdb;
908         Operation op = {0};
909         Opheader ohdr = {0};
910
911         assert( be != NULL );
912         assert( slapMode & SLAP_TOOL_MODE );
913
914         assert( text != NULL );
915         assert( text->bv_val != NULL );
916         assert( text->bv_val[0] == '\0' );      /* overconservative? */
917
918         assert ( e->e_id != NOID );
919
920         Debug( LDAP_DEBUG_TRACE,
921                 "=> " LDAP_XSTRING(mdb_tool_entry_modify) "( %ld, \"%s\" )\n",
922                 (long) e->e_id, e->e_dn, 0 );
923
924         mdb = (struct mdb_info *) be->be_private;
925
926         if( cursor ) {
927                 mdb_cursor_close( cursor );
928                 cursor = NULL;
929         }
930         if ( !txn ) {
931                 rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn );
932                 if( rc != 0 ) {
933                         snprintf( text->bv_val, text->bv_len,
934                                 "txn_begin failed: %s (%d)",
935                                 mdb_strerror(rc), rc );
936                         Debug( LDAP_DEBUG_ANY,
937                                 "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
938                                  text->bv_val, 0, 0 );
939                         return NOID;
940                 }
941         }
942
943         op.o_hdr = &ohdr;
944         op.o_bd = be;
945         op.o_tmpmemctx = NULL;
946         op.o_tmpmfuncs = &ch_mfuncs;
947
948         /* id2entry index */
949         rc = mdb_id2entry_update( &op, txn, NULL, e );
950         if( rc != 0 ) {
951                 snprintf( text->bv_val, text->bv_len,
952                                 "id2entry_update failed: err=%d", rc );
953                 Debug( LDAP_DEBUG_ANY,
954                         "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
955                         text->bv_val, 0, 0 );
956                 goto done;
957         }
958
959 done:
960         if( rc == 0 ) {
961                 rc = mdb_txn_commit( txn );
962                 if( rc != 0 ) {
963                         snprintf( text->bv_val, text->bv_len,
964                                         "txn_commit failed: %s (%d)",
965                                         mdb_strerror(rc), rc );
966                         Debug( LDAP_DEBUG_ANY,
967                                 "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": "
968                                 "%s\n", text->bv_val, 0, 0 );
969                         e->e_id = NOID;
970                 }
971
972         } else {
973                 mdb_txn_abort( txn );
974                 snprintf( text->bv_val, text->bv_len,
975                         "txn_aborted! %s (%d)",
976                         mdb_strerror(rc), rc );
977                 Debug( LDAP_DEBUG_ANY,
978                         "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
979                         text->bv_val, 0, 0 );
980                 e->e_id = NOID;
981         }
982         txn = NULL;
983         idcursor = NULL;
984
985         return e->e_id;
986 }
987
988 static void *
989 mdb_tool_index_task( void *ctx, void *ptr )
990 {
991         int base = *(int *)ptr;
992
993         free( ptr );
994         while ( 1 ) {
995                 ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
996                 mdb_tool_index_tcount--;
997                 if ( !mdb_tool_index_tcount )
998                         ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
999                 ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_work,
1000                         &mdb_tool_index_mutex );
1001                 if ( slapd_shutdown ) {
1002                         mdb_tool_index_tcount--;
1003                         if ( !mdb_tool_index_tcount )
1004                                 ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
1005                         ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
1006                         break;
1007                 }
1008                 ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
1009                 mdb_tool_index_rec[base].ir_i = mdb_index_recrun( mdb_tool_ix_op,
1010                         mdb_tool_ix_txn,
1011                         mdb_tool_info, mdb_tool_index_rec, mdb_tool_ix_id, base );
1012         }
1013
1014         return NULL;
1015 }
1016
1017 #ifdef MDB_TOOL_IDL_CACHING
1018 static int
1019 mdb_tool_idl_cmp( const void *v1, const void *v2 )
1020 {
1021         const mdb_tool_idl_cache *c1 = v1, *c2 = v2;
1022         int rc;
1023
1024         if (( rc = c1->kstr.bv_len - c2->kstr.bv_len )) return rc;
1025         return memcmp( c1->kstr.bv_val, c2->kstr.bv_val, c1->kstr.bv_len );
1026 }
1027
1028 static int
1029 mdb_tool_idl_flush_one( MDB_cursor *mc, AttrInfo *ai, mdb_tool_idl_cache *ic )
1030 {
1031         mdb_tool_idl_cache_entry *ice;
1032         MDB_val key, data[2];
1033         int i, rc;
1034         ID id, nid;
1035
1036         /* Freshly allocated, ignore it */
1037         if ( !ic->head && ic->count <= MDB_IDL_DB_SIZE ) {
1038                 return 0;
1039         }
1040
1041         key.mv_data = ic->kstr.bv_val;
1042         key.mv_size = ic->kstr.bv_len;
1043
1044         if ( ic->count > MDB_IDL_DB_SIZE ) {
1045                 while ( ic->flags & WAS_FOUND ) {
1046                         rc = mdb_cursor_get( mc, &key, data, MDB_SET );
1047                         if ( rc ) {
1048                                 /* FIXME: find out why this happens */
1049                                 ic->flags = 0;
1050                                 break;
1051                         }
1052                         if ( ic->flags & WAS_RANGE ) {
1053                                 /* Skip lo */
1054                                 rc = mdb_cursor_get( mc, &key, data, MDB_NEXT_DUP );
1055
1056                                 /* Get hi */
1057                                 rc = mdb_cursor_get( mc, &key, data, MDB_NEXT_DUP );
1058
1059                                 /* Store range hi */
1060                                 data[0].mv_data = &ic->last;
1061                                 rc = mdb_cursor_put( mc, &key, data, MDB_CURRENT );
1062                         } else {
1063                                 /* Delete old data, replace with range */
1064                                 ic->first = *(ID *)data[0].mv_data;
1065                                 mdb_cursor_del( mc, MDB_NODUPDATA );
1066                         }
1067                         break;
1068                 }
1069                 if ( !(ic->flags & WAS_RANGE)) {
1070                         /* range, didn't exist before */
1071                         nid = 0;
1072                         data[0].mv_size = sizeof(ID);
1073                         data[0].mv_data = &nid;
1074                         rc = mdb_cursor_put( mc, &key, data, 0 );
1075                         if ( rc == 0 ) {
1076                                 data[0].mv_data = &ic->first;
1077                                 rc = mdb_cursor_put( mc, &key, data, 0 );
1078                                 if ( rc == 0 ) {
1079                                         data[0].mv_data = &ic->last;
1080                                         rc = mdb_cursor_put( mc, &key, data, 0 );
1081                                 }
1082                         }
1083                         if ( rc ) {
1084                                 rc = -1;
1085                         }
1086                 }
1087         } else {
1088                 /* Normal write */
1089                 int n;
1090
1091                 data[0].mv_size = sizeof(ID);
1092                 rc = 0;
1093                 i = ic->offset;
1094                 for ( ice = ic->head, n=0; ice; ice = ice->next, n++ ) {
1095                         int end;
1096                         if ( ice->next ) {
1097                                 end = IDBLOCK;
1098                         } else {
1099                                 end = ic->count & (IDBLOCK-1);
1100                                 if ( !end )
1101                                         end = IDBLOCK;
1102                         }
1103                         data[1].mv_size = end - i;
1104                         data[0].mv_data = &ice->ids[i];
1105                         i = 0;
1106                         rc = mdb_cursor_put( mc, &key, data, MDB_NODUPDATA|MDB_APPEND|MDB_MULTIPLE );
1107                         if ( rc ) {
1108                                 if ( rc == MDB_KEYEXIST ) {
1109                                         rc = 0;
1110                                         continue;
1111                                 }
1112                                 rc = -1;
1113                                 break;
1114                         }
1115                 }
1116                 if ( ic->head ) {
1117                         ic->tail->next = ai->ai_flist;
1118                         ai->ai_flist = ic->head;
1119                 }
1120         }
1121         ic->head = ai->ai_clist;
1122         ai->ai_clist = ic;
1123         return rc;
1124 }
1125
1126 static int
1127 mdb_tool_idl_flush_db( MDB_txn *txn, AttrInfo *ai )
1128 {
1129         MDB_cursor *mc;
1130         Avlnode *root;
1131         int rc;
1132
1133         mdb_cursor_open( txn, ai->ai_dbi, &mc );
1134         root = tavl_end( ai->ai_root, TAVL_DIR_LEFT );
1135         do {
1136                 rc = mdb_tool_idl_flush_one( mc, ai, root->avl_data );
1137                 if ( rc != -1 )
1138                         rc = 0;
1139         } while ((root = tavl_next(root, TAVL_DIR_RIGHT)));
1140         mdb_cursor_close( mc );
1141
1142         return rc;
1143 }
1144
1145 static int
1146 mdb_tool_idl_flush( BackendDB *be, MDB_txn *txn )
1147 {
1148         struct mdb_info *mdb = (struct mdb_info *) be->be_private;
1149         int rc = 0;
1150         unsigned int i, dbi;
1151
1152         for ( i=0; i < mdb->mi_nattrs; i++ ) {
1153                 if ( !mdb->mi_attrs[i]->ai_root ) continue;
1154                 rc = mdb_tool_idl_flush_db( txn, mdb->mi_attrs[i] );
1155                 tavl_free(mdb->mi_attrs[i]->ai_root, NULL);
1156                 mdb->mi_attrs[i]->ai_root = NULL;
1157                 if ( rc )
1158                         break;
1159         }
1160         return rc;
1161 }
1162
1163 int mdb_tool_idl_add(
1164         BackendDB *be,
1165         MDB_cursor *mc,
1166         struct berval *keys,
1167         ID id )
1168 {
1169         MDB_dbi dbi;
1170         mdb_tool_idl_cache *ic, itmp;
1171         mdb_tool_idl_cache_entry *ice;
1172         int i, rc, lcount;
1173         AttrInfo *ai = (AttrInfo *)mc;
1174         mc = ai->ai_cursor;
1175
1176         dbi = ai->ai_dbi;
1177         for (i=0; keys[i].bv_val; i++) {
1178         itmp.kstr = keys[i];
1179         ic = tavl_find( (Avlnode *)ai->ai_root, &itmp, mdb_tool_idl_cmp );
1180
1181         /* No entry yet, create one */
1182         if ( !ic ) {
1183                 MDB_val key, data;
1184                 ID nid;
1185                 int rc;
1186
1187                 if ( ai->ai_clist ) {
1188                         ic = ai->ai_clist;
1189                         ai->ai_clist = ic->head;
1190                 } else {
1191                         ic = ch_malloc( sizeof( mdb_tool_idl_cache ) + itmp.kstr.bv_len + 4 );
1192                 }
1193                 ic->kstr.bv_len = itmp.kstr.bv_len;
1194                 ic->kstr.bv_val = (char *)(ic+1);
1195                 memcpy( ic->kstr.bv_val, itmp.kstr.bv_val, ic->kstr.bv_len );
1196                 ic->head = ic->tail = NULL;
1197                 ic->last = 0;
1198                 ic->count = 0;
1199                 ic->offset = 0;
1200                 ic->flags = 0;
1201                 tavl_insert( (Avlnode **)&ai->ai_root, ic, mdb_tool_idl_cmp,
1202                         avl_dup_error );
1203
1204                 /* load existing key count here */
1205                 key.mv_size = keys[i].bv_len;
1206                 key.mv_data = keys[i].bv_val;
1207                 rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
1208                 if ( rc == 0 ) {
1209                         ic->flags |= WAS_FOUND;
1210                         nid = *(ID *)data.mv_data;
1211                         if ( nid == 0 ) {
1212                                 ic->count = MDB_IDL_DB_SIZE+1;
1213                                 ic->flags |= WAS_RANGE;
1214                         } else {
1215                                 size_t count;
1216
1217                                 mdb_cursor_count( mc, &count );
1218                                 ic->count = count;
1219                                 ic->first = nid;
1220                                 ic->offset = count & (IDBLOCK-1);
1221                         }
1222                 }
1223         }
1224         /* are we a range already? */
1225         if ( ic->count > MDB_IDL_DB_SIZE ) {
1226                 ic->last = id;
1227                 continue;
1228         /* Are we at the limit, and converting to a range? */
1229         } else if ( ic->count == MDB_IDL_DB_SIZE ) {
1230                 if ( ic->head ) {
1231                         ic->tail->next = ai->ai_flist;
1232                         ai->ai_flist = ic->head;
1233                 }
1234                 ic->head = ic->tail = NULL;
1235                 ic->last = id;
1236                 ic->count++;
1237                 continue;
1238         }
1239         /* No free block, create that too */
1240         lcount = ic->count & (IDBLOCK-1);
1241         if ( !ic->tail || lcount == 0) {
1242                 if ( ai->ai_flist ) {
1243                         ice = ai->ai_flist;
1244                         ai->ai_flist = ice->next;
1245                 } else {
1246                         ice = ch_malloc( sizeof( mdb_tool_idl_cache_entry ));
1247                 }
1248                 ice->next = NULL;
1249                 if ( !ic->head ) {
1250                         ic->head = ice;
1251                 } else {
1252                         ic->tail->next = ice;
1253                 }
1254                 ic->tail = ice;
1255                 if ( lcount )
1256                         ice->ids[lcount-1] = 0;
1257                 if ( !ic->count )
1258                         ic->first = id;
1259         }
1260         ice = ic->tail;
1261         if (!lcount || ice->ids[lcount-1] != id)
1262                 ice->ids[lcount] = id;
1263         ic->count++;
1264         }
1265
1266         return 0;
1267 }
1268 #endif /* MDB_TOOL_IDL_CACHING */