2012-12-13 16:31:59 +04:00
/*
2006-07-11 22:01:26 +04:00
Unix SMB / CIFS implementation .
trivial database library
Copyright ( C ) Andrew Tridgell 1999 - 2005
Copyright ( C ) Paul ` Rusty ' Russell 2000
Copyright ( C ) Jeremy Allison 2000 - 2003
2010-03-25 12:19:48 +03:00
2006-07-11 22:01:26 +04:00
* * NOTE ! The following LGPL license applies to the tdb
* * library . This does NOT imply that all of Samba is released
* * under the LGPL
2010-03-25 12:19:48 +03:00
2006-07-11 22:01:26 +04:00
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
2007-07-10 05:44:42 +04:00
version 3 of the License , or ( at your option ) any later version .
2006-07-11 22:01:26 +04:00
This library is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
2007-07-10 07:42:26 +04:00
License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
2006-07-11 22:01:26 +04:00
*/
# include "tdb_private.h"
2009-08-06 07:13:42 +04:00
# define TDB_NEXT_LOCK_ERR ((tdb_off_t)-1)
/* Uses traverse lock: 0 = finish, TDB_NEXT_LOCK_ERR = error,
other = record offset */
static tdb_off_t tdb_next_lock ( struct tdb_context * tdb , struct tdb_traverse_lock * tlock ,
2009-10-23 15:51:03 +04:00
struct tdb_record * rec )
2006-07-11 22:01:26 +04:00
{
int want_next = ( tlock - > off ! = 0 ) ;
/* Lock each chain from the start one. */
2017-07-02 08:46:17 +03:00
for ( ; tlock - > list < tdb - > hash_size ; tlock - > list + + ) {
if ( ! tlock - > off & & tlock - > list ! = 0 ) {
2006-07-11 22:01:26 +04:00
/* this is an optimisation for the common case where
the hash chain is empty , which is particularly
common for the use of tdb with ldb , where large
hashes are used . In that case we spend most of our
time in tdb_brlock ( ) , locking empty hash chains .
2010-03-25 12:19:48 +03:00
2006-07-11 22:01:26 +04:00
To avoid this , we do an unlocked pre - check to see
if the hash chain is empty before starting to look
inside it . If it is empty then we can avoid that
hash chain . If it isn ' t empty then we can ' t believe
the value we get back , as we read it without a
lock , so instead we get the lock and re - fetch the
value below .
2010-03-25 12:19:48 +03:00
2006-07-11 22:01:26 +04:00
Notice that not doing this optimisation on the
first hash chain is critical . We must guarantee
that we have done at least one fcntl lock at the
start of a search to guarantee that memory is
coherent on SMP systems . If records are added by
2023-04-13 14:17:08 +03:00
others during the search then that ' s OK , and we
2006-07-11 22:01:26 +04:00
could possibly miss those with this trick , but we
could miss them anyway without this trick , so the
semantics don ' t change .
2010-03-25 12:19:48 +03:00
2006-07-11 22:01:26 +04:00
With a non - indexed ldb search this trick gains us a
factor of around 80 in speed on a linux 2.6 . x
system ( testing using ldbtest ) .
*/
2017-07-02 08:46:17 +03:00
tdb - > methods - > next_hash_chain ( tdb , & tlock - > list ) ;
if ( tlock - > list = = tdb - > hash_size ) {
2006-07-11 22:01:26 +04:00
continue ;
}
}
2017-07-02 08:46:17 +03:00
if ( tdb_lock ( tdb , tlock - > list , tlock - > lock_rw ) = = - 1 )
2009-08-06 07:13:42 +04:00
return TDB_NEXT_LOCK_ERR ;
2006-07-11 22:01:26 +04:00
/* No previous record? Start at top of chain. */
if ( ! tlock - > off ) {
2017-07-02 08:46:17 +03:00
if ( tdb_ofs_read ( tdb , TDB_HASH_TOP ( tlock - > list ) ,
2006-07-11 22:01:26 +04:00
& tlock - > off ) = = - 1 )
goto fail ;
} else {
/* Otherwise unlock the previous record. */
if ( tdb_unlock_record ( tdb , tlock - > off ) ! = 0 )
goto fail ;
}
if ( want_next ) {
/* We have offset of old record: grab next */
if ( tdb_rec_read ( tdb , tlock - > off , rec ) = = - 1 )
goto fail ;
tlock - > off = rec - > next ;
}
/* Iterate through chain */
while ( tlock - > off ) {
if ( tdb_rec_read ( tdb , tlock - > off , rec ) = = - 1 )
goto fail ;
/* Detect infinite loops. From "Shlomi Yaakobovich" <Shlomi@exanet.com>. */
if ( tlock - > off = = rec - > next ) {
2009-08-06 07:13:42 +04:00
tdb - > ecode = TDB_ERR_CORRUPT ;
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_next_lock: loop detected. \n " ) ) ;
2006-07-11 22:01:26 +04:00
goto fail ;
}
if ( ! TDB_DEAD ( rec ) ) {
/* Woohoo: we found one! */
if ( tdb_lock_record ( tdb , tlock - > off ) ! = 0 )
goto fail ;
return tlock - > off ;
}
tlock - > off = rec - > next ;
}
2017-07-02 08:46:17 +03:00
tdb_unlock ( tdb , tlock - > list , tlock - > lock_rw ) ;
2006-07-11 22:01:26 +04:00
want_next = 0 ;
}
/* We finished iteration without finding anything */
2009-10-21 17:39:43 +04:00
tdb - > ecode = TDB_SUCCESS ;
return 0 ;
2006-07-11 22:01:26 +04:00
fail :
tlock - > off = 0 ;
2017-07-02 08:46:17 +03:00
if ( tdb_unlock ( tdb , tlock - > list , tlock - > lock_rw ) ! = 0 )
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_next_lock: On error unlock failed! \n " ) ) ;
2009-08-06 07:13:42 +04:00
return TDB_NEXT_LOCK_ERR ;
2006-07-11 22:01:26 +04: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
*/
2012-12-13 16:31:59 +04:00
static int tdb_traverse_internal ( struct tdb_context * tdb ,
2006-07-11 22:01:26 +04:00
tdb_traverse_func fn , void * private_data ,
struct tdb_traverse_lock * tl )
{
TDB_DATA key , dbuf ;
2009-10-23 15:51:03 +04:00
struct tdb_record rec ;
2009-08-06 07:13:42 +04:00
int ret = 0 , count = 0 ;
tdb_off_t off ;
2015-07-22 12:19:08 +03:00
size_t recbuf_len ;
recbuf_len = 4096 ;
key . dptr = malloc ( recbuf_len ) ;
if ( key . dptr = = NULL ) {
return - 1 ;
}
2006-07-11 22:01:26 +04:00
2012-12-14 17:50:08 +04:00
/* This was in the initialization, above, but the IRIX compiler
2006-07-11 22:01:26 +04:00
* did not like it . crh
*/
tl - > next = tdb - > travlocks . next ;
/* 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 */
2009-08-06 07:13:42 +04:00
while ( ( off = tdb_next_lock ( tdb , tl , & rec ) ) ! = 0 ) {
2018-03-04 12:21:09 +03:00
tdb_len_t full_len ;
2015-07-22 12:19:08 +03:00
int nread ;
2018-03-04 12:21:09 +03:00
if ( off = = TDB_NEXT_LOCK_ERR ) {
ret = - 1 ;
goto out ;
}
full_len = rec . key_len + rec . data_len ;
2015-07-22 12:19:08 +03:00
if ( full_len > recbuf_len ) {
recbuf_len = full_len ;
/*
* No realloc , we don ' t need the old data and thus can
* do without the memcpy
*/
free ( key . dptr ) ;
key . dptr = malloc ( recbuf_len ) ;
if ( key . dptr = = NULL ) {
ret = - 1 ;
2017-07-02 08:46:17 +03:00
if ( tdb_unlock ( tdb , tl - > list , tl - > lock_rw )
2015-07-22 12:19:08 +03:00
! = 0 ) {
goto out ;
}
if ( tdb_unlock_record ( tdb , tl - > off ) ! = 0 ) {
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL ,
" tdb_traverse: malloc "
" failed and unlock_record "
" failed! \n " ) ) ;
}
goto out ;
}
}
2006-07-11 22:01:26 +04:00
count + + ;
/* now read the full record */
2015-07-22 12:19:08 +03:00
nread = tdb - > methods - > tdb_read ( tdb , tl - > off + sizeof ( rec ) ,
key . dptr , full_len , 0 ) ;
if ( nread = = - 1 ) {
2006-07-11 22:01:26 +04:00
ret = - 1 ;
2017-07-02 08:46:17 +03:00
if ( tdb_unlock ( tdb , tl - > list , tl - > lock_rw ) ! = 0 )
2006-07-11 22:01:26 +04:00
goto out ;
if ( tdb_unlock_record ( tdb , tl - > off ) ! = 0 )
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_traverse: key.dptr == NULL and unlock_record failed! \n " ) ) ;
2006-07-11 22:01:26 +04:00
goto out ;
}
key . dsize = rec . key_len ;
dbuf . dptr = key . dptr + rec . key_len ;
dbuf . dsize = rec . data_len ;
2009-10-20 05:49:41 +04:00
tdb_trace_1rec_retrec ( tdb , " traverse " , key , dbuf ) ;
2006-07-11 22:01:26 +04:00
/* Drop chain lock, call out */
2017-07-02 08:46:17 +03:00
if ( tdb_unlock ( tdb , tl - > list , tl - > lock_rw ) ! = 0 ) {
2006-07-11 22:01:26 +04:00
ret = - 1 ;
goto out ;
}
if ( fn & & fn ( tdb , key , dbuf , private_data ) ) {
/* They want us to terminate traversal */
2009-10-20 05:49:41 +04:00
tdb_trace_ret ( tdb , " tdb_traverse_end " , count ) ;
2006-07-11 22:01:26 +04:00
if ( tdb_unlock_record ( tdb , tl - > off ) ! = 0 ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_traverse: unlock_record failed! \n " ) ) ; ;
2006-07-11 22:01:26 +04:00
ret = - 1 ;
}
goto out ;
}
}
2009-10-20 05:49:41 +04:00
tdb_trace ( tdb , " tdb_traverse_end " ) ;
2006-07-11 22:01:26 +04:00
out :
2015-07-22 12:19:08 +03:00
SAFE_FREE ( key . dptr ) ;
2006-07-11 22:01:26 +04:00
tdb - > travlocks . next = tl - > next ;
if ( ret < 0 )
return - 1 ;
else
return count ;
}
/*
2017-03-31 07:34:13 +03:00
a read style traverse - temporarily marks each record read only
2006-07-11 22:01:26 +04:00
*/
2012-12-13 16:31:59 +04:00
_PUBLIC_ int tdb_traverse_read ( struct tdb_context * tdb ,
2006-07-11 22:01:26 +04:00
tdb_traverse_func fn , void * private_data )
{
struct tdb_traverse_lock tl = { NULL , 0 , 0 , F_RDLCK } ;
int ret ;
2007-11-07 09:57:35 +03:00
2006-07-11 22:01:26 +04:00
tdb - > traverse_read + + ;
2009-10-20 05:49:41 +04:00
tdb_trace ( tdb , " tdb_traverse_read_start " ) ;
2006-07-11 22:01:26 +04:00
ret = tdb_traverse_internal ( tdb , fn , private_data , & tl ) ;
tdb - > traverse_read - - ;
return ret ;
}
/*
a write style traverse - needs to get the transaction lock to
prevent deadlocks
2007-11-28 15:43:31 +03:00
WARNING : The data buffer given to the callback fn does NOT meet the
2014-09-24 21:46:17 +04:00
alignment guarantees malloc gives you .
2006-07-11 22:01:26 +04:00
*/
2012-12-13 16:31:59 +04:00
_PUBLIC_ int tdb_traverse ( struct tdb_context * tdb ,
2006-07-11 22:01:26 +04:00
tdb_traverse_func fn , void * private_data )
{
struct tdb_traverse_lock tl = { NULL , 0 , 0 , F_WRLCK } ;
2015-07-06 14:13:36 +03:00
enum tdb_lock_flags lock_flags ;
2006-07-11 22:01:26 +04:00
int ret ;
if ( tdb - > read_only | | tdb - > traverse_read ) {
return tdb_traverse_read ( tdb , fn , private_data ) ;
}
2009-10-20 05:49:41 +04:00
2015-07-06 14:13:36 +03:00
lock_flags = TDB_LOCK_WAIT ;
if ( tdb - > allrecord_lock . count ! = 0 ) {
/*
* This avoids a deadlock between tdb_lockall ( ) and
* tdb_traverse ( ) . See
* https : //bugzilla.samba.org/show_bug.cgi?id=11381
*/
lock_flags = TDB_LOCK_NOWAIT ;
}
if ( tdb_transaction_lock ( tdb , F_WRLCK , lock_flags ) ) {
2009-07-18 09:58:58 +04:00
return - 1 ;
2006-07-11 22:01:26 +04:00
}
2008-01-12 02:08:37 +03:00
tdb - > traverse_write + + ;
2009-10-20 05:49:41 +04:00
tdb_trace ( tdb , " tdb_traverse_start " ) ;
2006-07-11 22:01:26 +04:00
ret = tdb_traverse_internal ( tdb , fn , private_data , & tl ) ;
2008-01-12 02:08:37 +03:00
tdb - > traverse_write - - ;
2006-07-11 22:01:26 +04:00
2010-02-17 04:47:19 +03:00
tdb_transaction_unlock ( tdb , F_WRLCK ) ;
2006-07-11 22:01:26 +04:00
return ret ;
}
/* find the first entry in the database and return its key */
2010-10-21 13:51:37 +04:00
_PUBLIC_ TDB_DATA tdb_firstkey ( struct tdb_context * tdb )
2006-07-11 22:01:26 +04:00
{
TDB_DATA key ;
2009-10-23 15:51:03 +04:00
struct tdb_record rec ;
2009-08-06 07:13:42 +04:00
tdb_off_t off ;
2006-07-11 22:01:26 +04:00
/* release any old lock */
if ( tdb_unlock_record ( tdb , tdb - > travlocks . off ) ! = 0 )
return tdb_null ;
2017-07-02 08:46:17 +03:00
tdb - > travlocks . off = tdb - > travlocks . list = 0 ;
2006-08-08 20:08:22 +04:00
tdb - > travlocks . lock_rw = F_RDLCK ;
2006-07-11 22:01:26 +04:00
2007-06-07 04:14:06 +04:00
/* Grab first record: locks chain and returned record. */
2009-10-20 05:49:41 +04:00
off = tdb_next_lock ( tdb , & tdb - > travlocks , & rec ) ;
if ( off = = 0 | | off = = TDB_NEXT_LOCK_ERR ) {
tdb_trace_retrec ( tdb , " tdb_firstkey " , tdb_null ) ;
2006-07-11 22:01:26 +04:00
return tdb_null ;
2009-10-20 05:49:41 +04:00
}
2006-07-11 22:01:26 +04:00
/* now read the key */
key . dsize = rec . key_len ;
key . dptr = tdb_alloc_read ( tdb , tdb - > travlocks . off + sizeof ( rec ) , key . dsize ) ;
2007-06-07 04:00:45 +04:00
2009-10-20 05:49:41 +04:00
tdb_trace_retrec ( tdb , " tdb_firstkey " , key ) ;
2007-06-07 04:00:45 +04:00
/* Unlock the hash chain of the record we just read. */
2017-07-02 08:46:17 +03:00
if ( tdb_unlock ( tdb , tdb - > travlocks . list , tdb - > travlocks . lock_rw ) ! = 0 )
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_firstkey: error occurred while tdb_unlocking! \n " ) ) ;
2006-07-11 22:01:26 +04:00
return key ;
}
/* find the next entry in the database, returning its key */
2010-10-21 13:51:37 +04:00
_PUBLIC_ TDB_DATA tdb_nextkey ( struct tdb_context * tdb , TDB_DATA oldkey )
2006-07-11 22:01:26 +04:00
{
2017-07-02 08:46:17 +03:00
uint32_t oldlist ;
2006-07-11 22:01:26 +04:00
TDB_DATA key = tdb_null ;
2009-10-23 15:51:03 +04:00
struct tdb_record rec ;
2007-03-29 13:35:51 +04:00
unsigned char * k = NULL ;
2009-08-06 07:13:42 +04:00
tdb_off_t off ;
2006-07-11 22:01:26 +04:00
/* Is locked key the old key? If so, traverse will be reliable. */
if ( tdb - > travlocks . off ) {
2017-07-02 08:46:17 +03:00
if ( tdb_lock ( tdb , tdb - > travlocks . list , tdb - > travlocks . lock_rw ) )
2006-07-11 22:01:26 +04:00
return tdb_null ;
if ( tdb_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 */
if ( tdb_unlock_record ( tdb , tdb - > travlocks . off ) ! = 0 ) {
2009-10-20 05:49:41 +04:00
tdb_trace_1rec_retrec ( tdb , " tdb_nextkey " ,
oldkey , tdb_null ) ;
2006-07-11 22:01:26 +04:00
SAFE_FREE ( k ) ;
return tdb_null ;
}
2017-07-02 08:46:17 +03:00
if ( tdb_unlock ( tdb , tdb - > travlocks . list , tdb - > travlocks . lock_rw ) ! = 0 ) {
2006-07-11 22:01:26 +04:00
SAFE_FREE ( k ) ;
return tdb_null ;
}
tdb - > travlocks . off = 0 ;
}
SAFE_FREE ( k ) ;
}
if ( ! tdb - > travlocks . off ) {
/* No previous element: do normal find, and lock record */
2007-06-07 04:00:45 +04:00
tdb - > travlocks . off = tdb_find_lock_hash ( tdb , oldkey , tdb - > hash_fn ( & oldkey ) , tdb - > travlocks . lock_rw , & rec ) ;
2009-10-20 05:49:41 +04:00
if ( ! tdb - > travlocks . off ) {
tdb_trace_1rec_retrec ( tdb , " tdb_nextkey " , oldkey , tdb_null ) ;
2006-07-11 22:01:26 +04:00
return tdb_null ;
2009-10-20 05:49:41 +04:00
}
2017-07-02 08:46:17 +03:00
tdb - > travlocks . list = BUCKET ( rec . full_hash ) ;
2006-07-11 22:01:26 +04:00
if ( tdb_lock_record ( tdb , tdb - > travlocks . off ) ! = 0 ) {
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_nextkey: lock_record failed (%s)! \n " , strerror ( errno ) ) ) ;
2006-07-11 22:01:26 +04:00
return tdb_null ;
}
}
2017-07-02 08:46:17 +03:00
oldlist = tdb - > travlocks . list ;
2006-07-11 22:01:26 +04:00
2007-06-07 04:14:06 +04:00
/* Grab next record: locks chain and returned record,
2006-07-11 22:01:26 +04:00
unlocks old record */
2009-08-06 07:13:42 +04:00
off = tdb_next_lock ( tdb , & tdb - > travlocks , & rec ) ;
if ( off ! = TDB_NEXT_LOCK_ERR & & off ! = 0 ) {
2006-07-11 22:01:26 +04:00
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 */
2017-07-02 08:46:17 +03:00
if ( tdb_unlock ( tdb , tdb - > travlocks . list , tdb - > travlocks . lock_rw ) ! = 0 )
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_nextkey: WARNING tdb_unlock failed! \n " ) ) ;
2006-07-11 22:01:26 +04:00
}
/* Unlock the chain of old record */
2017-07-02 08:46:17 +03:00
if ( tdb_unlock ( tdb , oldlist , tdb - > travlocks . lock_rw ) ! = 0 )
2006-10-20 13:55:47 +04:00
TDB_LOG ( ( tdb , TDB_DEBUG_FATAL , " tdb_nextkey: WARNING tdb_unlock failed! \n " ) ) ;
2009-10-20 05:49:41 +04:00
tdb_trace_1rec_retrec ( tdb , " tdb_nextkey " , oldkey , key ) ;
2006-07-11 22:01:26 +04:00
return key ;
}
2008-01-12 02:08:37 +03:00
2018-10-28 11:06:39 +03:00
_PUBLIC_ int tdb_traverse_chain ( struct tdb_context * tdb ,
unsigned chain ,
tdb_traverse_func fn ,
void * private_data )
{
tdb_off_t rec_ptr ;
struct tdb_chainwalk_ctx chainwalk ;
int count = 0 ;
int ret ;
if ( chain > = tdb - > hash_size ) {
tdb - > ecode = TDB_ERR_EINVAL ;
return - 1 ;
}
if ( tdb - > traverse_read ! = 0 ) {
tdb - > ecode = TDB_ERR_LOCK ;
return - 1 ;
}
ret = tdb_lock ( tdb , chain , F_RDLCK ) ;
if ( ret = = - 1 ) {
return - 1 ;
}
tdb - > traverse_read + = 1 ;
ret = tdb_ofs_read ( tdb , TDB_HASH_TOP ( chain ) , & rec_ptr ) ;
if ( ret = = - 1 ) {
goto fail ;
}
tdb_chainwalk_init ( & chainwalk , rec_ptr ) ;
while ( rec_ptr ! = 0 ) {
struct tdb_record rec ;
bool ok ;
ret = tdb_rec_read ( tdb , rec_ptr , & rec ) ;
if ( ret = = - 1 ) {
goto fail ;
}
if ( ! TDB_DEAD ( & rec ) ) {
/* no overflow checks, tdb_rec_read checked it */
tdb_off_t key_ofs = rec_ptr + sizeof ( rec ) ;
size_t full_len = rec . key_len + rec . data_len ;
uint8_t * buf = NULL ;
TDB_DATA key = { . dsize = rec . key_len } ;
TDB_DATA data = { . dsize = rec . data_len } ;
if ( ( tdb - > transaction = = NULL ) & &
( tdb - > map_ptr ! = NULL ) ) {
2019-08-04 13:15:14 +03:00
ret = tdb_oob ( tdb , key_ofs , full_len , 0 ) ;
2018-10-28 11:06:39 +03:00
if ( ret = = - 1 ) {
goto fail ;
}
key . dptr = ( uint8_t * ) tdb - > map_ptr + key_ofs ;
} else {
buf = tdb_alloc_read ( tdb , key_ofs , full_len ) ;
if ( buf = = NULL ) {
goto fail ;
}
key . dptr = buf ;
}
data . dptr = key . dptr + key . dsize ;
ret = fn ( tdb , key , data , private_data ) ;
free ( buf ) ;
count + = 1 ;
if ( ret ! = 0 ) {
break ;
}
}
rec_ptr = rec . next ;
ok = tdb_chainwalk_check ( tdb , & chainwalk , rec_ptr ) ;
if ( ! ok ) {
goto fail ;
}
}
tdb - > traverse_read - = 1 ;
tdb_unlock ( tdb , chain , F_RDLCK ) ;
return count ;
fail :
tdb - > traverse_read - = 1 ;
tdb_unlock ( tdb , chain , F_RDLCK ) ;
return - 1 ;
}
_PUBLIC_ int tdb_traverse_key_chain ( struct tdb_context * tdb ,
TDB_DATA key ,
tdb_traverse_func fn ,
void * private_data )
{
uint32_t hash , chain ;
int ret ;
hash = tdb - > hash_fn ( & key ) ;
chain = BUCKET ( hash ) ;
ret = tdb_traverse_chain ( tdb , chain , fn , private_data ) ;
return ret ;
}