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;
155 int major, minor, patch;
156 version = db_version( &major, &minor, &patch );
158 if( major != DB_VERSION_MAJOR ||
159 minor < DB_VERSION_MINOR )
163 "ldbm_initialize(): version mismatch\nexpected: %s\ngot: %s\n",
164 DB_VERSION_STRING, version );
170 #if DB_VERSION_MAJOR < 3
171 ldbm_Env = calloc( 1, sizeof( DB_ENV ));
173 if( ldbm_Env == NULL ) return 1;
175 ldbm_Env->db_errcall = ldbm_db_errcall;
176 ldbm_Env->db_errpfx = "==>";
178 envFlags = DB_CREATE | DB_USE_ENVIRON;
180 /* add optional flags */
182 envFlags |= DB_PRIVATE;
184 #ifdef HAVE_BERKELEY_DB_THREAD
185 envFlags |= DB_THREAD;
188 err = db_appinit( home, NULL, ldbm_Env, envFlags );
192 syslog( LOG_INFO, "ldbm_initialize(): "
193 "FATAL error (%d) in db_appinit()\n", err );
204 int ldbm_shutdown( void )
206 if( !ldbm_initialized ) return 1;
208 #if DB_VERSION_MAJOR < 3
209 db_appexit( ldbm_Env );
216 #else /* some DB other than Berkeley V2 or greater */
218 int ldbm_initialize( const char * home )
220 if(ldbm_initialized++) return 1;
227 int ldbm_shutdown( void )
229 if( !ldbm_initialized ) return 1;
236 #endif /* HAVE_BERKELEY_DB */
238 #if defined( HAVE_BERKELEY_DB ) && (DB_VERSION_MAJOR >= 3)
240 DB_ENV *ldbm_initialize_env(const char *home, int dbcachesize, int *envdirok)
249 err = db_env_create( &env, 0 );
253 syslog( LOG_INFO, "ldbm_initialize_env(): "
254 "FATAL error in db_env_create() : %s (%d)\n",
255 db_strerror( err ), err );
260 #if DB_VERSION_X >= 0x030300
261 /* This interface appeared in 3.3 */
262 env->set_alloc( env, ldbm_malloc, NULL, NULL );
265 env->set_errcall( env, ldbm_db_errcall );
266 env->set_errpfx( env, "==>" );
268 env->set_cachesize( env, 0, dbcachesize, 0 );
271 envFlags = DB_CREATE | DB_INIT_MPOOL | DB_USE_ENVIRON;
273 envFlags |= DB_PRIVATE;
275 #ifdef DB_MPOOL_PRIVATE
276 envFlags |= DB_MPOOL_PRIVATE;
278 #ifdef HAVE_BERKELEY_DB_THREAD
279 envFlags |= DB_THREAD;
283 strncpy(n2, home, sizeof(n2)-1);
284 n2[sizeof(n2)-1] = '\0';
288 #if DB_VERSION_X >= 0x030100
289 err = env->open( env, home, envFlags, 0 );
291 /* 3.0.x requires an extra argument */
292 err = env->open( env, home, NULL, envFlags, 0 );
297 syslog( LOG_INFO, "ldbm_initialize_env(): "
298 "FATAL error in dbEnv->open() : %s (%d)\n",
299 db_strerror( err ), err );
301 env->close( env, 0 );
309 void ldbm_shutdown_env(DB_ENV *env)
311 env->close( env, 0 );
316 DB_ENV *ldbm_initialize_env(const char *home, int dbcachesize, int *envdirok)
321 void ldbm_shutdown_env(DB_ENV *env)
327 #if defined( LDBM_USE_DBHASH ) || defined( LDBM_USE_DBBTREE )
329 /*****************************************************************
331 * use berkeley db hash or btree package *
333 *****************************************************************/
336 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
343 #if DB_VERSION_MAJOR >= 3
348 err = db_create( &ret, env, 0 );
350 (void)ret->close(ret, 0);
356 #if DB_VERSION_X < 0x030300
357 ret->set_malloc( ret, ldbm_malloc );
360 ret->set_pagesize( ret, DEFAULT_DB_PAGE_SIZE );
362 /* likely should use ber_mem* routines */
365 strncpy(n2, name, sizeof(n2)-1);
366 n2[sizeof(n2)-1] = '\0';
370 #if DB_VERSION_X >= 0x040111
371 err = ret->open( ret, NULL, name, NULL, DB_TYPE, rw, mode);
373 err = ret->open( ret, name, NULL, DB_TYPE, rw, mode);
378 (void)ret->close(ret, 0);
387 #elif DB_VERSION_MAJOR >= 2
390 memset( &dbinfo, '\0', sizeof( dbinfo ));
392 #if DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR == 4
394 * BerkeleyDB 2.4 do not allow db_cachesize
395 * to be specified if an DB_ENV is.
398 /* set db_cachesize of MPOOL is NOT being used. */
399 if (( ldbm_Env == NULL ) || ( ldbm_Env->mp_info == NULL )) {
400 dbinfo.db_cachesize = dbcachesize;
404 dbinfo.db_pagesize = DEFAULT_DB_PAGE_SIZE;
405 dbinfo.db_malloc = ldbm_malloc;
408 (void) db_open( name, DB_TYPE, rw, mode, ldbm_Env, &dbinfo, &ret );
416 if ( DB_TYPE == DB_HASH ) {
417 memset( (char *) &hinfo, '\0', sizeof(hinfo) );
418 hinfo.cachesize = dbcachesize;
420 } else if ( DB_TYPE == DB_BTREE ) {
421 memset( (char *) &binfo, '\0', sizeof(binfo) );
422 binfo.cachesize = dbcachesize;
429 ret = dbopen( name, rw, mode, DB_TYPE, info );
437 ldbm_close( LDBM ldbm )
440 #if DB_VERSION_MAJOR >= 2
441 ldbm->close( ldbm, 0 );
449 ldbm_sync( LDBM ldbm )
452 (*ldbm->sync)( ldbm, 0 );
457 ldbm_fetch( LDBM ldbm, Datum key )
464 #if DB_VERSION_MAJOR >= 2
465 ldbm_datum_init( data );
467 data.flags = DB_DBT_MALLOC;
469 if ( (rc = ldbm->get( ldbm, NULL, &key, &data, 0 )) != 0 ) {
470 ldbm_datum_free( ldbm, data );
475 if ( (rc = ldbm->get( ldbm, &key, &data, 0 )) == 0 ) {
476 /* Berkeley DB 1.85 don't malloc the data for us */
477 /* duplicate it for to ensure reentrancy */
478 data = ldbm_datum_dup( ldbm, data );
491 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
497 #if DB_VERSION_MAJOR >= 2
498 rc = ldbm->put( ldbm, NULL, &key, &data, flags & ~LDBM_SYNC );
501 rc = ldbm->put( ldbm, &key, &data, flags & ~LDBM_SYNC );
504 if ( flags & LDBM_SYNC )
505 ldbm->sync( ldbm, 0 );
513 ldbm_delete( LDBM ldbm, Datum key )
519 #if DB_VERSION_MAJOR >= 2
520 rc = ldbm->del( ldbm, NULL, &key, 0 );
523 rc = ldbm->del( ldbm, &key, 0 );
525 ldbm->sync( ldbm, 0 );
533 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbch )
538 #if DB_VERSION_MAJOR >= 2
541 ldbm_datum_init( key );
542 ldbm_datum_init( data );
544 key.flags = data.flags = DB_DBT_MALLOC;
548 /* acquire a cursor for the DB */
549 # if DB_VERSION_X >= 0x020600
550 rc = ldbm->cursor( ldbm, NULL, &dbci, 0 );
552 rc = ldbm->cursor( ldbm, NULL, &dbci );
559 if ( dbci->c_get( dbci, &key, &data, DB_NEXT ) == 0 ) {
560 ldbm_datum_free( ldbm, data );
572 rc = ldbm->seq( ldbm, &key, &data, R_FIRST );
575 key = ldbm_datum_dup( ldbm, key );
588 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
595 #if DB_VERSION_MAJOR >= 2
596 ldbm_datum_init( data );
598 ldbm_datum_free( ldbm, key );
599 key.flags = data.flags = DB_DBT_MALLOC;
601 rc = dbcp->c_get( dbcp, &key, &data, DB_NEXT );
603 ldbm_datum_free( ldbm, data );
606 rc = ldbm->seq( ldbm, &key, &data, R_NEXT );
609 key = ldbm_datum_dup( ldbm, key );
622 ldbm_errno( LDBM ldbm )
627 /******************************************************************
629 * END Berkeley section *
631 ******************************************************************/
633 #elif defined( HAVE_GDBM )
635 #ifdef HAVE_ST_BLKSIZE
636 #include <sys/stat.h>
639 /*****************************************************************
643 *****************************************************************/
646 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
649 #ifdef HAVE_ST_BLKSIZE
655 strncpy(n2, name, sizeof(n2)-1);
656 n2[sizeof(n2)-1] = '\0';
663 if ( (db = gdbm_open( name, 0, rw | GDBM_FAST, mode, 0 )) == NULL ) {
668 #ifdef HAVE_ST_BLKSIZE
669 if ( dbcachesize > 0 && stat( name, &st ) == 0 ) {
670 dbcachesize /= st.st_blksize;
671 if( dbcachesize == 0 ) dbcachesize = 1;
672 gdbm_setopt( db, GDBM_CACHESIZE, &dbcachesize, sizeof(int) );
675 if ( dbcachesize > 0 ) {
677 if( dbcachesize == 0 ) dbcachesize = 1;
678 gdbm_setopt( db, GDBM_CACHESIZE, &dbcachesize, sizeof(int) );
688 ldbm_close( LDBM ldbm )
696 ldbm_sync( LDBM ldbm )
704 ldbm_fetch( LDBM ldbm, Datum key )
709 d = gdbm_fetch( ldbm, key );
716 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
721 rc = gdbm_store( ldbm, key, data, flags & ~LDBM_SYNC );
722 if ( flags & LDBM_SYNC )
730 ldbm_delete( LDBM ldbm, Datum key )
735 rc = gdbm_delete( ldbm, key );
743 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbcp )
748 d = gdbm_firstkey( ldbm );
751 if ( d.dptr != NULL ) {
752 *dbcp = (Datum *) malloc( sizeof( Datum ) );
753 **dbcp = ldbm_datum_dup( ldbm, d );
760 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
765 d = gdbm_nextkey( ldbm, *dbcp );
768 ldbm_datum_free( ldbm, *dbcp );
770 if ( d.dptr != NULL ) {
771 *dbcp = ldbm_datum_dup( ldbm, d );
780 ldbm_errno( LDBM ldbm )
793 /* MMAPED DBM HASHING DATABASE */
795 #include <ac/string.h>
797 /* #define MDBM_DEBUG */
804 /* #define MDBM_CHAIN */
810 #define mdbm_store mdbm_chain_store
811 #define mdbm_fetch mdbm_chain_fetch
812 #define mdbm_delete mdbm_chain_delete
813 #define mdbm_first mdbm_chain_first
814 #define mdbm_next mdbm_chain_next
818 #define MDBM_PG_SZ (4*1024)
820 /*****************************************************************
824 *****************************************************************/
827 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
833 "==>(mdbm)ldbm_open(name=%s,rw=%x,mode=%x,cachesize=%d)\n",
834 name ? name : "NULL", rw, mode, dbcachesize );
838 LDBM_WLOCK; /* We need locking here, this is the only non-thread
839 * safe function we have. */
841 if ( (db = mdbm_open( name, rw, mode, MDBM_PG_SZ )) == NULL ) {
844 fprintf( stdout, "<==(mdbm)ldbm_open(db=NULL)\n" );
851 (void)mdbm_set_chain(db);
857 fprintf( stdout, "<==(mdbm)ldbm_open(db=%p)\n", db );
865 ldbm_close( LDBM ldbm )
867 /* Open and close are not reentrant so we need to use locks here */
871 "==>(mdbm)ldbm_close(db=%p)\n", ldbm );
880 fprintf( stdout, "<==(mdbm)ldbm_close()\n" );
886 ldbm_sync( LDBM ldbm )
888 /* XXX: Not sure if this is re-entrant need to check code, if so
889 * you can leave LOCKS out.
897 #define MAX_MDBM_RETRY 5
900 ldbm_fetch( LDBM ldbm, Datum key )
906 /* This hack is needed because MDBM does not take keys
907 * which begin with NULL when working in the chaining
912 k.key.dsize = key.dsize + 1;
913 k.key.dptr = malloc(k.key.dsize);
915 AC_MEMCPY( (void *)(k.key.dptr + 1), key.dptr, key.dsize );
925 d = mdbm_fetch( ldbm, k );
928 if ( k.val.dptr != NULL ) {
932 if ( (k.val.dptr = malloc( d.dsize )) != NULL ) {
933 k.val.dsize = d.dsize;
934 d = mdbm_fetch( ldbm, k );
940 }/* if ( d.dsize > 0 ) */
941 } while ((d.dsize > k.val.dsize) && (++retry < MAX_MDBM_RETRY));
952 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
955 Datum int_key; /* Internal key */
959 "==>(mdbm)ldbm_store(db=%p, key(dptr=%p,sz=%d), data(dptr=%p,sz=%d), flags=%x)\n",
960 ldbm, key.dptr, key.dsize, data.dptr, data.dsize, flags );
967 int_key.dsize = key.dsize + 1;
968 int_key.dptr = malloc( int_key.dsize );
969 *(int_key.dptr) = 'l'; /* Must not be NULL !*/
970 AC_MEMCPY( (void *)(int_key.dptr + 1), key.dptr, key.dsize );
975 rc = mdbm_store( ldbm, int_key, data, flags );
976 if ( flags & LDBM_SYNC ) {
983 fprintf( stdout, "<==(mdbm)ldbm_store(rc=%d)\n", rc );
995 ldbm_delete( LDBM ldbm, Datum key )
1003 int_key.dsize = key.dsize + 1;
1004 int_key.dptr = malloc(int_key.dsize);
1005 *(int_key.dptr) = 'l';
1006 AC_MEMCPY( (void *)(int_key.dptr + 1), key.dptr, key.dsize );
1011 rc = mdbm_delete( ldbm, int_key );
1022 ldbm_get_next( LDBM ldbm, kvpair (*fptr)(MDBM *, kvpair) )
1027 size_t sz = MDBM_PAGE_SIZE(ldbm);
1036 in.key.dsize = sz; /* Assume first key in one pg */
1037 in.key.dptr = malloc(sz);
1039 in.val.dptr = NULL; /* Don't need data just key */
1045 out = fptr( ldbm, in );
1047 if (out.key.dsize > 0) {
1048 ret.dsize = out.key.dsize - delta;
1050 if ((ret.dptr = (char *)malloc(ret.dsize)) == NULL) {
1055 AC_MEMCPY(ret.dptr, (void *)(out.key.dptr + delta),
1067 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbcp )
1069 return ldbm_get_next( ldbm, mdbm_first );
1073 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
1076 * don't know if this will affect the LDAP server operation
1077 * but mdbm cannot take and input key.
1080 return ldbm_get_next( ldbm, mdbm_next );
1084 ldbm_errno( LDBM ldbm )
1086 /* XXX: best we can do with current mdbm interface */
1090 #elif defined( HAVE_NDBM )
1092 /*****************************************************************
1094 * if no gdbm or mdbm, fall back to using ndbm, the standard unix thing *
1096 *****************************************************************/
1100 ldbm_open( DB_ENV *env, char *name, int rw, int mode, int dbcachesize )
1105 ldbm = dbm_open( name, rw, mode );
1112 ldbm_close( LDBM ldbm )
1121 ldbm_sync( LDBM ldbm )
1127 ldbm_fetch( LDBM ldbm, Datum key )
1132 d = ldbm_datum_dup( ldbm, dbm_fetch( ldbm, key ) );
1139 ldbm_store( LDBM ldbm, Datum key, Datum data, int flags )
1144 rc = dbm_store( ldbm, key, data, flags );
1151 ldbm_delete( LDBM ldbm, Datum key )
1156 rc = dbm_delete( ldbm, key );
1163 ldbm_firstkey( LDBM ldbm, LDBMCursor **dbcp )
1168 d = dbm_firstkey( ldbm );
1175 ldbm_nextkey( LDBM ldbm, Datum key, LDBMCursor *dbcp )
1180 d = dbm_nextkey( ldbm );
1187 ldbm_errno( LDBM ldbm )
1192 err = dbm_error( ldbm );