2012-12-13 16:31:59 +04:00
/*
2006-07-11 22:01:26 +04:00
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
2010-03-25 12:19:48 +03:00
2006-07-11 22:01:26 +04:00
* * NOTE ! The following LGPL license applies to the tdb
* * library . This does NOT imply that all of Samba is released
* * under the LGPL
2010-03-25 12:19:48 +03:00
2006-07-11 22:01:26 +04:00
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
2007-07-10 05:44:42 +04:00
version 3 of the License , or ( at your option ) any later version .
2006-07-11 22:01:26 +04:00
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
2007-07-10 07:42:26 +04:00
License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
2006-07-11 22:01:26 +04:00
*/
# include "tdb_private.h"
/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
static struct tdb_context * tdbs = NULL ;
2010-09-13 14:35:59 +04:00
/* We use two hashes to double-check they're using the right hash function. */
void tdb_header_hash ( struct tdb_context * tdb ,
uint32_t * magic1_hash , uint32_t * magic2_hash )
{
TDB_DATA hash_key ;
uint32_t tdb_magic = TDB_MAGIC ;
2010-09-24 10:09:43 +04:00
hash_key . dptr = discard_const_p ( unsigned char , TDB_MAGIC_FOOD ) ;
2010-09-13 14:35:59 +04:00
hash_key . dsize = sizeof ( TDB_MAGIC_FOOD ) ;
* magic1_hash = tdb - > hash_fn ( & hash_key ) ;
2010-09-21 03:01:51 +04:00
hash_key . dptr = ( unsigned char * ) CONVERT ( tdb_magic ) ;
2010-09-13 14:35:59 +04:00
hash_key . dsize = sizeof ( tdb_magic ) ;
* magic2_hash = tdb - > hash_fn ( & hash_key ) ;
/* Make sure at least one hash is non-zero! */
if ( * magic1_hash = = 0 & & * magic2_hash = = 0 )
* magic1_hash = 1 ;
}
2006-07-11 22:01:26 +04:00
/* initialise a new database with a specified hash size */
2012-12-20 19:14:23 +04:00
static int tdb_new_database ( struct tdb_context * tdb , struct tdb_header * header ,
int hash_size )
2006-07-11 22:01:26 +04:00
{
struct tdb_header * newdb ;
2007-07-20 19:00:58 +04:00
size_t size ;
int ret = - 1 ;
2006-07-11 22:01:26 +04:00
/* We make it up in memory, then write it out if not internal */
size = sizeof ( struct tdb_header ) + ( hash_size + 1 ) * sizeof ( tdb_off_t ) ;
2009-10-21 17:39:43 +04:00
if ( ! ( newdb = ( struct tdb_header * ) calloc ( size , 1 ) ) ) {
tdb - > ecode = TDB_ERR_OOM ;
return - 1 ;
}
2006-07-11 22:01:26 +04:00
/* Fill in the header */
newdb - > version = TDB_VERSION ;
newdb - > hash_size = hash_size ;
2010-09-13 14:35:59 +04:00
tdb_header_hash ( tdb , & newdb - > magic1_hash , & newdb - > magic2_hash ) ;
2010-09-24 10:15:11 +04:00
/* Make sure older tdbs (which don't check the magic hash fields)
* will refuse to open this TDB . */
if ( tdb - > flags & TDB_INCOMPATIBLE_HASH )
newdb - > rwlocks = TDB_HASH_RWLOCK_MAGIC ;
2013-02-21 19:34:32 +04:00
/*
* We create a tdb with TDB_FEATURE_FLAG_MUTEX support ,
* the flag combination and runtime feature checks
* are done by the caller already .
*/
if ( tdb - > flags & TDB_MUTEX_LOCKING ) {
newdb - > feature_flags | = TDB_FEATURE_FLAG_MUTEX ;
}
2014-02-05 02:35:53 +04:00
/*
* If we have any features we add the FEATURE_FLAG_MAGIC , overwriting the
* TDB_HASH_RWLOCK_MAGIC above .
*/
if ( newdb - > feature_flags ! = 0 ) {
newdb - > rwlocks = TDB_FEATURE_FLAG_MAGIC ;
}
/*
2023-04-13 14:17:08 +03:00
* It ' s required for some following code paths
2014-02-05 02:35:53 +04:00
* to have the fields on ' tdb ' up - to - date .
2013-02-21 19:34:32 +04:00
*
* E . g . tdb_mutex_size ( ) requires it
2014-02-05 02:35:53 +04:00
*/
tdb - > feature_flags = newdb - > feature_flags ;
2013-02-21 19:34:32 +04:00
tdb - > hash_size = newdb - > hash_size ;
2014-02-05 02:35:53 +04:00
2006-07-11 22:01:26 +04:00
if ( tdb - > flags & TDB_INTERNAL ) {
tdb - > map_size = size ;
tdb - > map_ptr = ( char * ) newdb ;
2012-12-20 19:14:23 +04:00
memcpy ( header , newdb , sizeof ( * header ) ) ;
2006-07-11 22:01:26 +04:00
/* Convert the `ondisk' version if asked. */
CONVERT ( * newdb ) ;
return 0 ;
}
if ( lseek ( tdb - > fd , 0 , SEEK_SET ) = = - 1 )
goto fail ;
if ( ftruncate ( tdb - > fd , 0 ) = = - 1 )
goto fail ;
2013-02-21 19:34:32 +04:00
if ( newdb - > feature_flags & TDB_FEATURE_FLAG_MUTEX ) {
newdb - > mutex_size = tdb_mutex_size ( tdb ) ;
tdb - > hdr_ofs = newdb - > mutex_size ;
}
2006-07-11 22:01:26 +04:00
/* This creates an endian-converted header, as if read from disk */
CONVERT ( * newdb ) ;
2012-12-20 19:14:23 +04:00
memcpy ( header , newdb , sizeof ( * header ) ) ;
2006-07-11 22:01:26 +04:00
/* Don't endian-convert the magic food! */
memcpy ( newdb - > magic_food , TDB_MAGIC_FOOD , strlen ( TDB_MAGIC_FOOD ) + 1 ) ;
2012-12-14 18:53:08 +04:00
if ( ! tdb_write_all ( tdb - > fd , newdb , size ) )
goto fail ;
2013-02-21 19:34:32 +04:00
if ( newdb - > feature_flags & TDB_FEATURE_FLAG_MUTEX ) {
/*
* Now we init the mutex area
* followed by a second header .
*/
ret = ftruncate (
tdb - > fd ,
newdb - > mutex_size + sizeof ( struct tdb_header ) ) ;
if ( ret = = - 1 ) {
goto fail ;
}
ret = tdb_mutex_init ( tdb ) ;
if ( ret = = - 1 ) {
goto fail ;
}
/*
* Write a second header behind the mutexes . That ' s the area
* that will be mmapp ' ed .
*/
ret = lseek ( tdb - > fd , newdb - > mutex_size , SEEK_SET ) ;
if ( ret = = - 1 ) {
goto fail ;
}
if ( ! tdb_write_all ( tdb - > fd , newdb , size ) ) {
goto fail ;
}
}
2012-12-14 18:53:08 +04:00
ret = 0 ;
2006-07-11 22:01:26 +04:00
fail :
SAFE_FREE ( newdb ) ;
return ret ;
}
static int tdb_already_open ( dev_t device ,
ino_t ino )
{
struct tdb_context * i ;
2010-03-25 12:19:48 +03:00
2006-07-11 22:01:26 +04:00
for ( i = tdbs ; i ; i = i - > next ) {
if ( i - > device = = device & & i - > inode = = ino ) {
return 1 ;
}
}
return 0 ;
}
2012-12-13 16:31:59 +04:00
/* open the database, creating it if necessary
2006-07-11 22:01:26 +04:00
The open_flags and mode are passed straight to the open call on the
database file . A flags value of O_WRONLY is invalid . The hash size
is advisory , use zero for a default value .
2012-12-13 16:31:59 +04:00
Return is NULL on error , in which case errno is also set . Don ' t
2006-07-11 22:01:26 +04:00
try to call tdb_error or tdb_errname , just do strerror ( errno ) .
@ param name may be NULL for internal databases . */
2010-10-21 13:51:37 +04:00
_PUBLIC_ struct tdb_context * tdb_open ( const char * name , int hash_size , int tdb_flags ,
2006-07-11 22:01:26 +04:00
int open_flags , mode_t mode )
{
return tdb_open_ex ( name , hash_size , tdb_flags , open_flags , mode , NULL , NULL ) ;
}
/* a default logging function */
2006-10-20 13:55:47 +04:00
static void null_log_fn ( struct tdb_context * tdb , enum tdb_debug_level level , const char * fmt , . . . ) PRINTF_ATTRIBUTE ( 3 , 4 ) ;
static void null_log_fn ( struct tdb_context * tdb , enum tdb_debug_level level , const char * fmt , . . . )
2006-07-11 22:01:26 +04:00
{
}
2010-09-24 10:09:43 +04:00
static bool check_header_hash ( struct tdb_context * tdb ,
2012-12-20 19:14:23 +04:00
struct tdb_header * header ,
2010-09-24 10:09:43 +04:00
bool default_hash , uint32_t * m1 , uint32_t * m2 )
{
tdb_header_hash ( tdb , m1 , m2 ) ;
2012-12-20 19:14:23 +04:00
if ( header - > magic1_hash = = * m1 & &
header - > magic2_hash = = * m2 ) {
2010-09-24 10:09:43 +04:00
return true ;
}
/* If they explicitly set a hash, always respect it. */
if ( ! default_hash )
return false ;
/* Otherwise, try the other inbuilt hash. */
2010-09-24 10:15:11 +04:00
if ( tdb - > hash_fn = = tdb_old_hash )
tdb - > hash_fn = tdb_jenkins_hash ;
else
tdb - > hash_fn = tdb_old_hash ;
2012-12-20 19:14:23 +04:00
return check_header_hash ( tdb , header , false , m1 , m2 ) ;
2010-09-24 10:09:43 +04:00
}
2006-07-11 22:01:26 +04:00
2013-02-21 19:34:32 +04:00
static bool tdb_mutex_open_ok ( struct tdb_context * tdb ,
const struct tdb_header * header )
{
2014-11-04 11:51:51 +03:00
if ( tdb - > flags & TDB_NOLOCK ) {
/*
* We don ' t look at locks , so it does not matter to have a
* compatible mutex implementation . Allow the open .
*/
return true ;
}
2013-02-21 19:34:32 +04:00
if ( ! ( tdb - > flags & TDB_MUTEX_LOCKING ) ) {
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_mutex_open_ok[%s]: "
" Can use mutexes only with "
" MUTEX_LOCKING or NOLOCK \n " ,
tdb - > name ) ) ;
return false ;
}
if ( tdb_mutex_size ( tdb ) ! = header - > mutex_size ) {
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_mutex_open_ok[%s]: "
2018-10-22 08:42:14 +03:00
" Mutex size changed from % " PRIu32 " to %zu \n . " ,
2013-02-21 19:34:32 +04:00
tdb - > name ,
2018-10-22 08:42:14 +03:00
header - > mutex_size ,
tdb_mutex_size ( tdb ) ) ) ;
2013-02-21 19:34:32 +04:00
return false ;
}
return true ;
}
2010-10-21 13:51:37 +04:00
_PUBLIC_ struct tdb_context * tdb_open_ex ( const char * name , int hash_size , int tdb_flags ,
2006-10-20 13:55:47 +04:00
int open_flags , mode_t mode ,
const struct tdb_logging_context * log_ctx ,
tdb_hash_func hash_fn )
2006-07-11 22:01:26 +04:00
{
2014-01-30 19:07:10 +04:00
int orig_errno = errno ;
2018-12-12 23:26:35 +03:00
struct tdb_header header = {
. version = 0 ,
} ;
2006-07-11 22:01:26 +04:00
struct tdb_context * tdb ;
struct stat st ;
2017-08-16 16:21:14 +03:00
int rev = 0 ;
bool locked = false ;
2006-07-11 22:01:26 +04:00
unsigned char * vp ;
2007-08-12 04:55:03 +04:00
uint32_t vertest ;
2008-01-12 02:08:37 +03:00
unsigned v ;
2010-09-13 14:35:59 +04:00
const char * hash_alg ;
2010-09-24 10:09:43 +04:00
uint32_t magic1 , magic2 ;
2013-02-21 19:34:32 +04:00
int ret ;
2006-07-11 22:01:26 +04:00
2006-07-30 14:42:11 +04:00
if ( ! ( tdb = ( struct tdb_context * ) calloc ( 1 , sizeof * tdb ) ) ) {
2006-07-11 22:01:26 +04:00
/* Can't log this */
errno = ENOMEM ;
goto fail ;
}
tdb_io_init ( tdb ) ;
2014-02-10 19:23:48 +04:00
if ( tdb_flags & TDB_INTERNAL ) {
tdb_flags | = TDB_INCOMPATIBLE_HASH ;
}
2013-02-21 19:34:32 +04:00
if ( tdb_flags & TDB_MUTEX_LOCKING ) {
tdb_flags | = TDB_INCOMPATIBLE_HASH ;
}
2014-02-10 19:23:48 +04:00
2006-07-11 22:01:26 +04:00
tdb - > fd = - 1 ;
2009-11-19 11:38:48 +03:00
# ifdef TDB_TRACE
tdb - > tracefd = - 1 ;
# endif
2006-07-11 22:01:26 +04:00
tdb - > name = NULL ;
tdb - > map_ptr = NULL ;
tdb - > flags = tdb_flags ;
tdb - > open_flags = open_flags ;
2006-10-20 13:55:47 +04:00
if ( log_ctx ) {
tdb - > log = * log_ctx ;
} else {
tdb - > log . log_fn = null_log_fn ;
tdb - > log . log_private = NULL ;
}
2010-09-13 14:35:59 +04:00
2010-11-11 11:36:25 +03:00
if ( name = = NULL & & ( tdb_flags & TDB_INTERNAL ) ) {
name = " __TDB_INTERNAL__ " ;
}
if ( name = = NULL ) {
2010-11-27 22:19:34 +03:00
tdb - > name = discard_const_p ( char , " __NULL__ " ) ;
2010-11-11 11:36:25 +03:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_open_ex: called with name == NULL \n " ) ) ;
tdb - > name = NULL ;
errno = EINVAL ;
goto fail ;
}
2013-02-16 16:26:36 +04:00
/* now make a copy of the name, as the caller memory might go away */
2010-11-11 11:36:25 +03:00
if ( ! ( tdb - > name = ( char * ) strdup ( name ) ) ) {
/*
* set the name as the given string , so that tdb_name ( ) will
* work in case of an error .
*/
2010-11-27 22:19:34 +03:00
tdb - > name = discard_const_p ( char , name ) ;
2010-11-11 11:36:25 +03:00
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: can't strdup(%s) \n " ,
name ) ) ;
tdb - > name = NULL ;
errno = ENOMEM ;
goto fail ;
}
2010-09-13 14:35:59 +04:00
if ( hash_fn ) {
tdb - > hash_fn = hash_fn ;
2010-09-24 10:09:43 +04:00
hash_alg = " the user defined " ;
2010-09-13 14:35:59 +04:00
} else {
2010-09-24 10:15:11 +04:00
/* This controls what we use when creating a tdb. */
if ( tdb - > flags & TDB_INCOMPATIBLE_HASH ) {
tdb - > hash_fn = tdb_jenkins_hash ;
} else {
tdb - > hash_fn = tdb_old_hash ;
}
2010-09-24 10:09:43 +04:00
hash_alg = " either default " ;
2010-09-13 14:35:59 +04:00
}
2006-07-11 22:01:26 +04:00
/* cache the page size */
tdb - > page_size = getpagesize ( ) ;
if ( tdb - > page_size < = 0 ) {
tdb - > page_size = 0x2000 ;
}
2008-01-16 23:04:52 +03:00
tdb - > max_dead_records = ( tdb_flags & TDB_VOLATILE ) ? 5 : 0 ;
2007-05-27 13:22:11 +04:00
2006-07-11 22:01:26 +04:00
if ( ( open_flags & O_ACCMODE ) = = O_WRONLY ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: can't open tdb %s write-only \n " ,
2006-07-11 22:01:26 +04:00
name ) ) ;
errno = EINVAL ;
goto fail ;
}
2010-03-25 12:19:48 +03:00
2006-07-11 22:01:26 +04:00
if ( hash_size = = 0 )
hash_size = DEFAULT_HASH_SIZE ;
if ( ( open_flags & O_ACCMODE ) = = O_RDONLY ) {
tdb - > read_only = 1 ;
/* read only databases don't do locking or clear if first */
tdb - > flags | = TDB_NOLOCK ;
2014-11-04 11:51:51 +03:00
tdb - > flags & = ~ ( TDB_CLEAR_IF_FIRST | TDB_MUTEX_LOCKING ) ;
2006-07-11 22:01:26 +04:00
}
2009-11-19 11:34:05 +03:00
if ( ( tdb - > flags & TDB_ALLOW_NESTING ) & &
( tdb - > flags & TDB_DISALLOW_NESTING ) ) {
tdb - > ecode = TDB_ERR_NESTING ;
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_open_ex: "
" allow_nesting and disallow_nesting are not allowed together! " ) ) ;
errno = EINVAL ;
goto fail ;
}
2013-02-21 19:34:32 +04:00
if ( tdb - > flags & TDB_MUTEX_LOCKING ) {
/*
* Here we catch bugs in the callers ,
* the runtime check for existing tdb ' s comes later .
*/
if ( tdb - > flags & TDB_INTERNAL ) {
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: "
" invalid flags for %s - TDB_MUTEX_LOCKING and "
" TDB_INTERNAL are not allowed together \n " , name ) ) ;
errno = EINVAL ;
goto fail ;
}
if ( tdb - > flags & TDB_NOMMAP ) {
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: "
" invalid flags for %s - TDB_MUTEX_LOCKING and "
" TDB_NOMMAP are not allowed together \n " , name ) ) ;
errno = EINVAL ;
goto fail ;
}
if ( tdb - > read_only ) {
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: "
" invalid flags for %s - TDB_MUTEX_LOCKING "
" not allowed read only \n " , name ) ) ;
errno = EINVAL ;
goto fail ;
}
/*
* The callers should have called
* tdb_runtime_check_for_robust_mutexes ( )
* before using TDB_MUTEX_LOCKING !
*
* This makes sure the caller understands
* that the locking may behave a bit differently
* than with pure fcntl locking . E . g . multiple
* read locks are not supported .
*/
if ( ! tdb_runtime_check_for_robust_mutexes ( ) ) {
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: "
" invalid flags for %s - TDB_MUTEX_LOCKING "
" requires support for robust_mutexes \n " ,
name ) ) ;
errno = ENOSYS ;
goto fail ;
}
}
2010-09-16 14:06:44 +04:00
if ( getenv ( " TDB_NO_FSYNC " ) ) {
tdb - > flags | = TDB_NOSYNC ;
}
2009-11-19 11:34:05 +03:00
/*
* TDB_ALLOW_NESTING is the default behavior .
* Note : this may change in future versions !
*/
if ( ! ( tdb - > flags & TDB_DISALLOW_NESTING ) ) {
tdb - > flags | = TDB_ALLOW_NESTING ;
}
2006-07-11 22:01:26 +04:00
/* internal databases don't mmap or lock, and start off cleared */
if ( tdb - > flags & TDB_INTERNAL ) {
tdb - > flags | = ( TDB_NOLOCK | TDB_NOMMAP ) ;
tdb - > flags & = ~ TDB_CLEAR_IF_FIRST ;
2012-12-20 19:36:02 +04:00
if ( tdb_new_database ( tdb , & header , hash_size ) ! = 0 ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: tdb_new_database failed! " ) ) ;
2006-07-11 22:01:26 +04:00
goto fail ;
}
2012-12-20 19:36:02 +04:00
tdb - > hash_size = hash_size ;
2006-07-11 22:01:26 +04:00
goto internal ;
}
if ( ( tdb - > fd = open ( name , open_flags , mode ) ) = = - 1 ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_WARNING , " tdb_open_ex: could not open file %s: %s \n " ,
2006-07-11 22:01:26 +04:00
name , strerror ( errno ) ) ) ;
goto fail ; /* errno set by open(2) */
}
2008-01-12 02:08:37 +03:00
/* on exec, don't inherit the fd */
v = fcntl ( tdb - > fd , F_GETFD , 0 ) ;
fcntl ( tdb - > fd , F_SETFD , v | FD_CLOEXEC ) ;
2006-07-11 22:01:26 +04:00
/* ensure there is only one process initialising at once */
2010-02-22 06:28:07 +03:00
if ( tdb_nest_lock ( tdb , OPEN_LOCK , F_WRLCK , TDB_LOCK_WAIT ) = = - 1 ) {
2010-02-17 04:48:33 +03:00
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: failed to get open lock on %s: %s \n " ,
2006-07-11 22:01:26 +04:00
name , strerror ( errno ) ) ) ;
goto fail ; /* errno set by tdb_brlock */
}
/* we need to zero database if we are the only one with it open */
if ( ( tdb_flags & TDB_CLEAR_IF_FIRST ) & &
2017-08-16 16:21:14 +03:00
( ! tdb - > read_only ) ) {
ret = tdb_nest_lock ( tdb , ACTIVE_LOCK , F_WRLCK ,
TDB_LOCK_NOWAIT | TDB_LOCK_PROBE ) ;
locked = ( ret = = 0 ) ;
if ( locked ) {
ret = tdb_brlock ( tdb , F_WRLCK , FREELIST_TOP , 0 ,
TDB_LOCK_WAIT ) ;
if ( ret = = - 1 ) {
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_open_ex: "
" tdb_brlock failed for %s: %s \n " ,
name , strerror ( errno ) ) ) ;
goto fail ;
}
ret = tdb_new_database ( tdb , & header , hash_size ) ;
if ( ret = = - 1 ) {
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_open_ex: "
" tdb_new_database failed for "
" %s: %s \n " , name , strerror ( errno ) ) ) ;
tdb_unlockall ( tdb ) ;
goto fail ;
}
ret = tdb_brunlock ( tdb , F_WRLCK , FREELIST_TOP , 0 ) ;
if ( ret = = - 1 ) {
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_open_ex: "
" tdb_unlockall failed for %s: %s \n " ,
name , strerror ( errno ) ) ) ;
goto fail ;
}
ret = lseek ( tdb - > fd , 0 , SEEK_SET ) ;
if ( ret = = - 1 ) {
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_open_ex: "
" lseek failed for %s: %s \n " ,
name , strerror ( errno ) ) ) ;
goto fail ;
}
2006-07-11 22:01:26 +04:00
}
}
2007-07-20 19:00:58 +04:00
errno = 0 ;
2012-12-20 19:36:02 +04:00
if ( read ( tdb - > fd , & header , sizeof ( header ) ) ! = sizeof ( header )
2023-10-06 03:54:02 +03:00
/*
* Call strncmp ( ) rather than strcmp ( ) in case header . magic_food is
* not zero ‐ terminated . We ’ re still checking the full string for
* equality , as tdb_header : : magic_food is larger than
* TDB_MAGIC_FOOD .
*/
| | strncmp ( header . magic_food , TDB_MAGIC_FOOD , sizeof ( header . magic_food ) ) ! = 0 ) {
2012-12-20 19:14:23 +04:00
if ( ! ( open_flags & O_CREAT ) | |
2012-12-20 19:36:02 +04:00
tdb_new_database ( tdb , & header , hash_size ) = = - 1 ) {
2007-07-20 19:00:58 +04:00
if ( errno = = 0 ) {
2009-08-28 06:26:34 +04:00
errno = EIO ; /* ie bad format or something */
2007-07-20 19:00:58 +04:00
}
2006-07-11 22:01:26 +04:00
goto fail ;
}
rev = ( tdb - > flags & TDB_CONVERT ) ;
2012-12-20 19:36:02 +04:00
} else if ( header . version ! = TDB_VERSION
& & ! ( rev = ( header . version = = TDB_BYTEREV ( TDB_VERSION ) ) ) ) {
2009-08-28 06:26:34 +04:00
/* wrong version */
errno = EIO ;
goto fail ;
2006-07-11 22:01:26 +04:00
}
2012-12-20 19:36:02 +04:00
vp = ( unsigned char * ) & header . version ;
2007-08-12 04:55:03 +04:00
vertest = ( ( ( uint32_t ) vp [ 0 ] ) < < 24 ) | ( ( ( uint32_t ) vp [ 1 ] ) < < 16 ) |
( ( ( uint32_t ) vp [ 2 ] ) < < 8 ) | ( uint32_t ) vp [ 3 ] ;
2006-07-11 22:01:26 +04:00
tdb - > flags | = ( vertest = = TDB_VERSION ) ? TDB_BIGENDIAN : 0 ;
if ( ! rev )
tdb - > flags & = ~ TDB_CONVERT ;
else {
tdb - > flags | = TDB_CONVERT ;
2012-12-20 19:36:02 +04:00
tdb_convert ( & header , sizeof ( header ) ) ;
2006-07-11 22:01:26 +04:00
}
2013-02-21 19:34:32 +04:00
/*
* We only use st . st_dev and st . st_ino from the raw fstat ( )
* call , everything else needs to use tdb_fstat ( ) in order
* to skip tdb - > hdr_ofs !
*/
if ( fstat ( tdb - > fd , & st ) = = - 1 ) {
2006-07-11 22:01:26 +04:00
goto fail ;
2013-02-21 19:34:32 +04:00
}
tdb - > device = st . st_dev ;
tdb - > inode = st . st_ino ;
ZERO_STRUCT ( st ) ;
2006-07-11 22:01:26 +04:00
2012-12-20 19:36:02 +04:00
if ( header . rwlocks ! = 0 & &
2014-02-05 02:35:53 +04:00
header . rwlocks ! = TDB_FEATURE_FLAG_MAGIC & &
2012-12-20 19:36:02 +04:00
header . rwlocks ! = TDB_HASH_RWLOCK_MAGIC ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: spinlocks no longer supported \n " ) ) ;
2014-05-06 13:52:49 +04:00
errno = ENOSYS ;
2006-07-11 22:01:26 +04:00
goto fail ;
}
2015-11-09 04:10:11 +03:00
if ( header . hash_size = = 0 ) {
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: invalid database: 0 hash_size \n " ) ) ;
errno = ENOSYS ;
goto fail ;
}
2012-12-20 19:36:02 +04:00
tdb - > hash_size = header . hash_size ;
2006-07-11 22:01:26 +04:00
2014-02-05 02:35:53 +04:00
if ( header . rwlocks = = TDB_FEATURE_FLAG_MAGIC ) {
tdb - > feature_flags = header . feature_flags ;
}
if ( tdb - > feature_flags & ~ TDB_SUPPORTED_FEATURE_FLAGS ) {
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: unsupported "
" features in tdb %s: 0x%08x (supported: 0x%08x) \n " ,
name , ( unsigned ) tdb - > feature_flags ,
( unsigned ) TDB_SUPPORTED_FEATURE_FLAGS ) ) ;
errno = ENOSYS ;
goto fail ;
}
2013-02-21 19:34:32 +04:00
if ( tdb - > feature_flags & TDB_FEATURE_FLAG_MUTEX ) {
if ( ! tdb_mutex_open_ok ( tdb , & header ) ) {
errno = EINVAL ;
goto fail ;
}
/*
* We need to remember the hdr_ofs
* also for the TDB_NOLOCK case
* if the current library doesn ' t support
* mutex locking .
*/
tdb - > hdr_ofs = header . mutex_size ;
2018-10-22 09:57:00 +03:00
if ( ( ! ( tdb_flags & TDB_CLEAR_IF_FIRST ) ) & & ( ! tdb - > read_only ) ) {
/*
* Open an existing mutexed tdb , but without
* CLEAR_IF_FIRST . We need to initialize the
* mutex array and keep the CLEAR_IF_FIRST
* lock locked .
*/
ret = tdb_nest_lock ( tdb , ACTIVE_LOCK , F_WRLCK ,
TDB_LOCK_NOWAIT | TDB_LOCK_PROBE ) ;
locked = ( ret = = 0 ) ;
if ( locked ) {
ret = tdb_mutex_init ( tdb ) ;
if ( ret = = - 1 ) {
TDB_LOG ( ( tdb ,
TDB_DEBUG_FATAL ,
" tdb_open_ex: tdb_mutex_init "
" failed for " " %s: %s \n " ,
name , strerror ( errno ) ) ) ;
goto fail ;
}
}
}
2013-02-21 19:34:32 +04:00
}
2012-12-20 19:36:02 +04:00
if ( ( header . magic1_hash = = 0 ) & & ( header . magic2_hash = = 0 ) ) {
2010-09-13 14:35:59 +04:00
/* older TDB without magic hash references */
2010-09-24 10:15:11 +04:00
tdb - > hash_fn = tdb_old_hash ;
2012-12-20 19:36:02 +04:00
} else if ( ! check_header_hash ( tdb , & header , ! hash_fn ,
2012-12-20 19:14:23 +04:00
& magic1 , & magic2 ) ) {
2010-09-13 14:35:59 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_open_ex: "
2010-09-24 10:09:43 +04:00
" %s was not created with %s hash function we are using \n "
2010-09-13 14:35:59 +04:00
" magic1_hash[0x%08X %s 0x%08X] "
" magic2_hash[0x%08X %s 0x%08X] \n " ,
name , hash_alg ,
2012-12-20 19:36:02 +04:00
header . magic1_hash ,
( header . magic1_hash = = magic1 ) ? " == " : " != " ,
2010-09-24 10:09:43 +04:00
magic1 ,
2012-12-20 19:36:02 +04:00
header . magic2_hash ,
( header . magic2_hash = = magic2 ) ? " == " : " != " ,
2010-09-24 10:09:43 +04:00
magic2 ) ) ;
2010-09-13 14:35:59 +04:00
errno = EINVAL ;
goto fail ;
}
2006-07-11 22:01:26 +04:00
/* Is it already in the open list? If so, fail. */
2013-02-21 19:34:32 +04:00
if ( tdb_already_open ( tdb - > device , tdb - > inode ) ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: "
2006-07-11 22:01:26 +04:00
" %s (%d,%d) is already open in this process \n " ,
2013-02-21 19:34:32 +04:00
name , ( int ) tdb - > device , ( int ) tdb - > inode ) ) ;
2006-07-11 22:01:26 +04:00
errno = EBUSY ;
goto fail ;
}
2013-02-21 19:34:32 +04:00
/*
* We had tdb_mmap ( tdb ) here before ,
* but we need to use tdb_fstat ( ) ,
* which is triggered from tdb_oob ( ) before calling tdb_mmap ( ) .
* As this skips tdb - > hdr_ofs .
*/
tdb - > map_size = 0 ;
2019-08-04 13:15:14 +03:00
ret = tdb_oob ( tdb , 0 , 1 , 0 ) ;
2013-02-21 19:34:32 +04:00
if ( ret = = - 1 ) {
2011-12-19 09:17:50 +04:00
errno = EIO ;
goto fail ;
}
2013-02-21 19:34:32 +04:00
if ( tdb - > feature_flags & TDB_FEATURE_FLAG_MUTEX ) {
if ( ! ( tdb - > flags & TDB_NOLOCK ) ) {
ret = tdb_mutex_mmap ( tdb ) ;
if ( ret ! = 0 ) {
goto fail ;
}
}
}
2018-03-04 13:09:10 +03:00
if ( tdb - > hash_size > UINT32_MAX / 4 ) {
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_open_ex: "
" hash size % " PRIu32 " too large \n " , tdb - > hash_size ) ) ;
errno = EINVAL ;
goto fail ;
}
2019-08-04 13:15:14 +03:00
ret = tdb_oob ( tdb , FREELIST_TOP , 4 * tdb - > hash_size , 1 ) ;
2018-03-04 13:09:10 +03:00
if ( ret = = - 1 ) {
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_open_ex: "
" hash size % " PRIu32 " does not fit \n " , tdb - > hash_size ) ) ;
errno = EINVAL ;
goto fail ;
}
2006-07-11 22:01:26 +04:00
if ( locked ) {
2010-02-24 03:14:40 +03:00
if ( tdb_nest_unlock ( tdb , ACTIVE_LOCK , F_WRLCK , false ) = = - 1 ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: "
2010-02-17 04:47:19 +03:00
" failed to release ACTIVE_LOCK on %s: %s \n " ,
2006-07-11 22:01:26 +04:00
name , strerror ( errno ) ) ) ;
goto fail ;
}
2018-10-22 09:57:00 +03:00
2006-07-11 22:01:26 +04:00
}
2018-10-22 09:57:00 +03:00
if ( locked | | ( tdb_flags & TDB_CLEAR_IF_FIRST ) ) {
/*
* We always need to do this if the CLEAR_IF_FIRST
* flag is set , even if we didn ' t get the initial
* exclusive lock as we need to let all other users
* know we ' re using it .
*/
2006-07-11 22:01:26 +04:00
2018-10-22 09:57:00 +03:00
ret = tdb_nest_lock ( tdb , ACTIVE_LOCK , F_RDLCK , TDB_LOCK_WAIT ) ;
if ( ret = = - 1 ) {
2006-07-11 22:01:26 +04:00
goto fail ;
2010-02-24 03:14:40 +03:00
}
2006-07-11 22:01:26 +04:00
}
/* if needed, run recovery */
if ( tdb_transaction_recover ( tdb ) = = - 1 ) {
goto fail ;
}
2024-07-10 07:04:27 +03:00
internal :
/* Internal (memory-only) databases skip all the code above to
* do with disk files , and resume here by releasing their
* open lock and hooking into the active list . */
2009-10-20 05:49:41 +04:00
# ifdef TDB_TRACE
{
2024-07-10 07:04:27 +03:00
char tracefile [ 64 ] ;
if ( tdb - > flags & TDB_INTERNAL ) {
snprintf ( tracefile , sizeof ( tracefile ) ,
" tdb_%p.trace.%li " , tdb , ( long ) getpid ( ) ) ;
} else {
snprintf ( tracefile , sizeof ( tracefile ) ,
" %s.trace.%li " , name , ( long ) getpid ( ) ) ;
}
2009-10-20 05:49:41 +04:00
tdb - > tracefd = open ( tracefile , O_WRONLY | O_CREAT | O_EXCL , 0600 ) ;
if ( tdb - > tracefd > = 0 ) {
tdb_enable_seqnum ( tdb ) ;
tdb_trace_open ( tdb , " tdb_open " , hash_size , tdb_flags ,
open_flags ) ;
} else
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: failed to open trace file %s! \n " , tracefile ) ) ;
}
# endif
2010-02-22 06:28:07 +03:00
if ( tdb_nest_unlock ( tdb , OPEN_LOCK , F_WRLCK , false ) = = - 1 ) {
2006-07-11 22:01:26 +04:00
goto fail ;
2010-02-22 06:28:07 +03:00
}
2006-07-11 22:01:26 +04:00
tdb - > next = tdbs ;
tdbs = tdb ;
2014-01-30 19:07:10 +04:00
errno = orig_errno ;
2006-07-11 22:01:26 +04:00
return tdb ;
fail :
{ int save_errno = errno ;
if ( ! tdb )
return NULL ;
2009-10-20 05:49:41 +04:00
# ifdef TDB_TRACE
close ( tdb - > tracefd ) ;
# endif
2006-07-11 22:01:26 +04:00
if ( tdb - > map_ptr ) {
if ( tdb - > flags & TDB_INTERNAL )
SAFE_FREE ( tdb - > map_ptr ) ;
else
tdb_munmap ( tdb ) ;
}
if ( tdb - > fd ! = - 1 )
if ( close ( tdb - > fd ) ! = 0 )
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_open_ex: failed to close tdb->fd on error! \n " ) ) ;
2010-02-24 03:14:40 +03:00
SAFE_FREE ( tdb - > lockrecs ) ;
2010-11-11 11:36:25 +03:00
SAFE_FREE ( tdb - > name ) ;
2006-07-11 22:01:26 +04:00
SAFE_FREE ( tdb ) ;
errno = save_errno ;
return NULL ;
}
}
2007-03-06 13:11:15 +03:00
/*
* Set the maximum number of dead records per hash chain
*/
2010-10-21 13:51:37 +04:00
_PUBLIC_ void tdb_set_max_dead ( struct tdb_context * tdb , int max_dead )
2007-03-06 13:11:15 +03:00
{
tdb - > max_dead_records = max_dead ;
}
2006-07-11 22:01:26 +04:00
/**
* Close a database .
*
* @ returns - 1 for error ; 0 for success .
* */
2010-10-21 13:51:37 +04:00
_PUBLIC_ int tdb_close ( struct tdb_context * tdb )
2006-07-11 22:01:26 +04:00
{
struct tdb_context * * i ;
int ret = 0 ;
if ( tdb - > transaction ) {
2010-02-17 04:47:19 +03:00
tdb_transaction_cancel ( tdb ) ;
2006-07-11 22:01:26 +04:00
}
2010-02-17 04:47:19 +03:00
tdb_trace ( tdb , " tdb_close " ) ;
2006-07-11 22:01:26 +04:00
if ( tdb - > map_ptr ) {
if ( tdb - > flags & TDB_INTERNAL )
SAFE_FREE ( tdb - > map_ptr ) ;
else
tdb_munmap ( tdb ) ;
}
2013-02-21 19:34:32 +04:00
tdb_mutex_munmap ( tdb ) ;
2006-07-11 22:01:26 +04:00
SAFE_FREE ( tdb - > name ) ;
2009-10-21 21:18:56 +04:00
if ( tdb - > fd ! = - 1 ) {
2006-07-11 22:01:26 +04:00
ret = close ( tdb - > fd ) ;
2009-10-21 21:18:56 +04:00
tdb - > fd = - 1 ;
}
2007-02-13 02:16:02 +03:00
SAFE_FREE ( tdb - > lockrecs ) ;
2006-07-11 22:01:26 +04:00
/* Remove from contexts list */
for ( i = & tdbs ; * i ; i = & ( * i ) - > next ) {
if ( * i = = tdb ) {
* i = tdb - > next ;
break ;
}
}
2009-10-20 05:49:41 +04:00
# ifdef TDB_TRACE
close ( tdb - > tracefd ) ;
# endif
2006-07-11 22:01:26 +04:00
memset ( tdb , 0 , sizeof ( * tdb ) ) ;
SAFE_FREE ( tdb ) ;
return ret ;
}
2023-04-13 14:17:08 +03:00
/* register a logging function */
2010-10-21 13:51:37 +04:00
_PUBLIC_ void tdb_set_logging_function ( struct tdb_context * tdb ,
const struct tdb_logging_context * log_ctx )
2006-07-11 22:01:26 +04:00
{
2006-11-21 22:45:53 +03:00
tdb - > log = * log_ctx ;
2006-07-11 22:01:26 +04:00
}
2010-10-21 13:51:37 +04:00
_PUBLIC_ void * tdb_get_logging_private ( struct tdb_context * tdb )
2006-10-20 13:55:47 +04:00
{
return tdb - > log . log_private ;
}
2006-07-11 22:01:26 +04:00
2009-07-30 06:22:39 +04:00
static int tdb_reopen_internal ( struct tdb_context * tdb , bool active_lock )
2006-07-11 22:01:26 +04:00
{
2009-08-06 22:47:08 +04:00
# if !defined(LIBREPLACE_PREAD_NOT_REPLACED) || \
! defined ( LIBREPLACE_PWRITE_NOT_REPLACED )
2006-07-11 22:01:26 +04:00
struct stat st ;
2009-08-06 22:47:08 +04:00
# endif
2006-07-11 22:01:26 +04:00
if ( tdb - > flags & TDB_INTERNAL ) {
return 0 ; /* Nothing to do. */
}
2010-02-17 05:04:26 +03:00
if ( tdb_have_extra_locks ( tdb ) ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_reopen: reopen not allowed with locks held \n " ) ) ;
2006-07-11 22:01:26 +04:00
goto fail ;
}
if ( tdb - > transaction ! = 0 ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_reopen: reopen not allowed inside a transaction \n " ) ) ;
2006-07-11 22:01:26 +04:00
goto fail ;
}
2009-07-30 06:22:08 +04:00
/* If we have real pread & pwrite, we can skip reopen. */
# if !defined(LIBREPLACE_PREAD_NOT_REPLACED) || \
! defined ( LIBREPLACE_PWRITE_NOT_REPLACED )
2006-07-11 22:01:26 +04:00
if ( tdb_munmap ( tdb ) ! = 0 ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_reopen: munmap failed (%s) \n " , strerror ( errno ) ) ) ;
2006-07-11 22:01:26 +04:00
goto fail ;
}
if ( close ( tdb - > fd ) ! = 0 )
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_reopen: WARNING closing tdb->fd failed! \n " ) ) ;
2006-07-11 22:01:26 +04:00
tdb - > fd = open ( tdb - > name , tdb - > open_flags & ~ ( O_CREAT | O_TRUNC ) , 0 ) ;
if ( tdb - > fd = = - 1 ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_reopen: open failed (%s) \n " , strerror ( errno ) ) ) ;
2006-07-11 22:01:26 +04:00
goto fail ;
}
2013-02-21 19:34:32 +04:00
/*
* We only use st . st_dev and st . st_ino from the raw fstat ( )
* call , everything else needs to use tdb_fstat ( ) in order
* to skip tdb - > hdr_ofs !
*/
2006-07-11 22:01:26 +04:00
if ( fstat ( tdb - > fd , & st ) ! = 0 ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_reopen: fstat failed (%s) \n " , strerror ( errno ) ) ) ;
2006-07-11 22:01:26 +04:00
goto fail ;
}
if ( st . st_ino ! = tdb - > inode | | st . st_dev ! = tdb - > device ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_reopen: file dev/inode has changed! \n " ) ) ;
2006-07-11 22:01:26 +04:00
goto fail ;
}
2013-02-21 19:34:32 +04:00
ZERO_STRUCT ( st ) ;
/*
* We had tdb_mmap ( tdb ) here before ,
* but we need to use tdb_fstat ( ) ,
* which is triggered from tdb_oob ( ) before calling tdb_mmap ( ) .
* As this skips tdb - > hdr_ofs .
*/
tdb - > map_size = 0 ;
2019-08-04 13:15:14 +03:00
if ( tdb_oob ( tdb , 0 , 1 , 0 ) ! = 0 ) {
2012-03-22 04:17:26 +04:00
goto fail ;
}
2009-07-30 06:22:08 +04:00
# endif /* fake pread or pwrite */
2010-02-24 03:14:40 +03:00
/* We may still think we hold the active lock. */
tdb - > num_lockrecs = 0 ;
SAFE_FREE ( tdb - > lockrecs ) ;
2013-12-09 13:56:33 +04:00
tdb - > lockrecs_array_length = 0 ;
2010-02-24 03:14:40 +03:00
if ( active_lock & & tdb_nest_lock ( tdb , ACTIVE_LOCK , F_RDLCK , TDB_LOCK_WAIT ) = = - 1 ) {
2009-07-30 06:22:08 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_reopen: failed to obtain active lock \n " ) ) ;
goto fail ;
}
2006-07-11 22:01:26 +04:00
return 0 ;
fail :
tdb_close ( tdb ) ;
return - 1 ;
}
2009-07-30 06:22:39 +04:00
/* reopen a tdb - this can be used after a fork to ensure that we have an independent
seek pointer from our parent and to re - establish locks */
2010-10-21 13:51:37 +04:00
_PUBLIC_ int tdb_reopen ( struct tdb_context * tdb )
2009-07-30 06:22:39 +04:00
{
2018-10-22 09:57:00 +03:00
bool active_lock ;
active_lock = ( tdb - > flags & ( TDB_CLEAR_IF_FIRST | TDB_MUTEX_LOCKING ) ) ;
return tdb_reopen_internal ( tdb , active_lock ) ;
2009-07-30 06:22:39 +04:00
}
2006-07-11 22:01:26 +04:00
/* reopen all tdb's */
2010-10-21 13:51:37 +04:00
_PUBLIC_ int tdb_reopen_all ( int parent_longlived )
2006-07-11 22:01:26 +04:00
{
struct tdb_context * tdb ;
for ( tdb = tdbs ; tdb ; tdb = tdb - > next ) {
2018-10-22 09:57:00 +03:00
bool active_lock ;
active_lock =
( tdb - > flags & ( TDB_CLEAR_IF_FIRST | TDB_MUTEX_LOCKING ) ) ;
2009-07-30 06:22:39 +04:00
2006-07-11 22:01:26 +04:00
/*
* If the parent is longlived ( ie . a
* parent daemon architecture ) , we know
* it will keep it ' s active lock on a
* tdb opened with CLEAR_IF_FIRST . Thus
* for child processes we don ' t have to
* add an active lock . This is essential
* to improve performance on systems that
* keep POSIX locks as a non - scalable data
* structure in the kernel .
*/
if ( parent_longlived ) {
/* Ensure no clear-if-first. */
2009-07-30 06:22:39 +04:00
active_lock = false ;
2006-07-11 22:01:26 +04:00
}
2009-07-30 06:22:39 +04:00
if ( tdb_reopen_internal ( tdb , active_lock ) ! = 0 )
2006-07-11 22:01:26 +04:00
return - 1 ;
}
return 0 ;
}