2000-04-30 15:04:28 +04:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
2000-04-30 15:04:28 +04:00
stat cache code
Copyright ( C ) Andrew Tridgell 1992 - 2000
2001-09-25 10:38:07 +04:00
Copyright ( C ) Jeremy Allison 1999 - 2000
2003-03-18 01:23:51 +03:00
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2003
2000-04-30 15:04:28 +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
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 .
*/
# include "includes.h"
extern BOOL case_sensitive ;
/****************************************************************************
Stat cache code used in unix_convert .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
typedef struct {
2003-03-18 01:23:51 +03:00
char * original_path ;
char * translated_path ;
size_t translated_path_length ;
char names [ 2 ] ; /* This is extended via malloc... */
2000-04-30 15:04:28 +04:00
} stat_cache_entry ;
# define INIT_STAT_CACHE_SIZE 512
static hash_table stat_cache ;
2003-03-18 01:23:51 +03:00
/**
* Add an entry into the stat cache .
*
* @ param full_orig_name The original name as specified by the client
* @ param orig_translated_path The name on our filesystem .
*
* @ note Only the first strlen ( orig_translated_path ) characters are stored
* into the cache . This means that full_orig_name will be internally
* truncated .
*
*/
2000-04-30 15:04:28 +04:00
2003-03-18 01:23:51 +03:00
void stat_cache_add ( const char * full_orig_name , const char * orig_translated_path )
2000-04-30 15:04:28 +04:00
{
2003-07-02 04:08:29 +04:00
stat_cache_entry * scp ;
stat_cache_entry * found_scp ;
char * translated_path ;
size_t translated_path_length ;
char * original_path ;
size_t original_path_length ;
hash_element * hash_elem ;
if ( ! lp_stat_cache ( ) )
return ;
/*
2003-09-05 23:59:55 +04:00
* Don ' t cache trivial valid directory entries such as . and . .
2003-07-02 04:08:29 +04:00
*/
2003-09-05 23:59:55 +04:00
if ( ( * full_orig_name = = ' \0 ' ) | | ( full_orig_name [ 0 ] = = ' . ' & &
( ( full_orig_name [ 1 ] = = ' \0 ' ) | |
( full_orig_name [ 1 ] = = ' . ' & & full_orig_name [ 1 ] = = ' \0 ' ) ) ) )
2003-07-02 04:08:29 +04:00
return ;
/*
* If we are in case insentive mode , we don ' t need to
* store names that need no translation - else , it
* would be a waste .
*/
if ( case_sensitive & & ( strcmp ( full_orig_name , orig_translated_path ) = = 0 ) )
return ;
/*
* Remove any trailing ' / ' characters from the
* translated path .
*/
translated_path = strdup ( orig_translated_path ) ;
if ( ! translated_path )
return ;
translated_path_length = strlen ( translated_path ) ;
if ( translated_path [ translated_path_length - 1 ] = = ' / ' ) {
translated_path [ translated_path_length - 1 ] = ' \0 ' ;
translated_path_length - - ;
}
2003-07-27 07:40:45 +04:00
if ( case_sensitive ) {
original_path = strdup ( full_orig_name ) ;
} else {
original_path = strdup_upper ( full_orig_name ) ;
}
2003-07-02 04:08:29 +04:00
if ( ! original_path ) {
SAFE_FREE ( translated_path ) ;
return ;
}
original_path_length = strlen ( original_path ) ;
if ( original_path [ original_path_length - 1 ] = = ' / ' ) {
original_path [ original_path_length - 1 ] = ' \0 ' ;
original_path_length - - ;
}
if ( original_path_length ! = translated_path_length ) {
if ( original_path_length < translated_path_length ) {
2003-11-03 17:34:25 +03:00
DEBUG ( 0 , ( " OOPS - tried to store stat cache entry for weird length paths [%s] %lu and [%s] %lu)! \n " ,
original_path , ( unsigned long ) original_path_length , translated_path , ( unsigned long ) translated_path_length ) ) ;
2003-07-02 04:08:29 +04:00
SAFE_FREE ( original_path ) ;
SAFE_FREE ( translated_path ) ;
return ;
}
/* we only want to store the first part of original_path,
up to the length of translated_path */
original_path [ translated_path_length ] = ' \0 ' ;
original_path_length = translated_path_length ;
}
/*
* Check this name doesn ' t exist in the cache before we
* add it .
*/
if ( ( hash_elem = hash_lookup ( & stat_cache , original_path ) ) ) {
found_scp = ( stat_cache_entry * ) ( hash_elem - > value ) ;
if ( strcmp ( ( found_scp - > translated_path ) , orig_translated_path ) = = 0 ) {
/* already in hash table */
SAFE_FREE ( original_path ) ;
SAFE_FREE ( translated_path ) ;
return ;
}
/* hash collision - remove before we re-add */
hash_remove ( & stat_cache , hash_elem ) ;
}
2003-03-18 01:23:51 +03:00
2003-07-02 04:08:29 +04:00
/*
* New entry .
*/
2003-03-18 01:23:51 +03:00
2003-07-02 04:08:29 +04:00
if ( ( scp = ( stat_cache_entry * ) malloc ( sizeof ( stat_cache_entry )
+ original_path_length
+ translated_path_length ) ) = = NULL ) {
DEBUG ( 0 , ( " stat_cache_add: Out of memory ! \n " ) ) ;
SAFE_FREE ( original_path ) ;
SAFE_FREE ( translated_path ) ;
return ;
}
scp - > original_path = scp - > names ;
2003-07-27 07:40:45 +04:00
/* pointer into the structure... */
2003-07-02 04:08:29 +04:00
scp - > translated_path = scp - > names + original_path_length + 1 ;
safe_strcpy ( scp - > original_path , original_path , original_path_length ) ;
safe_strcpy ( scp - > translated_path , translated_path , translated_path_length ) ;
scp - > translated_path_length = translated_path_length ;
hash_insert ( & stat_cache , ( char * ) scp , original_path ) ;
SAFE_FREE ( original_path ) ;
SAFE_FREE ( translated_path ) ;
DEBUG ( 5 , ( " stat_cache_add: Added entry %s -> %s \n " , scp - > original_path , scp - > translated_path ) ) ;
2000-04-30 15:04:28 +04:00
}
2003-03-18 01:23:51 +03:00
/**
* Look through the stat cache for an entry
*
* The hash - table ' s internals will promote it to the top if found .
*
* @ param conn A connection struct to do the stat ( ) with .
* @ param name The path we are attempting to cache , modified by this routine
* to be correct as far as the cache can tell us
* @ param dirpath The path as far as the stat cache told us .
* @ param start A pointer into name , for where to ' start ' in fixing the rest of the name up .
* @ param psd A stat buffer , NOT from the cache , but just a side - effect .
*
* @ return True if we translated ( and did a scuccessful stat on ) the entire name .
*
*/
2000-04-30 15:04:28 +04:00
2003-03-18 01:23:51 +03:00
BOOL stat_cache_lookup ( connection_struct * conn , pstring name , pstring dirpath ,
2000-04-30 15:04:28 +04:00
char * * start , SMB_STRUCT_STAT * pst )
{
2003-07-02 04:08:29 +04:00
stat_cache_entry * scp ;
2003-07-27 07:40:45 +04:00
char * chk_name ;
2003-07-02 04:08:29 +04:00
size_t namelen ;
hash_element * hash_elem ;
char * sp ;
2003-07-03 00:01:51 +04:00
BOOL sizechanged = False ;
unsigned int num_components = 0 ;
2003-07-02 04:08:29 +04:00
if ( ! lp_stat_cache ( ) )
return False ;
2000-04-30 15:04:28 +04:00
2003-07-02 04:08:29 +04:00
namelen = strlen ( name ) ;
* start = name ;
DO_PROFILE_INC ( statcache_lookups ) ;
/*
* Don ' t lookup trivial valid directory entries .
*/
2003-09-06 01:30:50 +04:00
if ( ( * name = = ' \0 ' ) | | ( name [ 0 ] = = ' . ' & &
( ( name [ 1 ] = = ' \0 ' ) | |
( name [ 1 ] = = ' . ' & & name [ 1 ] = = ' \0 ' ) ) ) )
2003-07-02 04:08:29 +04:00
return False ;
2003-07-27 07:40:45 +04:00
if ( case_sensitive ) {
chk_name = strdup ( name ) ;
if ( ! chk_name ) {
DEBUG ( 0 , ( " stat_cache_lookup: strdup failed! \n " ) ) ;
return False ;
}
} else {
chk_name = strdup_upper ( name ) ;
if ( ! chk_name ) {
DEBUG ( 0 , ( " stat_cache_lookup: strdup_upper failed! \n " ) ) ;
return False ;
}
2003-07-03 00:01:51 +04:00
/*
* In some language encodings the length changes
* if we uppercase . We need to treat this differently
* below .
*/
if ( strlen ( chk_name ) ! = namelen )
sizechanged = True ;
}
2003-07-02 04:08:29 +04:00
while ( 1 ) {
hash_elem = hash_lookup ( & stat_cache , chk_name ) ;
if ( hash_elem = = NULL ) {
2003-09-06 01:30:50 +04:00
DEBUG ( 10 , ( " stat_cache_lookup: lookup failed for name [%s] \n " , chk_name ) ) ;
2003-07-02 04:08:29 +04:00
/*
* Didn ' t find it - remove last component for next try .
*/
sp = strrchr_m ( chk_name , ' / ' ) ;
if ( sp ) {
* sp = ' \0 ' ;
2003-07-03 00:01:51 +04:00
/*
* Count the number of times we have done this ,
* we ' ll need it when reconstructing the string .
*/
if ( sizechanged )
num_components + + ;
2003-07-02 04:08:29 +04:00
} else {
/*
* We reached the end of the name - no match .
*/
DO_PROFILE_INC ( statcache_misses ) ;
2003-07-27 07:40:45 +04:00
SAFE_FREE ( chk_name ) ;
2003-07-02 04:08:29 +04:00
return False ;
}
if ( ( * chk_name = = ' \0 ' ) | | ( strcmp ( chk_name , " . " ) = = 0 )
| | ( strcmp ( chk_name , " .. " ) = = 0 ) ) {
DO_PROFILE_INC ( statcache_misses ) ;
2003-07-27 07:40:45 +04:00
SAFE_FREE ( chk_name ) ;
2003-07-02 04:08:29 +04:00
return False ;
}
} else {
scp = ( stat_cache_entry * ) ( hash_elem - > value ) ;
2003-09-06 01:30:50 +04:00
DEBUG ( 10 , ( " stat_cache_lookup: lookup succeeded for name [%s] -> [%s] \n " , chk_name , scp - > translated_path ) ) ;
2003-07-02 04:08:29 +04:00
DO_PROFILE_INC ( statcache_hits ) ;
if ( SMB_VFS_STAT ( conn , scp - > translated_path , pst ) ! = 0 ) {
/* Discard this entry - it doesn't exist in the filesystem. */
hash_remove ( & stat_cache , hash_elem ) ;
2003-07-27 07:40:45 +04:00
SAFE_FREE ( chk_name ) ;
2003-07-02 04:08:29 +04:00
return False ;
}
2003-07-03 00:01:51 +04:00
if ( ! sizechanged ) {
memcpy ( name , scp - > translated_path , MIN ( sizeof ( pstring ) - 1 , scp - > translated_path_length ) ) ;
2003-07-08 00:22:35 +04:00
} else if ( num_components = = 0 ) {
pstrcpy ( name , scp - > translated_path ) ;
2003-07-03 00:01:51 +04:00
} else {
sp = strnrchr_m ( name , ' / ' , num_components ) ;
2003-07-08 00:22:35 +04:00
if ( sp ) {
pstring last_component ;
pstrcpy ( last_component , sp ) ;
pstrcpy ( name , scp - > translated_path ) ;
pstrcat ( name , last_component ) ;
} else {
pstrcpy ( name , scp - > translated_path ) ;
2003-07-03 00:01:51 +04:00
}
}
2003-07-02 04:08:29 +04:00
/* set pointer for 'where to start' on fixing the rest of the name */
* start = & name [ scp - > translated_path_length ] ;
if ( * * start = = ' / ' )
+ + * start ;
pstrcpy ( dirpath , scp - > translated_path ) ;
2003-07-27 07:40:45 +04:00
SAFE_FREE ( chk_name ) ;
2003-07-02 04:08:29 +04:00
return ( namelen = = scp - > translated_path_length ) ;
}
}
2000-04-30 15:04:28 +04:00
}
/*************************************************************************** **
* Initializes or clears the stat cache .
*
* Input : none .
* Output : none .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
BOOL reset_stat_cache ( void )
{
2000-11-27 22:14:10 +03:00
static BOOL initialised ;
2003-07-02 04:08:29 +04:00
if ( ! lp_stat_cache ( ) )
return True ;
2000-05-04 10:28:38 +04:00
2001-05-12 17:04:15 +04:00
if ( initialised ) {
hash_clear ( & stat_cache ) ;
2000-05-02 11:10:54 +04:00
}
2000-10-05 02:37:33 +04:00
2001-05-12 17:04:15 +04:00
initialised = hash_table_init ( & stat_cache , INIT_STAT_CACHE_SIZE ,
( compare_function ) ( strcmp ) ) ;
return initialised ;
2003-07-02 04:08:29 +04:00
}