2007-12-18 11:41:03 +03:00
/*
Unix SMB / CIFS implementation .
In - memory cache
Copyright ( C ) Volker Lendecke 2007
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/>.
*/
2014-07-17 14:49:48 +04:00
# include "replace.h"
# include <talloc.h>
# include "../lib/util/debug.h"
2015-03-05 13:26:46 +03:00
# include "../lib/util/samba_util.h"
2014-07-17 14:49:48 +04:00
# include "../lib/util/dlinklist.h"
2008-10-12 18:27:00 +04:00
# include "../lib/util/rbtree.h"
2014-07-17 14:49:48 +04:00
# include "memcache.h"
2007-12-18 11:41:03 +03:00
2007-12-20 12:55:45 +03:00
static struct memcache * global_cache ;
2007-12-18 11:41:03 +03:00
struct memcache_element {
struct rb_node rb_node ;
struct memcache_element * prev , * next ;
size_t keylength , valuelength ;
2014-07-17 14:41:20 +04:00
uint8_t n ; /* This is really an enum, but save memory */
2007-12-18 11:41:03 +03:00
char data [ 1 ] ; /* placeholder for offsetof */
} ;
struct memcache {
2010-02-06 04:40:03 +03:00
struct memcache_element * mru ;
2007-12-18 11:41:03 +03:00
struct rb_root tree ;
size_t size ;
size_t max_size ;
} ;
2007-12-20 16:41:58 +03:00
static void memcache_element_parse ( struct memcache_element * e ,
DATA_BLOB * key , DATA_BLOB * value ) ;
2008-11-14 01:50:19 +03:00
static bool memcache_is_talloc ( enum memcache_number n )
{
bool result ;
switch ( n ) {
case GETPWNAM_CACHE :
case PDB_GETPWSID_CACHE :
case SINGLETON_CACHE_TALLOC :
2015-04-02 21:19:22 +03:00
case SHARE_MODE_LOCK_CACHE :
2008-11-14 01:50:19 +03:00
result = true ;
break ;
default :
result = false ;
break ;
}
return result ;
}
2007-12-18 11:41:03 +03:00
static int memcache_destructor ( struct memcache * cache ) {
struct memcache_element * e , * next ;
for ( e = cache - > mru ; e ! = NULL ; e = next ) {
next = e - > next ;
2014-07-17 14:48:51 +04:00
TALLOC_FREE ( e ) ;
2007-12-18 11:41:03 +03:00
}
return 0 ;
}
struct memcache * memcache_init ( TALLOC_CTX * mem_ctx , size_t max_size )
{
struct memcache * result ;
2011-06-07 05:44:43 +04:00
result = talloc_zero ( mem_ctx , struct memcache ) ;
2007-12-18 11:41:03 +03:00
if ( result = = NULL ) {
return NULL ;
}
result - > max_size = max_size ;
talloc_set_destructor ( result , memcache_destructor ) ;
return result ;
}
2007-12-20 12:55:45 +03:00
void memcache_set_global ( struct memcache * cache )
{
TALLOC_FREE ( global_cache ) ;
global_cache = cache ;
}
2007-12-18 11:41:03 +03:00
static struct memcache_element * memcache_node2elem ( struct rb_node * node )
{
return ( struct memcache_element * )
( ( char * ) node - offsetof ( struct memcache_element , rb_node ) ) ;
}
static void memcache_element_parse ( struct memcache_element * e ,
DATA_BLOB * key , DATA_BLOB * value )
{
2014-07-17 14:41:20 +04:00
key - > data = ( ( uint8_t * ) e ) + offsetof ( struct memcache_element , data ) ;
2007-12-18 11:41:03 +03:00
key - > length = e - > keylength ;
value - > data = key - > data + e - > keylength ;
value - > length = e - > valuelength ;
}
static size_t memcache_element_size ( size_t key_length , size_t value_length )
{
return sizeof ( struct memcache_element ) - 1 + key_length + value_length ;
}
static int memcache_compare ( struct memcache_element * e , enum memcache_number n ,
DATA_BLOB key )
{
DATA_BLOB this_key , this_value ;
2008-05-20 20:35:23 +04:00
if ( ( int ) e - > n < ( int ) n ) return 1 ;
if ( ( int ) e - > n > ( int ) n ) return - 1 ;
2007-12-18 11:41:03 +03:00
2008-05-20 20:35:23 +04:00
if ( e - > keylength < key . length ) return 1 ;
if ( e - > keylength > key . length ) return - 1 ;
2007-12-18 11:41:03 +03:00
memcache_element_parse ( e , & this_key , & this_value ) ;
return memcmp ( this_key . data , key . data , key . length ) ;
}
static struct memcache_element * memcache_find (
struct memcache * cache , enum memcache_number n , DATA_BLOB key )
{
struct rb_node * node ;
node = cache - > tree . rb_node ;
while ( node ! = NULL ) {
struct memcache_element * elem = memcache_node2elem ( node ) ;
int cmp ;
cmp = memcache_compare ( elem , n , key ) ;
if ( cmp = = 0 ) {
return elem ;
}
node = ( cmp < 0 ) ? node - > rb_left : node - > rb_right ;
}
return NULL ;
}
bool memcache_lookup ( struct memcache * cache , enum memcache_number n ,
DATA_BLOB key , DATA_BLOB * value )
{
struct memcache_element * e ;
2007-12-20 12:55:45 +03:00
if ( cache = = NULL ) {
cache = global_cache ;
}
if ( cache = = NULL ) {
return false ;
}
2007-12-18 11:41:03 +03:00
e = memcache_find ( cache , n , key ) ;
if ( e = = NULL ) {
return false ;
}
if ( cache - > size ! = 0 ) {
DLIST_PROMOTE ( cache - > mru , e ) ;
}
memcache_element_parse ( e , & key , value ) ;
return true ;
}
2007-12-20 16:41:58 +03:00
void * memcache_lookup_talloc ( struct memcache * cache , enum memcache_number n ,
DATA_BLOB key )
{
DATA_BLOB value ;
void * result ;
if ( ! memcache_lookup ( cache , n , key , & value ) ) {
return NULL ;
}
if ( value . length ! = sizeof ( result ) ) {
return NULL ;
}
memcpy ( & result , value . data , sizeof ( result ) ) ;
return result ;
}
2007-12-18 11:41:03 +03:00
static void memcache_delete_element ( struct memcache * cache ,
struct memcache_element * e )
{
rb_erase ( & e - > rb_node , & cache - > tree ) ;
DLIST_REMOVE ( cache - > mru , e ) ;
2008-11-14 01:50:19 +03:00
if ( memcache_is_talloc ( e - > n ) ) {
DATA_BLOB cache_key , cache_value ;
void * ptr ;
memcache_element_parse ( e , & cache_key , & cache_value ) ;
SMB_ASSERT ( cache_value . length = = sizeof ( ptr ) ) ;
memcpy ( & ptr , cache_value . data , sizeof ( ptr ) ) ;
TALLOC_FREE ( ptr ) ;
}
2007-12-18 11:41:03 +03:00
cache - > size - = memcache_element_size ( e - > keylength , e - > valuelength ) ;
2014-07-17 14:48:51 +04:00
TALLOC_FREE ( e ) ;
2007-12-18 11:41:03 +03:00
}
static void memcache_trim ( struct memcache * cache )
{
if ( cache - > max_size = = 0 ) {
return ;
}
2010-02-06 04:40:03 +03:00
while ( ( cache - > size > cache - > max_size ) & & DLIST_TAIL ( cache - > mru ) ) {
memcache_delete_element ( cache , DLIST_TAIL ( cache - > mru ) ) ;
2007-12-18 11:41:03 +03:00
}
}
void memcache_delete ( struct memcache * cache , enum memcache_number n ,
DATA_BLOB key )
{
struct memcache_element * e ;
2007-12-20 12:55:45 +03:00
if ( cache = = NULL ) {
cache = global_cache ;
}
if ( cache = = NULL ) {
return ;
}
2007-12-18 11:41:03 +03:00
e = memcache_find ( cache , n , key ) ;
if ( e = = NULL ) {
return ;
}
memcache_delete_element ( cache , e ) ;
}
void memcache_add ( struct memcache * cache , enum memcache_number n ,
DATA_BLOB key , DATA_BLOB value )
{
struct memcache_element * e ;
struct rb_node * * p ;
struct rb_node * parent ;
DATA_BLOB cache_key , cache_value ;
size_t element_size ;
2007-12-20 12:55:45 +03:00
if ( cache = = NULL ) {
cache = global_cache ;
}
if ( cache = = NULL ) {
return ;
}
2007-12-18 11:41:03 +03:00
if ( key . length = = 0 ) {
return ;
}
e = memcache_find ( cache , n , key ) ;
if ( e ! = NULL ) {
memcache_element_parse ( e , & cache_key , & cache_value ) ;
if ( value . length < = cache_value . length ) {
2008-11-14 01:50:19 +03:00
if ( memcache_is_talloc ( e - > n ) ) {
void * ptr ;
SMB_ASSERT ( cache_value . length = = sizeof ( ptr ) ) ;
memcpy ( & ptr , cache_value . data , sizeof ( ptr ) ) ;
TALLOC_FREE ( ptr ) ;
}
2007-12-18 11:41:03 +03:00
/*
* We can reuse the existing record
*/
memcpy ( cache_value . data , value . data , value . length ) ;
e - > valuelength = value . length ;
return ;
}
memcache_delete_element ( cache , e ) ;
}
element_size = memcache_element_size ( key . length , value . length ) ;
2014-07-17 14:48:51 +04:00
e = talloc_size ( cache , element_size ) ;
2007-12-18 11:41:03 +03:00
if ( e = = NULL ) {
2014-07-17 14:48:51 +04:00
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
2007-12-18 11:41:03 +03:00
return ;
}
2014-07-17 14:48:51 +04:00
talloc_set_type ( e , struct memcache_element ) ;
2007-12-18 11:41:03 +03:00
e - > n = n ;
e - > keylength = key . length ;
e - > valuelength = value . length ;
memcache_element_parse ( e , & cache_key , & cache_value ) ;
memcpy ( cache_key . data , key . data , key . length ) ;
memcpy ( cache_value . data , value . data , value . length ) ;
parent = NULL ;
p = & cache - > tree . rb_node ;
while ( * p ) {
struct memcache_element * elem = memcache_node2elem ( * p ) ;
int cmp ;
parent = ( * p ) ;
cmp = memcache_compare ( elem , n , key ) ;
p = ( cmp < 0 ) ? & ( * p ) - > rb_left : & ( * p ) - > rb_right ;
}
rb_link_node ( & e - > rb_node , parent , p ) ;
rb_insert_color ( & e - > rb_node , & cache - > tree ) ;
DLIST_ADD ( cache - > mru , e ) ;
cache - > size + = element_size ;
memcache_trim ( cache ) ;
}
2007-12-20 16:41:58 +03:00
void memcache_add_talloc ( struct memcache * cache , enum memcache_number n ,
2008-11-15 02:50:33 +03:00
DATA_BLOB key , void * pptr )
2007-12-20 16:41:58 +03:00
{
2008-11-14 15:42:54 +03:00
void * * ptr = ( void * * ) pptr ;
void * p ;
if ( cache = = NULL ) {
cache = global_cache ;
}
if ( cache = = NULL ) {
return ;
}
p = talloc_move ( cache , ptr ) ;
2008-11-14 01:50:19 +03:00
memcache_add ( cache , n , key , data_blob_const ( & p , sizeof ( p ) ) ) ;
2007-12-20 16:41:58 +03:00
}
2007-12-18 11:41:03 +03:00
void memcache_flush ( struct memcache * cache , enum memcache_number n )
{
struct rb_node * node ;
2007-12-20 12:55:45 +03:00
if ( cache = = NULL ) {
cache = global_cache ;
}
if ( cache = = NULL ) {
return ;
}
2007-12-18 11:41:03 +03:00
/*
* Find the smallest element of number n
*/
node = cache - > tree . rb_node ;
if ( node = = NULL ) {
return ;
}
2008-05-20 20:35:23 +04:00
/*
* First , find * any * element of number n
*/
2007-12-18 11:41:03 +03:00
while ( true ) {
struct memcache_element * elem = memcache_node2elem ( node ) ;
struct rb_node * next ;
2008-05-20 20:35:23 +04:00
if ( ( int ) elem - > n = = ( int ) n ) {
break ;
}
2007-12-18 11:41:03 +03:00
if ( ( int ) elem - > n < ( int ) n ) {
next = node - > rb_right ;
}
else {
next = node - > rb_left ;
}
if ( next = = NULL ) {
break ;
}
node = next ;
}
2008-05-20 20:35:23 +04:00
/*
* Then , find the leftmost element with number n
*/
while ( true ) {
struct rb_node * prev = rb_prev ( node ) ;
struct memcache_element * elem ;
if ( prev = = NULL ) {
break ;
}
elem = memcache_node2elem ( prev ) ;
if ( ( int ) elem - > n ! = ( int ) n ) {
break ;
}
node = prev ;
}
2007-12-18 11:41:03 +03:00
while ( node ! = NULL ) {
struct memcache_element * e = memcache_node2elem ( node ) ;
struct rb_node * next = rb_next ( node ) ;
2008-05-20 20:35:23 +04:00
if ( e - > n ! = n ) {
break ;
}
2007-12-18 11:41:03 +03:00
memcache_delete_element ( cache , e ) ;
node = next ;
}
}