1/*	$NetBSD$	*/
2
3/* dbcache.c - manage cache of open databases */
4/* OpenLDAP: pkg/ldap/servers/slapd/back-bdb/dbcache.c,v 1.43.2.9 2010/04/13 20:23:24 kurt Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2000-2010 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 "portable.h"
20
21#include <stdio.h>
22
23#include <ac/errno.h>
24#include <ac/socket.h>
25#include <ac/string.h>
26#include <ac/time.h>
27#include <sys/stat.h>
28
29#include "slap.h"
30#include "back-bdb.h"
31#include "lutil_hash.h"
32
33#ifdef BDB_INDEX_USE_HASH
34/* Pass-thru hash function. Since the indexer is already giving us hash
35 * values as keys, we don't need BDB to re-hash them.
36 */
37static u_int32_t
38bdb_db_hash(
39	DB *db,
40	const void *bytes,
41	u_int32_t length
42)
43{
44	u_int32_t ret = 0;
45	unsigned char *dst = (unsigned char *)&ret;
46	const unsigned char *src = (const unsigned char *)bytes;
47
48	if ( length > sizeof(u_int32_t) )
49		length = sizeof(u_int32_t);
50
51	while ( length ) {
52		*dst++ = *src++;
53		length--;
54	}
55	return ret;
56}
57#define	BDB_INDEXTYPE	DB_HASH
58#else
59#define	BDB_INDEXTYPE	DB_BTREE
60#endif
61
62/* If a configured size is found, return it, otherwise return 0 */
63int
64bdb_db_findsize(
65	struct bdb_info *bdb,
66	struct berval *name
67)
68{
69	struct bdb_db_pgsize *bp;
70	int rc;
71
72	for ( bp = bdb->bi_pagesizes; bp; bp=bp->bdp_next ) {
73		rc = strncmp( name->bv_val, bp->bdp_name.bv_val, name->bv_len );
74		if ( !rc ) {
75			if ( name->bv_len == bp->bdp_name.bv_len )
76				return bp->bdp_size;
77			if ( name->bv_len < bp->bdp_name.bv_len &&
78				bp->bdp_name.bv_val[name->bv_len] == '.' )
79				return bp->bdp_size;
80		}
81	}
82	return 0;
83}
84
85int
86bdb_db_cache(
87	Backend	*be,
88	struct berval *name,
89	DB **dbout )
90{
91	int i, flags;
92	int rc;
93	struct bdb_info *bdb = (struct bdb_info *) be->be_private;
94	struct bdb_db_info *db;
95	char *file;
96
97	*dbout = NULL;
98
99	for( i=BDB_NDB; i < bdb->bi_ndatabases; i++ ) {
100		if( !ber_bvcmp( &bdb->bi_databases[i]->bdi_name, name) ) {
101			*dbout = bdb->bi_databases[i]->bdi_db;
102			return 0;
103		}
104	}
105
106	ldap_pvt_thread_mutex_lock( &bdb->bi_database_mutex );
107
108	/* check again! may have been added by another thread */
109	for( i=BDB_NDB; i < bdb->bi_ndatabases; i++ ) {
110		if( !ber_bvcmp( &bdb->bi_databases[i]->bdi_name, name) ) {
111			*dbout = bdb->bi_databases[i]->bdi_db;
112			ldap_pvt_thread_mutex_unlock( &bdb->bi_database_mutex );
113			return 0;
114		}
115	}
116
117	if( i >= BDB_INDICES ) {
118		ldap_pvt_thread_mutex_unlock( &bdb->bi_database_mutex );
119		return -1;
120	}
121
122	db = (struct bdb_db_info *) ch_calloc(1, sizeof(struct bdb_db_info));
123
124	ber_dupbv( &db->bdi_name, name );
125
126	rc = db_create( &db->bdi_db, bdb->bi_dbenv, 0 );
127	if( rc != 0 ) {
128		Debug( LDAP_DEBUG_ANY,
129			"bdb_db_cache: db_create(%s) failed: %s (%d)\n",
130			bdb->bi_dbenv_home, db_strerror(rc), rc );
131		ldap_pvt_thread_mutex_unlock( &bdb->bi_database_mutex );
132		ch_free( db );
133		return rc;
134	}
135
136	if( !BER_BVISNULL( &bdb->bi_db_crypt_key )) {
137		rc = db->bdi_db->set_flags( db->bdi_db, DB_ENCRYPT );
138		if ( rc ) {
139			Debug( LDAP_DEBUG_ANY,
140				"bdb_db_cache: db set_flags(DB_ENCRYPT)(%s) failed: %s (%d)\n",
141				bdb->bi_dbenv_home, db_strerror(rc), rc );
142			ldap_pvt_thread_mutex_unlock( &bdb->bi_database_mutex );
143			db->bdi_db->close( db->bdi_db, 0 );
144			ch_free( db );
145			return rc;
146		}
147	}
148
149	if( bdb->bi_flags & BDB_CHKSUM ) {
150		rc = db->bdi_db->set_flags( db->bdi_db, DB_CHKSUM );
151		if ( rc ) {
152			Debug( LDAP_DEBUG_ANY,
153				"bdb_db_cache: db set_flags(DB_CHKSUM)(%s) failed: %s (%d)\n",
154				bdb->bi_dbenv_home, db_strerror(rc), rc );
155			ldap_pvt_thread_mutex_unlock( &bdb->bi_database_mutex );
156			db->bdi_db->close( db->bdi_db, 0 );
157			ch_free( db );
158			return rc;
159		}
160	}
161
162	/* If no explicit size set, use the FS default */
163	flags = bdb_db_findsize( bdb, name );
164	if ( flags )
165		rc = db->bdi_db->set_pagesize( db->bdi_db, flags );
166
167#ifdef BDB_INDEX_USE_HASH
168	rc = db->bdi_db->set_h_hash( db->bdi_db, bdb_db_hash );
169#endif
170	rc = db->bdi_db->set_flags( db->bdi_db, DB_DUP | DB_DUPSORT );
171
172	file = ch_malloc( db->bdi_name.bv_len + sizeof(BDB_SUFFIX) );
173	strcpy( file, db->bdi_name.bv_val );
174	strcpy( file+db->bdi_name.bv_len, BDB_SUFFIX );
175
176#ifdef HAVE_EBCDIC
177	__atoe( file );
178#endif
179	flags = DB_CREATE | DB_THREAD;
180#ifdef DB_AUTO_COMMIT
181	if ( !( slapMode & SLAP_TOOL_QUICK ))
182		flags |= DB_AUTO_COMMIT;
183#endif
184	/* Cannot Truncate when Transactions are in use */
185	if ( (slapMode & (SLAP_TOOL_QUICK|SLAP_TRUNCATE_MODE)) ==
186		(SLAP_TOOL_QUICK|SLAP_TRUNCATE_MODE))
187			flags |= DB_TRUNCATE;
188
189	rc = DB_OPEN( db->bdi_db,
190		file, NULL /* name */,
191		BDB_INDEXTYPE, bdb->bi_db_opflags | flags, bdb->bi_dbenv_mode );
192
193	ch_free( file );
194
195	if( rc != 0 ) {
196		Debug( LDAP_DEBUG_ANY,
197			"bdb_db_cache: db_open(%s) failed: %s (%d)\n",
198			name->bv_val, db_strerror(rc), rc );
199		ldap_pvt_thread_mutex_unlock( &bdb->bi_database_mutex );
200		return rc;
201	}
202
203	bdb->bi_databases[i] = db;
204	bdb->bi_ndatabases = i+1;
205
206	*dbout = db->bdi_db;
207
208	ldap_pvt_thread_mutex_unlock( &bdb->bi_database_mutex );
209	return 0;
210}
211