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"
/* 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
if necessary
*/
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 ;
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_oob off %d len %d wrap \n " ,
( int ) off , ( int ) len ) ) ;
}
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
}
if ( fstat ( tdb - > fd , & 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
if ( st . st_size < ( size_t ) off + len ) {
2006-07-11 18:01:26 +00:00
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 eof at %u \n " ,
( int ) ( off + len ) , ( int ) st . st_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
}
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 ;
}
2006-07-11 18:01:26 +00:00
/* Unmap, update size, remap */
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-03-22 10:47:26 +10:30
return tdb_mmap ( tdb ) ;
2006-07-11 18:01:26 +00:00
}
/* write a lump of data at a specified offset */
static int tdb_write ( struct tdb_context * tdb , tdb_off_t off ,
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
2007-08-28 14:25:46 +00:00
ssize_t written = pwrite ( tdb - > fd , buf , len , off ) ;
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 "
" %d of %d bytes at %d, trying once more \n " ,
2007-08-28 15:07:13 +00:00
( int ) written , len , off ) ) ;
2009-10-22 00:09:43 +10:30
written = pwrite ( tdb - > fd , ( const char * ) buf + written ,
2007-08-28 14:25:46 +00:00
len - written ,
off + written ) ;
}
if ( written = = - 1 ) {
2009-10-22 00:09:43 +10:30
/* Ensure ecode is set for log fn. */
tdb - > ecode = TDB_ERR_IO ;
2007-08-28 14:25:46 +00:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_write failed at %d "
" len=%d (%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 "
" write %d bytes at %d in two attempts \n " ,
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 */
static int tdb_read ( struct tdb_context * tdb , tdb_off_t off , void * buf ,
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
2006-07-11 18:01:26 +00:00
ssize_t ret = pread ( tdb - > fd , buf , len , off ) ;
if ( ret ! = ( ssize_t ) len ) {
/* Ensure ecode is set for log fn. */
tdb - > ecode = TDB_ERR_IO ;
2007-01-03 06:39:18 +00:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_read failed at %d "
" len=%d ret=%d (%s) map_size=%d \n " ,
2007-01-03 09:30:55 +00:00
( int ) off , ( int ) len , ( int ) ret , strerror ( errno ) ,
2007-01-03 06:39:18 +00:00
( int ) 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
*/
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 ) {
for ( ; h < tdb - > header . 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 ;
2006-07-11 18:01:26 +00:00
for ( ; h < tdb - > header . hash_size ; h + + ) {
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 ) ) {
2006-07-11 18:01:26 +00:00
tdb - > map_ptr = mmap ( NULL , tdb - > map_size ,
PROT_READ | ( tdb - > read_only ? 0 : PROT_WRITE ) ,
MAP_SHARED | MAP_FILE , tdb - > fd , 0 ) ;
/*
* NB . When mmap fails it returns MAP_FAILED * NOT * NULL ! ! ! !
*/
if ( tdb - > map_ptr = = MAP_FAILED ) {
tdb - > map_ptr = NULL ;
2006-10-20 09:55:47 +00:00
TDB_LOG ( ( tdb , TDB_DEBUG_WARNING , " tdb_mmap failed for size %d (%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 ] ;
2006-07-11 18:01:26 +00:00
if ( tdb - > read_only | | tdb - > traverse_read ) {
tdb - > ecode = TDB_ERR_RDONLY ;
return - 1 ;
}
if ( ftruncate ( tdb - > fd , size + addition ) = = - 1 ) {
char b = 0 ;
2007-07-20 16:31:32 +00:00
ssize_t written = pwrite ( tdb - > fd , & b , 1 , ( size + addition ) - 1 ) ;
if ( written = = 0 ) {
/* try once more, potentially revealing errno */
written = pwrite ( tdb - > fd , & b , 1 , ( size + addition ) - 1 ) ;
}
if ( written = = 0 ) {
/* again - give up, guessing errno */
errno = ENOSPC ;
}
if ( written ! = 1 ) {
2006-10-20 09:55:47 +00:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " expand_file to %d failed (%s) \n " ,
2006-07-11 18:01:26 +00:00
size + addition , strerror ( errno ) ) ) ;
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 ;
ssize_t written = pwrite ( tdb - > fd , buf , n , size ) ;
if ( written = = 0 ) {
/* prevent infinite loops: try _once_ more */
written = pwrite ( tdb - > fd , buf , n , size ) ;
}
if ( written = = 0 ) {
/* give up, trying to provide a useful errno */
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " expand_file write "
" returned 0 twice: giving up! \n " ) ) ;
errno = ENOSPC ;
return - 1 ;
} else if ( written = = - 1 ) {
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " expand_file write of "
2007-07-25 18:53:16 +00:00
" %d bytes failed (%s) \n " , ( int ) n ,
strerror ( errno ) ) ) ;
2006-07-11 18:01:26 +00:00
return - 1 ;
2007-07-20 14:23:12 +00:00
} else if ( written ! = n ) {
TDB_LOG ( ( tdb , TDB_DEBUG_WARNING , " expand_file: wrote "
2007-07-25 18:53:16 +00:00
" only %d of %d bytes - retrying \n " , ( int ) written ,
( int ) 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 ;
}
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
{
2011-12-21 14:17:16 +10:30
tdb_off_t new_size , top_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 ) {
2011-12-21 14:17:16 +10:30
top_size = map_size + size * 2 ;
2011-04-18 22:15:11 +09:30
} else {
2011-12-21 14:17:16 +10:30
top_size = map_size + size * 100 ;
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
}
/* 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 ) ;
return TDB_ALIGN ( new_size , page_size ) - map_size ;
}
/* 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 ;
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 ) ;
size = tdb_expand_adjust ( tdb - > map_size , size , tdb - > page_size ) ;
2006-07-11 18:01:26 +00:00
/* expand the file itself */
if ( ! ( tdb - > flags & TDB_INTERNAL ) ) {
if ( tdb - > methods - > tdb_expand_file ( tdb , tdb - > map_size , size ) ! = 0 )
goto fail ;
}
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 ) {
2006-07-30 10:42:11 +00:00
char * new_map_ptr = ( char * ) realloc ( tdb - > map_ptr ,
2012-03-23 10:45:18 +10:30
tdb - > map_size + size ) ;
2006-07-11 18:01:26 +00:00
if ( ! new_map_ptr ) {
goto fail ;
}
tdb - > map_ptr = new_map_ptr ;
2012-03-23 10:45:18 +10:30
tdb - > map_size + = size ;
2006-07-11 18:01:26 +00:00
} else {
2012-03-23 10:45:18 +10:30
/* Explicitly remap: if we're in a transaction, this won't
* happen automatically ! */
tdb_munmap ( tdb ) ;
tdb - > map_size + = 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 ;
2006-10-20 09:55:47 +00:00
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR , " tdb_alloc_read malloc failed len=%d (%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 ;
2006-10-20 09:55:47 +00:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_rec_read bad magic 0x%x at offset=%d \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 ;
}