2007-11-10 01:43:24 +03:00
/*
Unix SMB / CIFS implementation .
Database interface wrapper around red - black trees
2008-01-12 12:38:17 +03:00
Copyright ( C ) Volker Lendecke 2007 , 2008
2007-11-10 01:43:24 +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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2010-08-18 20:59:23 +04:00
# include "dbwrap.h"
2008-10-12 18:27:00 +04:00
# include "../lib/util/rbtree.h"
2007-11-10 01:43:24 +03:00
2008-06-19 11:50:34 +04:00
# define DBWRAP_RBT_ALIGN(_size_) (((_size_)+15)&~15)
2008-01-12 12:38:17 +03:00
2007-11-10 01:43:24 +03:00
struct db_rbt_ctx {
struct rb_root tree ;
} ;
struct db_rbt_rec {
struct db_rbt_ctx * db_ctx ;
struct db_rbt_node * node ;
} ;
/* The structure that ends up in the tree */
struct db_rbt_node {
struct rb_node rb_node ;
size_t keysize , valuesize ;
/*
* key and value are appended implicitly , " data " is only here as a
* target for offsetof ( )
*/
2008-06-19 11:50:34 +04:00
char data [ 1 ] ;
2007-11-10 01:43:24 +03:00
} ;
2008-01-12 12:38:17 +03:00
/*
* Hide the ugly pointer calculations in a function
*/
static struct db_rbt_node * db_rbt2node ( struct rb_node * node )
{
return ( struct db_rbt_node * )
( ( char * ) node - offsetof ( struct db_rbt_node , rb_node ) ) ;
}
/*
* Compare two keys
*/
static int db_rbt_compare ( TDB_DATA a , TDB_DATA b )
{
int res ;
res = memcmp ( a . dptr , b . dptr , MIN ( a . dsize , b . dsize ) ) ;
if ( ( res < 0 ) | | ( ( res = = 0 ) & & ( a . dsize < b . dsize ) ) ) {
return - 1 ;
}
if ( ( res > 0 ) | | ( ( res = = 0 ) & & ( a . dsize > b . dsize ) ) ) {
return 1 ;
}
return 0 ;
}
2007-11-10 01:43:24 +03:00
/*
* dissect a db_rbt_node into its implicit key and value parts
*/
static void db_rbt_parse_node ( struct db_rbt_node * node ,
TDB_DATA * key , TDB_DATA * value )
{
key - > dptr = ( ( uint8 * ) node ) + offsetof ( struct db_rbt_node , data ) ;
key - > dsize = node - > keysize ;
value - > dptr = key - > dptr + node - > keysize ;
value - > dsize = node - > valuesize ;
}
static NTSTATUS db_rbt_store ( struct db_record * rec , TDB_DATA data , int flag )
{
2008-01-12 12:38:17 +03:00
struct db_rbt_rec * rec_priv = ( struct db_rbt_rec * ) rec - > private_data ;
2007-11-10 01:43:24 +03:00
struct db_rbt_node * node ;
struct rb_node * * p ;
struct rb_node * parent ;
TDB_DATA this_key , this_val ;
if ( rec_priv - > node ! = NULL ) {
/*
* The record was around previously
*/
db_rbt_parse_node ( rec_priv - > node , & this_key , & this_val ) ;
SMB_ASSERT ( this_key . dsize = = rec - > key . dsize ) ;
SMB_ASSERT ( memcmp ( this_key . dptr , rec - > key . dptr ,
this_key . dsize ) = = 0 ) ;
if ( this_val . dsize > = data . dsize ) {
/*
* The new value fits into the old space
*/
memcpy ( this_val . dptr , data . dptr , data . dsize ) ;
rec_priv - > node - > valuesize = data . dsize ;
return NT_STATUS_OK ;
}
/*
* We need to delete the key from the tree and start fresh ,
* there ' s not enough space in the existing record
*/
rb_erase ( & rec_priv - > node - > rb_node , & rec_priv - > db_ctx - > tree ) ;
2007-12-19 15:48:49 +03:00
/*
* Keep the existing node around for a while : If the record
* existed before , we reference the key data in there .
*/
2007-11-10 01:43:24 +03:00
}
2009-02-27 17:28:52 +03:00
node = ( struct db_rbt_node * ) talloc_size ( rec_priv - > db_ctx ,
2007-11-10 01:43:24 +03:00
offsetof ( struct db_rbt_node , data ) + rec - > key . dsize
+ data . dsize ) ;
if ( node = = NULL ) {
2009-02-27 17:28:52 +03:00
TALLOC_FREE ( rec_priv - > node ) ;
2007-11-10 01:43:24 +03:00
return NT_STATUS_NO_MEMORY ;
}
ZERO_STRUCT ( node - > rb_node ) ;
node - > keysize = rec - > key . dsize ;
node - > valuesize = data . dsize ;
db_rbt_parse_node ( node , & this_key , & this_val ) ;
memcpy ( this_key . dptr , rec - > key . dptr , node - > keysize ) ;
2009-02-27 17:28:52 +03:00
TALLOC_FREE ( rec_priv - > node ) ;
2007-11-10 01:43:24 +03:00
2007-12-19 15:48:49 +03:00
memcpy ( this_val . dptr , data . dptr , node - > valuesize ) ;
2007-12-19 04:30:02 +03:00
2007-11-10 01:43:24 +03:00
parent = NULL ;
p = & rec_priv - > db_ctx - > tree . rb_node ;
while ( * p ) {
struct db_rbt_node * r ;
TDB_DATA search_key , search_val ;
int res ;
parent = ( * p ) ;
2008-01-12 12:38:17 +03:00
r = db_rbt2node ( * p ) ;
2007-11-10 01:43:24 +03:00
db_rbt_parse_node ( r , & search_key , & search_val ) ;
2008-01-12 12:38:17 +03:00
res = db_rbt_compare ( this_key , search_key ) ;
2007-11-10 01:43:24 +03:00
2008-01-12 12:38:17 +03:00
if ( res = = - 1 ) {
2007-11-10 01:43:24 +03:00
p = & ( * p ) - > rb_left ;
}
2008-01-12 12:38:17 +03:00
else if ( res = = 1 ) {
2007-11-10 01:43:24 +03:00
p = & ( * p ) - > rb_right ;
}
else {
smb_panic ( " someone messed with the tree " ) ;
}
}
rb_link_node ( & node - > rb_node , parent , p ) ;
rb_insert_color ( & node - > rb_node , & rec_priv - > db_ctx - > tree ) ;
return NT_STATUS_OK ;
}
static NTSTATUS db_rbt_delete ( struct db_record * rec )
{
2008-01-12 12:38:17 +03:00
struct db_rbt_rec * rec_priv = ( struct db_rbt_rec * ) rec - > private_data ;
2007-11-10 01:43:24 +03:00
if ( rec_priv - > node = = NULL ) {
return NT_STATUS_OK ;
}
rb_erase ( & rec_priv - > node - > rb_node , & rec_priv - > db_ctx - > tree ) ;
2009-02-27 17:28:52 +03:00
TALLOC_FREE ( rec_priv - > node ) ;
2007-11-10 01:43:24 +03:00
return NT_STATUS_OK ;
}
static struct db_record * db_rbt_fetch_locked ( struct db_context * db_ctx ,
TALLOC_CTX * mem_ctx ,
TDB_DATA key )
{
struct db_rbt_ctx * ctx = talloc_get_type_abort (
db_ctx - > private_data , struct db_rbt_ctx ) ;
struct db_rbt_rec * rec_priv ;
struct db_record * result ;
struct rb_node * n ;
2008-01-12 12:38:17 +03:00
size_t size ;
bool found = false ;
struct db_rbt_node * r = NULL ;
TDB_DATA search_key = tdb_null , search_val = tdb_null ;
2007-11-10 01:43:24 +03:00
2008-01-12 12:38:17 +03:00
n = ctx - > tree . rb_node ;
2007-11-10 01:43:24 +03:00
2008-01-12 12:38:17 +03:00
while ( n ! = NULL ) {
int res ;
r = db_rbt2node ( n ) ;
db_rbt_parse_node ( r , & search_key , & search_val ) ;
res = db_rbt_compare ( key , search_key ) ;
if ( res = = - 1 ) {
n = n - > rb_left ;
}
else if ( res = = 1 ) {
n = n - > rb_right ;
}
else {
found = true ;
break ;
}
2007-11-10 01:43:24 +03:00
}
2008-01-12 12:38:17 +03:00
/*
* In this low - level routine , play tricks to reduce the number of
* tallocs to one . Not recommened for general use , but here it pays
* off .
*/
2007-11-10 01:43:24 +03:00
2008-06-19 11:50:34 +04:00
size = DBWRAP_RBT_ALIGN ( sizeof ( struct db_record ) )
+ sizeof ( struct db_rbt_rec ) ;
2008-01-12 12:38:17 +03:00
if ( ! found ) {
/*
* We need to keep the key around for later store
*/
size + = key . dsize ;
}
result = ( struct db_record * ) talloc_size ( mem_ctx , size ) ;
if ( result = = NULL ) {
2007-11-10 01:43:24 +03:00
return NULL ;
}
2008-01-12 12:38:17 +03:00
rec_priv = ( struct db_rbt_rec * )
2008-06-19 11:50:34 +04:00
( ( char * ) result + DBWRAP_RBT_ALIGN ( sizeof ( struct db_record ) ) ) ;
2007-11-10 01:43:24 +03:00
rec_priv - > db_ctx = ctx ;
result - > store = db_rbt_store ;
result - > delete_rec = db_rbt_delete ;
result - > private_data = rec_priv ;
2008-01-12 12:38:17 +03:00
if ( found ) {
rec_priv - > node = r ;
result - > key = search_key ;
result - > value = search_val ;
}
else {
rec_priv - > node = NULL ;
result - > key . dptr = ( uint8 * )
( ( char * ) rec_priv + sizeof ( * rec_priv ) ) ;
result - > key . dsize = key . dsize ;
memcpy ( result - > key . dptr , key . dptr , key . dsize ) ;
result - > value = tdb_null ;
}
return result ;
}
static int db_rbt_fetch ( struct db_context * db , TALLOC_CTX * mem_ctx ,
TDB_DATA key , TDB_DATA * data )
{
struct db_rbt_ctx * ctx = talloc_get_type_abort (
db - > private_data , struct db_rbt_ctx ) ;
struct rb_node * n ;
bool found = false ;
struct db_rbt_node * r = NULL ;
TDB_DATA search_key , search_val ;
uint8_t * result ;
2007-11-10 01:43:24 +03:00
n = ctx - > tree . rb_node ;
while ( n ! = NULL ) {
int res ;
2008-01-12 12:38:17 +03:00
r = db_rbt2node ( n ) ;
2007-11-10 01:43:24 +03:00
db_rbt_parse_node ( r , & search_key , & search_val ) ;
2008-01-12 12:38:17 +03:00
res = db_rbt_compare ( key , search_key ) ;
2007-11-10 01:43:24 +03:00
2008-01-12 12:38:17 +03:00
if ( res = = - 1 ) {
2007-11-10 01:43:24 +03:00
n = n - > rb_left ;
}
2008-01-12 12:38:17 +03:00
else if ( res = = 1 ) {
2007-11-10 01:43:24 +03:00
n = n - > rb_right ;
}
else {
2008-01-12 12:38:17 +03:00
found = true ;
break ;
2007-11-10 01:43:24 +03:00
}
}
2008-01-12 12:38:17 +03:00
if ( ! found ) {
* data = tdb_null ;
return 0 ;
2007-11-10 01:43:24 +03:00
}
2008-01-12 12:38:17 +03:00
result = ( uint8 * ) talloc_memdup ( mem_ctx , search_val . dptr ,
search_val . dsize ) ;
if ( result = = NULL ) {
2007-11-10 01:43:24 +03:00
return - 1 ;
}
2008-01-12 12:38:17 +03:00
data - > dptr = result ;
data - > dsize = search_val . dsize ;
2007-11-10 01:43:24 +03:00
return 0 ;
}
2009-08-08 15:43:41 +04:00
static int db_rbt_traverse_internal ( struct rb_node * n ,
int ( * f ) ( struct db_record * db ,
void * private_data ) ,
void * private_data )
{
struct db_rbt_node * r ;
struct db_record rec ;
int ret ;
if ( n = = NULL ) {
return 0 ;
}
ret = db_rbt_traverse_internal ( n - > rb_left , f , private_data ) ;
if ( ret ! = 0 ) {
return ret ;
}
r = db_rbt2node ( n ) ;
ZERO_STRUCT ( rec ) ;
db_rbt_parse_node ( r , & rec . key , & rec . value ) ;
ret = f ( & rec , private_data ) ;
if ( ret ! = 0 ) {
return ret ;
}
return db_rbt_traverse_internal ( n - > rb_right , f , private_data ) ;
}
2007-11-10 01:43:24 +03:00
static int db_rbt_traverse ( struct db_context * db ,
int ( * f ) ( struct db_record * db ,
void * private_data ) ,
void * private_data )
{
2009-08-08 15:43:41 +04:00
struct db_rbt_ctx * ctx = talloc_get_type_abort (
db - > private_data , struct db_rbt_ctx ) ;
return db_rbt_traverse_internal ( ctx - > tree . rb_node , f , private_data ) ;
2007-11-10 01:43:24 +03:00
}
static int db_rbt_get_seqnum ( struct db_context * db )
{
return 0 ;
}
2008-03-10 12:17:05 +03:00
static int db_rbt_trans_dummy ( struct db_context * db )
{
/*
* Transactions are pretty pointless in - memory , just return success .
*/
return 0 ;
}
2007-11-10 01:43:24 +03:00
struct db_context * db_open_rbt ( TALLOC_CTX * mem_ctx )
{
struct db_context * result ;
result = talloc ( mem_ctx , struct db_context ) ;
if ( result = = NULL ) {
return NULL ;
}
result - > private_data = TALLOC_ZERO_P ( result , struct db_rbt_ctx ) ;
if ( result - > private_data = = NULL ) {
TALLOC_FREE ( result ) ;
return NULL ;
}
result - > fetch_locked = db_rbt_fetch_locked ;
result - > fetch = db_rbt_fetch ;
result - > traverse = db_rbt_traverse ;
result - > traverse_read = db_rbt_traverse ;
result - > get_seqnum = db_rbt_get_seqnum ;
2008-03-10 12:17:05 +03:00
result - > transaction_start = db_rbt_trans_dummy ;
result - > transaction_commit = db_rbt_trans_dummy ;
result - > transaction_cancel = db_rbt_trans_dummy ;
2007-11-10 01:43:24 +03:00
return result ;
}