]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/tools.c
Sync with HEAD
[openldap] / servers / slapd / back-bdb / 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 2000-2005 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
22 #define AVL_INTERNAL
23 #include "back-bdb.h"
24
25 static DBC *cursor = NULL;
26 static DBT key, data;
27
28 typedef struct dn_id {
29         ID id;
30         struct berval dn;
31 } dn_id;
32
33 #define HOLE_SIZE       4096
34 static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
35 static unsigned nhmax = HOLE_SIZE;
36 static unsigned nholes;
37
38 static Avlnode *index_attrs, index_dummy;
39
40 int bdb_tool_entry_open(
41         BackendDB *be, int mode )
42 {
43         /* initialize key and data thangs */
44         DBTzero( &key );
45         DBTzero( &data );
46         key.flags = DB_DBT_REALLOC;
47         data.flags = DB_DBT_REALLOC;
48
49         return 0;
50 }
51
52 int bdb_tool_entry_close(
53         BackendDB *be )
54 {
55         assert( be != NULL );
56
57         if( key.data ) {
58                 ch_free( key.data );
59                 key.data = NULL;
60         }
61         if( data.data ) {
62                 ch_free( data.data );
63                 data.data = NULL;
64         }
65
66         if( cursor ) {
67                 cursor->c_close( cursor );
68                 cursor = NULL;
69         }
70
71         if( nholes ) {
72                 unsigned i;
73                 fprintf( stderr, "Error, entries missing!\n");
74                 for (i=0; i<nholes; i++) {
75                         fprintf(stderr, "  entry %ld: %s\n",
76                                 holes[i].id, holes[i].dn.bv_val);
77                 }
78                 return -1;
79         }
80                         
81         return 0;
82 }
83
84 static int bdb_reindex_cmp(const void *a, const void *b) { return 0; }
85
86 ID bdb_tool_entry_next(
87         BackendDB *be )
88 {
89         int rc;
90         ID id;
91         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
92
93         assert( be != NULL );
94         assert( slapMode & SLAP_TOOL_MODE );
95         assert( bdb != NULL );
96         
97         /* Initialization */
98         if (cursor == NULL) {
99                 rc = bdb->bi_id2entry->bdi_db->cursor(
100                         bdb->bi_id2entry->bdi_db, NULL, &cursor,
101                         bdb->bi_db_opflags );
102                 if( rc != 0 ) {
103                         return NOID;
104                 }
105         }
106
107         rc = cursor->c_get( cursor, &key, &data, DB_NEXT );
108
109         if( rc != 0 ) {
110                 /* If we're doing linear indexing and there are more attrs to
111                  * index, and we're at the end of the database, start over.
112                  */
113                 if ( bdb->bi_attrs == &index_dummy ) {
114                         if ( index_attrs && rc == DB_NOTFOUND ) {
115                                 /* optional - do a checkpoint here? */
116                                 index_dummy.avl_data = avl_delete(&index_attrs, NULL, bdb_reindex_cmp);
117                                 rc = cursor->c_get( cursor, &key, &data, DB_FIRST );
118                         }
119                         if ( rc ) {
120                                 bdb->bi_attrs = NULL;
121                                 return NOID;
122                         }
123                 } else {
124                         return NOID;
125                 }
126         }
127
128         if( data.data == NULL ) {
129                 return NOID;
130         }
131
132         BDB_DISK2ID( key.data, &id );
133         return id;
134 }
135
136 ID bdb_tool_dn2id_get(
137         Backend *be,
138         struct berval *dn
139 )
140 {
141         Operation op = {0};
142         Opheader ohdr = {0};
143         EntryInfo *ei = NULL;
144         int rc;
145
146         if ( BER_BVISEMPTY(dn) )
147                 return 0;
148
149         op.o_hdr = &ohdr;
150         op.o_bd = be;
151         op.o_tmpmemctx = NULL;
152         op.o_tmpmfuncs = &ch_mfuncs;
153
154         rc = bdb_cache_find_ndn( &op, NULL, dn, &ei );
155         if ( ei ) bdb_cache_entryinfo_unlock( ei );
156         if ( rc == DB_NOTFOUND )
157                 return NOID;
158         
159         return ei->bei_id;
160 }
161
162 int bdb_tool_id2entry_get(
163         Backend *be,
164         ID id,
165         Entry **e
166 )
167 {
168         int rc = bdb_id2entry( be, NULL, id, e );
169
170         if ( rc == DB_NOTFOUND && id == 0 ) {
171                 Entry *dummy = ch_calloc( 1, sizeof(Entry) );
172                 struct berval gluebv = BER_BVC("glue");
173                 dummy->e_name.bv_val = ch_strdup( "" );
174                 dummy->e_nname.bv_val = ch_strdup( "" );
175                 attr_merge_one( dummy, slap_schema.si_ad_objectClass, &gluebv, NULL );
176                 attr_merge_one( dummy, slap_schema.si_ad_structuralObjectClass,
177                         &gluebv, NULL );
178                 *e = dummy;
179                 rc = LDAP_SUCCESS;
180         }
181         return rc;
182 }
183
184 Entry* bdb_tool_entry_get( BackendDB *be, ID id )
185 {
186         int rc;
187         Entry *e = NULL;
188 #ifndef BDB_HIER
189         struct berval bv;
190 #endif
191
192         assert( be != NULL );
193         assert( slapMode & SLAP_TOOL_MODE );
194         assert( data.data != NULL );
195
196 #ifndef BDB_HIER
197         DBT2bv( &data, &bv );
198
199 #ifdef SLAP_ZONE_ALLOC
200         /* FIXME: will add ctx later */
201         rc = entry_decode( &bv, &e, NULL );
202 #else
203         rc = entry_decode( &bv, &e );
204 #endif
205
206         if( rc == LDAP_SUCCESS ) {
207                 e->e_id = id;
208         }
209 #else
210         {
211                 EntryInfo *ei = NULL;
212                 Operation op = {0};
213                 Opheader ohdr = {0};
214
215                 op.o_hdr = &ohdr;
216                 op.o_bd = be;
217                 op.o_tmpmemctx = NULL;
218                 op.o_tmpmfuncs = &ch_mfuncs;
219
220                 rc = bdb_cache_find_id( &op, NULL, id, &ei, 0, 0, NULL );
221                 if ( rc == LDAP_SUCCESS )
222                         e = ei->bei_e;
223         }
224 #endif
225         return e;
226 }
227
228 static int bdb_tool_next_id(
229         Operation *op,
230         DB_TXN *tid,
231         Entry *e,
232         struct berval *text,
233         int hole )
234 {
235         struct berval dn = e->e_name;
236         struct berval ndn = e->e_nname;
237         struct berval pdn, npdn;
238         EntryInfo *ei = NULL, eidummy;
239         int rc;
240
241         if (ndn.bv_len == 0) {
242                 e->e_id = 0;
243                 return 0;
244         }
245
246         rc = bdb_cache_find_ndn( op, tid, &ndn, &ei );
247         if ( ei ) bdb_cache_entryinfo_unlock( ei );
248         if ( rc == DB_NOTFOUND ) {
249                 if ( !be_issuffix( op->o_bd, &ndn ) ) {
250                         ID eid = e->e_id;
251                         dnParent( &dn, &pdn );
252                         dnParent( &ndn, &npdn );
253                         e->e_name = pdn;
254                         e->e_nname = npdn;
255                         rc = bdb_tool_next_id( op, tid, e, text, 1 );
256                         e->e_name = dn;
257                         e->e_nname = ndn;
258                         if ( rc ) {
259                                 return rc;
260                         }
261                         /* If parent didn't exist, it was created just now
262                          * and its ID is now in e->e_id. Make sure the current
263                          * entry gets added under the new parent ID.
264                          */
265                         if ( eid != e->e_id ) {
266                                 eidummy.bei_id = e->e_id;
267                                 ei = &eidummy;
268                         }
269                 }
270                 rc = bdb_next_id( op->o_bd, tid, &e->e_id );
271                 if ( rc ) {
272                         snprintf( text->bv_val, text->bv_len,
273                                 "next_id failed: %s (%d)",
274                                 db_strerror(rc), rc );
275                 Debug( LDAP_DEBUG_ANY,
276                         "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
277                         return rc;
278                 }
279                 rc = bdb_dn2id_add( op, tid, ei, e );
280                 if ( rc ) {
281                         snprintf( text->bv_val, text->bv_len, 
282                                 "dn2id_add failed: %s (%d)",
283                                 db_strerror(rc), rc );
284                 Debug( LDAP_DEBUG_ANY,
285                         "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
286                 } else if ( hole ) {
287                         if ( nholes == nhmax - 1 ) {
288                                 if ( holes == hbuf ) {
289                                         holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
290                                         AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
291                                 } else {
292                                         holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
293                                 }
294                                 nhmax *= 2;
295                         }
296                         ber_dupbv( &holes[nholes].dn, &ndn );
297                         holes[nholes++].id = e->e_id;
298                 }
299         } else if ( !hole ) {
300                 unsigned i;
301
302                 e->e_id = ei->bei_id;
303
304                 for ( i=0; i<nholes; i++) {
305                         if ( holes[i].id == e->e_id ) {
306                                 int j;
307                                 free(holes[i].dn.bv_val);
308                                 for (j=i;j<nholes;j++) holes[j] = holes[j+1];
309                                 holes[j].id = 0;
310                                 nholes--;
311                                 break;
312                         } else if ( holes[i].id > e->e_id ) {
313                                 break;
314                         }
315                 }
316         }
317         return rc;
318 }
319
320 ID bdb_tool_entry_put(
321         BackendDB *be,
322         Entry *e,
323         struct berval *text )
324 {
325         int rc;
326         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
327         DB_TXN *tid = NULL;
328         Operation op = {0};
329         Opheader ohdr = {0};
330
331         assert( be != NULL );
332         assert( slapMode & SLAP_TOOL_MODE );
333
334         assert( text != NULL );
335         assert( text->bv_val != NULL );
336         assert( text->bv_val[0] == '\0' );      /* overconservative? */
337
338         Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(bdb_tool_entry_put)
339                 "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
340
341         if (! (slapMode & SLAP_TOOL_QUICK)) {
342         rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid, 
343                 bdb->bi_db_opflags );
344         if( rc != 0 ) {
345                 snprintf( text->bv_val, text->bv_len,
346                         "txn_begin failed: %s (%d)",
347                         db_strerror(rc), rc );
348                 Debug( LDAP_DEBUG_ANY,
349                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
350                          text->bv_val, 0, 0 );
351                 return NOID;
352         }
353         }
354
355         op.o_hdr = &ohdr;
356         op.o_bd = be;
357         op.o_tmpmemctx = NULL;
358         op.o_tmpmfuncs = &ch_mfuncs;
359
360         /* add dn2id indices */
361         rc = bdb_tool_next_id( &op, tid, e, text, 0 );
362         if( rc != 0 ) {
363                 goto done;
364         }
365
366         /* id2entry index */
367         rc = bdb_id2entry_add( be, tid, e );
368         if( rc != 0 ) {
369                 snprintf( text->bv_val, text->bv_len,
370                                 "id2entry_add failed: %s (%d)",
371                                 db_strerror(rc), rc );
372                 Debug( LDAP_DEBUG_ANY,
373                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
374                         text->bv_val, 0, 0 );
375                 goto done;
376         }
377
378         if ( !bdb->bi_linear_index )
379                 rc = bdb_index_entry_add( &op, tid, e );
380         if( rc != 0 ) {
381                 snprintf( text->bv_val, text->bv_len,
382                                 "index_entry_add failed: %s (%d)",
383                                 db_strerror(rc), rc );
384                 Debug( LDAP_DEBUG_ANY,
385                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
386                         text->bv_val, 0, 0 );
387                 goto done;
388         }
389
390 done:
391         if( rc == 0 ) {
392                 if ( !( slapMode & SLAP_TOOL_QUICK )) {
393                 rc = TXN_COMMIT( tid, 0 );
394                 if( rc != 0 ) {
395                         snprintf( text->bv_val, text->bv_len,
396                                         "txn_commit failed: %s (%d)",
397                                         db_strerror(rc), rc );
398                         Debug( LDAP_DEBUG_ANY,
399                                 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
400                                 text->bv_val, 0, 0 );
401                         e->e_id = NOID;
402                 }
403                 }
404
405         } else {
406                 if ( !( slapMode & SLAP_TOOL_QUICK )) {
407                 TXN_ABORT( tid );
408                 snprintf( text->bv_val, text->bv_len,
409                         "txn_aborted! %s (%d)",
410                         db_strerror(rc), rc );
411                 Debug( LDAP_DEBUG_ANY,
412                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
413                         text->bv_val, 0, 0 );
414                 }
415                 e->e_id = NOID;
416         }
417
418         return e->e_id;
419 }
420
421 int bdb_tool_entry_reindex(
422         BackendDB *be,
423         ID id )
424 {
425         struct bdb_info *bi = (struct bdb_info *) be->be_private;
426         int rc;
427         Entry *e;
428         DB_TXN *tid = NULL;
429         Operation op = {0};
430         Opheader ohdr = {0};
431
432         Debug( LDAP_DEBUG_ARGS,
433                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld )\n",
434                 (long) id, 0, 0 );
435
436         /* No indexes configured, nothing to do. Could return an
437          * error here to shortcut things.
438          */
439         if (!bi->bi_attrs) {
440                 return 0;
441         }
442
443         /* Get the first attribute to index */
444         if (bi->bi_linear_index && !index_attrs && bi->bi_attrs != &index_dummy) {
445                 index_attrs = bi->bi_attrs;
446                 bi->bi_attrs = &index_dummy;
447                 index_dummy.avl_data = avl_delete(&index_attrs, NULL, bdb_reindex_cmp);
448         }
449
450         e = bdb_tool_entry_get( be, id );
451
452         if( e == NULL ) {
453                 Debug( LDAP_DEBUG_ANY,
454                         LDAP_XSTRING(bdb_tool_entry_reindex)
455                         ": could not locate id=%ld\n",
456                         (long) id, 0, 0 );
457                 return -1;
458         }
459
460         if (! (slapMode & SLAP_TOOL_QUICK)) {
461         rc = TXN_BEGIN( bi->bi_dbenv, NULL, &tid, bi->bi_db_opflags );
462         if( rc != 0 ) {
463                 Debug( LDAP_DEBUG_ANY,
464                         "=> " LDAP_XSTRING(bdb_tool_entry_reindex) ": "
465                         "txn_begin failed: %s (%d)\n",
466                         db_strerror(rc), rc, 0 );
467                 goto done;
468         }
469         }
470         
471         /*
472          * just (re)add them for now
473          * assume that some other routine (not yet implemented)
474          * will zap index databases
475          *
476          */
477
478         Debug( LDAP_DEBUG_TRACE,
479                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld, \"%s\" )\n",
480                 (long) id, e->e_dn, 0 );
481
482         op.o_hdr = &ohdr;
483         op.o_bd = be;
484         op.o_tmpmemctx = NULL;
485         op.o_tmpmfuncs = &ch_mfuncs;
486
487         rc = bdb_index_entry_add( &op, tid, e );
488
489 done:
490         if( rc == 0 ) {
491                 if (! (slapMode & SLAP_TOOL_QUICK)) {
492                 rc = TXN_COMMIT( tid, 0 );
493                 if( rc != 0 ) {
494                         Debug( LDAP_DEBUG_ANY,
495                                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
496                                 ": txn_commit failed: %s (%d)\n",
497                                 db_strerror(rc), rc, 0 );
498                         e->e_id = NOID;
499                 }
500                 }
501
502         } else {
503                 if (! (slapMode & SLAP_TOOL_QUICK)) {
504                 TXN_ABORT( tid );
505                 Debug( LDAP_DEBUG_ANY,
506                         "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
507                         ": txn_aborted! %s (%d)\n",
508                         db_strerror(rc), rc, 0 );
509                 }
510                 e->e_id = NOID;
511         }
512         bdb_entry_release( &op, e, 0 );
513
514         return rc;
515 }
516
517 ID bdb_tool_entry_modify(
518         BackendDB *be,
519         Entry *e,
520         struct berval *text )
521 {
522         int rc;
523         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
524         DB_TXN *tid = NULL;
525         Operation op = {0};
526         Opheader ohdr = {0};
527
528         assert( be != NULL );
529         assert( slapMode & SLAP_TOOL_MODE );
530
531         assert( text != NULL );
532         assert( text->bv_val != NULL );
533         assert( text->bv_val[0] == '\0' );      /* overconservative? */
534
535         assert ( e->e_id != NOID );
536
537         Debug( LDAP_DEBUG_TRACE,
538                 "=> " LDAP_XSTRING(bdb_tool_entry_modify) "( %ld, \"%s\" )\n",
539                 (long) e->e_id, e->e_dn, 0 );
540
541         if (! (slapMode & SLAP_TOOL_QUICK)) {
542         rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid, 
543                 bdb->bi_db_opflags );
544         if( rc != 0 ) {
545                 snprintf( text->bv_val, text->bv_len,
546                         "txn_begin failed: %s (%d)",
547                         db_strerror(rc), rc );
548                 Debug( LDAP_DEBUG_ANY,
549                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
550                          text->bv_val, 0, 0 );
551                 return NOID;
552         }
553         }
554
555         op.o_hdr = &ohdr;
556         op.o_bd = be;
557         op.o_tmpmemctx = NULL;
558         op.o_tmpmfuncs = &ch_mfuncs;
559
560         /* id2entry index */
561         rc = bdb_id2entry_update( be, tid, e );
562         if( rc != 0 ) {
563                 snprintf( text->bv_val, text->bv_len,
564                                 "id2entry_add failed: %s (%d)",
565                                 db_strerror(rc), rc );
566                 Debug( LDAP_DEBUG_ANY,
567                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
568                         text->bv_val, 0, 0 );
569                 goto done;
570         }
571
572 #if 0
573         /* FIXME: this is bogus, we don't have the old values to delete
574          * from the index because the given entry has already been modified.
575          */
576         rc = bdb_index_entry_del( &op, tid, e );
577         if( rc != 0 ) {
578                 snprintf( text->bv_val, text->bv_len,
579                                 "index_entry_del failed: %s (%d)",
580                                 db_strerror(rc), rc );
581                 Debug( LDAP_DEBUG_ANY,
582                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
583                         text->bv_val, 0, 0 );
584                 goto done;
585         }
586 #endif
587
588         rc = bdb_index_entry_add( &op, tid, e );
589         if( rc != 0 ) {
590                 snprintf( text->bv_val, text->bv_len,
591                                 "index_entry_add failed: %s (%d)",
592                                 db_strerror(rc), rc );
593                 Debug( LDAP_DEBUG_ANY,
594                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
595                         text->bv_val, 0, 0 );
596                 goto done;
597         }
598
599 done:
600         if( rc == 0 ) {
601                 if (! (slapMode & SLAP_TOOL_QUICK)) {
602                 rc = TXN_COMMIT( tid, 0 );
603                 if( rc != 0 ) {
604                         snprintf( text->bv_val, text->bv_len,
605                                         "txn_commit failed: %s (%d)",
606                                         db_strerror(rc), rc );
607                         Debug( LDAP_DEBUG_ANY,
608                                 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": "
609                                 "%s\n", text->bv_val, 0, 0 );
610                         e->e_id = NOID;
611                 }
612                 }
613
614         } else {
615                 if (! (slapMode & SLAP_TOOL_QUICK)) {
616                 TXN_ABORT( tid );
617                 snprintf( text->bv_val, text->bv_len,
618                         "txn_aborted! %s (%d)",
619                         db_strerror(rc), rc );
620                 Debug( LDAP_DEBUG_ANY,
621                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
622                         text->bv_val, 0, 0 );
623                 }
624                 e->e_id = NOID;
625         }
626
627         return e->e_id;
628 }