1999-12-21 06:04:37 +03:00
/*
Unix SMB / Netbios implementation .
Version 3.0
Samba database functions
2000-04-25 05:17:45 +04:00
Copyright ( C ) Andrew Tridgell 1999 - 2000
Copyright ( C ) Luke Kenneth Casson Leighton 2000
2000-12-06 03:05:15 +03:00
Copyright ( C ) Paul ` Rusty ' Russell 2000
Copyright ( C ) Jeremy Allison 2000
1999-12-21 06:04:37 +03:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program 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 General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2000-12-13 09:33:53 +03:00
# ifdef STANDALONE
2000-12-06 03:05:15 +03:00
# if HAVE_CONFIG_H
# include <config.h>
# endif
1999-12-21 06:04:37 +03:00
# include <stdlib.h>
# include <stdio.h>
# include <fcntl.h>
# include <unistd.h>
# include <string.h>
# include <fcntl.h>
1999-12-23 04:14:20 +03:00
# include <errno.h>
1999-12-21 06:04:37 +03:00
# include <sys/mman.h>
# include <sys/stat.h>
# include "tdb.h"
2000-12-06 03:05:15 +03:00
# include "spinlock.h"
1999-12-21 06:04:37 +03:00
# else
# include "includes.h"
# endif
2000-12-06 03:05:15 +03:00
# define TDB_MAGIC_FOOD "TDB file\n"
# define TDB_VERSION (0x26011967 + 6)
1999-12-24 11:45:02 +03:00
# define TDB_MAGIC (0x26011999U)
# define TDB_FREE_MAGIC (~TDB_MAGIC)
2000-12-06 03:05:15 +03:00
# define TDB_DEAD_MAGIC (0xFEE1DEAD)
# define TDB_ALIGNMENT 4
# define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT)
2000-05-10 05:32:51 +04:00
# define DEFAULT_HASH_SIZE 131
1999-12-24 11:45:02 +03:00
# define TDB_PAGE_SIZE 0x2000
2000-05-12 06:57:24 +04:00
# define FREELIST_TOP (sizeof(struct tdb_header))
2000-12-13 09:33:53 +03:00
# define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1))
2001-04-13 23:12:06 +04:00
# define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24))
2000-12-06 03:05:15 +03:00
# define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC)
# define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r))
# define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off))
2001-05-30 07:42:44 +04:00
# define TDB_LOG(x) (tdb->log_fn?((tdb->log_fn x),0) : 0)
1999-12-21 06:04:37 +03:00
2000-01-03 02:00:27 +03:00
/* lock offsets */
# define GLOBAL_LOCK 0
# define ACTIVE_LOCK 4
1999-12-23 04:14:20 +03:00
2000-01-14 11:33:20 +03:00
# ifndef MAP_FILE
# define MAP_FILE 0
# endif
2001-06-21 23:56:25 +04:00
# ifndef MAP_FAILED
# define MAP_FAILED ((void *)-1)
# endif
2000-12-06 03:05:15 +03:00
# define BUCKET(hash) ((hash) % tdb->header.hash_size)
TDB_DATA tdb_null ;
/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */
static TDB_CONTEXT * tdbs = NULL ;
2001-05-06 06:05:00 +04:00
static void tdb_munmap ( TDB_CONTEXT * tdb )
2000-12-06 03:05:15 +03:00
{
2001-08-03 00:17:40 +04:00
if ( tdb - > flags & TDB_INTERNAL )
return ;
2000-12-06 03:05:15 +03:00
# ifdef HAVE_MMAP
2001-08-03 00:17:40 +04:00
if ( tdb - > map_ptr )
munmap ( tdb - > map_ptr , tdb - > map_size ) ;
2000-12-06 03:05:15 +03:00
# endif
2001-05-06 06:05:00 +04:00
tdb - > map_ptr = NULL ;
2000-12-06 03:05:15 +03:00
}
2001-05-06 06:05:00 +04:00
static void tdb_mmap ( TDB_CONTEXT * tdb )
2000-12-06 03:05:15 +03:00
{
2001-08-03 00:17:40 +04:00
if ( tdb - > flags & TDB_INTERNAL )
return ;
2000-12-06 03:05:15 +03:00
# ifdef HAVE_MMAP
2001-05-30 09:40:52 +04:00
if ( ! ( tdb - > flags & TDB_NOMMAP ) ) {
2001-05-06 06:05:00 +04:00
tdb - > map_ptr = mmap ( NULL , tdb - > map_size ,
PROT_READ | ( tdb - > read_only ? 0 : PROT_WRITE ) ,
MAP_SHARED | MAP_FILE , tdb - > fd , 0 ) ;
2001-06-21 23:50:21 +04:00
/*
2001-06-21 23:56:25 +04:00
* NB . When mmap fails it returns MAP_FAILED * NOT * NULL ! ! ! !
2001-06-21 23:50:21 +04:00
*/
2001-06-21 23:56:25 +04:00
if ( tdb - > map_ptr = = MAP_FAILED ) {
2001-06-21 23:50:21 +04:00
tdb - > map_ptr = NULL ;
2001-05-30 09:40:52 +04:00
TDB_LOG ( ( tdb , 2 , " tdb_mmap failed for size %d (%s) \n " ,
tdb - > map_size , strerror ( errno ) ) ) ;
}
2001-09-19 01:17:58 +04:00
} else {
tdb - > map_ptr = NULL ;
2001-05-30 09:40:52 +04:00
}
2001-05-06 06:05:00 +04:00
# else
tdb - > map_ptr = NULL ;
2001-01-30 00:34:08 +03:00
# endif
2000-12-06 03:05:15 +03:00
}
/* Endian conversion: we only ever deal with 4 byte quantities */
static void * convert ( void * buf , u32 size )
{
u32 i , * p = buf ;
2001-08-03 00:17:40 +04:00
for ( i = 0 ; i < size / 4 ; i + + )
p [ i ] = TDB_BYTEREV ( p [ i ] ) ;
2000-12-06 03:05:15 +03:00
return buf ;
}
# define DOCONV() (tdb->flags & TDB_CONVERT)
# define CONVERT(x) (DOCONV() ? convert(&x, sizeof(x)) : &x)
1999-12-21 06:04:37 +03:00
/* the body of the database is made of one list_struct for the free space
plus a separate data list for each hash value */
struct list_struct {
tdb_off next ; /* offset of the next record in the list */
2000-12-06 03:05:15 +03:00
tdb_len rec_len ; /* total byte length of record */
1999-12-21 06:04:37 +03:00
tdb_len key_len ; /* byte length of key */
tdb_len data_len ; /* byte length of data */
2000-12-06 03:05:15 +03:00
u32 full_hash ; /* the full 32 bit hash of the key */
u32 magic ; /* try to catch errors */
/* the following union is implied:
2001-07-25 22:59:56 +04:00
union {
char record [ rec_len ] ;
struct {
char key [ key_len ] ;
char data [ data_len ] ;
}
u32 totalsize ; ( tailer )
}
*/
1999-12-21 06:04:37 +03:00
} ;
1999-12-21 12:25:59 +03:00
/* a byte range locking function - return 0 on success
this functions locks / unlocks 1 byte at the specified offset */
2000-01-03 02:00:27 +03:00
static int tdb_brlock ( TDB_CONTEXT * tdb , tdb_off offset ,
2001-05-30 09:40:52 +04:00
int rw_type , int lck_type , int probe )
1999-12-21 12:25:59 +03:00
{
struct flock fl ;
2001-08-03 00:17:40 +04:00
if ( tdb - > flags & TDB_NOLOCK )
return 0 ;
if ( tdb - > read_only )
return - 1 ;
1999-12-23 04:14:20 +03:00
2000-06-15 19:29:39 +04:00
fl . l_type = rw_type ;
1999-12-21 12:25:59 +03:00
fl . l_whence = SEEK_SET ;
fl . l_start = offset ;
fl . l_len = 1 ;
fl . l_pid = 0 ;
2001-05-30 09:40:52 +04:00
if ( fcntl ( tdb - > fd , lck_type , & fl ) ) {
if ( ! probe ) {
TDB_LOG ( ( tdb , 5 , " tdb_brlock failed at offset %d rw_type=%d lck_type=%d \n " ,
offset , rw_type , lck_type ) ) ;
}
return TDB_ERRCODE ( TDB_ERR_LOCK , - 1 ) ;
}
1999-12-21 12:25:59 +03:00
return 0 ;
}
1999-12-23 04:14:20 +03:00
/* lock a list in the database. list -1 is the alloc list */
2000-12-06 03:05:15 +03:00
static int tdb_lock ( TDB_CONTEXT * tdb , int list , int ltype )
1999-12-23 04:14:20 +03:00
{
2001-05-30 09:40:52 +04:00
if ( list < - 1 | | list > = ( int ) tdb - > header . hash_size ) {
TDB_LOG ( ( tdb , 0 , " tdb_lock: invalid list %d for ltype=%d \n " ,
list , ltype ) ) ;
return - 1 ;
}
2001-08-03 00:17:40 +04:00
if ( tdb - > flags & TDB_NOLOCK )
return 0 ;
2000-05-01 04:41:47 +04:00
2000-12-06 03:05:15 +03:00
/* Since fcntl locks don't nest, we do a lock for the first one,
and simply bump the count for future ones */
if ( tdb - > locked [ list + 1 ] . count = = 0 ) {
if ( tdb - > header . rwlocks ) {
2001-05-30 09:40:52 +04:00
if ( tdb_spinlock ( tdb , list , ltype ) ) {
TDB_LOG ( ( tdb , 0 , " tdb_lock spinlock on list ltype=%d \n " ,
list , ltype ) ) ;
return - 1 ;
}
} else if ( tdb_brlock ( tdb , FREELIST_TOP + 4 * list , ltype , F_SETLKW , 0 ) ) {
TDB_LOG ( ( tdb , 0 , " tdb_lock failed on list %d ltype=%d (%s) \n " ,
list , ltype , strerror ( errno ) ) ) ;
1999-12-23 04:14:20 +03:00
return - 1 ;
2001-05-30 09:40:52 +04:00
}
2000-12-06 03:05:15 +03:00
tdb - > locked [ list + 1 ] . ltype = ltype ;
1999-12-23 04:14:20 +03:00
}
2000-06-15 19:29:39 +04:00
tdb - > locked [ list + 1 ] . count + + ;
1999-12-23 04:14:20 +03:00
return 0 ;
}
2000-12-06 03:05:15 +03:00
/* unlock the database: returns void because it's too late for errors. */
static void tdb_unlock ( TDB_CONTEXT * tdb , int list , int ltype )
1999-12-23 04:14:20 +03:00
{
2001-08-03 00:17:40 +04:00
if ( tdb - > flags & TDB_NOLOCK )
return ;
1999-12-23 04:14:20 +03:00
2000-12-06 03:05:15 +03:00
/* Sanity checks */
2001-08-03 00:17:40 +04:00
if ( list < - 1 | | list > = ( int ) tdb - > header . hash_size )
return ;
if ( tdb - > locked [ list + 1 ] . count = = 0 )
return ;
2000-05-01 04:41:47 +04:00
2000-06-15 19:29:39 +04:00
if ( tdb - > locked [ list + 1 ] . count = = 1 ) {
2000-12-06 03:05:15 +03:00
/* Down to last nested lock: unlock underneath */
2001-08-03 00:17:40 +04:00
if ( tdb - > header . rwlocks )
tdb_spinunlock ( tdb , list , ltype ) ;
else
tdb_brlock ( tdb , FREELIST_TOP + 4 * list , F_UNLCK , F_SETLKW , 0 ) ;
1999-12-23 04:14:20 +03:00
}
2000-06-15 19:29:39 +04:00
tdb - > locked [ list + 1 ] . count - - ;
1999-12-23 04:14:20 +03:00
}
1999-12-21 12:25:59 +03:00
2000-12-06 03:05:15 +03:00
/* This is based on the hash agorithm from gdbm */
static u32 tdb_hash ( TDB_DATA * key )
1999-12-21 06:04:37 +03:00
{
2000-12-06 03:05:15 +03:00
u32 value ; /* Used to compute the hash value. */
u32 i ; /* Used to cycle through random values. */
1999-12-21 06:04:37 +03:00
/* Set the initial value from the key size. */
2000-12-06 03:05:15 +03:00
for ( value = 0x238F13AF * key - > dsize , i = 0 ; i < key - > dsize ; i + + )
1999-12-21 06:04:37 +03:00
value = ( value + ( key - > dptr [ i ] < < ( i * 5 % 24 ) ) ) ;
2000-12-06 03:05:15 +03:00
return ( 1103515243 * value + 12345 ) ;
1999-12-21 06:04:37 +03:00
}
1999-12-21 07:54:30 +03: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
2001-05-30 09:40:52 +04:00
if necessary
note that " len " is the minimum length needed for the db
*/
static int tdb_oob ( TDB_CONTEXT * tdb , tdb_off len , int probe )
1999-12-21 07:54:30 +03:00
{
struct stat st ;
2001-08-03 00:17:40 +04:00
if ( len < = tdb - > map_size )
return 0 ;
if ( tdb - > flags & TDB_INTERNAL )
return 0 ;
1999-12-21 07:54:30 +03:00
2001-07-25 22:59:56 +04:00
if ( fstat ( tdb - > fd , & st ) = = - 1 )
return TDB_ERRCODE ( TDB_ERR_IO , - 1 ) ;
2001-05-30 09:40:52 +04:00
if ( st . st_size < ( size_t ) len ) {
if ( ! probe ) {
TDB_LOG ( ( tdb , 0 , " tdb_oob len %d beyond eof at %d \n " ,
( int ) len , ( int ) st . st_size ) ) ;
}
return TDB_ERRCODE ( TDB_ERR_IO , - 1 ) ;
}
1999-12-21 07:54:30 +03:00
2000-12-06 03:05:15 +03:00
/* Unmap, update size, remap */
2001-05-06 06:05:00 +04:00
tdb_munmap ( tdb ) ;
1999-12-21 07:54:30 +03:00
tdb - > map_size = st . st_size ;
2001-05-06 06:05:00 +04:00
tdb_mmap ( tdb ) ;
1999-12-21 07:54:30 +03:00
return 0 ;
}
1999-12-21 06:04:37 +03:00
/* write a lump of data at a specified offset */
2000-12-06 03:05:15 +03:00
static int tdb_write ( TDB_CONTEXT * tdb , tdb_off off , void * buf , tdb_len len )
1999-12-21 06:04:37 +03:00
{
2001-08-03 00:17:40 +04:00
if ( tdb_oob ( tdb , off + len , 0 ) ! = 0 )
return - 1 ;
1999-12-21 06:04:37 +03:00
2001-08-03 00:17:40 +04:00
if ( tdb - > map_ptr )
memcpy ( off + ( char * ) tdb - > map_ptr , buf , len ) ;
2000-12-06 03:05:15 +03:00
else if ( lseek ( tdb - > fd , off , SEEK_SET ) ! = off
2001-05-30 09:40:52 +04:00
| | write ( tdb - > fd , buf , len ) ! = ( ssize_t ) len ) {
TDB_LOG ( ( tdb , 0 , " tdb_write failed at %d len=%d (%s) \n " ,
off , len , strerror ( errno ) ) ) ;
2000-12-06 03:05:15 +03:00
return TDB_ERRCODE ( TDB_ERR_IO , - 1 ) ;
2001-05-30 09:40:52 +04:00
}
1999-12-21 06:04:37 +03:00
return 0 ;
}
2000-12-06 03:05:15 +03:00
/* read a lump of data at a specified offset, maybe convert */
static int tdb_read ( TDB_CONTEXT * tdb , tdb_off off , void * buf , tdb_len len , int cv )
1999-12-21 06:04:37 +03:00
{
2001-08-03 00:17:40 +04:00
if ( tdb_oob ( tdb , off + len , 0 ) ! = 0 )
return - 1 ;
1999-12-21 06:04:37 +03:00
2001-08-03 00:17:40 +04:00
if ( tdb - > map_ptr )
memcpy ( buf , off + ( char * ) tdb - > map_ptr , len ) ;
2000-12-06 03:05:15 +03:00
else if ( lseek ( tdb - > fd , off , SEEK_SET ) ! = off
2001-05-30 09:40:52 +04:00
| | read ( tdb - > fd , buf , len ) ! = ( ssize_t ) len ) {
TDB_LOG ( ( tdb , 0 , " tdb_read failed at %d len=%d (%s) \n " ,
off , len , strerror ( errno ) ) ) ;
2000-12-06 03:05:15 +03:00
return TDB_ERRCODE ( TDB_ERR_IO , - 1 ) ;
2001-05-30 09:40:52 +04:00
}
2001-08-03 00:17:40 +04:00
if ( cv )
convert ( buf , len ) ;
1999-12-21 06:04:37 +03:00
return 0 ;
}
/* read a lump of data, allocating the space for it */
static char * tdb_alloc_read ( TDB_CONTEXT * tdb , tdb_off offset , tdb_len len )
{
char * buf ;
2001-05-30 09:40:52 +04:00
if ( ! ( buf = malloc ( len ) ) ) {
TDB_LOG ( ( tdb , 0 , " tdb_alloc_read malloc failed len=%d (%s) \n " ,
len , strerror ( errno ) ) ) ;
return TDB_ERRCODE ( TDB_ERR_OOM , buf ) ;
}
2000-12-06 03:05:15 +03:00
if ( tdb_read ( tdb , offset , buf , len , 0 ) = = - 1 ) {
1999-12-21 06:04:37 +03:00
free ( buf ) ;
return NULL ;
}
return buf ;
}
2000-12-06 03:05:15 +03:00
/* read/write a tdb_off */
static int ofs_read ( TDB_CONTEXT * tdb , tdb_off offset , tdb_off * d )
1999-12-23 04:14:20 +03:00
{
2000-12-06 03:05:15 +03:00
return tdb_read ( tdb , offset , ( char * ) d , sizeof ( * d ) , DOCONV ( ) ) ;
1999-12-23 04:14:20 +03:00
}
1999-12-24 11:45:02 +03:00
static int ofs_write ( TDB_CONTEXT * tdb , tdb_off offset , tdb_off * d )
{
2000-12-06 03:05:15 +03:00
tdb_off off = * d ;
return tdb_write ( tdb , offset , CONVERT ( off ) , sizeof ( * d ) ) ;
1999-12-24 11:45:02 +03:00
}
2000-12-06 03:05:15 +03:00
/* read/write a record */
static int rec_read ( TDB_CONTEXT * tdb , tdb_off offset , struct list_struct * rec )
1999-12-24 11:45:02 +03:00
{
2001-08-03 00:17:40 +04:00
if ( tdb_read ( tdb , offset , rec , sizeof ( * rec ) , DOCONV ( ) ) = = - 1 )
return - 1 ;
2001-05-30 09:40:52 +04:00
if ( TDB_BAD_MAGIC ( rec ) ) {
TDB_LOG ( ( tdb , 0 , " rec_read bad magic 0x%x at offset=%d \n " , rec - > magic , offset ) ) ;
return TDB_ERRCODE ( TDB_ERR_CORRUPT , - 1 ) ;
}
return tdb_oob ( tdb , rec - > next + sizeof ( * rec ) , 0 ) ;
1999-12-24 11:45:02 +03:00
}
2000-12-06 03:05:15 +03:00
static int rec_write ( TDB_CONTEXT * tdb , tdb_off offset , struct list_struct * rec )
1999-12-22 04:22:14 +03:00
{
2000-12-06 03:05:15 +03:00
struct list_struct r = * rec ;
return tdb_write ( tdb , offset , CONVERT ( r ) , sizeof ( r ) ) ;
1999-12-22 04:22:14 +03:00
}
2000-12-05 06:12:57 +03:00
/* read a freelist record and check for simple errors */
2001-05-30 09:40:52 +04:00
static int rec_free_read ( TDB_CONTEXT * tdb , tdb_off off , struct list_struct * rec )
2000-12-05 06:12:57 +03:00
{
2001-08-03 00:17:40 +04:00
if ( tdb_read ( tdb , off , rec , sizeof ( * rec ) , DOCONV ( ) ) = = - 1 )
return - 1 ;
2000-12-05 06:12:57 +03:00
if ( rec - > magic ! = TDB_FREE_MAGIC ) {
2001-05-30 09:40:52 +04:00
TDB_LOG ( ( tdb , 0 , " rec_free_read bad magic 0x%x at offset=%d \n " ,
rec - > magic , off ) ) ;
2000-12-06 03:05:15 +03:00
return TDB_ERRCODE ( TDB_ERR_CORRUPT , - 1 ) ;
2000-12-05 06:12:57 +03:00
}
2001-08-03 00:17:40 +04:00
if ( tdb_oob ( tdb , rec - > next + sizeof ( * rec ) , 0 ) ! = 0 )
return - 1 ;
2000-12-05 06:12:57 +03:00
return 0 ;
}
2000-12-06 03:05:15 +03:00
/* update a record tailer (must hold allocation lock) */
static int update_tailer ( TDB_CONTEXT * tdb , tdb_off offset ,
const struct list_struct * rec )
2000-12-05 06:12:57 +03:00
{
2000-12-06 03:05:15 +03:00
tdb_off totalsize ;
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* Offset of tailer from record header */
totalsize = sizeof ( * rec ) + rec - > rec_len ;
return ofs_write ( tdb , offset + totalsize - sizeof ( tdb_off ) ,
& totalsize ) ;
}
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
# ifdef TDB_DEBUG
2001-05-28 17:29:06 +04:00
static tdb_off tdb_dump_record ( TDB_CONTEXT * tdb , tdb_off offset )
{
struct list_struct rec ;
tdb_off tailer_ofs , tailer ;
if ( tdb_read ( tdb , offset , ( char * ) & rec , sizeof ( rec ) , DOCONV ( ) ) = = - 1 ) {
printf ( " ERROR: failed to read record at %u \n " , offset ) ;
return 0 ;
}
printf ( " rec: offset=%u next=%d rec_len=%d key_len=%d data_len=%d full_hash=0x%x magic=0x%x \n " ,
offset , rec . next , rec . rec_len , rec . key_len , rec . data_len , rec . full_hash , rec . magic ) ;
tailer_ofs = offset + sizeof ( rec ) + rec . rec_len - sizeof ( tdb_off ) ;
if ( ofs_read ( tdb , tailer_ofs , & tailer ) = = - 1 ) {
printf ( " ERROR: failed to read tailer at %u \n " , tailer_ofs ) ;
return rec . next ;
}
if ( tailer ! = rec . rec_len + sizeof ( rec ) ) {
printf ( " ERROR: tailer does not match record! tailer=%u totalsize=%u \n " , tailer , rec . rec_len + sizeof ( rec ) ) ;
}
return rec . next ;
}
static void tdb_dump_chain ( TDB_CONTEXT * tdb , int i )
{
tdb_off rec_ptr , top ;
top = TDB_HASH_TOP ( i ) ;
tdb_lock ( tdb , i , F_WRLCK ) ;
if ( ofs_read ( tdb , top , & rec_ptr ) = = - 1 ) {
tdb_unlock ( tdb , i , F_WRLCK ) ;
return ;
}
2001-08-03 00:17:40 +04:00
if ( rec_ptr )
printf ( " hash=%d \n " , i ) ;
2001-05-28 17:29:06 +04:00
while ( rec_ptr ) {
rec_ptr = tdb_dump_record ( tdb , rec_ptr ) ;
}
tdb_unlock ( tdb , i , F_WRLCK ) ;
}
void tdb_dump_all ( TDB_CONTEXT * tdb )
{
tdb_off off ;
int i ;
for ( i = 0 ; i < tdb - > header . hash_size ; i + + ) {
tdb_dump_chain ( tdb , i ) ;
}
printf ( " freelist: \n " ) ;
tdb_dump_chain ( tdb , - 1 ) ;
}
2000-12-06 03:05:15 +03:00
void tdb_printfreelist ( TDB_CONTEXT * tdb )
{
2001-01-30 00:34:08 +03:00
long total_free = 0 ;
2001-05-15 18:53:06 +04:00
tdb_off offset , rec_ptr , last_ptr ;
2000-12-06 03:05:15 +03:00
struct list_struct rec , lastrec , newrec ;
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
tdb_lock ( tdb , - 1 , F_WRLCK ) ;
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
last_ptr = 0 ;
offset = FREELIST_TOP ;
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* read in the freelist top */
if ( ofs_read ( tdb , offset , & rec_ptr ) = = - 1 ) {
return ;
2000-12-05 06:12:57 +03:00
}
2000-12-06 03:05:15 +03:00
printf ( " freelist top=[0x%08x] \n " , rec_ptr ) ;
while ( rec_ptr ) {
if ( tdb_read ( tdb , rec_ptr , ( char * ) & rec , sizeof ( rec ) , DOCONV ( ) ) = = - 1 ) {
return ;
2000-12-05 06:12:57 +03:00
}
2000-12-06 03:05:15 +03:00
if ( rec . magic ! = TDB_FREE_MAGIC ) {
printf ( " bad magic 0x%08x in free list \n " , rec . magic ) ;
return ;
2000-12-05 06:12:57 +03:00
}
2001-01-30 00:34:08 +03:00
printf ( " entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)] \n " , rec . next , rec . rec_len , rec . rec_len ) ;
total_free + = rec . rec_len ;
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* move to the next record */
rec_ptr = rec . next ;
2000-12-05 06:12:57 +03:00
}
2001-01-30 00:34:08 +03:00
printf ( " total rec_len = [0x%08x (%d)] \n " , total_free , total_free ) ;
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
tdb_unlock ( tdb , - 1 , F_WRLCK ) ;
}
# endif
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* Remove an element from the freelist. Must have alloc lock. */
static int remove_from_freelist ( TDB_CONTEXT * tdb , tdb_off off , tdb_off next )
{
tdb_off last_ptr , i ;
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* read in the freelist top */
last_ptr = FREELIST_TOP ;
while ( ofs_read ( tdb , last_ptr , & i ) ! = - 1 & & i ! = 0 ) {
if ( i = = off ) {
/* We've found it! */
return ofs_write ( tdb , last_ptr , & next ) ;
2000-12-05 06:12:57 +03:00
}
2000-12-06 03:05:15 +03:00
/* Follow chain (next offset is at start of record) */
last_ptr = i ;
2000-12-05 06:12:57 +03:00
}
2001-05-30 09:40:52 +04:00
TDB_LOG ( ( tdb , 0 , " remove_from_freelist: not on list at off=%d \n " , off ) ) ;
2000-12-06 03:05:15 +03:00
return TDB_ERRCODE ( TDB_ERR_CORRUPT , - 1 ) ;
}
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* Add an element into the freelist. Merge adjacent records if
neccessary . */
static int tdb_free ( TDB_CONTEXT * tdb , tdb_off offset , struct list_struct * rec )
{
tdb_off right , left ;
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* Allocation and tailer lock */
2001-08-03 00:17:40 +04:00
if ( tdb_lock ( tdb , - 1 , F_WRLCK ) ! = 0 )
return - 1 ;
2000-12-05 06:12:57 +03:00
2001-05-28 17:29:06 +04:00
/* set an initial tailer, so if we fail we don't leave a bogus record */
update_tailer ( tdb , offset , rec ) ;
2000-12-06 03:05:15 +03:00
/* Look right first (I'm an Australian, dammit) */
right = offset + sizeof ( * rec ) + rec - > rec_len ;
2001-05-30 09:40:52 +04:00
if ( right + sizeof ( * rec ) < = tdb - > map_size ) {
2000-12-06 03:05:15 +03:00
struct list_struct r ;
2000-12-05 06:12:57 +03:00
2001-05-28 17:29:06 +04:00
if ( tdb_read ( tdb , right , & r , sizeof ( r ) , DOCONV ( ) ) = = - 1 ) {
TDB_LOG ( ( tdb , 0 , " tdb_free: right read failed at %u \n " , right ) ) ;
goto left ;
}
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* If it's free, expand to include it. */
if ( r . magic = = TDB_FREE_MAGIC ) {
2001-05-28 17:29:06 +04:00
if ( remove_from_freelist ( tdb , right , r . next ) = = - 1 ) {
TDB_LOG ( ( tdb , 0 , " tdb_free: right free failed at %u \n " , right ) ) ;
goto left ;
}
2000-12-06 03:05:15 +03:00
rec - > rec_len + = sizeof ( r ) + r . rec_len ;
}
2000-12-05 06:12:57 +03:00
}
2001-05-28 17:29:06 +04:00
left :
2000-12-06 03:05:15 +03:00
/* Look left */
2001-05-28 17:29:06 +04:00
left = offset - sizeof ( tdb_off ) ;
2000-12-06 03:05:15 +03:00
if ( left > TDB_HASH_TOP ( tdb - > header . hash_size - 1 ) ) {
struct list_struct l ;
tdb_off leftsize ;
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* Read in tailer and jump back to header */
2001-05-28 17:29:06 +04:00
if ( ofs_read ( tdb , left , & leftsize ) = = - 1 ) {
TDB_LOG ( ( tdb , 0 , " tdb_free: left offset read failed at %u \n " , left ) ) ;
goto update ;
}
2000-12-06 03:05:15 +03:00
left = offset - leftsize ;
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* Now read in record */
2001-05-28 17:29:06 +04:00
if ( tdb_read ( tdb , left , & l , sizeof ( l ) , DOCONV ( ) ) = = - 1 ) {
TDB_LOG ( ( tdb , 0 , " tdb_free: left read failed at %u (%u) \n " , left , leftsize ) ) ;
goto update ;
}
2000-12-06 03:05:15 +03:00
/* If it's free, expand to include it. */
if ( l . magic = = TDB_FREE_MAGIC ) {
2001-05-28 17:29:06 +04:00
if ( remove_from_freelist ( tdb , left , l . next ) = = - 1 ) {
TDB_LOG ( ( tdb , 0 , " tdb_free: left free failed at %u \n " , left ) ) ;
goto update ;
} else {
offset = left ;
rec - > rec_len + = leftsize ;
}
2000-12-05 06:12:57 +03:00
}
}
2001-05-28 17:29:06 +04:00
update :
if ( update_tailer ( tdb , offset , rec ) = = - 1 ) {
TDB_LOG ( ( tdb , 0 , " tdb_free: update_tailer failed at %u \n " , offset ) ) ;
goto fail ;
}
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* Now, prepend to free list */
rec - > magic = TDB_FREE_MAGIC ;
2000-12-05 06:12:57 +03:00
2001-05-30 09:40:52 +04:00
if ( ofs_read ( tdb , FREELIST_TOP , & rec - > next ) = = - 1 | |
rec_write ( tdb , offset , rec ) = = - 1 | |
ofs_write ( tdb , FREELIST_TOP , & offset ) = = - 1 ) {
TDB_LOG ( ( tdb , 0 , " tdb_free record write failed at offset=%d \n " , offset ) ) ;
goto fail ;
}
2000-12-05 06:12:57 +03:00
2000-12-06 03:05:15 +03:00
/* And we're done. */
tdb_unlock ( tdb , - 1 , F_WRLCK ) ;
2000-12-05 06:12:57 +03:00
return 0 ;
2000-12-06 03:05:15 +03:00
fail :
tdb_unlock ( tdb , - 1 , F_WRLCK ) ;
2000-12-05 06:12:57 +03:00
return - 1 ;
}
2001-05-15 18:53:06 +04:00
/* expand a file. we prefer to use ftruncate, as that is what posix
says to use for mmap expansion */
2001-05-30 09:40:52 +04:00
static int expand_file ( TDB_CONTEXT * tdb , tdb_off size , tdb_off addition )
2001-05-15 18:53:06 +04:00
{
char buf [ 1024 ] ;
# if HAVE_FTRUNCATE_EXTEND
2001-05-30 09:40:52 +04:00
if ( ftruncate ( tdb - > fd , size + addition ) ! = 0 ) {
TDB_LOG ( ( tdb , 0 , " expand_file ftruncate to %d failed (%s) \n " ,
size + addition , strerror ( errno ) ) ) ;
return - 1 ;
}
2001-05-15 18:53:06 +04:00
# else
char b = 0 ;
2001-05-30 09:40:52 +04:00
if ( lseek ( tdb - > fd , ( size + addition ) - 1 , SEEK_SET ) ! = ( size + addition ) - 1 | |
write ( tdb - > fd , & b , 1 ) ! = 1 ) {
TDB_LOG ( ( tdb , 0 , " expand_file to %d failed (%s) \n " ,
size + addition , strerror ( errno ) ) ) ;
return - 1 ;
}
2001-05-15 18:53:06 +04:00
# endif
/* 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 , 0x42 , sizeof ( buf ) ) ;
2001-08-03 00:17:40 +04:00
if ( lseek ( tdb - > fd , size , SEEK_SET ) ! = size )
return - 1 ;
2001-05-15 18:53:06 +04:00
while ( addition ) {
int n = addition > sizeof ( buf ) ? sizeof ( buf ) : addition ;
2001-05-30 09:40:52 +04:00
int ret = write ( tdb - > fd , buf , n ) ;
if ( ret ! = n ) {
TDB_LOG ( ( tdb , 0 , " expand_file write of %d failed (%s) \n " ,
n , strerror ( errno ) ) ) ;
return - 1 ;
}
2001-05-15 18:53:06 +04:00
addition - = n ;
}
return 0 ;
}
2000-12-06 03:05:15 +03:00
/* expand the database at least size bytes by expanding the underlying
file and doing the mmap again if necessary */
static int tdb_expand ( TDB_CONTEXT * tdb , tdb_off size )
1999-12-21 06:04:37 +03:00
{
struct list_struct rec ;
2000-12-06 03:05:15 +03:00
tdb_off offset ;
1999-12-21 06:04:37 +03:00
2001-05-30 09:40:52 +04:00
if ( tdb_lock ( tdb , - 1 , F_WRLCK ) = = - 1 ) {
TDB_LOG ( ( tdb , 0 , " lock failed in tdb_expand \n " ) ) ;
return 0 ;
}
1999-12-23 04:14:20 +03:00
2000-12-06 03:05:15 +03:00
/* must know about any previous expansions by another process */
2001-05-30 09:40:52 +04:00
tdb_oob ( tdb , tdb - > map_size + 1 , 1 ) ;
1999-12-24 11:45:02 +03:00
2000-12-06 03:05:15 +03:00
/* always make room for at least 10 more records, and round
the database up to a multiple of TDB_PAGE_SIZE */
size = TDB_ALIGN ( tdb - > map_size + size * 10 , TDB_PAGE_SIZE ) - tdb - > map_size ;
1999-12-21 06:04:37 +03:00
2001-05-06 06:05:00 +04:00
if ( ! ( tdb - > flags & TDB_INTERNAL ) )
tdb_munmap ( tdb ) ;
1999-12-21 06:04:37 +03:00
2001-05-05 02:23:37 +04:00
/*
* We must ensure the file is unmapped before doing this
* to ensure consistency with systems like OpenBSD where
* writes and mmaps are not consistent .
*/
/* expand the file itself */
if ( ! ( tdb - > flags & TDB_INTERNAL ) ) {
2001-08-03 00:17:40 +04:00
if ( expand_file ( tdb , tdb - > map_size , size ) ! = 0 )
goto fail ;
2001-05-05 02:23:37 +04:00
}
2000-12-06 03:05:15 +03:00
tdb - > map_size + = size ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
if ( tdb - > flags & TDB_INTERNAL )
2000-05-01 04:41:47 +04:00
tdb - > map_ptr = realloc ( tdb - > map_ptr , tdb - > map_size ) ;
2001-08-03 00:17:40 +04:00
else {
/*
* We must ensure the file is remapped before adding the space
* to ensure consistency with systems like OpenBSD where
* writes and mmaps are not consistent .
*/
1999-12-21 06:04:37 +03:00
2001-08-03 00:17:40 +04:00
/* We're ok if the mmap fails as we'll fallback to read/write */
tdb_mmap ( tdb ) ;
}
2001-05-05 02:23:37 +04:00
2000-12-05 06:12:57 +03:00
/* form a new freelist record */
memset ( & rec , ' \0 ' , sizeof ( rec ) ) ;
2000-12-06 03:05:15 +03:00
rec . rec_len = size - sizeof ( rec ) ;
2000-12-05 06:12:57 +03:00
1999-12-21 06:04:37 +03:00
/* link it into the free list */
2000-12-06 03:05:15 +03:00
offset = tdb - > map_size - size ;
2001-08-03 00:17:40 +04:00
if ( tdb_free ( tdb , offset , & rec ) = = - 1 )
goto fail ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
tdb_unlock ( tdb , - 1 , F_WRLCK ) ;
1999-12-21 06:04:37 +03:00
return 0 ;
1999-12-23 04:14:20 +03:00
fail :
2000-12-06 03:05:15 +03:00
tdb_unlock ( tdb , - 1 , F_WRLCK ) ;
1999-12-23 04:14:20 +03:00
return - 1 ;
1999-12-21 06:04:37 +03:00
}
/* allocate some space from the free list. The offset returned points
to a unconnected list_struct within the database with room for at
least length bytes of total data
0 is returned if the space could not be allocated
*/
2000-12-13 09:33:53 +03:00
static tdb_off tdb_allocate ( TDB_CONTEXT * tdb , tdb_len length ,
struct list_struct * rec )
1999-12-21 06:04:37 +03:00
{
2000-12-13 09:33:53 +03:00
tdb_off rec_ptr , last_ptr , newrec_ptr ;
struct list_struct newrec ;
1999-12-21 06:04:37 +03:00
2001-08-03 00:17:40 +04:00
if ( tdb_lock ( tdb , - 1 , F_WRLCK ) = = - 1 )
return 0 ;
2000-12-06 03:05:15 +03:00
/* Extra bytes required for tailer */
length + = sizeof ( tdb_off ) ;
1999-12-23 04:14:20 +03:00
1999-12-21 06:04:37 +03:00
again :
2000-12-06 03:05:15 +03:00
last_ptr = FREELIST_TOP ;
1999-12-21 06:04:37 +03:00
/* read in the freelist top */
2001-08-03 00:17:40 +04:00
if ( ofs_read ( tdb , FREELIST_TOP , & rec_ptr ) = = - 1 )
goto fail ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* keep looking until we find a freelist record big enough */
1999-12-21 06:04:37 +03:00
while ( rec_ptr ) {
2001-08-03 00:17:40 +04:00
if ( rec_free_read ( tdb , rec_ptr , rec ) = = - 1 )
goto fail ;
1999-12-22 04:22:14 +03:00
2000-12-13 09:33:53 +03:00
if ( rec - > rec_len > = length ) {
1999-12-21 06:04:37 +03:00
/* found it - now possibly split it up */
2000-12-13 09:33:53 +03:00
if ( rec - > rec_len > length + MIN_REC_SIZE ) {
/* Length of left piece */
2000-12-06 03:05:15 +03:00
length = TDB_ALIGN ( length , TDB_ALIGNMENT ) ;
1999-12-21 06:04:37 +03:00
2000-12-13 09:33:53 +03:00
/* Right piece to go on free list */
newrec . rec_len = rec - > rec_len
- ( sizeof ( * rec ) + length ) ;
newrec_ptr = rec_ptr + sizeof ( * rec ) + length ;
1999-12-22 04:22:14 +03:00
2000-12-13 09:33:53 +03:00
/* And left record is shortened */
rec - > rec_len = length ;
} else
newrec_ptr = 0 ;
1999-12-21 06:04:37 +03:00
2000-12-13 09:33:53 +03:00
/* Remove allocated record from the free list */
if ( ofs_write ( tdb , last_ptr , & rec - > next ) = = - 1 )
goto fail ;
/* Update header: do this before we drop alloc
lock , otherwise tdb_free ( ) might try to
merge with us , thinking we ' re free .
( Thanks Jeremy Allison ) . */
rec - > magic = TDB_MAGIC ;
if ( rec_write ( tdb , rec_ptr , rec ) = = - 1 )
goto fail ;
/* Did we create new block? */
if ( newrec_ptr ) {
/* Update allocated record tailer (we
shortened it ) . */
if ( update_tailer ( tdb , rec_ptr , rec ) = = - 1 )
1999-12-21 06:04:37 +03:00
goto fail ;
2000-12-13 09:33:53 +03:00
/* Free new record */
if ( tdb_free ( tdb , newrec_ptr , & newrec ) = = - 1 )
1999-12-21 06:04:37 +03:00
goto fail ;
2000-12-13 09:33:53 +03:00
}
1999-12-21 06:04:37 +03:00
/* all done - return the new record offset */
2000-12-06 03:05:15 +03:00
tdb_unlock ( tdb , - 1 , F_WRLCK ) ;
1999-12-21 06:04:37 +03:00
return rec_ptr ;
}
/* move to the next record */
last_ptr = rec_ptr ;
2000-12-13 09:33:53 +03:00
rec_ptr = rec - > next ;
1999-12-21 06:04:37 +03:00
}
1999-12-24 11:45:02 +03:00
/* we didn't find enough space. See if we can expand the
database and if we can then try again */
2001-08-03 00:17:40 +04:00
if ( tdb_expand ( tdb , length + sizeof ( * rec ) ) = = 0 )
goto again ;
1999-12-21 06:04:37 +03:00
fail :
2000-12-06 03:05:15 +03:00
tdb_unlock ( tdb , - 1 , F_WRLCK ) ;
1999-12-21 06:04:37 +03:00
return 0 ;
}
/* initialise a new database with a specified hash size */
static int tdb_new_database ( TDB_CONTEXT * tdb , int hash_size )
{
2000-12-06 03:05:15 +03:00
struct tdb_header * newdb ;
2001-08-03 00:17:40 +04:00
int size , ret = - 1 ;
2000-12-06 03:05:15 +03: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 ) ;
2001-08-03 00:17:40 +04:00
if ( ! ( newdb = calloc ( size , 1 ) ) )
return TDB_ERRCODE ( TDB_ERR_OOM , - 1 ) ;
2000-12-06 03:05:15 +03:00
2001-08-03 00:17:40 +04:00
/* Fill in the header */
newdb - > version = TDB_VERSION ;
newdb - > hash_size = hash_size ;
2000-12-06 03:05:15 +03:00
# ifdef USE_SPINLOCKS
newdb - > rwlocks = size ;
1999-12-21 06:04:37 +03:00
# endif
2000-12-06 03:05:15 +03:00
if ( tdb - > flags & TDB_INTERNAL ) {
tdb - > map_size = size ;
tdb - > map_ptr = ( char * ) newdb ;
memcpy ( & tdb - > header , newdb , sizeof ( tdb - > header ) ) ;
/* Convert the `ondisk' version if asked. */
CONVERT ( * newdb ) ;
return 0 ;
}
2001-08-03 00:17:40 +04:00
if ( lseek ( tdb - > fd , 0 , SEEK_SET ) = = - 1 )
goto fail ;
if ( ftruncate ( tdb - > fd , 0 ) = = - 1 )
goto fail ;
2000-12-06 03:05:15 +03:00
/* This creates an endian-converted header, as if read from disk */
CONVERT ( * newdb ) ;
memcpy ( & tdb - > header , newdb , sizeof ( tdb - > header ) ) ;
/* Don't endian-convert the magic food! */
memcpy ( newdb - > magic_food , TDB_MAGIC_FOOD , strlen ( TDB_MAGIC_FOOD ) + 1 ) ;
2001-08-03 00:17:40 +04:00
if ( write ( tdb - > fd , newdb , size ) ! = size )
ret = - 1 ;
else
ret = tdb_create_rwlocks ( tdb - > fd , hash_size ) ;
2000-12-06 03:05:15 +03:00
2001-08-03 00:17:40 +04:00
fail :
2000-12-06 03:05:15 +03:00
free ( newdb ) ;
return ret ;
1999-12-21 06:04:37 +03:00
}
2000-01-07 06:01:55 +03:00
/* Returns 0 on fail. On success, return offset of record, and fills
in rec */
2000-12-06 03:05:15 +03:00
static tdb_off tdb_find ( TDB_CONTEXT * tdb , TDB_DATA key , u32 hash ,
struct list_struct * r )
1999-12-21 06:04:37 +03:00
{
2000-12-06 03:05:15 +03:00
tdb_off rec_ptr ;
2000-01-07 06:01:55 +03:00
1999-12-21 06:04:37 +03:00
/* read in the hash top */
2001-08-03 00:17:40 +04:00
if ( ofs_read ( tdb , TDB_HASH_TOP ( hash ) , & rec_ptr ) = = - 1 )
return 0 ;
1999-12-21 06:04:37 +03:00
/* keep looking until we find the right record */
while ( rec_ptr ) {
2001-08-03 00:17:40 +04:00
if ( rec_read ( tdb , rec_ptr , r ) = = - 1 )
return 0 ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
if ( ! TDB_DEAD ( r ) & & hash = = r - > full_hash & & key . dsize = = r - > key_len ) {
2000-01-07 06:01:55 +03:00
char * k ;
/* a very likely hit - read the key */
2000-12-06 03:05:15 +03:00
k = tdb_alloc_read ( tdb , rec_ptr + sizeof ( * r ) ,
r - > key_len ) ;
2001-08-03 00:17:40 +04:00
if ( ! k )
return 0 ;
1999-12-21 06:04:37 +03:00
2000-01-07 06:01:55 +03:00
if ( memcmp ( key . dptr , k , key . dsize ) = = 0 ) {
free ( k ) ;
return rec_ptr ;
}
free ( k ) ;
1999-12-21 06:04:37 +03:00
}
2000-12-06 03:05:15 +03:00
rec_ptr = r - > next ;
1999-12-21 06:04:37 +03:00
}
2000-12-06 03:05:15 +03:00
return TDB_ERRCODE ( TDB_ERR_NOEXIST , 0 ) ;
1999-12-21 06:04:37 +03:00
}
2000-12-06 03:05:15 +03:00
/* If they do lockkeys, check that this hash is one they locked */
static int tdb_keylocked ( TDB_CONTEXT * tdb , u32 hash )
{
u32 i ;
2001-08-03 00:17:40 +04:00
if ( ! tdb - > lockedkeys )
return 1 ;
2000-12-06 03:05:15 +03:00
for ( i = 0 ; i < tdb - > lockedkeys [ 0 ] ; i + + )
2001-08-03 00:17:40 +04:00
if ( tdb - > lockedkeys [ i + 1 ] = = hash )
return 1 ;
2000-12-06 03:05:15 +03:00
return TDB_ERRCODE ( TDB_ERR_NOLOCK , 0 ) ;
}
/* As tdb_find, but if you succeed, keep the lock */
static tdb_off tdb_find_lock ( TDB_CONTEXT * tdb , TDB_DATA key , int locktype ,
struct list_struct * rec )
{
u32 hash , rec_ptr ;
hash = tdb_hash ( & key ) ;
2001-08-03 00:17:40 +04:00
if ( ! tdb_keylocked ( tdb , hash ) )
return 0 ;
if ( tdb_lock ( tdb , BUCKET ( hash ) , locktype ) = = - 1 )
return 0 ;
2000-12-06 03:05:15 +03:00
if ( ! ( rec_ptr = tdb_find ( tdb , key , hash , rec ) ) )
tdb_unlock ( tdb , BUCKET ( hash ) , locktype ) ;
return rec_ptr ;
}
enum TDB_ERROR tdb_error ( TDB_CONTEXT * tdb )
{
return tdb - > ecode ;
}
static struct tdb_errname {
enum TDB_ERROR ecode ; const char * estring ;
} emap [ ] = { { TDB_SUCCESS , " Success " } ,
{ TDB_ERR_CORRUPT , " Corrupt database " } ,
{ TDB_ERR_IO , " IO Error " } ,
{ TDB_ERR_LOCK , " Locking error " } ,
{ TDB_ERR_OOM , " Out of memory " } ,
{ TDB_ERR_EXISTS , " Record exists " } ,
{ TDB_ERR_NOLOCK , " Lock exists on other keys " } ,
{ TDB_ERR_NOEXIST , " Record does not exist " } } ;
/* Error string for the last tdb error */
const char * tdb_errorstr ( TDB_CONTEXT * tdb )
2000-01-08 11:16:02 +03:00
{
2000-12-06 03:05:15 +03:00
u32 i ;
for ( i = 0 ; i < sizeof ( emap ) / sizeof ( struct tdb_errname ) ; i + + )
2001-08-03 00:17:40 +04:00
if ( tdb - > ecode = = emap [ i ] . ecode )
return emap [ i ] . estring ;
2000-01-08 11:16:02 +03:00
return " Invalid error code " ;
}
2000-01-07 06:01:55 +03:00
/* update an entry in place - this only works if the new data size
is < = the old data size and the key exists .
on failure return - 1
*/
2000-12-06 03:05:15 +03:00
static int tdb_update ( TDB_CONTEXT * tdb , TDB_DATA key , TDB_DATA dbuf )
1999-12-21 06:04:37 +03:00
{
struct list_struct rec ;
2000-01-07 06:01:55 +03:00
tdb_off rec_ptr ;
int ret = - 1 ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* find entry */
2001-08-03 00:17:40 +04:00
if ( ! ( rec_ptr = tdb_find_lock ( tdb , key , F_WRLCK , & rec ) ) )
return - 1 ;
1999-12-23 04:14:20 +03:00
2000-12-06 03:05:15 +03:00
/* must be long enough key, data and tailer */
2001-07-31 04:03:24 +04:00
if ( rec . rec_len < key . dsize + dbuf . dsize + sizeof ( tdb_off ) ) {
tdb - > ecode = TDB_SUCCESS ; /* Not really an error */
goto out ;
}
1999-12-21 06:04:37 +03:00
2000-01-07 06:01:55 +03:00
if ( tdb_write ( tdb , rec_ptr + sizeof ( rec ) + rec . key_len ,
dbuf . dptr , dbuf . dsize ) = = - 1 )
goto out ;
1999-12-21 06:04:37 +03:00
2000-01-07 06:01:55 +03:00
if ( dbuf . dsize ! = rec . data_len ) {
/* update size */
rec . data_len = dbuf . dsize ;
ret = rec_write ( tdb , rec_ptr , & rec ) ;
2001-08-03 00:17:40 +04:00
} else
ret = 0 ;
2000-01-07 06:01:55 +03:00
out :
2000-12-06 03:05:15 +03:00
tdb_unlock ( tdb , BUCKET ( rec . full_hash ) , F_WRLCK ) ;
2000-01-07 06:01:55 +03:00
return ret ;
}
1999-12-21 06:04:37 +03:00
2000-01-07 06:01:55 +03:00
/* find an entry in the database given a key */
TDB_DATA tdb_fetch ( TDB_CONTEXT * tdb , TDB_DATA key )
{
tdb_off rec_ptr ;
struct list_struct rec ;
2000-12-06 03:05:15 +03:00
TDB_DATA ret ;
2000-02-17 01:57:57 +03:00
2000-01-07 06:01:55 +03:00
/* find which hash bucket it is in */
2001-08-03 00:17:40 +04:00
if ( ! ( rec_ptr = tdb_find_lock ( tdb , key , F_RDLCK , & rec ) ) )
return tdb_null ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
ret . dptr = tdb_alloc_read ( tdb , rec_ptr + sizeof ( rec ) + rec . key_len ,
rec . data_len ) ;
ret . dsize = rec . data_len ;
tdb_unlock ( tdb , BUCKET ( rec . full_hash ) , F_RDLCK ) ;
2000-01-07 06:01:55 +03:00
return ret ;
1999-12-21 06:04:37 +03:00
}
/* check if an entry in the database exists
note that 1 is returned if the key is found and 0 is returned if not found
this doesn ' t match the conventions in the rest of this module , but is
compatible with gdbm
*/
int tdb_exists ( TDB_CONTEXT * tdb , TDB_DATA key )
{
struct list_struct rec ;
2000-01-07 06:01:55 +03:00
2001-08-03 00:17:40 +04:00
if ( tdb_find_lock ( tdb , key , F_RDLCK , & rec ) = = 0 )
return 0 ;
2000-12-06 03:05:15 +03:00
tdb_unlock ( tdb , BUCKET ( rec . full_hash ) , F_RDLCK ) ;
return 1 ;
}
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* record lock stops delete underneath */
static int lock_record ( TDB_CONTEXT * tdb , tdb_off off )
{
2001-05-30 09:40:52 +04:00
return off ? tdb_brlock ( tdb , off , F_RDLCK , F_SETLKW , 0 ) : 0 ;
2000-12-06 03:05:15 +03:00
}
/* write locks override our own fcntl readlocks, so check it here */
static int write_lock_record ( TDB_CONTEXT * tdb , tdb_off off )
{
struct tdb_traverse_lock * i ;
2001-08-03 00:17:40 +04:00
for ( i = & tdb - > travlocks ; i ; i = i - > next )
if ( i - > off = = off )
return - 1 ;
2001-09-19 01:17:58 +04:00
return tdb_brlock ( tdb , off , F_WRLCK , F_SETLKW , 1 ) ;
2000-12-06 03:05:15 +03:00
}
static int write_unlock_record ( TDB_CONTEXT * tdb , tdb_off off )
{
2001-09-19 01:17:58 +04:00
return tdb_brlock ( tdb , off , F_UNLCK , F_SETLKW , 0 ) ;
2000-12-06 03:05:15 +03:00
}
/* fcntl locks don't stack: avoid unlocking someone else's */
static int unlock_record ( TDB_CONTEXT * tdb , tdb_off off )
{
struct tdb_traverse_lock * i ;
u32 count = 0 ;
1999-12-21 06:04:37 +03:00
2001-08-03 00:17:40 +04:00
if ( off = = 0 )
return 0 ;
for ( i = & tdb - > travlocks ; i ; i = i - > next )
if ( i - > off = = off )
count + + ;
2001-05-30 09:40:52 +04:00
return ( count = = 1 ? tdb_brlock ( tdb , off , F_UNLCK , F_SETLKW , 0 ) : 0 ) ;
2000-01-07 06:01:55 +03:00
}
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* actually delete an entry in the database given the offset */
static int do_delete ( TDB_CONTEXT * tdb , tdb_off rec_ptr , struct list_struct * rec )
1999-12-21 06:04:37 +03:00
{
2000-12-06 03:05:15 +03:00
tdb_off last_ptr , i ;
struct list_struct lastrec ;
1999-12-21 06:04:37 +03:00
2000-12-11 09:10:32 +03:00
if ( tdb - > read_only ) return - 1 ;
2000-12-06 03:05:15 +03:00
if ( write_lock_record ( tdb , rec_ptr ) = = - 1 ) {
/* Someone traversing here: mark it as dead */
rec - > magic = TDB_DEAD_MAGIC ;
return rec_write ( tdb , rec_ptr , rec ) ;
}
write_unlock_record ( tdb , rec_ptr ) ;
2000-02-17 01:57:57 +03:00
2000-12-06 03:05:15 +03:00
/* find previous record in hash chain */
2001-08-03 00:17:40 +04:00
if ( ofs_read ( tdb , TDB_HASH_TOP ( rec - > full_hash ) , & i ) = = - 1 )
return - 1 ;
2000-12-06 03:05:15 +03:00
for ( last_ptr = 0 ; i ! = rec_ptr ; last_ptr = i , i = lastrec . next )
2001-08-03 00:17:40 +04:00
if ( rec_read ( tdb , i , & lastrec ) = = - 1 )
return - 1 ;
1999-12-23 04:14:20 +03:00
2000-12-06 03:05:15 +03:00
/* unlink it: next ptr is at start of record. */
2001-08-03 00:17:40 +04:00
if ( last_ptr = = 0 )
last_ptr = TDB_HASH_TOP ( rec - > full_hash ) ;
if ( ofs_write ( tdb , last_ptr , & rec - > next ) = = - 1 )
return - 1 ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* recover the space */
2001-08-03 00:17:40 +04:00
if ( tdb_free ( tdb , rec_ptr , rec ) = = - 1 )
return - 1 ;
2000-12-06 03:05:15 +03:00
return 0 ;
}
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */
static int tdb_next_lock ( TDB_CONTEXT * tdb , struct tdb_traverse_lock * tlock ,
struct list_struct * rec )
{
2000-12-13 09:33:53 +03:00
int want_next = ( tlock - > off ! = 0 ) ;
1999-12-21 06:04:37 +03:00
2000-12-13 09:33:53 +03:00
/* No traversal allows if you've called tdb_lockkeys() */
2001-08-03 00:17:40 +04:00
if ( tdb - > lockedkeys )
return TDB_ERRCODE ( TDB_ERR_NOLOCK , - 1 ) ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* Lock each chain from the start one. */
for ( ; tlock - > hash < tdb - > header . hash_size ; tlock - > hash + + ) {
2001-08-03 00:17:40 +04:00
if ( tdb_lock ( tdb , tlock - > hash , F_WRLCK ) = = - 1 )
return - 1 ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* No previous record? Start at top of chain. */
if ( ! tlock - > off ) {
if ( ofs_read ( tdb , TDB_HASH_TOP ( tlock - > hash ) ,
& tlock - > off ) = = - 1 )
goto fail ;
} else {
2000-12-13 09:33:53 +03:00
/* Otherwise unlock the previous record. */
unlock_record ( tdb , tlock - > off ) ;
}
1999-12-21 06:04:37 +03:00
2000-12-13 09:33:53 +03:00
if ( want_next ) {
/* We have offset of old record: grab next */
2001-08-03 00:17:40 +04:00
if ( rec_read ( tdb , tlock - > off , rec ) = = - 1 )
goto fail ;
2000-12-06 03:05:15 +03:00
tlock - > off = rec - > next ;
}
/* Iterate through chain */
2000-12-13 09:33:53 +03:00
while ( tlock - > off ) {
tdb_off current ;
2001-08-03 00:17:40 +04:00
if ( rec_read ( tdb , tlock - > off , rec ) = = - 1 )
goto fail ;
2000-12-06 03:05:15 +03:00
if ( ! TDB_DEAD ( rec ) ) {
/* Woohoo: we found one! */
lock_record ( tdb , tlock - > off ) ;
return tlock - > off ;
}
/* Try to clean dead ones from old traverses */
2000-12-13 09:33:53 +03:00
current = tlock - > off ;
tlock - > off = rec - > next ;
do_delete ( tdb , current , rec ) ;
2000-12-06 03:05:15 +03:00
}
tdb_unlock ( tdb , tlock - > hash , F_WRLCK ) ;
2000-12-13 09:33:53 +03:00
want_next = 0 ;
2000-12-06 03:05:15 +03:00
}
/* We finished iteration without finding anything */
return TDB_ERRCODE ( TDB_SUCCESS , 0 ) ;
1999-12-23 04:14:20 +03:00
fail :
2000-12-06 03:05:15 +03:00
tlock - > off = 0 ;
tdb_unlock ( tdb , tlock - > hash , F_WRLCK ) ;
1999-12-23 04:14:20 +03:00
return - 1 ;
1999-12-21 06:04:37 +03:00
}
2000-12-06 03:05:15 +03:00
/* traverse the entire database - calling fn(tdb, key, data) on each element.
return - 1 on error or the record count traversed
if fn is NULL then it is not called
a non - zero return value from fn ( ) indicates that the traversal should stop
*/
int tdb_traverse ( TDB_CONTEXT * tdb , tdb_traverse_func fn , void * state )
1999-12-21 06:04:37 +03:00
{
2000-12-06 03:05:15 +03:00
TDB_DATA key , dbuf ;
1999-12-21 06:04:37 +03:00
struct list_struct rec ;
2000-12-31 02:17:30 +03:00
struct tdb_traverse_lock tl = { NULL , 0 , 0 } ;
2000-12-06 03:05:15 +03:00
int ret , count = 0 ;
2001-05-14 10:15:46 +04:00
/* This was in the initializaton, above, but the IRIX compiler
* did not like it . crh
*/
tl . next = tdb - > travlocks . next ;
2000-12-31 02:17:30 +03:00
2000-12-06 03:05:15 +03:00
/* fcntl locks don't stack: beware traverse inside traverse */
tdb - > travlocks . next = & tl ;
/* tdb_next_lock places locks on the record returned, and its chain */
while ( ( ret = tdb_next_lock ( tdb , & tl , & rec ) ) > 0 ) {
count + + ;
/* now read the full record */
key . dptr = tdb_alloc_read ( tdb , tl . off + sizeof ( rec ) ,
rec . key_len + rec . data_len ) ;
if ( ! key . dptr ) {
tdb_unlock ( tdb , tl . hash , F_WRLCK ) ;
unlock_record ( tdb , tl . off ) ;
tdb - > travlocks . next = tl . next ;
return - 1 ;
1999-12-21 06:04:37 +03:00
}
2000-12-06 03:05:15 +03:00
key . dsize = rec . key_len ;
dbuf . dptr = key . dptr + rec . key_len ;
dbuf . dsize = rec . data_len ;
/* Drop chain lock, call out */
tdb_unlock ( tdb , tl . hash , F_WRLCK ) ;
if ( fn & & fn ( tdb , key , dbuf , state ) ) {
/* They want us to terminate traversal */
unlock_record ( tdb , tl . off ) ;
tdb - > travlocks . next = tl . next ;
free ( key . dptr ) ;
return count ;
}
free ( key . dptr ) ;
1999-12-21 06:04:37 +03:00
}
2000-12-06 03:05:15 +03:00
tdb - > travlocks . next = tl . next ;
2001-05-14 10:15:46 +04:00
if ( ret < 0 )
return - 1 ;
else
return count ;
2000-12-06 03:05:15 +03:00
}
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* find the first entry in the database and return its key */
TDB_DATA tdb_firstkey ( TDB_CONTEXT * tdb )
{
TDB_DATA key ;
struct list_struct rec ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* release any old lock */
unlock_record ( tdb , tdb - > travlocks . off ) ;
tdb - > travlocks . off = tdb - > travlocks . hash = 0 ;
1999-12-23 04:14:20 +03:00
2001-08-03 00:17:40 +04:00
if ( tdb_next_lock ( tdb , & tdb - > travlocks , & rec ) < = 0 )
return tdb_null ;
2000-12-06 03:05:15 +03:00
/* now read the key */
key . dsize = rec . key_len ;
key . dptr = tdb_alloc_read ( tdb , tdb - > travlocks . off + sizeof ( rec ) , key . dsize ) ;
tdb_unlock ( tdb , BUCKET ( tdb - > travlocks . hash ) , F_WRLCK ) ;
return key ;
1999-12-21 06:04:37 +03:00
}
/* find the next entry in the database, returning its key */
2000-12-06 03:05:15 +03:00
TDB_DATA tdb_nextkey ( TDB_CONTEXT * tdb , TDB_DATA oldkey )
1999-12-21 06:04:37 +03:00
{
2000-12-06 03:05:15 +03:00
u32 oldhash ;
TDB_DATA key = tdb_null ;
1999-12-21 06:04:37 +03:00
struct list_struct rec ;
2000-12-06 03:05:15 +03:00
char * k = NULL ;
/* Is locked key the old key? If so, traverse will be reliable. */
if ( tdb - > travlocks . off ) {
2001-08-03 00:17:40 +04:00
if ( tdb_lock ( tdb , tdb - > travlocks . hash , F_WRLCK ) )
return tdb_null ;
2000-12-06 03:05:15 +03:00
if ( rec_read ( tdb , tdb - > travlocks . off , & rec ) = = - 1
| | ! ( k = tdb_alloc_read ( tdb , tdb - > travlocks . off + sizeof ( rec ) ,
rec . key_len ) )
| | memcmp ( k , oldkey . dptr , oldkey . dsize ) ! = 0 ) {
/* No, it wasn't: unlock it and start from scratch */
unlock_record ( tdb , tdb - > travlocks . off ) ;
tdb_unlock ( tdb , tdb - > travlocks . hash , F_WRLCK ) ;
tdb - > travlocks . off = 0 ;
1999-12-21 06:04:37 +03:00
}
2000-12-16 00:29:06 +03:00
if ( k )
free ( k ) ;
1999-12-21 06:04:37 +03:00
}
2000-12-06 03:05:15 +03:00
if ( ! tdb - > travlocks . off ) {
/* No previous element: do normal find, and lock record */
tdb - > travlocks . off = tdb_find_lock ( tdb , oldkey , F_WRLCK , & rec ) ;
2001-08-03 00:17:40 +04:00
if ( ! tdb - > travlocks . off )
return tdb_null ;
2000-12-06 03:05:15 +03:00
tdb - > travlocks . hash = BUCKET ( rec . full_hash ) ;
lock_record ( tdb , tdb - > travlocks . off ) ;
1999-12-21 06:04:37 +03:00
}
2000-12-06 03:05:15 +03:00
oldhash = tdb - > travlocks . hash ;
1999-12-23 04:14:20 +03:00
2000-12-06 03:05:15 +03:00
/* Grab next record: locks chain and returned record,
unlocks old record */
if ( tdb_next_lock ( tdb , & tdb - > travlocks , & rec ) > 0 ) {
key . dsize = rec . key_len ;
key . dptr = tdb_alloc_read ( tdb , tdb - > travlocks . off + sizeof ( rec ) ,
key . dsize ) ;
/* Unlock the chain of this new record */
tdb_unlock ( tdb , tdb - > travlocks . hash , F_WRLCK ) ;
}
/* Unlock the chain of old record */
tdb_unlock ( tdb , BUCKET ( oldhash ) , F_WRLCK ) ;
return key ;
1999-12-21 06:04:37 +03:00
}
/* delete an entry in the database given a key */
int tdb_delete ( TDB_CONTEXT * tdb , TDB_DATA key )
{
2000-12-06 03:05:15 +03:00
tdb_off rec_ptr ;
struct list_struct rec ;
int ret ;
1999-12-23 04:14:20 +03:00
2001-08-03 00:17:40 +04:00
if ( ! ( rec_ptr = tdb_find_lock ( tdb , key , F_WRLCK , & rec ) ) )
return - 1 ;
2000-12-06 03:05:15 +03:00
ret = do_delete ( tdb , rec_ptr , & rec ) ;
tdb_unlock ( tdb , BUCKET ( rec . full_hash ) , F_WRLCK ) ;
return ret ;
1999-12-21 06:04:37 +03:00
}
/* store an element in the database, replacing any existing element
with the same key
return 0 on success , - 1 on failure
*/
int tdb_store ( TDB_CONTEXT * tdb , TDB_DATA key , TDB_DATA dbuf , int flag )
{
struct list_struct rec ;
2000-12-06 03:05:15 +03:00
u32 hash ;
tdb_off rec_ptr ;
2000-01-02 04:40:35 +03:00
char * p = NULL ;
2000-12-06 03:05:15 +03:00
int ret = 0 ;
2000-02-17 01:57:57 +03:00
1999-12-23 04:14:20 +03:00
/* find which hash bucket it is in */
hash = tdb_hash ( & key ) ;
2001-08-03 00:17:40 +04:00
if ( ! tdb_keylocked ( tdb , hash ) )
return - 1 ;
if ( tdb_lock ( tdb , BUCKET ( hash ) , F_WRLCK ) = = - 1 )
return - 1 ;
2000-06-15 19:29:39 +04:00
2000-04-25 05:17:45 +04:00
/* check for it existing, on insert. */
2000-12-06 03:05:15 +03:00
if ( flag = = TDB_INSERT ) {
if ( tdb_exists ( tdb , key ) ) {
tdb - > ecode = TDB_ERR_EXISTS ;
goto fail ;
}
} else {
/* first try in-place update, on modify or replace. */
2001-08-03 00:17:40 +04:00
if ( tdb_update ( tdb , key , dbuf ) = = 0 )
goto out ;
2000-12-06 03:05:15 +03:00
if ( flag = = TDB_MODIFY & & tdb - > ecode = = TDB_ERR_NOEXIST )
goto fail ;
2000-04-25 05:17:45 +04:00
}
/* reset the error code potentially set by the tdb_update() */
2000-12-06 03:05:15 +03:00
tdb - > ecode = TDB_SUCCESS ;
1999-12-24 11:45:02 +03:00
2000-12-06 03:05:15 +03:00
/* delete any existing record - if it doesn't exist we don't
care . Doing this first reduces fragmentation , and avoids
coalescing with ` allocated ' block before it ' s updated . */
2001-08-03 00:17:40 +04:00
if ( flag ! = TDB_INSERT )
tdb_delete ( tdb , key ) ;
1999-12-22 04:22:14 +03:00
2001-01-30 00:34:08 +03:00
/* Copy key+value *before* allocating free space in case malloc
fails and we are left with a dead spot in the tdb . */
if ( ! ( p = ( char * ) malloc ( key . dsize + dbuf . dsize ) ) ) {
tdb - > ecode = TDB_ERR_OOM ;
goto fail ;
}
memcpy ( p , key . dptr , key . dsize ) ;
memcpy ( p + key . dsize , dbuf . dptr , dbuf . dsize ) ;
2000-12-06 03:05:15 +03:00
/* now we're into insert / modify / replace of a record which
* we know could not be optimised by an in - place store ( for
* various reasons ) . */
2000-12-13 09:33:53 +03:00
if ( ! ( rec_ptr = tdb_allocate ( tdb , key . dsize + dbuf . dsize , & rec ) ) )
goto fail ;
1999-12-21 06:04:37 +03:00
2000-12-13 09:33:53 +03:00
/* Read hash top into next ptr */
2001-08-03 00:17:40 +04:00
if ( ofs_read ( tdb , TDB_HASH_TOP ( hash ) , & rec . next ) = = - 1 )
goto fail ;
1999-12-21 06:04:37 +03:00
rec . key_len = key . dsize ;
rec . data_len = dbuf . dsize ;
rec . full_hash = hash ;
1999-12-22 04:22:14 +03:00
rec . magic = TDB_MAGIC ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* write out and point the top of the hash chain at it */
if ( rec_write ( tdb , rec_ptr , & rec ) = = - 1
| | tdb_write ( tdb , rec_ptr + sizeof ( rec ) , p , key . dsize + dbuf . dsize ) = = - 1
| | ofs_write ( tdb , TDB_HASH_TOP ( hash ) , & rec_ptr ) = = - 1 ) {
fail :
2001-01-30 00:34:08 +03:00
/* Need to tdb_unallocate() here */
2000-12-06 03:05:15 +03:00
ret = - 1 ;
}
out :
2001-06-12 00:56:18 +04:00
if ( p )
free ( p ) ;
2000-12-06 03:05:15 +03:00
tdb_unlock ( tdb , BUCKET ( hash ) , F_WRLCK ) ;
return ret ;
1999-12-21 06:04:37 +03:00
}
/* open the database, creating it if necessary
2000-12-06 03:05:15 +03: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 .
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
return is NULL on error */
2000-01-03 02:00:27 +03:00
TDB_CONTEXT * tdb_open ( char * name , int hash_size , int tdb_flags ,
int open_flags , mode_t mode )
1999-12-21 06:04:37 +03:00
{
2000-12-06 03:05:15 +03:00
TDB_CONTEXT tdb , * ret , * i ;
1999-12-21 06:04:37 +03:00
struct stat st ;
2000-12-06 03:05:15 +03:00
int rev = 0 , locked ;
1999-12-21 06:04:37 +03:00
2000-02-28 03:25:37 +03:00
memset ( & tdb , 0 , sizeof ( tdb ) ) ;
1999-12-21 06:04:37 +03:00
tdb . fd = - 1 ;
tdb . name = NULL ;
tdb . map_ptr = NULL ;
2000-12-06 03:05:15 +03:00
tdb . lockedkeys = NULL ;
2000-05-01 04:41:47 +04:00
tdb . flags = tdb_flags ;
1999-12-21 06:04:37 +03:00
2001-08-03 00:17:40 +04:00
if ( ( open_flags & O_ACCMODE ) = = O_WRONLY )
goto fail ;
if ( hash_size = = 0 )
hash_size = DEFAULT_HASH_SIZE ;
2000-12-06 03:05:15 +03:00
if ( ( open_flags & O_ACCMODE ) = = O_RDONLY ) {
tdb . read_only = 1 ;
/* read only databases don't do locking */
tdb . flags | = TDB_NOLOCK ;
}
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* internal databases don't mmap or lock, and start off cleared */
2000-05-01 04:41:47 +04:00
if ( tdb . flags & TDB_INTERNAL ) {
tdb . flags | = ( TDB_NOLOCK | TDB_NOMMAP ) ;
tdb . flags & = ~ TDB_CLEAR_IF_FIRST ;
tdb_new_database ( & tdb , hash_size ) ;
goto internal ;
}
2000-04-30 15:04:28 +04:00
2001-08-03 00:17:40 +04:00
if ( ( tdb . fd = open ( name , open_flags , mode ) ) = = - 1 )
goto fail ;
1999-12-21 06:04:37 +03:00
2000-01-03 02:00:27 +03:00
/* ensure there is only one process initialising at once */
2001-08-03 00:17:40 +04:00
if ( tdb_brlock ( & tdb , GLOBAL_LOCK , F_WRLCK , F_SETLKW , 0 ) = = - 1 )
goto fail ;
2000-12-06 03:05:15 +03:00
/* we need to zero database if we are the only one with it open */
2001-05-30 09:40:52 +04:00
if ( ( locked = ( tdb_brlock ( & tdb , ACTIVE_LOCK , F_WRLCK , F_SETLK , 0 ) = = 0 ) )
2000-12-06 03:05:15 +03:00
& & ( tdb_flags & TDB_CLEAR_IF_FIRST ) ) {
open_flags | = O_CREAT ;
2001-08-03 00:17:40 +04:00
if ( ftruncate ( tdb . fd , 0 ) = = - 1 )
goto fail ;
2000-01-03 02:00:27 +03:00
}
2000-12-06 03:05:15 +03:00
if ( read ( tdb . fd , & tdb . header , sizeof ( tdb . header ) ) ! = sizeof ( tdb . header )
| | strcmp ( tdb . header . magic_food , TDB_MAGIC_FOOD ) ! = 0
| | ( tdb . header . version ! = TDB_VERSION
& & ! ( rev = ( tdb . header . version = = TDB_BYTEREV ( TDB_VERSION ) ) ) ) ) {
1999-12-21 06:04:37 +03:00
/* its not a valid database - possibly initialise it */
2001-08-03 00:17:40 +04:00
if ( ! ( open_flags & O_CREAT ) | | tdb_new_database ( & tdb , hash_size ) = = - 1 )
goto fail ;
2000-12-06 03:05:15 +03:00
rev = ( tdb . flags & TDB_CONVERT ) ;
}
2001-08-03 00:17:40 +04:00
if ( ! rev )
tdb . flags & = ~ TDB_CONVERT ;
2000-12-06 03:05:15 +03:00
else {
tdb . flags | = TDB_CONVERT ;
convert ( & tdb . header , sizeof ( tdb . header ) ) ;
1999-12-21 06:04:37 +03:00
}
2001-08-03 00:17:40 +04:00
if ( fstat ( tdb . fd , & st ) = = - 1 )
goto fail ;
2000-12-06 03:05:15 +03:00
/* Is it already in the open list? If so, fail. */
for ( i = tdbs ; i ; i = i - > next ) {
if ( i - > device = = st . st_dev & & i - > inode = = st . st_ino ) {
errno = EBUSY ;
close ( tdb . fd ) ;
return NULL ;
}
}
2000-02-28 03:25:37 +03:00
2000-04-30 15:04:28 +04:00
/* map the database and fill in the return structure */
tdb . name = ( char * ) strdup ( name ) ;
2001-08-03 00:17:40 +04:00
if ( ! tdb . name )
goto fail ;
2000-04-30 15:04:28 +04:00
tdb . map_size = st . st_size ;
2000-12-06 03:05:15 +03:00
tdb . device = st . st_dev ;
tdb . inode = st . st_ino ;
2001-08-03 00:17:40 +04:00
tdb . locked = calloc ( tdb . header . hash_size + 1 , sizeof ( tdb . locked [ 0 ] ) ) ;
if ( ! tdb . locked )
goto fail ;
2001-05-06 06:05:00 +04:00
tdb_mmap ( & tdb ) ;
2000-12-06 03:05:15 +03:00
if ( locked ) {
tdb_clear_spinlocks ( & tdb ) ;
2001-08-03 00:17:40 +04:00
if ( tdb_brlock ( & tdb , ACTIVE_LOCK , F_UNLCK , F_SETLK , 0 ) = = - 1 )
goto fail ;
2000-12-06 03:05:15 +03:00
}
/* leave this lock in place to indicate it's in use */
2001-08-03 00:17:40 +04:00
if ( tdb_brlock ( & tdb , ACTIVE_LOCK , F_RDLCK , F_SETLKW , 0 ) = = - 1 )
goto fail ;
1999-12-21 06:04:37 +03:00
2000-05-01 04:41:47 +04:00
internal :
2001-08-03 00:17:40 +04:00
if ( ! ( ret = malloc ( sizeof ( tdb ) ) ) )
goto fail ;
1999-12-21 06:04:37 +03:00
* ret = tdb ;
2001-08-03 00:17:40 +04:00
if ( tdb_brlock ( & tdb , GLOBAL_LOCK , F_UNLCK , F_SETLKW , 0 ) = = - 1 )
goto fail ;
2000-12-06 03:05:15 +03:00
ret - > next = tdbs ;
tdbs = ret ;
1999-12-21 06:04:37 +03:00
return ret ;
fail :
2001-08-03 00:17:40 +04:00
if ( tdb . map_ptr ) {
if ( tdb . flags & TDB_INTERNAL )
free ( tdb . map_ptr ) ;
else
tdb_munmap ( & tdb ) ;
}
if ( tdb . name )
free ( tdb . name ) ;
if ( tdb . fd ! = - 1 )
close ( tdb . fd ) ;
if ( tdb . locked )
free ( tdb . locked ) ;
1999-12-21 06:04:37 +03:00
return NULL ;
}
/* close a database */
int tdb_close ( TDB_CONTEXT * tdb )
{
2000-12-06 03:05:15 +03:00
TDB_CONTEXT * * i ;
int ret = 0 ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
if ( tdb - > map_ptr ) {
2001-08-03 00:17:40 +04:00
if ( tdb - > flags & TDB_INTERNAL )
free ( tdb - > map_ptr ) ;
else
tdb_munmap ( tdb ) ;
2000-12-06 03:05:15 +03:00
}
2001-08-03 00:17:40 +04:00
if ( tdb - > name )
free ( tdb - > name ) ;
if ( tdb - > fd ! = - 1 )
2000-12-06 03:05:15 +03:00
ret = close ( tdb - > fd ) ;
2001-08-03 00:17:40 +04:00
if ( tdb - > locked )
free ( tdb - > locked ) ;
if ( tdb - > lockedkeys )
free ( tdb - > lockedkeys ) ;
1999-12-21 06:04:37 +03:00
2000-12-06 03:05:15 +03:00
/* Remove from contexts list */
for ( i = & tdbs ; * i ; i = & ( * i ) - > next ) {
if ( * i = = tdb ) {
* i = tdb - > next ;
break ;
2000-05-01 04:41:47 +04:00
}
2000-12-06 03:05:15 +03:00
}
2000-02-28 03:25:37 +03:00
1999-12-21 06:04:37 +03:00
memset ( tdb , 0 , sizeof ( * tdb ) ) ;
free ( tdb ) ;
2000-12-06 03:05:15 +03:00
return ret ;
1999-12-21 06:04:37 +03:00
}
2000-12-06 03:05:15 +03:00
/* lock/unlock entire database */
int tdb_lockall ( TDB_CONTEXT * tdb )
1999-12-21 12:25:59 +03:00
{
2000-12-06 03:05:15 +03:00
u32 i ;
2000-02-17 01:57:57 +03:00
2000-12-06 03:05:15 +03:00
/* There are no locks on read-only dbs */
2001-08-03 00:17:40 +04:00
if ( tdb - > read_only )
return TDB_ERRCODE ( TDB_ERR_LOCK , - 1 ) ;
if ( tdb - > lockedkeys )
return TDB_ERRCODE ( TDB_ERR_NOLOCK , - 1 ) ;
2000-12-16 00:29:06 +03:00
for ( i = 0 ; i < tdb - > header . hash_size ; i + + )
if ( tdb_lock ( tdb , i , F_WRLCK ) )
break ;
/* If error, release locks we have... */
if ( i < tdb - > header . hash_size ) {
u32 j ;
for ( j = 0 ; j < i ; j + + )
tdb_unlock ( tdb , j , F_WRLCK ) ;
return TDB_ERRCODE ( TDB_ERR_NOLOCK , - 1 ) ;
}
2000-12-06 03:05:15 +03:00
return 0 ;
1999-12-21 12:25:59 +03:00
}
2000-12-06 03:05:15 +03:00
void tdb_unlockall ( TDB_CONTEXT * tdb )
1999-12-21 12:25:59 +03:00
{
2000-12-06 03:05:15 +03:00
u32 i ;
2001-08-03 00:17:40 +04:00
for ( i = 0 ; i < tdb - > header . hash_size ; i + + )
tdb_unlock ( tdb , i , F_WRLCK ) ;
1999-12-21 06:04:37 +03:00
}
2000-04-16 10:23:11 +04:00
2000-12-06 03:05:15 +03:00
int tdb_lockkeys ( TDB_CONTEXT * tdb , u32 number , TDB_DATA keys [ ] )
2000-12-02 04:04:11 +03:00
{
2000-12-06 03:05:15 +03:00
u32 i , j , hash ;
/* Can't lock more keys if already locked */
2001-08-03 00:17:40 +04:00
if ( tdb - > lockedkeys )
return TDB_ERRCODE ( TDB_ERR_NOLOCK , - 1 ) ;
2000-12-06 03:05:15 +03:00
if ( ! ( tdb - > lockedkeys = malloc ( sizeof ( u32 ) * ( number + 1 ) ) ) )
return TDB_ERRCODE ( TDB_ERR_OOM , - 1 ) ;
/* First number in array is # keys */
tdb - > lockedkeys [ 0 ] = number ;
/* Insertion sort by bucket */
for ( i = 0 ; i < number ; i + + ) {
hash = tdb_hash ( & keys [ i ] ) ;
2001-08-03 00:17:40 +04:00
for ( j = 0 ; j < i & & BUCKET ( tdb - > lockedkeys [ j + 1 ] ) < BUCKET ( hash ) ; j + + ) ;
memmove ( & tdb - > lockedkeys [ j + 2 ] , & tdb - > lockedkeys [ j + 1 ] , sizeof ( u32 ) * ( i - j ) ) ;
2000-12-06 03:05:15 +03:00
tdb - > lockedkeys [ j + 1 ] = hash ;
}
/* Finally, lock in order */
2000-12-16 00:29:06 +03:00
for ( i = 0 ; i < number ; i + + )
if ( tdb_lock ( tdb , i , F_WRLCK ) )
break ;
/* If error, release locks we have... */
if ( i < number ) {
for ( j = 0 ; j < i ; j + + )
tdb_unlock ( tdb , j , F_WRLCK ) ;
free ( tdb - > lockedkeys ) ;
tdb - > lockedkeys = NULL ;
return TDB_ERRCODE ( TDB_ERR_NOLOCK , - 1 ) ;
}
2000-12-06 03:05:15 +03:00
return 0 ;
}
2000-12-02 04:04:11 +03:00
2000-12-06 03:05:15 +03:00
/* Unlock the keys previously locked by tdb_lockkeys() */
void tdb_unlockkeys ( TDB_CONTEXT * tdb )
{
u32 i ;
for ( i = 0 ; i < tdb - > lockedkeys [ 0 ] ; i + + )
tdb_unlock ( tdb , tdb - > lockedkeys [ i + 1 ] , F_WRLCK ) ;
free ( tdb - > lockedkeys ) ;
tdb - > lockedkeys = NULL ;
}
2000-12-02 04:04:11 +03:00
2000-12-06 03:05:15 +03:00
/* lock/unlock one hash chain. This is meant to be used to reduce
contention - it cannot guarantee how many records will be locked */
int tdb_chainlock ( TDB_CONTEXT * tdb , TDB_DATA key )
{
return tdb_lock ( tdb , BUCKET ( tdb_hash ( & key ) ) , F_WRLCK ) ;
}
void tdb_chainunlock ( TDB_CONTEXT * tdb , TDB_DATA key )
{
tdb_unlock ( tdb , BUCKET ( tdb_hash ( & key ) ) , F_WRLCK ) ;
2000-12-02 04:04:11 +03:00
}
2001-05-28 17:29:06 +04:00
/* register a loging function */
void tdb_logging_function ( TDB_CONTEXT * tdb , void ( * fn ) ( TDB_CONTEXT * , int , const char * , . . . ) )
{
tdb - > log_fn = fn ;
}