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