2002-09-11 18:07:21 +04:00
/*
Unix SMB / CIFS implementation .
Generic , persistent and shared between processes cache mechanism for use
by various parts of the Samba code
Copyright ( C ) Rafal Szczesniak 2002
2009-09-20 20:10:01 +04:00
Copyright ( C ) Volker Lendecke 2009
2009-01-19 02:01:08 +03:00
2002-09-11 18:07:21 +04: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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2002-09-11 18:07:21 +04:00
( at your option ) any later version .
2009-01-19 02:01:08 +03:00
2002-09-11 18:07:21 +04:00
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 .
2009-01-19 02:01:08 +03:00
2002-09-11 18:07:21 +04:00
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2002-09-11 18:07:21 +04:00
*/
# include "includes.h"
2011-02-26 01:20:06 +03:00
# include "system/filesys.h"
2011-02-25 18:34:46 +03:00
# include "system/glob.h"
2011-05-05 13:25:29 +04:00
# include "util_tdb.h"
2014-11-18 00:30:49 +03:00
# include "tdb_wrap/tdb_wrap.h"
2002-09-11 18:07:21 +04:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_TDB
2009-07-14 13:33:04 +04:00
# define CACHE_DATA_FMT "%12u / "
2002-09-11 18:07:21 +04:00
2014-11-18 01:44:47 +03:00
static struct tdb_wrap * cache ;
2014-11-18 00:30:49 +03:00
static struct tdb_wrap * cache_notrans ;
2002-09-11 18:07:21 +04:00
/**
* @ file gencache . c
* @ brief Generic , persistent and shared between processes cache mechanism
* for use by various parts of the Samba code
*
* */
/**
* Cache initialisation function . Opens cache tdb file or creates
* it if does not exist .
*
* @ return true on successful initialisation of the cache or
* false on failure
* */
2009-07-10 14:24:56 +04:00
static bool gencache_init ( void )
2002-09-11 18:07:21 +04:00
{
char * cache_fname = NULL ;
2009-07-13 19:04:29 +04:00
int open_flags = O_RDWR | O_CREAT ;
2009-01-19 02:01:08 +03:00
2002-09-11 18:07:21 +04:00
/* skip file open if it's already opened */
2015-12-13 17:17:27 +03:00
if ( cache ) {
return true ;
}
2002-09-11 18:07:21 +04:00
2013-03-28 14:00:27 +04:00
cache_fname = cache_path ( " gencache.tdb " ) ;
2014-10-06 20:21:13 +04:00
if ( cache_fname = = NULL ) {
return false ;
}
2002-09-11 18:07:21 +04:00
2006-09-10 01:05:51 +04:00
DEBUG ( 5 , ( " Opening cache file at %s \n " , cache_fname ) ) ;
2014-11-18 01:44:47 +03:00
cache = tdb_wrap_open ( NULL , cache_fname , 0 ,
TDB_DEFAULT | TDB_INCOMPATIBLE_HASH ,
open_flags , 0644 ) ;
2009-12-04 18:46:34 +03:00
if ( cache ) {
int ret ;
2014-11-18 01:44:47 +03:00
ret = tdb_check ( cache - > tdb , NULL , NULL ) ;
2009-12-04 18:46:34 +03:00
if ( ret ! = 0 ) {
2014-11-18 01:44:47 +03:00
TALLOC_FREE ( cache ) ;
2013-06-11 23:03:22 +04:00
/*
* Retry with CLEAR_IF_FIRST .
*
* Warning : Converting this to dbwrap won ' t work
* directly . gencache . c does transactions on this tdb ,
* and dbwrap forbids this for CLEAR_IF_FIRST
* databases . tdb does allow transactions on
* CLEAR_IF_FIRST databases , so lets use it here to
* clean up a broken database .
*/
2014-11-18 01:44:47 +03:00
cache = tdb_wrap_open ( NULL , cache_fname , 0 ,
TDB_DEFAULT |
TDB_INCOMPATIBLE_HASH |
TDB_CLEAR_IF_FIRST ,
open_flags , 0644 ) ;
2009-12-04 18:46:34 +03:00
}
}
2002-09-11 18:07:21 +04:00
2006-10-03 03:34:03 +04:00
if ( ! cache & & ( errno = = EACCES ) ) {
2009-07-13 19:04:29 +04:00
open_flags = O_RDONLY ;
2014-11-18 01:44:47 +03:00
cache = tdb_wrap_open ( NULL , cache_fname , 0 ,
TDB_DEFAULT | TDB_INCOMPATIBLE_HASH ,
open_flags , 0644 ) ;
2006-10-03 03:34:03 +04:00
if ( cache ) {
DEBUG ( 5 , ( " gencache_init: Opening cache file %s read-only. \n " , cache_fname ) ) ;
}
}
2014-10-06 20:21:13 +04:00
TALLOC_FREE ( cache_fname ) ;
2006-10-03 03:34:03 +04:00
2002-09-11 18:07:21 +04:00
if ( ! cache ) {
2003-02-12 04:20:56 +03:00
DEBUG ( 5 , ( " Attempt to open gencache.tdb has failed. \n " ) ) ;
2015-12-13 17:17:27 +03:00
return false ;
2002-09-11 18:07:21 +04:00
}
2009-07-13 19:04:29 +04:00
cache_fname = lock_path ( " gencache_notrans.tdb " ) ;
2014-10-06 20:21:13 +04:00
if ( cache_fname = = NULL ) {
2014-11-18 01:44:47 +03:00
TALLOC_FREE ( cache ) ;
2014-10-06 20:21:13 +04:00
return false ;
}
2009-07-13 19:04:29 +04:00
DEBUG ( 5 , ( " Opening cache file at %s \n " , cache_fname ) ) ;
2014-11-18 00:30:49 +03:00
cache_notrans = tdb_wrap_open ( NULL , cache_fname , 0 ,
TDB_CLEAR_IF_FIRST |
TDB_INCOMPATIBLE_HASH |
2014-11-18 00:59:34 +03:00
TDB_NOSYNC |
TDB_MUTEX_LOCKING ,
2014-11-18 00:30:49 +03:00
open_flags , 0644 ) ;
2009-07-13 19:04:29 +04:00
if ( cache_notrans = = NULL ) {
DEBUG ( 5 , ( " Opening %s failed: %s \n " , cache_fname ,
strerror ( errno ) ) ) ;
2014-10-06 20:21:13 +04:00
TALLOC_FREE ( cache_fname ) ;
2014-11-18 01:44:47 +03:00
TALLOC_FREE ( cache ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
2014-10-06 20:21:13 +04:00
TALLOC_FREE ( cache_fname ) ;
2009-07-13 19:04:29 +04:00
2015-12-13 17:17:27 +03:00
return true ;
2002-09-11 18:07:21 +04:00
}
2009-07-13 19:04:29 +04:00
static TDB_DATA last_stabilize_key ( void )
{
TDB_DATA result ;
2011-05-06 00:42:05 +04:00
result . dptr = discard_const_p ( uint8_t , " @LAST_STABILIZED " ) ;
2009-07-13 19:04:29 +04:00
result . dsize = 17 ;
return result ;
}
2002-09-11 18:07:21 +04:00
2012-11-20 12:50:57 +04:00
struct gencache_have_val_state {
time_t new_timeout ;
const DATA_BLOB * data ;
bool gotit ;
} ;
static void gencache_have_val_parser ( time_t old_timeout , DATA_BLOB data ,
void * private_data )
{
struct gencache_have_val_state * state =
( struct gencache_have_val_state * ) private_data ;
time_t now = time ( NULL ) ;
int cache_time_left , new_time_left , additional_time ;
/*
* Excuse the many variables , but these time calculations are
* confusing to me . We do not want to write to gencache with a
* possibly expensive transaction if we are about to write the same
* value , just extending the remaining timeout by less than 10 % .
*/
cache_time_left = old_timeout - now ;
if ( cache_time_left < = 0 ) {
/*
* timed out , write new value
*/
return ;
}
new_time_left = state - > new_timeout - now ;
if ( new_time_left < = 0 ) {
/*
* Huh - - no new timeout ? ? Write it .
*/
return ;
}
if ( new_time_left < cache_time_left ) {
/*
* Someone wants to shorten the timeout . Let it happen .
*/
return ;
}
/*
* By how much does the new timeout extend the remaining cache time ?
*/
additional_time = new_time_left - cache_time_left ;
if ( additional_time * 10 < 0 ) {
/*
* Integer overflow . We extend by so much that we have to write it .
*/
return ;
}
/*
* The comparison below is essentially equivalent to
*
* new_time_left > cache_time_left * 1.10
*
* but without floating point calculations .
*/
if ( additional_time * 10 > cache_time_left ) {
/*
* We extend the cache timeout by more than 10 % . Do it .
*/
return ;
}
/*
* Now the more expensive data compare .
*/
if ( data_blob_cmp ( state - > data , & data ) ! = 0 ) {
/*
* Write a new value . Certainly do it .
*/
return ;
}
/*
* Extending the timeout by less than 10 % for the same cache value is
* not worth the trouble writing a value into gencache under a
* possibly expensive transaction .
*/
state - > gotit = true ;
}
static bool gencache_have_val ( const char * keystr , const DATA_BLOB * data ,
time_t timeout )
{
struct gencache_have_val_state state ;
state . new_timeout = timeout ;
state . data = data ;
state . gotit = false ;
if ( ! gencache_parse ( keystr , gencache_have_val_parser , & state ) ) {
return false ;
}
return state . gotit ;
}
2014-11-26 00:03:18 +03:00
static int last_stabilize_parser ( TDB_DATA key , TDB_DATA data ,
void * private_data )
{
time_t * last_stabilize = private_data ;
if ( ( data . dsize ! = 0 ) & & ( data . dptr [ data . dsize - 1 ] = = ' \0 ' ) ) {
* last_stabilize = atoi ( ( char * ) data . dptr ) ;
}
return 0 ;
}
2002-09-11 18:07:21 +04:00
/**
2003-01-04 11:48:15 +03:00
* Set an entry in the cache file . If there ' s no such
* one , then add it .
2002-09-11 18:07:21 +04:00
*
2003-04-14 06:18:10 +04:00
* @ param keystr string that represents a key of this entry
2009-07-14 13:33:04 +04:00
* @ param blob DATA_BLOB value being cached
2002-09-11 18:07:21 +04:00
* @ param timeout time when the value is expired
*
2015-04-28 09:38:43 +03:00
* @ retval true when entry is successfully stored
2003-04-14 06:18:10 +04:00
* @ retval false on failure
2002-09-11 18:07:21 +04:00
* */
2009-01-19 02:01:08 +03:00
2009-07-14 13:33:04 +04:00
bool gencache_set_data_blob ( const char * keystr , const DATA_BLOB * blob ,
time_t timeout )
2002-09-11 18:07:21 +04:00
{
int ret ;
2015-12-13 23:16:36 +03:00
fstring hdr ;
int hdr_len ;
2009-07-14 13:33:04 +04:00
char * val ;
2009-07-13 19:04:29 +04:00
time_t last_stabilize ;
2009-07-14 20:31:28 +04:00
static int writecount ;
2009-07-13 19:04:29 +04:00
if ( tdb_data_cmp ( string_term_tdb_data ( keystr ) ,
last_stabilize_key ( ) ) = = 0 ) {
DEBUG ( 10 , ( " Can't store %s as a key \n " , keystr ) ) ;
return false ;
}
2009-01-19 02:01:08 +03:00
2009-07-14 13:33:04 +04:00
if ( ( keystr = = NULL ) | | ( blob = = NULL ) ) {
2009-07-10 12:54:33 +04:00
return false ;
}
2002-09-11 18:07:21 +04:00
2015-12-13 17:17:27 +03:00
if ( ! gencache_init ( ) ) {
return false ;
}
2009-01-19 02:01:08 +03:00
2012-11-20 12:50:57 +04:00
if ( gencache_have_val ( keystr , blob , timeout ) ) {
DEBUG ( 10 , ( " Did not store value for %s, we already got it \n " ,
keystr ) ) ;
return true ;
}
2015-12-13 23:16:36 +03:00
hdr_len = fstr_sprintf ( hdr , CACHE_DATA_FMT , ( int ) timeout ) ;
if ( hdr_len = = - 1 ) {
2015-12-13 17:17:27 +03:00
return false ;
2008-02-25 17:24:49 +03:00
}
2015-12-13 23:16:36 +03:00
if ( ( blob - > length + ( size_t ) hdr_len ) < blob - > length ) {
2009-07-14 13:33:04 +04:00
return false ;
}
2015-12-13 23:16:36 +03:00
val = talloc_array ( talloc_tos ( ) , char , hdr_len + blob - > length ) ;
2009-07-14 13:33:04 +04:00
if ( val = = NULL ) {
return false ;
}
2003-03-23 02:32:50 +03:00
2015-12-13 23:16:36 +03:00
memcpy ( val , hdr , hdr_len ) ;
memcpy ( val + hdr_len , blob - > data , blob - > length ) ;
2013-02-11 16:41:12 +04:00
DEBUG ( 10 , ( " Adding cache entry with key=[%s] and timeout= "
" [%s] (%d seconds %s) \n " , keystr ,
2013-02-13 12:46:33 +04:00
timestring ( talloc_tos ( ) , timeout ) ,
2003-09-08 05:28:48 +04:00
( int ) ( timeout - time ( NULL ) ) ,
timeout > time ( NULL ) ? " ahead " : " in the past " ) ) ;
2009-07-14 13:33:04 +04:00
ret = tdb_store_bystring (
2014-11-18 00:30:49 +03:00
cache_notrans - > tdb , keystr ,
2009-07-14 13:33:04 +04:00
make_tdb_data ( ( uint8_t * ) val , talloc_array_length ( val ) ) ,
0 ) ;
TALLOC_FREE ( val ) ;
2009-01-19 02:01:08 +03:00
2009-07-13 19:04:29 +04:00
if ( ret ! = 0 ) {
return false ;
}
2009-07-14 20:31:28 +04:00
/*
* Every 100 writes within a single process , stabilize the cache with
* a transaction . This is done to prevent a single transaction to
* become huge and chew lots of memory .
*/
writecount + = 1 ;
if ( writecount > lp_parm_int ( - 1 , " gencache " , " stabilize_count " , 100 ) ) {
gencache_stabilize ( ) ;
writecount = 0 ;
goto done ;
}
2009-07-13 19:04:29 +04:00
/*
* Every 5 minutes , call gencache_stabilize ( ) to not let grow
* gencache_notrans . tdb too large .
*/
2010-03-26 15:15:53 +03:00
last_stabilize = 0 ;
2014-11-26 00:03:18 +03:00
2014-11-18 00:30:49 +03:00
tdb_parse_record ( cache_notrans - > tdb , last_stabilize_key ( ) ,
2014-11-26 00:03:18 +03:00
last_stabilize_parser , & last_stabilize ) ;
2010-03-26 15:15:53 +03:00
if ( ( last_stabilize
+ lp_parm_int ( - 1 , " gencache " , " stabilize_interval " , 300 ) )
< time ( NULL ) ) {
gencache_stabilize ( ) ;
2009-07-13 19:04:29 +04:00
}
2009-07-14 20:31:28 +04:00
done :
2003-02-20 01:50:29 +03:00
return ret = = 0 ;
2002-09-11 18:07:21 +04:00
}
2016-03-03 17:59:05 +03:00
static void gencache_del_parser ( time_t timeout , DATA_BLOB blob ,
void * private_data )
{
if ( timeout ! = 0 ) {
bool * exists = private_data ;
* exists = true ;
}
}
2002-09-11 18:07:21 +04:00
/**
* Delete one entry from the cache file .
*
2003-04-14 06:18:10 +04:00
* @ param keystr string that represents a key of this entry
2002-09-11 18:07:21 +04:00
*
2003-04-14 06:18:10 +04:00
* @ retval true upon successful deletion
* @ retval false in case of failure
2002-09-11 18:07:21 +04:00
* */
2007-10-19 04:40:25 +04:00
bool gencache_del ( const char * keystr )
2002-09-11 18:07:21 +04:00
{
2016-03-03 17:59:05 +03:00
TDB_DATA key = string_term_tdb_data ( keystr ) ;
bool exists = false ;
bool result = false ;
int ret ;
2009-01-19 02:01:08 +03:00
2009-07-10 12:54:33 +04:00
if ( keystr = = NULL ) {
return false ;
}
2002-09-11 18:07:21 +04:00
2015-12-13 17:17:27 +03:00
if ( ! gencache_init ( ) ) {
return false ;
}
2009-01-19 02:01:08 +03:00
2013-02-11 16:43:32 +04:00
DEBUG ( 10 , ( " Deleting cache entry (key=[%s]) \n " , keystr ) ) ;
2009-01-19 02:01:08 +03:00
2016-03-03 17:59:05 +03:00
ret = tdb_chainlock ( cache_notrans - > tdb , key ) ;
if ( ret = = - 1 ) {
return false ;
}
2009-07-13 19:04:29 +04:00
2016-03-03 17:59:05 +03:00
gencache_parse ( keystr , gencache_del_parser , & exists ) ;
2009-09-23 17:47:05 +04:00
2016-03-03 17:59:05 +03:00
if ( exists ) {
2009-09-23 17:47:05 +04:00
/*
2016-03-03 17:59:05 +03:00
* We delete an element by setting its timeout to
* 0. This way we don ' t have to do a transaction on
* gencache . tdb every time we delete an element .
2009-09-23 17:47:05 +04:00
*/
2016-03-03 17:59:05 +03:00
result = gencache_set ( keystr , " " , 0 ) ;
2009-09-23 17:47:05 +04:00
}
2016-03-03 17:59:05 +03:00
tdb_chainunlock ( cache_notrans - > tdb , key ) ;
return result ;
2002-09-11 18:07:21 +04:00
}
2015-07-22 17:03:47 +03:00
static bool gencache_pull_timeout ( uint8_t * val , time_t * pres , char * * payload )
2009-07-13 19:04:29 +04:00
{
time_t res ;
char * endptr ;
2010-11-27 17:48:21 +03:00
if ( val = = NULL ) {
return false ;
}
2015-07-22 17:00:03 +03:00
res = strtol ( ( char * ) val , & endptr , 10 ) ;
2009-07-13 19:04:29 +04:00
if ( ( endptr = = NULL ) | | ( * endptr ! = ' / ' ) ) {
2015-07-22 17:00:03 +03:00
DEBUG ( 2 , ( " Invalid gencache data format: %s \n " , ( char * ) val ) ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
if ( pres ! = NULL ) {
* pres = res ;
}
2015-07-22 17:03:47 +03:00
if ( payload ! = NULL ) {
* payload = endptr + 1 ;
2009-07-13 19:04:29 +04:00
}
return true ;
}
2002-09-11 18:07:21 +04:00
2010-11-27 02:40:25 +03:00
struct gencache_parse_state {
void ( * parser ) ( time_t timeout , DATA_BLOB blob , void * private_data ) ;
void * private_data ;
} ;
static int gencache_parse_fn ( TDB_DATA key , TDB_DATA data , void * private_data )
{
struct gencache_parse_state * state ;
DATA_BLOB blob ;
time_t t ;
2015-07-22 17:03:47 +03:00
char * payload ;
2010-11-27 02:40:25 +03:00
bool ret ;
if ( data . dptr = = NULL ) {
return - 1 ;
}
2015-07-22 17:03:47 +03:00
ret = gencache_pull_timeout ( data . dptr , & t , & payload ) ;
2010-11-27 02:40:25 +03:00
if ( ! ret ) {
return - 1 ;
}
state = ( struct gencache_parse_state * ) private_data ;
blob = data_blob_const (
2015-07-22 17:03:47 +03:00
payload , data . dsize - PTR_DIFF ( payload , data . dptr ) ) ;
2010-11-27 02:40:25 +03:00
state - > parser ( t , blob , state - > private_data ) ;
2014-03-10 18:41:32 +04:00
2010-11-27 02:40:25 +03:00
return 0 ;
}
bool gencache_parse ( const char * keystr ,
void ( * parser ) ( time_t timeout , DATA_BLOB blob ,
void * private_data ) ,
void * private_data )
{
struct gencache_parse_state state ;
2013-09-08 15:29:33 +04:00
TDB_DATA key = string_term_tdb_data ( keystr ) ;
2010-11-27 02:40:25 +03:00
int ret ;
if ( keystr = = NULL ) {
return false ;
}
2013-09-08 15:29:33 +04:00
if ( tdb_data_cmp ( key , last_stabilize_key ( ) ) = = 0 ) {
2010-11-27 02:40:25 +03:00
return false ;
}
if ( ! gencache_init ( ) ) {
return false ;
}
state . parser = parser ;
state . private_data = private_data ;
2014-11-18 00:30:49 +03:00
ret = tdb_parse_record ( cache_notrans - > tdb , key ,
gencache_parse_fn , & state ) ;
2011-06-20 13:10:32 +04:00
if ( ret = = 0 ) {
2010-11-27 02:40:25 +03:00
return true ;
}
2014-11-18 01:44:47 +03:00
ret = tdb_parse_record ( cache - > tdb , key , gencache_parse_fn , & state ) ;
2011-06-20 13:10:32 +04:00
return ( ret = = 0 ) ;
2010-11-27 02:40:25 +03:00
}
2010-11-27 13:36:52 +03:00
struct gencache_get_data_blob_state {
2013-09-04 10:22:43 +04:00
TALLOC_CTX * mem_ctx ;
2010-11-27 13:36:52 +03:00
DATA_BLOB * blob ;
time_t timeout ;
bool result ;
} ;
static void gencache_get_data_blob_parser ( time_t timeout , DATA_BLOB blob ,
void * private_data )
{
struct gencache_get_data_blob_state * state =
( struct gencache_get_data_blob_state * ) private_data ;
if ( timeout = = 0 ) {
state - > result = false ;
return ;
}
state - > timeout = timeout ;
if ( state - > blob = = NULL ) {
state - > result = true ;
return ;
}
2013-09-04 10:22:43 +04:00
* state - > blob = data_blob_talloc ( state - > mem_ctx , blob . data ,
blob . length ) ;
2010-11-27 13:36:52 +03:00
if ( state - > blob - > data = = NULL ) {
state - > result = false ;
return ;
}
state - > result = true ;
}
2002-09-11 18:07:21 +04:00
/**
* Get existing entry from the cache file .
*
2003-04-14 06:18:10 +04:00
* @ param keystr string that represents a key of this entry
2009-07-14 13:33:04 +04:00
* @ param blob DATA_BLOB that is filled with entry ' s blob
2008-07-11 19:44:15 +04:00
* @ param timeout pointer to a time_t that is filled with entry ' s
* timeout
2002-09-11 18:07:21 +04:00
*
2003-04-14 06:18:10 +04:00
* @ retval true when entry is successfuly fetched
2015-12-13 17:17:27 +03:00
* @ retval false for failure
2002-09-11 18:07:21 +04:00
* */
2013-09-04 10:22:43 +04:00
bool gencache_get_data_blob ( const char * keystr , TALLOC_CTX * mem_ctx ,
DATA_BLOB * blob ,
2009-09-23 17:21:40 +04:00
time_t * timeout , bool * was_expired )
2002-09-11 18:07:21 +04:00
{
2010-11-27 13:36:52 +03:00
struct gencache_get_data_blob_state state ;
2009-09-23 17:21:40 +04:00
bool expired = false ;
2002-09-11 18:07:21 +04:00
2010-11-27 13:36:52 +03:00
state . result = false ;
2013-09-04 10:22:43 +04:00
state . mem_ctx = mem_ctx ;
2010-11-27 13:36:52 +03:00
state . blob = blob ;
2009-07-13 19:04:29 +04:00
2010-11-27 13:36:52 +03:00
if ( ! gencache_parse ( keystr , gencache_get_data_blob_parser , & state ) ) {
2009-09-23 17:21:40 +04:00
goto fail ;
2006-09-10 01:31:56 +04:00
}
2010-11-27 13:36:52 +03:00
if ( ! state . result ) {
2009-09-23 17:21:40 +04:00
goto fail ;
2006-09-10 01:31:56 +04:00
}
2010-11-27 13:36:52 +03:00
if ( state . timeout < = time ( NULL ) ) {
2009-07-13 19:04:29 +04:00
/*
* We ' re expired , delete the entry . We can ' t use gencache_del
* here , because that uses gencache_get_data_blob for checking
* the existence of a record . We know the thing exists and
* directly store an empty value with 0 timeout .
*/
gencache_set ( keystr , " " , 0 ) ;
2009-09-23 17:21:40 +04:00
expired = true ;
goto fail ;
2006-09-10 01:40:47 +04:00
}
2008-07-11 19:44:15 +04:00
if ( timeout ) {
2010-11-27 13:36:52 +03:00
* timeout = state . timeout ;
2006-06-16 01:03:40 +04:00
}
2003-01-25 00:25:12 +03:00
2015-12-13 17:17:27 +03:00
return true ;
2009-09-23 17:21:40 +04:00
fail :
if ( was_expired ! = NULL ) {
* was_expired = expired ;
}
2010-12-17 15:26:16 +03:00
if ( state . result & & state . blob ) {
data_blob_free ( state . blob ) ;
}
2009-09-23 17:21:40 +04:00
return false ;
2008-07-11 19:44:15 +04:00
}
2006-09-10 01:31:56 +04:00
2009-07-13 19:04:29 +04:00
struct stabilize_state {
bool written ;
} ;
static int stabilize_fn ( struct tdb_context * tdb , TDB_DATA key , TDB_DATA val ,
void * priv ) ;
2014-07-02 09:44:04 +04:00
static int wipe_fn ( struct tdb_context * tdb , TDB_DATA key , TDB_DATA val ,
void * priv ) ;
2009-07-13 19:04:29 +04:00
/**
* Stabilize gencache
*
* Migrate the clear - if - first gencache data to the stable ,
* transaction - based gencache . tdb
*/
bool gencache_stabilize ( void )
{
struct stabilize_state state ;
int res ;
char * now ;
if ( ! gencache_init ( ) ) {
return false ;
}
2014-11-18 01:44:47 +03:00
res = tdb_transaction_start_nonblock ( cache - > tdb ) ;
2011-06-20 13:10:31 +04:00
if ( res ! = 0 ) {
2014-11-18 01:44:47 +03:00
if ( tdb_error ( cache - > tdb ) = = TDB_ERR_NOLOCK )
2011-06-20 13:10:32 +04:00
{
2010-03-26 15:18:52 +03:00
/*
* Someone else already does the stabilize ,
* this does not have to be done twice
*/
return true ;
}
2009-07-13 19:04:29 +04:00
DEBUG ( 10 , ( " Could not start transaction on gencache.tdb: "
2015-03-12 17:19:02 +03:00
" %s \n " , tdb_errorstr ( cache - > tdb ) ) ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
2014-07-02 09:44:04 +04:00
2014-11-18 00:30:49 +03:00
res = tdb_lockall ( cache_notrans - > tdb ) ;
2011-06-20 13:10:31 +04:00
if ( res ! = 0 ) {
2014-11-18 01:44:47 +03:00
tdb_transaction_cancel ( cache - > tdb ) ;
2014-07-02 09:44:04 +04:00
DEBUG ( 10 , ( " Could not get allrecord lock on "
2009-07-13 19:04:29 +04:00
" gencache_notrans.tdb: %s \n " ,
2015-03-12 17:19:02 +03:00
tdb_errorstr ( cache_notrans - > tdb ) ) ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
state . written = false ;
2014-11-18 00:30:49 +03:00
res = tdb_traverse ( cache_notrans - > tdb , stabilize_fn , & state ) ;
2014-06-26 18:56:41 +04:00
if ( res < 0 ) {
2014-11-18 00:30:49 +03:00
tdb_unlockall ( cache_notrans - > tdb ) ;
2014-11-18 01:44:47 +03:00
tdb_transaction_cancel ( cache - > tdb ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
if ( ! state . written ) {
2014-11-18 00:30:49 +03:00
tdb_unlockall ( cache_notrans - > tdb ) ;
2014-11-18 01:44:47 +03:00
tdb_transaction_cancel ( cache - > tdb ) ;
2009-07-13 19:04:29 +04:00
return true ;
}
2014-11-18 01:44:47 +03:00
res = tdb_transaction_commit ( cache - > tdb ) ;
2011-06-20 13:10:31 +04:00
if ( res ! = 0 ) {
2009-07-13 19:04:29 +04:00
DEBUG ( 10 , ( " tdb_transaction_commit on gencache.tdb failed: "
2015-03-12 17:19:02 +03:00
" %s \n " , tdb_errorstr ( cache - > tdb ) ) ) ;
2014-11-18 00:30:49 +03:00
tdb_unlockall ( cache_notrans - > tdb ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
2014-11-18 00:30:49 +03:00
res = tdb_traverse ( cache_notrans - > tdb , wipe_fn , NULL ) ;
2015-02-04 16:47:20 +03:00
if ( res < 0 ) {
2014-07-02 09:44:04 +04:00
DEBUG ( 10 , ( " tdb_traverse with wipe_fn on gencache_notrans.tdb "
2014-11-18 00:30:49 +03:00
" failed: %s \n " ,
2015-03-12 17:19:02 +03:00
tdb_errorstr ( cache_notrans - > tdb ) ) ) ;
2014-11-18 00:30:49 +03:00
tdb_unlockall ( cache_notrans - > tdb ) ;
2014-07-02 09:44:04 +04:00
return false ;
}
2014-11-18 00:30:49 +03:00
res = tdb_unlockall ( cache_notrans - > tdb ) ;
2014-07-02 09:44:04 +04:00
if ( res ! = 0 ) {
DEBUG ( 10 , ( " tdb_unlockall on gencache.tdb failed: "
2015-03-12 17:19:02 +03:00
" %s \n " , tdb_errorstr ( cache - > tdb ) ) ) ;
2009-07-13 19:04:29 +04:00
return false ;
}
2010-03-26 15:15:53 +03:00
now = talloc_asprintf ( talloc_tos ( ) , " %d " , ( int ) time ( NULL ) ) ;
2009-07-13 19:04:29 +04:00
if ( now ! = NULL ) {
2014-11-18 00:30:49 +03:00
tdb_store ( cache_notrans - > tdb , last_stabilize_key ( ) ,
2009-07-13 19:04:29 +04:00
string_term_tdb_data ( now ) , 0 ) ;
TALLOC_FREE ( now ) ;
}
return true ;
}
static int stabilize_fn ( struct tdb_context * tdb , TDB_DATA key , TDB_DATA val ,
void * priv )
{
struct stabilize_state * state = ( struct stabilize_state * ) priv ;
int res ;
time_t timeout ;
if ( tdb_data_cmp ( key , last_stabilize_key ( ) ) = = 0 ) {
return 0 ;
}
2015-07-22 17:00:03 +03:00
if ( ! gencache_pull_timeout ( val . dptr , & timeout , NULL ) ) {
2009-07-13 19:04:29 +04:00
DEBUG ( 10 , ( " Ignoring invalid entry \n " ) ) ;
return 0 ;
}
if ( ( timeout < time ( NULL ) ) | | ( val . dsize = = 0 ) ) {
2014-11-18 01:44:47 +03:00
res = tdb_delete ( cache - > tdb , key ) ;
2014-06-26 18:37:17 +04:00
if ( res = = 0 ) {
2009-07-13 19:04:29 +04:00
state - > written = true ;
2014-11-18 01:44:47 +03:00
} else if ( tdb_error ( cache - > tdb ) = = TDB_ERR_NOEXIST ) {
2014-06-26 18:37:17 +04:00
res = 0 ;
2009-07-13 19:04:29 +04:00
}
} else {
2014-11-18 01:44:47 +03:00
res = tdb_store ( cache - > tdb , key , val , 0 ) ;
2009-07-13 19:04:29 +04:00
if ( res = = 0 ) {
state - > written = true ;
}
}
2011-06-20 13:10:31 +04:00
if ( res ! = 0 ) {
2009-07-13 19:04:29 +04:00
DEBUG ( 10 , ( " Transfer to gencache.tdb failed: %s \n " ,
2015-03-12 17:19:02 +03:00
tdb_errorstr ( cache - > tdb ) ) ) ;
2009-07-13 19:04:29 +04:00
return - 1 ;
}
2014-07-02 09:44:04 +04:00
return 0 ;
}
static int wipe_fn ( struct tdb_context * tdb , TDB_DATA key , TDB_DATA val ,
void * priv )
{
int res ;
bool ok ;
time_t timeout ;
res = tdb_data_cmp ( key , last_stabilize_key ( ) ) ;
if ( res = = 0 ) {
return 0 ;
}
2015-07-22 17:00:03 +03:00
ok = gencache_pull_timeout ( val . dptr , & timeout , NULL ) ;
2014-07-02 09:44:04 +04:00
if ( ! ok ) {
DEBUG ( 10 , ( " Ignoring invalid entry \n " ) ) ;
return 0 ;
}
res = tdb_delete ( tdb , key ) ;
if ( res ! = 0 ) {
2009-07-13 19:04:29 +04:00
DEBUG ( 10 , ( " tdb_delete from gencache_notrans.tdb failed: "
2015-03-12 17:19:02 +03:00
" %s \n " , tdb_errorstr ( cache_notrans - > tdb ) ) ) ;
2009-07-13 19:04:29 +04:00
return - 1 ;
}
2014-07-02 09:44:04 +04:00
2009-07-13 19:04:29 +04:00
return 0 ;
}
2014-07-02 09:44:04 +04:00
2007-08-28 16:40:01 +04:00
/**
* Get existing entry from the cache file .
*
* @ param keystr string that represents a key of this entry
2009-07-14 13:33:04 +04:00
* @ param valstr buffer that is allocated and filled with the entry value
* buffer ' s disposing must be done outside
* @ param timeout pointer to a time_t that is filled with entry ' s
* timeout
2007-08-28 16:40:01 +04:00
*
2016-03-06 12:27:06 +03:00
* @ retval true when entry is successfully fetched
2015-12-13 17:17:27 +03:00
* @ retval false for failure
2007-08-28 16:40:01 +04:00
* */
2013-09-04 10:56:23 +04:00
bool gencache_get ( const char * keystr , TALLOC_CTX * mem_ctx , char * * value ,
time_t * ptimeout )
2007-08-28 16:40:01 +04:00
{
2009-07-14 13:33:04 +04:00
DATA_BLOB blob ;
2015-12-13 17:17:27 +03:00
bool ret = false ;
2007-08-28 16:40:01 +04:00
2013-09-04 10:57:59 +04:00
ret = gencache_get_data_blob ( keystr , mem_ctx , & blob , ptimeout , NULL ) ;
2009-07-14 13:33:04 +04:00
if ( ! ret ) {
2009-07-10 12:54:33 +04:00
return false ;
}
2009-07-14 13:33:04 +04:00
if ( ( blob . data = = NULL ) | | ( blob . length = = 0 ) ) {
2013-09-04 10:46:34 +04:00
data_blob_free ( & blob ) ;
2009-07-14 13:33:04 +04:00
return false ;
2007-08-28 16:40:01 +04:00
}
2009-07-14 13:33:04 +04:00
if ( blob . data [ blob . length - 1 ] ! = ' \0 ' ) {
/* Not NULL terminated, can't be a string */
2013-09-04 10:46:34 +04:00
data_blob_free ( & blob ) ;
2009-07-14 13:33:04 +04:00
return false ;
2007-08-28 16:40:01 +04:00
}
2009-11-02 15:01:58 +03:00
if ( value ) {
2013-12-16 15:42:46 +04:00
/*
* talloc_move generates a type - punned warning here . As we
* leave the function immediately , do a simple talloc_steal .
*/
* value = ( char * ) talloc_steal ( mem_ctx , blob . data ) ;
2009-11-02 15:01:58 +03:00
return true ;
2007-08-28 16:40:01 +04:00
}
2009-11-02 15:01:58 +03:00
data_blob_free ( & blob ) ;
2009-07-14 13:33:04 +04:00
return true ;
2007-08-28 16:40:01 +04:00
}
/**
* Set an entry in the cache file . If there ' s no such
* one , then add it .
*
* @ param keystr string that represents a key of this entry
2009-07-14 13:33:04 +04:00
* @ param value text representation value being cached
2007-08-28 16:40:01 +04:00
* @ param timeout time when the value is expired
*
* @ retval true when entry is successfuly stored
* @ retval false on failure
* */
2009-07-14 13:33:04 +04:00
bool gencache_set ( const char * keystr , const char * value , time_t timeout )
2007-08-28 16:40:01 +04:00
{
2009-07-14 13:33:04 +04:00
DATA_BLOB blob = data_blob_const ( value , strlen ( value ) + 1 ) ;
return gencache_set_data_blob ( keystr , & blob , timeout ) ;
2007-08-28 16:40:01 +04:00
}
2002-09-11 18:07:21 +04:00
2010-11-27 17:48:21 +03:00
struct gencache_iterate_blobs_state {
void ( * fn ) ( const char * key , DATA_BLOB value ,
time_t timeout , void * private_data ) ;
2009-01-19 02:01:08 +03:00
const char * pattern ;
2010-11-27 17:48:21 +03:00
void * private_data ;
2009-07-13 19:04:29 +04:00
bool in_persistent ;
2009-01-19 02:01:08 +03:00
} ;
2010-11-27 17:48:21 +03:00
static int gencache_iterate_blobs_fn ( struct tdb_context * tdb , TDB_DATA key ,
TDB_DATA data , void * priv )
2009-01-19 02:01:08 +03:00
{
2010-11-27 17:48:21 +03:00
struct gencache_iterate_blobs_state * state =
( struct gencache_iterate_blobs_state * ) priv ;
2009-01-19 02:01:08 +03:00
char * keystr ;
char * free_key = NULL ;
time_t timeout ;
2015-07-22 17:03:47 +03:00
char * payload ;
2009-01-19 02:01:08 +03:00
2009-07-13 19:04:29 +04:00
if ( tdb_data_cmp ( key , last_stabilize_key ( ) ) = = 0 ) {
return 0 ;
}
2014-11-18 00:30:49 +03:00
if ( state - > in_persistent & & tdb_exists ( cache_notrans - > tdb , key ) ) {
2009-07-13 19:04:29 +04:00
return 0 ;
}
2009-01-19 02:01:08 +03:00
if ( key . dptr [ key . dsize - 1 ] = = ' \0 ' ) {
keystr = ( char * ) key . dptr ;
} else {
/* ensure 0-termination */
2013-09-04 10:57:59 +04:00
keystr = talloc_strndup ( talloc_tos ( ) , ( char * ) key . dptr , key . dsize ) ;
2009-01-19 02:01:08 +03:00
free_key = keystr ;
2013-09-04 10:57:59 +04:00
if ( keystr = = NULL ) {
goto done ;
}
2009-01-19 02:01:08 +03:00
}
2015-07-22 17:03:47 +03:00
if ( ! gencache_pull_timeout ( data . dptr , & timeout , & payload ) ) {
2009-01-19 02:01:08 +03:00
goto done ;
}
2016-03-03 17:59:05 +03:00
if ( timeout = = 0 ) {
/* delete marker */
goto done ;
}
2009-01-19 02:01:08 +03:00
if ( fnmatch ( state - > pattern , keystr , 0 ) ! = 0 ) {
goto done ;
}
2013-02-11 16:42:28 +04:00
DEBUG ( 10 , ( " Calling function with arguments "
" (key=[%s], timeout=[%s]) \n " ,
2013-02-13 12:43:19 +04:00
keystr , timestring ( talloc_tos ( ) , timeout ) ) ) ;
2009-01-19 02:01:08 +03:00
2010-11-27 17:48:21 +03:00
state - > fn ( keystr ,
2015-07-22 17:03:47 +03:00
data_blob_const ( payload ,
data . dsize - PTR_DIFF ( payload , data . dptr ) ) ,
2010-11-27 17:48:21 +03:00
timeout , state - > private_data ) ;
2009-01-19 02:01:08 +03:00
done :
2013-09-04 10:57:59 +04:00
TALLOC_FREE ( free_key ) ;
2009-01-19 02:01:08 +03:00
return 0 ;
}
2010-11-27 17:48:21 +03:00
void gencache_iterate_blobs ( void ( * fn ) ( const char * key , DATA_BLOB value ,
time_t timeout , void * private_data ) ,
void * private_data , const char * pattern )
2002-09-11 18:07:21 +04:00
{
2010-11-27 17:48:21 +03:00
struct gencache_iterate_blobs_state state ;
2002-09-11 18:07:21 +04:00
2010-11-27 17:48:21 +03:00
if ( ( fn = = NULL ) | | ( pattern = = NULL ) | | ! gencache_init ( ) ) {
2009-07-10 12:54:33 +04:00
return ;
}
2002-09-11 18:07:21 +04:00
2010-11-27 17:48:21 +03:00
DEBUG ( 5 , ( " Searching cache keys with pattern %s \n " , pattern ) ) ;
2006-06-16 01:03:40 +04:00
2009-01-19 02:01:08 +03:00
state . fn = fn ;
2010-11-27 17:48:21 +03:00
state . pattern = pattern ;
state . private_data = private_data ;
2009-07-13 19:04:29 +04:00
state . in_persistent = false ;
2014-11-18 00:30:49 +03:00
tdb_traverse ( cache_notrans - > tdb , gencache_iterate_blobs_fn , & state ) ;
2009-07-13 19:04:29 +04:00
state . in_persistent = true ;
2014-11-18 01:44:47 +03:00
tdb_traverse ( cache - > tdb , gencache_iterate_blobs_fn , & state ) ;
2010-11-27 17:48:21 +03:00
}
/**
* Iterate through all entries which key matches to specified pattern
*
* @ param fn pointer to the function that will be supplied with each single
* matching cache entry ( key , value and timeout ) as an arguments
* @ param data void pointer to an arbitrary data that is passed directly to the fn
* function on each call
* @ param keystr_pattern pattern the existing entries ' keys are matched to
*
* */
struct gencache_iterate_state {
void ( * fn ) ( const char * key , const char * value , time_t timeout ,
void * priv ) ;
void * private_data ;
} ;
static void gencache_iterate_fn ( const char * key , DATA_BLOB value ,
time_t timeout , void * private_data )
{
struct gencache_iterate_state * state =
( struct gencache_iterate_state * ) private_data ;
char * valstr ;
char * free_val = NULL ;
if ( value . data [ value . length - 1 ] = = ' \0 ' ) {
valstr = ( char * ) value . data ;
} else {
/* ensure 0-termination */
2013-09-04 10:57:59 +04:00
valstr = talloc_strndup ( talloc_tos ( ) , ( char * ) value . data , value . length ) ;
2010-11-27 17:48:21 +03:00
free_val = valstr ;
2013-09-04 10:57:59 +04:00
if ( valstr = = NULL ) {
goto done ;
}
2010-11-27 17:48:21 +03:00
}
DEBUG ( 10 , ( " Calling function with arguments "
2013-02-11 16:42:58 +04:00
" (key=[%s], value=[%s], timeout=[%s]) \n " ,
2013-02-13 12:45:09 +04:00
key , valstr , timestring ( talloc_tos ( ) , timeout ) ) ) ;
2010-11-27 17:48:21 +03:00
state - > fn ( key , valstr , timeout , state - > private_data ) ;
2013-09-04 10:57:59 +04:00
done :
TALLOC_FREE ( free_val ) ;
2010-11-27 17:48:21 +03:00
}
void gencache_iterate ( void ( * fn ) ( const char * key , const char * value ,
time_t timeout , void * dptr ) ,
void * private_data , const char * pattern )
{
struct gencache_iterate_state state ;
if ( fn = = NULL ) {
return ;
}
state . fn = fn ;
state . private_data = private_data ;
gencache_iterate_blobs ( gencache_iterate_fn , & state , pattern ) ;
2002-09-11 18:07:21 +04:00
}