1 /* ldbm.c - ldap dbm compatibility routines */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2004 The OpenLDAP Foundation.
6 * Portions Copyright 1998-2003 Kurt D. Zeilenga.
7 * Portions Copyright 1998-2001 Net Boolean Incorporated.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
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>.
19 * This work was originally developed by the University of Michigan
20 * (as part of U-MICH LDAP). Additional significant contributors
37 #include <ac/stdlib.h>
38 #include <ac/string.h>
42 #include "ldap_pvt_thread.h"
45 ldbm_datum_free( LDBM ldbm, Datum data )
49 memset( &data, '\0', sizeof( Datum ));
55 ldbm_datum_dup( LDBM ldbm, Datum data )
59 ldbm_datum_init( dup );
61 if ( data.dsize == 0 ) {
67 dup.dsize = data.dsize;
69 if ( (dup.dptr = (char *) malloc( data.dsize )) != NULL ) {
70 AC_MEMCPY( dup.dptr, data.dptr, data.dsize );
76 static int ldbm_initialized = 0;
78 #if defined( USE_BERKELEY_CDB )
79 /* not currently supported */
80 #define LDBM_RWLOCK_INIT ((void) 0)
81 #define LDBM_RWLOCK_DESTROY ((void) 0)
82 #define LDBM_WLOCK ((void) 0)
83 #define LDBM_WUNLOCK ((void) 0)
84 #define LDBM_RLOCK ((void) 0)
85 #define LDBM_RUNLOCK ((void) 0)
87 #elif defined( HAVE_BERKELEY_DB_THREAD )
88 static ldap_pvt_thread_rdwr_t ldbm_big_rdwr;
89 #define LDBM_RWLOCK_INIT (ldap_pvt_thread_rdwr_init( &ldbm_big_rdwr ))
90 #define LDBM_RWLOCK_DESTROY (ldap_pvt_thread_rdwr_destroy( &ldbm_big_rdwr ))
91 #define LDBM_WLOCK (ldap_pvt_thread_rdwr_wlock(&ldbm_big_rdwr))
92 #define LDBM_WUNLOCK (ldap_pvt_thread_rdwr_wunlock(&ldbm_big_rdwr))
93 #define LDBM_RLOCK (ldap_pvt_thread_rdwr_rlock(&ldbm_big_rdwr))
94 #define LDBM_RUNLOCK (ldap_pvt_thread_rdwr_runlock(&ldbm_big_rdwr))
97 static ldap_pvt_thread_mutex_t ldbm_big_mutex;
98 #define LDBM_RWLOCK_INIT (ldap_pvt_thread_mutex_init( &ldbm_big_mutex ))
99 #define LDBM_RWLOCK_DESTROY (ldap_pvt_thread_mutex_destroy( &ldbm_big_mutex ))
100 #define LDBM_WLOCK (ldap_pvt_thread_mutex_lock(&ldbm_big_mutex))
101 #define LDBM_WUNLOCK (ldap_pvt_thread_mutex_unlock(&ldbm_big_mutex))
102 #define LDBM_RLOCK LDBM_WLOCK
103 #define LDBM_RUNLOCK LDBM_WUNLOCK
106 #if !defined( HAVE_BERKELEY_DB ) || (DB_VERSION_MAJOR < 3)
107 /* a dbEnv for BERKELEYv2 */
108 DB_ENV *ldbm_Env = NULL; /* real or fake, depending on db and version */
111 /* Let's make the version comparisons a little easier... */
113 #ifdef HAVE_BERKELEY_DB
114 #define DB_VERSION_X ((DB_VERSION_MAJOR<<16)|(DB_VERSION_MINOR<<8)|DB_VERSION_PATCH)
117 /*******************************************************************
119 * Create some special functions to initialize Berkeley DB for *
120 * versions greater than 2. *
122 *******************************************************************/
123 #if defined( HAVE_BERKELEY_DB ) && (DB_VERSION_MAJOR >= 2)
126 ldbm_malloc( size_t size )
128 /* likely should use ber_mem* routines */
129 return( calloc( 1, size ) );
133 #include <ac/syslog.h>
137 ldbm_db_errcall( const char *prefix, char *message )
140 syslog( LOG_INFO, "ldbm: %s %s", prefix, message );
144 int ldbm_initialize( const char* home )
146 #if DB_VERSION_MAJOR < 3
151 if(ldbm_initialized++) return 1;
158 int major, minor, patch;
159 version = db_version( &major, &minor, &patch );
161 strcpy( v2, version );
166 if( major != DB_VERSION_MAJOR ||
167 minor < DB_VERSION_MINOR )
171 "ldbm_initialize(): version mismatch\nexpected: %s\ngot: %s\n",
172 DB_VERSION_STRING, version );
178 #if DB_VERSION_MAJOR < 3
179 ldbm_Env = calloc( 1, sizeof( DB_ENV ));
181 if( ldbm_Env == NULL ) return 1;
183 ldbm_Env->db_errcall = ldbm_db_errcall;
184 ldbm_Env->db_errpfx = "==>";
186 envFlags = DB_CREATE | DB_USE_ENVIRON;
188 /* add optional flags */
190 envFlags |= DB_PRIVATE;
192 #ifdef HAVE_BERKELEY_DB_THREAD
193 envFlags |= DB_THREAD;
196 err = db_appinit( home, NULL, ldbm_Env, envFlags );
200 syslog( LOG_INFO, "ldbm_initialize(): "
201 "FATAL error (%d) in db_appinit()\n", err );
212 int ldbm_shutdown( void )
214 if( !ldbm_initialized ) return 1;
216 #if DB_VERSION_MAJOR < 3
217 db_appexit( ldbm_Env );
224 #else /* some DB other than Berkeley V2 or greater */
226 int ldbm_initialize( const char * home )
228 if(ldbm_initialized++) return 1;
235 int ldbm_shutdown( void )
237 if( !ldbm_initialized ) return 1;
244 #endif /* HAVE_BERKELEY_DB */
246 #if defined( HAVE_BERKELEY_DB ) && (DB_VERSION_MAJOR >= 3)
248 DB_ENV *ldbm_initialize_env(const char *home, int dbcachesize, int *envdirok)
257 err = db_env_create( &env, 0 );
261 syslog( LOG_INFO, "ldbm_initialize_env(): "
262 "FATAL error in db_env_create() : %s (%d)\n",
263 db_strerror( err ), err );
268 #if DB_VERSION_X >= 0x030300
269 /* This interface appeared in 3.3 */
270 env->set_alloc( env, ldbm_malloc, NULL, NULL );
273 env->set_errcall( env, ldbm_db_errcall );
274 env->set_errpfx( env, "==>" );
276 env->set_cachesize( env, 0, dbcachesize, 0 );
279 envFlags = DB_CREATE | DB_INIT_MPOOL | DB_USE_ENVIRON;
281 envFlags |= DB_PRIVATE;
283 #ifdef DB_MPOOL_PRIVATE
284 envFlags |= DB_MPOOL_PRIVATE;
286 #ifdef HAVE_BERKELEY_DB_THREAD
287 envFlags |= DB_THREAD;
291 strncpy(n2, home, sizeof(n2)-1);
292 n2[sizeof(n2)-1] = '\0';
296 #if DB_VERSION_X >= 0x030100
297 err = env->open( env, home, envFlags, 0 );
299 /* 3.0.x requires an extra argument */
300 err = env->open( env, home, NULL, envFlags, 0 );
305 syslog( LOG_INFO, "ldbm_initialize_env(): "
306 "FATAL error in dbEnv->open() : %s (%d)\n",
307 db_strerror( err ), err );
309 env->close( env, 0 );
317 void ldbm_shutdown_env(DB_ENV *env)
319 env->close( env, 0 );
324 DB_ENV *ldbm_initialize_env(const char *home, int dbcachesize, int *envdirok)
329 void ldbm_shutdown_env(DB_ENV *env)
335 #if defined( LDBM_USE_DBHASH ) || defined( LDBM_USE_DBBTREE )
337 /*****************************************************************
339 * use berkeley db hash or btree package *
341 *****************************************************************/
344 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
351 #if DB_VERSION_MAJOR >= 3
356 err = db_create( &ret, env, 0 );
358 (void)ret->close(ret, 0);
364 #if DB_VERSION_X < 0x030300
365 ret->set_malloc( ret, ldbm_malloc );
368 ret->set_pagesize( ret, DEFAULT_DB_PAGE_SIZE );
370 /* likely should use ber_mem* routines */
373 strncpy(n2, name, sizeof(n2)-1);
374 n2[sizeof(n2)-1] = '\0';
378 #if DB_VERSION_X >= 0x040111
379 err = ret->open( ret, NULL, name, NULL, DB_TYPE, rw, mode);
381 err = ret->open( ret, name, NULL, DB_TYPE, rw, mode);
386 (void)ret->close(ret, 0);
395 #elif DB_VERSION_MAJOR >= 2
398 memset( &dbinfo, '\0', sizeof( dbinfo ));
400 #if DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR == 4
402 * BerkeleyDB 2.4 do not allow db_cachesize
403 * to be specified if an DB_ENV is.
406 /* set db_cachesize of MPOOL is NOT being used. */
407 if (( ldbm_Env == NULL ) || ( ldbm_Env->mp_info == NULL )) {
408 dbinfo.db_cachesize = dbcachesize;
412 dbinfo.db_pagesize = DEFAULT_DB_PAGE_SIZE;
413 dbinfo.db_malloc = ldbm_malloc;
416 (void) db_open( name, DB_TYPE, rw, mode, ldbm_Env, &dbinfo, &ret );
424 if ( DB_TYPE == DB_HASH ) {
425 memset( (char *) &hinfo, '\0', sizeof(hinfo) );
426 hinfo.cachesize = dbcachesize;
428 } else if ( DB_TYPE == DB_BTREE ) {
429 memset( (char *) &binfo, '\0', sizeof(binfo) );
430 binfo.cachesize = dbcachesize;
437 ret = dbopen( name, rw, mode, DB_TYPE, info );
445 ldbm_close( LDBM ldbm )
448 #if DB_VERSION_MAJOR >= 2
449 ldbm->close( ldbm, 0 );
457 ldbm_sync( LDBM ldbm )
460 (*ldbm->sync)( ldbm, 0 );
465 ldbm_fetch( LDBM ldbm, Datum key )
472 #if DB_VERSION_MAJOR >= 2
473 ldbm_datum_init( data );
475 data.flags = DB_DBT_MALLOC;
477 if ( (rc = ldbm->get( ldbm, NULL, &key, &data, 0 )) != 0 ) {
478 ldbm_datum_free( ldbm, data );
483 if ( (rc = ldbm->get( ldbm, &key, &data, 0 )) == 0 ) {
484 /* Berkeley DB 1.85 don't malloc the data for us */
485 /* duplicate it for to ensure reentrancy */
486 data = ldbm_datum_dup( ldbm, data );
499 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
505 #if DB_VERSION_MAJOR >= 2
506 rc = ldbm->put( ldbm, NULL, &key, &data, flags & ~LDBM_SYNC );
509 rc = ldbm->put( ldbm, &key, &data, flags & ~LDBM_SYNC );
512 if ( flags & LDBM_SYNC )
513 ldbm->sync( ldbm, 0 );
521 ldbm_delete( LDBM ldbm, Datum key )
527 #if DB_VERSION_MAJOR >= 2
528 rc = ldbm->del( ldbm, NULL, &key, 0 );
531 rc = ldbm->del( ldbm, &key, 0 );
533 ldbm->sync( ldbm, 0 );
541 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbch )
546 #if DB_VERSION_MAJOR >= 2
549 ldbm_datum_init( key );
550 ldbm_datum_init( data );
552 key.flags = data.flags = DB_DBT_MALLOC;
556 /* acquire a cursor for the DB */
557 # if DB_VERSION_X >= 0x020600
558 rc = ldbm->cursor( ldbm, NULL, &dbci, 0 );
560 rc = ldbm->cursor( ldbm, NULL, &dbci );
567 if ( dbci->c_get( dbci, &key, &data, DB_NEXT ) == 0 ) {
568 ldbm_datum_free( ldbm, data );
580 rc = ldbm->seq( ldbm, &key, &data, R_FIRST );
583 key = ldbm_datum_dup( ldbm, key );
596 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
603 #if DB_VERSION_MAJOR >= 2
604 ldbm_datum_init( data );
606 ldbm_datum_free( ldbm, key );
607 key.flags = data.flags = DB_DBT_MALLOC;
609 rc = dbcp->c_get( dbcp, &key, &data, DB_NEXT );
611 ldbm_datum_free( ldbm, data );
614 rc = ldbm->seq( ldbm, &key, &data, R_NEXT );
617 key = ldbm_datum_dup( ldbm, key );
630 ldbm_errno( LDBM ldbm )
635 /******************************************************************
637 * END Berkeley section *
639 ******************************************************************/
641 #elif defined( HAVE_GDBM )
643 #ifdef HAVE_ST_BLKSIZE
644 #include <sys/stat.h>
647 /*****************************************************************
651 *****************************************************************/
654 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
657 #ifdef HAVE_ST_BLKSIZE
663 strncpy(n2, name, sizeof(n2)-1);
664 n2[sizeof(n2)-1] = '\0';
671 if ( (db = gdbm_open( name, 0, rw | GDBM_FAST, mode, 0 )) == NULL ) {
676 #ifdef HAVE_ST_BLKSIZE
677 if ( dbcachesize > 0 && stat( name, &st ) == 0 ) {
678 dbcachesize /= st.st_blksize;
679 if( dbcachesize == 0 ) dbcachesize = 1;
680 gdbm_setopt( db, GDBM_CACHESIZE, &dbcachesize, sizeof(int) );
683 if ( dbcachesize > 0 ) {
685 if( dbcachesize == 0 ) dbcachesize = 1;
686 gdbm_setopt( db, GDBM_CACHESIZE, &dbcachesize, sizeof(int) );
696 ldbm_close( LDBM ldbm )
704 ldbm_sync( LDBM ldbm )
712 ldbm_fetch( LDBM ldbm, Datum key )
717 d = gdbm_fetch( ldbm, key );
724 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
729 rc = gdbm_store( ldbm, key, data, flags & ~LDBM_SYNC );
730 if ( flags & LDBM_SYNC )
738 ldbm_delete( LDBM ldbm, Datum key )
743 rc = gdbm_delete( ldbm, key );
751 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbcp )
756 d = gdbm_firstkey( ldbm );
759 if ( d.dptr != NULL ) {
760 *dbcp = (Datum *) malloc( sizeof( Datum ) );
761 **dbcp = ldbm_datum_dup( ldbm, d );
768 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
773 d = gdbm_nextkey( ldbm, *dbcp );
776 ldbm_datum_free( ldbm, *dbcp );
778 if ( d.dptr != NULL ) {
779 *dbcp = ldbm_datum_dup( ldbm, d );
788 ldbm_errno( LDBM ldbm )
801 /* MMAPED DBM HASHING DATABASE */
803 #include <ac/string.h>
805 /* #define MDBM_DEBUG */
812 /* #define MDBM_CHAIN */
818 #define mdbm_store mdbm_chain_store
819 #define mdbm_fetch mdbm_chain_fetch
820 #define mdbm_delete mdbm_chain_delete
821 #define mdbm_first mdbm_chain_first
822 #define mdbm_next mdbm_chain_next
826 #define MDBM_PG_SZ (4*1024)
828 /*****************************************************************
832 *****************************************************************/
835 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
841 "==>(mdbm)ldbm_open(name=%s,rw=%x,mode=%x,cachesize=%d)\n",
842 name ? name : "NULL", rw, mode, dbcachesize );
846 LDBM_WLOCK; /* We need locking here, this is the only non-thread
847 * safe function we have. */
849 if ( (db = mdbm_open( name, rw, mode, MDBM_PG_SZ )) == NULL ) {
852 fprintf( stdout, "<==(mdbm)ldbm_open(db=NULL)\n" );
859 (void)mdbm_set_chain(db);
865 fprintf( stdout, "<==(mdbm)ldbm_open(db=%p)\n", db );
873 ldbm_close( LDBM ldbm )
875 /* Open and close are not reentrant so we need to use locks here */
879 "==>(mdbm)ldbm_close(db=%p)\n", ldbm );
888 fprintf( stdout, "<==(mdbm)ldbm_close()\n" );
894 ldbm_sync( LDBM ldbm )
896 /* XXX: Not sure if this is re-entrant need to check code, if so
897 * you can leave LOCKS out.
905 #define MAX_MDBM_RETRY 5
908 ldbm_fetch( LDBM ldbm, Datum key )
914 /* This hack is needed because MDBM does not take keys
915 * which begin with NULL when working in the chaining
920 k.key.dsize = key.dsize + 1;
921 k.key.dptr = malloc(k.key.dsize);
923 AC_MEMCPY( (void *)(k.key.dptr + 1), key.dptr, key.dsize );
933 d = mdbm_fetch( ldbm, k );
936 if ( k.val.dptr != NULL ) {
940 if ( (k.val.dptr = malloc( d.dsize )) != NULL ) {
941 k.val.dsize = d.dsize;
942 d = mdbm_fetch( ldbm, k );
948 }/* if ( d.dsize > 0 ) */
949 } while ((d.dsize > k.val.dsize) && (++retry < MAX_MDBM_RETRY));
960 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
963 Datum int_key; /* Internal key */
967 "==>(mdbm)ldbm_store(db=%p, key(dptr=%p,sz=%d), data(dptr=%p,sz=%d), flags=%x)\n",
968 ldbm, key.dptr, key.dsize, data.dptr, data.dsize, flags );
975 int_key.dsize = key.dsize + 1;
976 int_key.dptr = malloc( int_key.dsize );
977 *(int_key.dptr) = 'l'; /* Must not be NULL !*/
978 AC_MEMCPY( (void *)(int_key.dptr + 1), key.dptr, key.dsize );
983 rc = mdbm_store( ldbm, int_key, data, flags );
984 if ( flags & LDBM_SYNC ) {
991 fprintf( stdout, "<==(mdbm)ldbm_store(rc=%d)\n", rc );
1003 ldbm_delete( LDBM ldbm, Datum key )
1011 int_key.dsize = key.dsize + 1;
1012 int_key.dptr = malloc(int_key.dsize);
1013 *(int_key.dptr) = 'l';
1014 AC_MEMCPY( (void *)(int_key.dptr + 1), key.dptr, key.dsize );
1019 rc = mdbm_delete( ldbm, int_key );
1030 ldbm_get_next( LDBM ldbm, kvpair (*fptr)(MDBM *, kvpair) )
1035 size_t sz = MDBM_PAGE_SIZE(ldbm);
1044 in.key.dsize = sz; /* Assume first key in one pg */
1045 in.key.dptr = malloc(sz);
1047 in.val.dptr = NULL; /* Don't need data just key */
1053 out = fptr( ldbm, in );
1055 if (out.key.dsize > 0) {
1056 ret.dsize = out.key.dsize - delta;
1058 if ((ret.dptr = (char *)malloc(ret.dsize)) == NULL) {
1063 AC_MEMCPY(ret.dptr, (void *)(out.key.dptr + delta),
1075 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbcp )
1077 return ldbm_get_next( ldbm, mdbm_first );
1081 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
1084 * don't know if this will affect the LDAP server operation
1085 * but mdbm cannot take and input key.
1088 return ldbm_get_next( ldbm, mdbm_next );
1092 ldbm_errno( LDBM ldbm )
1094 /* XXX: best we can do with current mdbm interface */
1098 #elif defined( HAVE_NDBM )
1100 /*****************************************************************
1102 * if no gdbm or mdbm, fall back to using ndbm, the standard unix thing *
1104 *****************************************************************/
1108 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
1113 ldbm = dbm_open( name, rw, mode );
1120 ldbm_close( LDBM ldbm )
1129 ldbm_sync( LDBM ldbm )
1135 ldbm_fetch( LDBM ldbm, Datum key )
1140 d = ldbm_datum_dup( ldbm, dbm_fetch( ldbm, key ) );
1147 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
1152 rc = dbm_store( ldbm, key, data, flags );
1159 ldbm_delete( LDBM ldbm, Datum key )
1164 rc = dbm_delete( ldbm, key );
1171 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbcp )
1176 d = dbm_firstkey( ldbm );
1183 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
1188 d = dbm_nextkey( ldbm );
1195 ldbm_errno( LDBM ldbm )
1200 err = dbm_error( ldbm );