2012-12-13 13:31:59 +01:00
/*
2006-07-11 18:01:26 +00: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 10:19:48 +01:00
2006-07-11 18:01:26 +00: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 10:19:48 +01:00
2006-07-11 18:01:26 +00: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 01:44:42 +00:00
version 3 of the License , or ( at your option ) any later version .
2006-07-11 18:01:26 +00: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 03:42:26 +00:00
License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
2006-07-11 18:01:26 +00:00
*/
# include "tdb_private.h"
2013-02-21 16:34:32 +01:00
/*
2013-02-21 16:34:32 +01:00
* We prepend the mutex area , so fixup offsets . See mutex . c for details .
* tdb - > hdr_ofs is 0 or header . mutex_size .
2013-02-21 16:34:32 +01:00
*
* Note : that we only have the 4 GB limit of tdb_off_t for
* tdb - > map_size . The file size on disk can be 4 GB + tdb - > hdr_ofs !
*/
static bool tdb_adjust_offset ( struct tdb_context * tdb , off_t * off )
{
off_t tmp = tdb - > hdr_ofs + * off ;
if ( ( tmp < tdb - > hdr_ofs ) | | ( tmp < * off ) ) {
errno = EIO ;
return false ;
}
* off = tmp ;
return true ;
}
static ssize_t tdb_pwrite ( struct tdb_context * tdb , const void * buf ,
size_t count , off_t offset )
{
2017-08-23 12:00:00 +02:00
ssize_t ret ;
2013-02-21 16:34:32 +01:00
if ( ! tdb_adjust_offset ( tdb , & offset ) ) {
return - 1 ;
}
2017-08-23 12:00:00 +02:00
do {
ret = pwrite ( tdb - > fd , buf , count , offset ) ;
} while ( ( ret = = - 1 ) & & ( errno = = EINTR ) ) ;
return ret ;
2013-02-21 16:34:32 +01:00
}
static ssize_t tdb_pread ( struct tdb_context * tdb , void * buf ,
size_t count , off_t offset )
{
2017-08-23 12:00:00 +02:00
ssize_t ret ;
2013-02-21 16:34:32 +01:00
if ( ! tdb_adjust_offset ( tdb , & offset ) ) {
return - 1 ;
}
2017-08-23 12:00:00 +02:00
do {
ret = pread ( tdb - > fd , buf , count , offset ) ;
} while ( ( ret = = - 1 ) & & ( errno = = EINTR ) ) ;
return ret ;
2013-02-21 16:34:32 +01:00
}
static int tdb_ftruncate ( struct tdb_context * tdb , off_t length )
{
2017-08-23 12:00:00 +02:00
ssize_t ret ;
2013-02-21 16:34:32 +01:00
if ( ! tdb_adjust_offset ( tdb , & length ) ) {
return - 1 ;
}
2017-08-23 12:00:00 +02:00
do {
ret = ftruncate ( tdb - > fd , length ) ;
} while ( ( ret = = - 1 ) & & ( errno = = EINTR ) ) ;
return ret ;
2013-02-21 16:34:32 +01:00
}
static int tdb_fstat ( struct tdb_context * tdb , struct stat * buf )
{
int ret ;
ret = fstat ( tdb - > fd , buf ) ;
if ( ret = = - 1 ) {
return - 1 ;
}
if ( buf - > st_size < tdb - > hdr_ofs ) {
errno = EIO ;
return - 1 ;
}
buf - > st_size - = tdb - > hdr_ofs ;
return ret ;
}
2006-07-11 18:01:26 +00:00
/* check for an out of bounds access - if it is out of bounds then
see if the database has been expanded by someone else and expand
2012-12-13 13:31:59 +01:00
if necessary
2006-07-11 18:01:26 +00:00
*/
2011-12-19 15:47:50 +10:30
static int tdb_oob ( struct tdb_context * tdb , tdb_off_t off , tdb_len_t len ,
int probe )
2006-07-11 18:01:26 +00:00
{
struct stat st ;
2011-12-19 15:47:50 +10:30
if ( len + off < len ) {
if ( ! probe ) {
/* Ensure ecode is set for log fn. */
tdb - > ecode = TDB_ERR_IO ;
2013-05-28 16:53:56 +09:30
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_oob off %u len %u wrap \n " ,
off , len ) ) ;
2011-12-19 15:47:50 +10:30
}
return - 1 ;
}
if ( off + len < = tdb - > map_size )
2006-07-11 18:01:26 +00:00
return 0 ;
if ( tdb - > flags & TDB_INTERNAL ) {
if ( ! probe ) {
/* Ensure ecode is set for log fn. */
tdb - > ecode = TDB_ERR_IO ;
2011-12-19 15:47:50 +10:30
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_oob len %u beyond internal malloc size %u \n " ,
( int ) ( off + len ) , ( int ) tdb - > map_size ) ) ;
2006-07-11 18:01:26 +00:00
}
2009-10-22 00:09:43 +10:30
return - 1 ;
2006-07-11 18:01:26 +00:00
}
2013-02-21 16:34:32 +01:00
if ( tdb_fstat ( tdb , & st ) = = - 1 ) {
2009-10-22 00:09:43 +10:30
tdb - > ecode = TDB_ERR_IO ;
return - 1 ;
2006-07-11 18:01:26 +00:00
}
2011-12-19 15:47:50 +10:30
/* Beware >4G files! */
if ( ( tdb_off_t ) st . st_size ! = st . st_size ) {
/* Ensure ecode is set for log fn. */
tdb - > ecode = TDB_ERR_IO ;
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_oob len %llu too large! \n " ,
( long long ) st . st_size ) ) ;
return - 1 ;
}
2012-10-06 13:23:05 +02:00
/* Unmap, update size, remap. We do this unconditionally, to handle
* the unusual case where the db is truncated .
*
* This can happen to a child using tdb_reopen_all ( true ) on a
* TDB_CLEAR_IF_FIRST tdb whose parent crashes : the next
* opener will truncate the database . */
2009-10-22 00:09:43 +10:30
if ( tdb_munmap ( tdb ) = = - 1 ) {
tdb - > ecode = TDB_ERR_IO ;
return - 1 ;
}
2006-07-11 18:01:26 +00:00
tdb - > map_size = st . st_size ;
2012-10-06 13:23:05 +02:00
if ( tdb_mmap ( tdb ) ! = 0 ) {
2013-03-18 10:53:49 +01:00
return - 1 ;
2012-10-06 13:23:05 +02:00
}
if ( st . st_size < ( size_t ) off + len ) {
if ( ! probe ) {
/* Ensure ecode is set for log fn. */
tdb - > ecode = TDB_ERR_IO ;
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_oob len %u beyond eof at %u \n " ,
( int ) ( off + len ) , ( int ) st . st_size ) ) ;
}
return - 1 ;
}
return 0 ;
2006-07-11 18:01:26 +00:00
}
/* write a lump of data at a specified offset */
2012-12-13 13:31:59 +01:00
static int tdb_write ( struct tdb_context * tdb , tdb_off_t off ,
2006-07-11 18:01:26 +00:00
const void * buf , tdb_len_t len )
{
if ( len = = 0 ) {
return 0 ;
}
if ( tdb - > read_only | | tdb - > traverse_read ) {
tdb - > ecode = TDB_ERR_RDONLY ;
return - 1 ;
}
2011-12-19 15:47:50 +10:30
if ( tdb - > methods - > tdb_oob ( tdb , off , len , 0 ) ! = 0 )
2006-07-11 18:01:26 +00:00
return - 1 ;
if ( tdb - > map_ptr ) {
memcpy ( off + ( char * ) tdb - > map_ptr , buf , len ) ;
2007-08-28 14:25:46 +00:00
} else {
2012-03-22 10:47:26 +10:30
# ifdef HAVE_INCOHERENT_MMAP
tdb - > ecode = TDB_ERR_IO ;
return - 1 ;
# else
2013-02-21 16:34:32 +01:00
ssize_t written ;
written = tdb_pwrite ( tdb , buf , len , off ) ;
2007-08-28 14:25:46 +00:00
if ( ( written ! = ( ssize_t ) len ) & & ( written ! = - 1 ) ) {
/* try once more */
2009-10-22 00:09:43 +10:30
tdb - > ecode = TDB_ERR_IO ;
2007-08-28 14:25:46 +00:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_write: wrote only "
2013-05-28 16:53:56 +09:30
" %zi of %u bytes at %u, trying once more \n " ,
written , len , off ) ) ;
2013-02-21 16:34:32 +01:00
written = tdb_pwrite ( tdb , ( const char * ) buf + written ,
len - written , off + written ) ;
2007-08-28 14:25:46 +00:00
}
if ( written = = - 1 ) {
2009-10-22 00:09:43 +10:30
/* Ensure ecode is set for log fn. */
tdb - > ecode = TDB_ERR_IO ;
2013-05-28 16:53:56 +09:30
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_write failed at %u "
" len=%u (%s) \n " , off , len , strerror ( errno ) ) ) ;
2009-10-22 00:09:43 +10:30
return - 1 ;
2007-08-28 14:25:46 +00:00
} else if ( written ! = ( ssize_t ) len ) {
2009-10-22 00:09:43 +10:30
tdb - > ecode = TDB_ERR_IO ;
2007-08-28 14:25:46 +00:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_write: failed to "
2013-05-28 16:53:56 +09:30
" write %u bytes at %u in two attempts \n " ,
2007-08-28 14:25:46 +00:00
len , off ) ) ;
2009-10-22 00:09:43 +10:30
return - 1 ;
}
2012-03-22 10:47:26 +10:30
# endif
2006-07-11 18:01:26 +00:00
}
return 0 ;
}
/* Endian conversion: we only ever deal with 4 byte quantities */
2007-08-12 00:55:03 +00:00
void * tdb_convert ( void * buf , uint32_t size )
2006-07-11 18:01:26 +00:00
{
2007-08-12 00:55:03 +00:00
uint32_t i , * p = ( uint32_t * ) buf ;
2006-07-11 18:01:26 +00:00
for ( i = 0 ; i < size / 4 ; i + + )
p [ i ] = TDB_BYTEREV ( p [ i ] ) ;
return buf ;
}
/* read a lump of data at a specified offset, maybe convert */
2012-12-13 13:31:59 +01:00
static int tdb_read ( struct tdb_context * tdb , tdb_off_t off , void * buf ,
2006-07-11 18:01:26 +00:00
tdb_len_t len , int cv )
{
2011-12-19 15:47:50 +10:30
if ( tdb - > methods - > tdb_oob ( tdb , off , len , 0 ) ! = 0 ) {
2006-07-11 18:01:26 +00:00
return - 1 ;
}
if ( tdb - > map_ptr ) {
memcpy ( buf , off + ( char * ) tdb - > map_ptr , len ) ;
} else {
2012-03-22 10:47:26 +10:30
# ifdef HAVE_INCOHERENT_MMAP
tdb - > ecode = TDB_ERR_IO ;
return - 1 ;
# else
2013-02-21 16:34:32 +01:00
ssize_t ret ;
ret = tdb_pread ( tdb , buf , len , off ) ;
2006-07-11 18:01:26 +00:00
if ( ret ! = ( ssize_t ) len ) {
/* Ensure ecode is set for log fn. */
tdb - > ecode = TDB_ERR_IO ;
2013-05-28 16:53:56 +09:30
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_read failed at %u "
" len=%u ret=%zi (%s) map_size=%u \n " ,
off , len , ret , strerror ( errno ) ,
tdb - > map_size ) ) ;
2009-10-22 00:09:43 +10:30
return - 1 ;
2006-07-11 18:01:26 +00:00
}
2012-03-22 10:47:26 +10:30
# endif
2006-07-11 18:01:26 +00:00
}
if ( cv ) {
tdb_convert ( buf , len ) ;
}
return 0 ;
}
/*
do an unlocked scan of the hash table heads to find the next non - zero head . The value
will then be confirmed with the lock held
2012-12-13 13:31:59 +01:00
*/
2007-08-12 00:55:03 +00:00
static void tdb_next_hash_chain ( struct tdb_context * tdb , uint32_t * chain )
2006-07-11 18:01:26 +00:00
{
2007-08-12 00:55:03 +00:00
uint32_t h = * chain ;
2006-07-11 18:01:26 +00:00
if ( tdb - > map_ptr ) {
2012-12-20 16:36:02 +01:00
for ( ; h < tdb - > hash_size ; h + + ) {
2007-08-12 00:55:03 +00:00
if ( 0 ! = * ( uint32_t * ) ( TDB_HASH_TOP ( h ) + ( unsigned char * ) tdb - > map_ptr ) ) {
2006-07-11 18:01:26 +00:00
break ;
}
}
} else {
2007-08-12 00:55:03 +00:00
uint32_t off = 0 ;
2012-12-20 16:36:02 +01:00
for ( ; h < tdb - > hash_size ; h + + ) {
2006-07-11 18:01:26 +00:00
if ( tdb_ofs_read ( tdb , TDB_HASH_TOP ( h ) , & off ) ! = 0 | | off ! = 0 ) {
break ;
}
}
}
( * chain ) = h ;
}
int tdb_munmap ( struct tdb_context * tdb )
{
if ( tdb - > flags & TDB_INTERNAL )
return 0 ;
# ifdef HAVE_MMAP
if ( tdb - > map_ptr ) {
2008-09-03 10:44:09 -04:00
int ret ;
ret = munmap ( tdb - > map_ptr , tdb - > map_size ) ;
2006-07-11 18:01:26 +00:00
if ( ret ! = 0 )
return ret ;
}
# endif
tdb - > map_ptr = NULL ;
return 0 ;
}
2012-03-22 10:47:26 +10:30
/* If mmap isn't coherent, *everyone* must always mmap. */
static bool should_mmap ( const struct tdb_context * tdb )
{
# ifdef HAVE_INCOHERENT_MMAP
return true ;
# else
return ! ( tdb - > flags & TDB_NOMMAP ) ;
# endif
}
int tdb_mmap ( struct tdb_context * tdb )
2006-07-11 18:01:26 +00:00
{
if ( tdb - > flags & TDB_INTERNAL )
2012-03-23 10:41:55 +10:30
return 0 ;
2006-07-11 18:01:26 +00:00
# ifdef HAVE_MMAP
2012-03-22 10:47:26 +10:30
if ( should_mmap ( tdb ) ) {
2012-12-13 13:31:59 +01:00
tdb - > map_ptr = mmap ( NULL , tdb - > map_size ,
PROT_READ | ( tdb - > read_only ? 0 : PROT_WRITE ) ,
2013-02-21 16:34:32 +01:00
MAP_SHARED | MAP_FILE , tdb - > fd ,
tdb - > hdr_ofs ) ;
2006-07-11 18:01:26 +00:00
/*
* NB . When mmap fails it returns MAP_FAILED * NOT * NULL ! ! ! !
*/
if ( tdb - > map_ptr = = MAP_FAILED ) {
tdb - > map_ptr = NULL ;
2013-05-28 16:53:56 +09:30
TDB_LOG ( ( tdb , TDB_DEBUG_WARNING , " tdb_mmap failed for size %u (%s) \n " ,
2006-07-11 18:01:26 +00:00
tdb - > map_size , strerror ( errno ) ) ) ;
2012-03-22 10:47:26 +10:30
# ifdef HAVE_INCOHERENT_MMAP
tdb - > ecode = TDB_ERR_IO ;
return - 1 ;
# endif
2006-07-11 18:01:26 +00:00
}
} else {
tdb - > map_ptr = NULL ;
}
# else
tdb - > map_ptr = NULL ;
# endif
2012-03-22 10:47:26 +10:30
return 0 ;
2006-07-11 18:01:26 +00:00
}
/* expand a file. we prefer to use ftruncate, as that is what posix
says to use for mmap expansion */
static int tdb_expand_file ( struct tdb_context * tdb , tdb_off_t size , tdb_off_t addition )
{
2008-01-11 15:08:37 -08:00
char buf [ 8192 ] ;
2013-05-28 12:56:57 +02:00
tdb_off_t new_size ;
2006-07-11 18:01:26 +00:00
if ( tdb - > read_only | | tdb - > traverse_read ) {
tdb - > ecode = TDB_ERR_RDONLY ;
return - 1 ;
}
2013-05-28 12:59:32 +02:00
if ( ! tdb_add_off_t ( size , addition , & new_size ) ) {
tdb - > ecode = TDB_ERR_OOM ;
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " expand_file write "
" overflow detected current size[%u] addition[%u]! \n " ,
( unsigned ) size , ( unsigned ) addition ) ) ;
errno = ENOSPC ;
return - 1 ;
}
2013-05-28 12:56:57 +02:00
2013-02-21 16:34:32 +01:00
if ( tdb_ftruncate ( tdb , new_size ) = = - 1 ) {
2006-07-11 18:01:26 +00:00
char b = 0 ;
2013-02-21 16:34:32 +01:00
ssize_t written = tdb_pwrite ( tdb , & b , 1 , new_size - 1 ) ;
2007-07-20 16:31:32 +00:00
if ( written = = 0 ) {
/* try once more, potentially revealing errno */
2013-02-21 16:34:32 +01:00
written = tdb_pwrite ( tdb , & b , 1 , new_size - 1 ) ;
2007-07-20 16:31:32 +00:00
}
if ( written = = 0 ) {
/* again - give up, guessing errno */
errno = ENOSPC ;
}
if ( written ! = 1 ) {
2013-05-28 12:59:32 +02:00
tdb - > ecode = TDB_ERR_OOM ;
2013-05-28 16:53:56 +09:30
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " expand_file to %u failed (%s) \n " ,
2013-05-28 12:56:57 +02:00
( unsigned ) new_size , strerror ( errno ) ) ) ;
2006-07-11 18:01:26 +00:00
return - 1 ;
}
}
/* now fill the file with something. This ensures that the
file isn ' t sparse , which would be very bad if we ran out of
disk . This must be done with write , not via mmap */
memset ( buf , TDB_PAD_BYTE , sizeof ( buf ) ) ;
while ( addition ) {
2007-07-20 14:23:12 +00:00
size_t n = addition > sizeof ( buf ) ? sizeof ( buf ) : addition ;
2013-02-21 16:34:32 +01:00
ssize_t written = tdb_pwrite ( tdb , buf , n , size ) ;
2007-07-20 14:23:12 +00:00
if ( written = = 0 ) {
/* prevent infinite loops: try _once_ more */
2013-02-21 16:34:32 +01:00
written = tdb_pwrite ( tdb , buf , n , size ) ;
2007-07-20 14:23:12 +00:00
}
if ( written = = 0 ) {
/* give up, trying to provide a useful errno */
2013-05-28 12:59:32 +02:00
tdb - > ecode = TDB_ERR_OOM ;
2007-07-20 14:23:12 +00:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " expand_file write "
" returned 0 twice: giving up! \n " ) ) ;
errno = ENOSPC ;
2017-08-23 12:48:03 +02:00
goto fail ;
2013-03-04 13:06:05 +01:00
}
if ( written = = - 1 ) {
2013-05-28 12:59:32 +02:00
tdb - > ecode = TDB_ERR_OOM ;
2007-07-20 14:23:12 +00:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " expand_file write of "
2013-05-28 16:53:56 +09:30
" %u bytes failed (%s) \n " , ( int ) n ,
2007-07-25 18:53:16 +00:00
strerror ( errno ) ) ) ;
2017-08-23 12:48:03 +02:00
goto fail ;
2013-03-04 13:06:05 +01:00
}
if ( written ! = n ) {
2007-07-20 14:23:12 +00:00
TDB_LOG ( ( tdb , TDB_DEBUG_WARNING , " expand_file: wrote "
2013-05-28 16:53:56 +09:30
" only %zu of %zi bytes - retrying \n " , written ,
n ) ) ;
2006-07-11 18:01:26 +00:00
}
2007-07-20 14:23:12 +00:00
addition - = written ;
size + = written ;
2006-07-11 18:01:26 +00:00
}
return 0 ;
2017-08-23 12:48:03 +02:00
fail :
{
int err = errno ;
int ret ;
/*
* We ' re holding the freelist lock or are inside a
* transaction . Cutting the file is safe , the space we
* tried to allocate can ' t have been used anywhere in
* the meantime .
*/
ret = tdb_ftruncate ( tdb , size ) ;
if ( ret = = - 1 ) {
TDB_LOG ( ( tdb , TDB_DEBUG_WARNING , " expand_file: "
" retruncate to %ju failed \n " ,
( uintmax_t ) size ) ) ;
}
errno = err ;
}
return - 1 ;
2006-07-11 18:01:26 +00:00
}
2011-12-21 14:17:16 +10:30
/* You need 'size', this tells you how much you should expand by. */
tdb_off_t tdb_expand_adjust ( tdb_off_t map_size , tdb_off_t size , int page_size )
2006-07-11 18:01:26 +00:00
{
2013-05-28 13:01:27 +02:00
tdb_off_t new_size , top_size , increment ;
tdb_off_t max_size = UINT32_MAX - map_size ;
if ( size > max_size ) {
/*
* We can ' t round up anymore , just give back
* what we ' re asked for .
*
* The caller has to take care of the ENOSPC handling .
*/
return size ;
}
2006-07-11 18:01:26 +00:00
2011-04-18 22:15:11 +09:30
/* limit size in order to avoid using up huge amounts of memory for
* in memory tdbs if an oddball huge record creeps in */
if ( size > 100 * 1024 ) {
2013-05-28 13:01:27 +02:00
increment = size * 2 ;
2011-04-18 22:15:11 +09:30
} else {
2013-05-28 13:01:27 +02:00
increment = size * 100 ;
}
if ( increment < size ) {
goto overflow ;
}
if ( ! tdb_add_off_t ( map_size , increment , & top_size ) ) {
goto overflow ;
2011-04-18 22:15:11 +09:30
}
/* always make room for at least top_size more records, and at
least 25 % more space . if the DB is smaller than 100 MiB ,
otherwise grow it by 10 % only . */
2011-12-21 14:17:16 +10:30
if ( map_size > 100 * 1024 * 1024 ) {
new_size = map_size * 1.10 ;
2011-04-18 22:15:11 +09:30
} else {
2011-12-21 14:17:16 +10:30
new_size = map_size * 1.25 ;
2011-04-18 22:15:11 +09:30
}
2013-05-28 13:01:27 +02:00
if ( new_size < map_size ) {
goto overflow ;
}
2011-04-18 22:15:11 +09:30
/* Round the database up to a multiple of the page size */
2011-12-21 14:17:16 +10:30
new_size = MAX ( top_size , new_size ) ;
2013-05-30 16:23:17 +02:00
if ( new_size + page_size < new_size ) {
/* There's a "+" in TDB_ALIGN that might overflow... */
goto overflow ;
}
2011-12-21 14:17:16 +10:30
return TDB_ALIGN ( new_size , page_size ) - map_size ;
2013-05-28 13:01:27 +02:00
overflow :
/*
* Somewhere in between we went over 4 GB . Make one big jump to
* exactly 4 GB database size .
*/
return max_size ;
2011-12-21 14:17:16 +10:30
}
/* expand the database at least size bytes by expanding the underlying
file and doing the mmap again if necessary */
int tdb_expand ( struct tdb_context * tdb , tdb_off_t size )
{
struct tdb_record rec ;
tdb_off_t offset ;
2013-05-28 13:04:29 +02:00
tdb_off_t new_size ;
2011-12-21 14:17:16 +10:30
if ( tdb_lock ( tdb , - 1 , F_WRLCK ) = = - 1 ) {
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " lock failed in tdb_expand \n " ) ) ;
return - 1 ;
}
/* must know about any previous expansions by another process */
tdb - > methods - > tdb_oob ( tdb , tdb - > map_size , 1 , 1 ) ;
2013-02-21 16:34:32 +01:00
/*
* Note : that we don ' t care about tdb - > hdr_ofs ! = 0 here
*
* The 4 GB limitation is just related to tdb - > map_size
* and the offset calculation in the records .
*
* The file on disk can be up to 4 GB + tdb - > hdr_ofs
*/
2011-12-21 14:17:16 +10:30
size = tdb_expand_adjust ( tdb - > map_size , size , tdb - > page_size ) ;
2006-07-11 18:01:26 +00:00
2013-05-28 13:04:29 +02:00
if ( ! tdb_add_off_t ( tdb - > map_size , size , & new_size ) ) {
tdb - > ecode = TDB_ERR_OOM ;
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_expand "
" overflow detected current map_size[%u] size[%u]! \n " ,
( unsigned ) tdb - > map_size , ( unsigned ) size ) ) ;
goto fail ;
2006-07-11 18:01:26 +00:00
}
2012-03-23 10:45:18 +10:30
/* form a new freelist record */
offset = tdb - > map_size ;
memset ( & rec , ' \0 ' , sizeof ( rec ) ) ;
rec . rec_len = size - sizeof ( rec ) ;
2006-07-11 18:01:26 +00:00
if ( tdb - > flags & TDB_INTERNAL ) {
2013-05-28 13:04:29 +02:00
char * new_map_ptr ;
new_map_ptr = ( char * ) realloc ( tdb - > map_ptr , new_size ) ;
2006-07-11 18:01:26 +00:00
if ( ! new_map_ptr ) {
2013-05-28 13:04:29 +02:00
tdb - > ecode = TDB_ERR_OOM ;
2006-07-11 18:01:26 +00:00
goto fail ;
}
tdb - > map_ptr = new_map_ptr ;
2013-05-28 13:04:29 +02:00
tdb - > map_size = new_size ;
2006-07-11 18:01:26 +00:00
} else {
2013-05-28 13:04:29 +02:00
int ret ;
/*
* expand the file itself
*/
ret = tdb - > methods - > tdb_expand_file ( tdb , tdb - > map_size , size ) ;
if ( ret ! = 0 ) {
goto fail ;
}
2012-03-23 10:45:18 +10:30
/* Explicitly remap: if we're in a transaction, this won't
* happen automatically ! */
tdb_munmap ( tdb ) ;
2013-05-28 13:04:29 +02:00
tdb - > map_size = new_size ;
2012-03-22 10:47:26 +10:30
if ( tdb_mmap ( tdb ) ! = 0 ) {
goto fail ;
}
2006-07-11 18:01:26 +00:00
}
/* link it into the free list */
if ( tdb_free ( tdb , offset , & rec ) = = - 1 )
goto fail ;
tdb_unlock ( tdb , - 1 , F_WRLCK ) ;
return 0 ;
fail :
tdb_unlock ( tdb , - 1 , F_WRLCK ) ;
return - 1 ;
}
/* read/write a tdb_off_t */
int tdb_ofs_read ( struct tdb_context * tdb , tdb_off_t offset , tdb_off_t * d )
{
return tdb - > methods - > tdb_read ( tdb , offset , ( char * ) d , sizeof ( * d ) , DOCONV ( ) ) ;
}
int tdb_ofs_write ( struct tdb_context * tdb , tdb_off_t offset , tdb_off_t * d )
{
tdb_off_t off = * d ;
return tdb - > methods - > tdb_write ( tdb , offset , CONVERT ( off ) , sizeof ( * d ) ) ;
}
/* read a lump of data, allocating the space for it */
2007-03-29 09:35:51 +00:00
unsigned char * tdb_alloc_read ( struct tdb_context * tdb , tdb_off_t offset , tdb_len_t len )
2006-07-11 18:01:26 +00:00
{
2007-03-29 09:35:51 +00:00
unsigned char * buf ;
2006-07-11 18:01:26 +00:00
/* some systems don't like zero length malloc */
2009-07-30 13:09:33 -07:00
if ( ! ( buf = ( unsigned char * ) malloc ( len ? len : 1 ) ) ) {
2006-07-11 18:01:26 +00:00
/* Ensure ecode is set for log fn. */
tdb - > ecode = TDB_ERR_OOM ;
2013-05-28 16:53:56 +09:30
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_alloc_read malloc failed len=%u (%s) \n " ,
2006-07-11 18:01:26 +00:00
len , strerror ( errno ) ) ) ;
2009-10-22 00:09:43 +10:30
return NULL ;
2006-07-11 18:01:26 +00:00
}
if ( tdb - > methods - > tdb_read ( tdb , offset , buf , len , 0 ) = = - 1 ) {
SAFE_FREE ( buf ) ;
return NULL ;
}
return buf ;
}
2007-02-19 11:19:53 +00:00
/* Give a piece of tdb data to a parser */
int tdb_parse_data ( struct tdb_context * tdb , TDB_DATA key ,
tdb_off_t offset , tdb_len_t len ,
int ( * parser ) ( TDB_DATA key , TDB_DATA data ,
void * private_data ) ,
void * private_data )
{
TDB_DATA data ;
int result ;
data . dsize = len ;
if ( ( tdb - > transaction = = NULL ) & & ( tdb - > map_ptr ! = NULL ) ) {
/*
* Optimize by avoiding the malloc / memcpy / free , point the
* parser directly at the mmap area .
*/
2011-12-19 15:47:50 +10:30
if ( tdb - > methods - > tdb_oob ( tdb , offset , len , 0 ) ! = 0 ) {
2007-02-19 11:19:53 +00:00
return - 1 ;
}
2007-03-29 09:35:51 +00:00
data . dptr = offset + ( unsigned char * ) tdb - > map_ptr ;
2007-02-19 11:19:53 +00:00
return parser ( key , data , private_data ) ;
}
if ( ! ( data . dptr = tdb_alloc_read ( tdb , offset , len ) ) ) {
return - 1 ;
}
result = parser ( key , data , private_data ) ;
free ( data . dptr ) ;
return result ;
}
2006-07-11 18:01:26 +00:00
/* read/write a record */
2009-10-23 13:51:03 +02:00
int tdb_rec_read ( struct tdb_context * tdb , tdb_off_t offset , struct tdb_record * rec )
2006-07-11 18:01:26 +00:00
{
if ( tdb - > methods - > tdb_read ( tdb , offset , rec , sizeof ( * rec ) , DOCONV ( ) ) = = - 1 )
return - 1 ;
if ( TDB_BAD_MAGIC ( rec ) ) {
/* Ensure ecode is set for log fn. */
tdb - > ecode = TDB_ERR_CORRUPT ;
2013-05-28 16:53:56 +09:30
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_rec_read bad magic 0x%x at offset=%u \n " , rec - > magic , offset ) ) ;
2009-10-22 00:09:43 +10:30
return - 1 ;
2006-07-11 18:01:26 +00:00
}
2011-12-19 15:47:50 +10:30
return tdb - > methods - > tdb_oob ( tdb , rec - > next , sizeof ( * rec ) , 0 ) ;
2006-07-11 18:01:26 +00:00
}
2009-10-23 13:51:03 +02:00
int tdb_rec_write ( struct tdb_context * tdb , tdb_off_t offset , struct tdb_record * rec )
2006-07-11 18:01:26 +00:00
{
2009-10-23 13:51:03 +02:00
struct tdb_record r = * rec ;
2006-07-11 18:01:26 +00:00
return tdb - > methods - > tdb_write ( tdb , offset , CONVERT ( r ) , sizeof ( r ) ) ;
}
static const struct tdb_methods io_methods = {
tdb_read ,
tdb_write ,
tdb_next_hash_chain ,
tdb_oob ,
tdb_expand_file ,
} ;
/*
initialise the default methods table
*/
void tdb_io_init ( struct tdb_context * tdb )
{
tdb - > methods = & io_methods ;
}