2011-09-14 07:22:13 +09:30
/*
Unix SMB / CIFS implementation .
trivial database library
Copyright ( C ) Andrew Tridgell 1999 - 2005
Copyright ( C ) Paul ` Rusty ' Russell 2000
Copyright ( C ) Jeremy Allison 2000 - 2003
* * NOTE ! The following LGPL license applies to the tdb
* * library . This does NOT imply that all of Samba is released
* * under the LGPL
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
version 3 of the License , or ( at your option ) any later version .
This library is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
*/
2011-09-14 07:47:13 +09:30
# include <assert.h>
2011-09-14 07:22:13 +09:30
# include "tdb1_private.h"
2011-09-14 07:49:13 +09:30
# include <assert.h>
2011-09-14 07:22:13 +09:30
/* We use two hashes to double-check they're using the right hash function. */
2011-09-14 07:43:13 +09:30
void tdb1_header_hash ( struct tdb_context * tdb ,
2011-09-14 07:22:13 +09:30
uint32_t * magic1_hash , uint32_t * magic2_hash )
{
uint32_t tdb1_magic = TDB1_MAGIC ;
2011-09-14 07:41:13 +09:30
* magic1_hash = tdb_hash ( tdb , TDB_MAGIC_FOOD , sizeof ( TDB_MAGIC_FOOD ) ) ;
* magic2_hash = tdb_hash ( tdb , TDB1_CONV ( tdb1_magic ) , sizeof ( tdb1_magic ) ) ;
2011-09-14 07:22:13 +09:30
/* Make sure at least one hash is non-zero! */
if ( * magic1_hash = = 0 & & * magic2_hash = = 0 )
* magic1_hash = 1 ;
}
2011-09-14 08:06:13 +09:30
static void tdb_context_init ( struct tdb_context * tdb ,
struct tdb_attribute_tdb1_max_dead * max_dead )
2011-09-14 07:49:13 +09:30
{
assert ( tdb - > flags & TDB_VERSION1 ) ;
tdb1_io_init ( tdb ) ;
tdb - > tdb1 . traverse_read = tdb - > tdb1 . traverse_write = 0 ;
memset ( & tdb - > tdb1 . travlocks , 0 , sizeof ( tdb - > tdb1 . travlocks ) ) ;
tdb - > tdb1 . transaction = NULL ;
/* cache the page size */
tdb - > tdb1 . page_size = getpagesize ( ) ;
if ( tdb - > tdb1 . page_size < = 0 ) {
tdb - > tdb1 . page_size = 0x2000 ;
}
2011-09-14 08:06:13 +09:30
if ( max_dead ) {
tdb - > tdb1 . max_dead_records = max_dead - > max_dead ;
} else {
tdb - > tdb1 . max_dead_records = 0 ;
}
2011-09-14 07:49:13 +09:30
}
/* initialise a new database */
enum TDB_ERROR tdb1_new_database ( struct tdb_context * tdb ,
2011-09-14 08:06:13 +09:30
struct tdb_attribute_tdb1_hashsize * hashsize ,
struct tdb_attribute_tdb1_max_dead * max_dead )
2011-09-14 07:22:13 +09:30
{
struct tdb1_header * newdb ;
size_t size ;
2011-09-14 07:49:13 +09:30
int hash_size = TDB1_DEFAULT_HASH_SIZE ;
2011-09-14 08:13:26 +09:30
enum TDB_ERROR ret ;
2011-09-14 07:49:13 +09:30
2011-09-14 08:06:13 +09:30
tdb_context_init ( tdb , max_dead ) ;
2011-09-14 07:49:13 +09:30
/* Default TDB2 hash becomes default TDB1 hash. */
if ( tdb - > hash_fn = = tdb_jenkins_hash )
tdb - > hash_fn = tdb1_old_hash ;
if ( hashsize )
hash_size = hashsize - > hsize ;
2011-09-14 07:22:13 +09:30
/* We make it up in memory, then write it out if not internal */
size = sizeof ( struct tdb1_header ) + ( hash_size + 1 ) * sizeof ( tdb1_off_t ) ;
if ( ! ( newdb = ( struct tdb1_header * ) calloc ( size , 1 ) ) ) {
2011-09-14 08:13:26 +09:30
return tdb_logerr ( tdb , TDB_ERR_OOM , TDB_LOG_ERROR ,
" Could not allocate new database header " ) ;
2011-09-14 07:22:13 +09:30
}
/* Fill in the header */
newdb - > version = TDB1_VERSION ;
newdb - > hash_size = hash_size ;
tdb1_header_hash ( tdb , & newdb - > magic1_hash , & newdb - > magic2_hash ) ;
/* Make sure older tdbs (which don't check the magic hash fields)
* will refuse to open this TDB . */
2011-09-14 07:34:13 +09:30
if ( tdb - > hash_fn = = tdb1_incompatible_hash )
2011-09-14 07:22:13 +09:30
newdb - > rwlocks = TDB1_HASH_RWLOCK_MAGIC ;
2011-09-14 07:49:13 +09:30
memcpy ( & tdb - > tdb1 . header , newdb , sizeof ( tdb - > tdb1 . header ) ) ;
/* This creates an endian-converted db. */
TDB1_CONV ( * newdb ) ;
/* Don't endian-convert the magic food! */
memcpy ( newdb - > magic_food , TDB_MAGIC_FOOD , strlen ( TDB_MAGIC_FOOD ) + 1 ) ;
2011-09-14 07:35:13 +09:30
if ( tdb - > flags & TDB_INTERNAL ) {
2011-09-14 07:37:13 +09:30
tdb - > file - > map_size = size ;
tdb - > file - > map_ptr = ( char * ) newdb ;
2011-09-14 07:49:13 +09:30
return TDB_SUCCESS ;
2011-09-14 07:22:13 +09:30
}
2011-09-14 08:13:26 +09:30
if ( lseek ( tdb - > file - > fd , 0 , SEEK_SET ) = = - 1 ) {
ret = tdb_logerr ( tdb , TDB_ERR_IO , TDB_LOG_ERROR ,
" tdb1_new_database: lseek failed " ) ;
2011-09-14 07:22:13 +09:30
goto fail ;
2011-09-14 08:13:26 +09:30
}
2011-09-14 07:22:13 +09:30
2011-09-14 08:13:26 +09:30
if ( ftruncate ( tdb - > file - > fd , 0 ) = = - 1 ) {
ret = tdb_logerr ( tdb , TDB_ERR_IO , TDB_LOG_ERROR ,
" tdb1_new_database: ftruncate failed " ) ;
2011-09-14 07:22:13 +09:30
goto fail ;
2011-09-14 08:13:26 +09:30
}
2011-09-14 07:22:13 +09:30
2011-09-14 08:13:26 +09:30
if ( ! tdb1_write_all ( tdb - > file - > fd , newdb , size ) ) {
ret = tdb_logerr ( tdb , TDB_ERR_IO , TDB_LOG_ERROR ,
" tdb1_new_database: write failed " ) ;
goto fail ;
}
ret = TDB_SUCCESS ;
2011-09-14 07:22:13 +09:30
fail :
SAFE_FREE ( newdb ) ;
return ret ;
}
2011-09-14 07:46:13 +09:30
typedef void ( * tdb1_log_func ) ( struct tdb_context * , enum tdb_log_level , enum TDB_ERROR ,
const char * , void * ) ;
typedef uint64_t ( * tdb1_hash_func ) ( const void * key , size_t len , uint64_t seed ,
void * data ) ;
2011-09-14 07:22:13 +09:30
2011-09-14 07:46:13 +09:30
struct tdb1_logging_context {
tdb1_log_func log_fn ;
void * log_private ;
} ;
2011-09-14 07:22:13 +09:30
2011-09-14 07:43:13 +09:30
static bool hash_correct ( struct tdb_context * tdb ,
2011-09-14 07:34:13 +09:30
uint32_t * m1 , uint32_t * m2 )
2011-09-14 07:22:13 +09:30
{
2011-09-14 07:49:13 +09:30
/* older TDB without magic hash references */
if ( tdb - > tdb1 . header . magic1_hash = = 0
& & tdb - > tdb1 . header . magic2_hash = = 0 ) {
return true ;
}
2011-09-14 07:22:13 +09:30
tdb1_header_hash ( tdb , m1 , m2 ) ;
2011-09-14 07:43:13 +09:30
return ( tdb - > tdb1 . header . magic1_hash = = * m1 & &
tdb - > tdb1 . header . magic2_hash = = * m2 ) ;
2011-09-14 07:34:13 +09:30
}
2011-09-14 07:22:13 +09:30
2011-09-14 07:43:13 +09:30
static bool check_header_hash ( struct tdb_context * tdb ,
2011-09-14 07:34:13 +09:30
uint32_t * m1 , uint32_t * m2 )
{
if ( hash_correct ( tdb , m1 , m2 ) )
return true ;
2011-09-14 07:22:13 +09:30
2011-09-14 07:34:13 +09:30
/* If they use one inbuilt, try the other inbuilt hash. */
2011-09-14 07:22:13 +09:30
if ( tdb - > hash_fn = = tdb1_old_hash )
2011-09-14 07:34:13 +09:30
tdb - > hash_fn = tdb1_incompatible_hash ;
else if ( tdb - > hash_fn = = tdb1_incompatible_hash )
2011-09-14 07:22:13 +09:30
tdb - > hash_fn = tdb1_old_hash ;
2011-09-14 07:34:13 +09:30
else
return false ;
return hash_correct ( tdb , m1 , m2 ) ;
2011-09-14 07:22:13 +09:30
}
2011-09-14 07:49:13 +09:30
/* We are hold the TDB open lock on tdb->fd. */
2011-09-14 08:06:13 +09:30
enum TDB_ERROR tdb1_open ( struct tdb_context * tdb ,
struct tdb_attribute_tdb1_max_dead * max_dead )
2011-09-14 07:22:13 +09:30
{
const char * hash_alg ;
uint32_t magic1 , magic2 ;
2011-09-14 07:49:13 +09:30
tdb - > flags | = TDB_VERSION1 ;
2011-09-14 07:22:13 +09:30
2011-09-14 08:06:13 +09:30
tdb_context_init ( tdb , max_dead ) ;
2011-09-14 07:22:13 +09:30
2011-09-14 07:49:13 +09:30
/* Default TDB2 hash becomes default TDB1 hash. */
if ( tdb - > hash_fn = = tdb_jenkins_hash ) {
2011-09-14 07:34:13 +09:30
tdb - > hash_fn = tdb1_old_hash ;
hash_alg = " default " ;
2011-09-14 07:49:13 +09:30
} else if ( tdb - > hash_fn = = tdb1_incompatible_hash )
hash_alg = " tdb1_incompatible_hash " ;
else
hash_alg = " the user defined " ;
if ( tdb - > tdb1 . header . version ! = TDB1_BYTEREV ( TDB1_VERSION ) ) {
if ( tdb - > flags & TDB_CONVERT ) {
return tdb_logerr ( tdb , TDB_ERR_IO , TDB_LOG_ERROR ,
" tdb1_open: "
" %s does not need TDB_CONVERT " ,
tdb - > name ) ;
2011-09-14 07:22:13 +09:30
}
2011-09-14 07:49:13 +09:30
} else {
2011-09-14 07:35:13 +09:30
tdb - > flags | = TDB_CONVERT ;
2011-09-14 07:43:13 +09:30
tdb1_convert ( & tdb - > tdb1 . header , sizeof ( tdb - > tdb1 . header ) ) ;
2011-09-14 07:22:13 +09:30
}
2011-09-14 07:43:13 +09:30
if ( tdb - > tdb1 . header . rwlocks ! = 0 & &
tdb - > tdb1 . header . rwlocks ! = TDB1_HASH_RWLOCK_MAGIC ) {
2011-09-14 07:49:13 +09:30
return tdb_logerr ( tdb , TDB_ERR_CORRUPT , TDB_LOG_ERROR ,
" tdb1_open: spinlocks no longer supported " ) ;
2011-09-14 07:22:13 +09:30
}
2011-09-14 07:49:13 +09:30
if ( ! check_header_hash ( tdb , & magic1 , & magic2 ) ) {
return tdb_logerr ( tdb , TDB_ERR_CORRUPT , TDB_LOG_USE_ERROR ,
" tdb1_open: "
2011-09-14 07:32:13 +09:30
" %s was not created with %s hash function we are using \n "
" magic1_hash[0x%08X %s 0x%08X] "
" magic2_hash[0x%08X %s 0x%08X] " ,
2011-09-14 07:49:13 +09:30
tdb - > name , hash_alg ,
2011-09-14 07:43:13 +09:30
tdb - > tdb1 . header . magic1_hash ,
( tdb - > tdb1 . header . magic1_hash = = magic1 ) ? " == " : " != " ,
2011-09-14 07:32:13 +09:30
magic1 ,
2011-09-14 07:43:13 +09:30
tdb - > tdb1 . header . magic2_hash ,
( tdb - > tdb1 . header . magic2_hash = = magic2 ) ? " == " : " != " ,
2011-09-14 07:32:13 +09:30
magic2 ) ;
2011-09-14 07:22:13 +09:30
}
2011-09-14 07:49:13 +09:30
return TDB_SUCCESS ;
2011-09-14 07:46:13 +09:30
}