2007-08-31 03:07:10 +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
2007-09-08 00:57:01 +04:00
Copyright ( C ) Jeremy Allison 1999 - 2007
2003-03-18 01:23:51 +03:00
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2003
2007-07-12 02:39:11 +04:00
Copyright ( C ) Volker Lendecke 2007
2007-08-31 03:07:10 +04:00
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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2000-04-30 15:04:28 +04:00
( at your option ) any later version .
2007-08-31 03:07:10 +04:00
2000-04-30 15:04:28 +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 .
2007-08-31 03:07:10 +04:00
2000-04-30 15:04:28 +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/>.
2000-04-30 15:04:28 +04:00
*/
# include "includes.h"
2010-08-18 14:24:35 +04:00
# include "memcache.h"
2011-03-22 18:57:01 +03:00
# include "smbd/smbd.h"
2011-03-24 17:31:06 +03:00
# include "messages.h"
2011-04-14 02:36:23 +04:00
# include "smbprofile.h"
2011-06-20 13:10:25 +04:00
# include "tdb_compat.h"
2000-04-30 15:04:28 +04:00
/****************************************************************************
Stat cache code used in unix_convert .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
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 .
2007-08-31 03:07:10 +04:00
*
* @ note Only the first strlen ( orig_translated_path ) characters are stored
2003-03-18 01:23:51 +03:00
* into the cache . This means that full_orig_name will be internally
* truncated .
*
*/
2000-04-30 15:04:28 +04:00
2007-09-08 00:57:01 +04:00
void stat_cache_add ( const char * full_orig_name ,
char * translated_path ,
2007-10-19 04:40:25 +04:00
bool case_sensitive )
2000-04-30 15:04:28 +04:00
{
2003-07-02 04:08:29 +04:00
size_t translated_path_length ;
char * original_path ;
size_t original_path_length ;
2007-09-08 00:57:01 +04:00
char saved_char ;
TALLOC_CTX * ctx = talloc_tos ( ) ;
2003-07-02 04:08:29 +04:00
2007-09-08 00:57:01 +04:00
if ( ! lp_stat_cache ( ) ) {
2003-07-02 04:08:29 +04:00
return ;
2007-09-08 00:57:01 +04:00
}
2003-07-02 04:08:29 +04:00
/*
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
*/
2007-07-12 02:39:11 +04:00
if ( ( * full_orig_name = = ' \0 ' )
| | ISDOT ( full_orig_name ) | | ISDOTDOT ( full_orig_name ) ) {
2003-07-02 04:08:29 +04:00
return ;
2007-07-12 02:39:11 +04:00
}
2003-07-02 04:08:29 +04:00
/*
* If we are in case insentive mode , we don ' t need to
* store names that need no translation - else , it
* would be a waste .
*/
2007-09-08 00:57:01 +04:00
if ( case_sensitive & & ( strcmp ( full_orig_name , translated_path ) = = 0 ) ) {
2003-07-02 04:08:29 +04:00
return ;
2007-09-08 00:57:01 +04:00
}
2003-07-02 04:08:29 +04:00
/*
* Remove any trailing ' / ' characters from the
* translated path .
*/
translated_path_length = strlen ( translated_path ) ;
if ( translated_path [ translated_path_length - 1 ] = = ' / ' ) {
translated_path_length - - ;
}
2003-07-27 07:40:45 +04:00
if ( case_sensitive ) {
2007-09-08 00:57:01 +04:00
original_path = talloc_strdup ( ctx , full_orig_name ) ;
2003-07-27 07:40:45 +04:00
} else {
2007-09-08 00:57:01 +04:00
original_path = talloc_strdup_upper ( ctx , full_orig_name ) ;
2003-07-27 07:40:45 +04:00
}
2003-07-02 04:08:29 +04:00
if ( ! original_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 ) {
2007-08-31 03:07:10 +04: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 ) ) ;
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( original_path ) ;
2003-07-02 04:08:29 +04:00
return ;
}
2004-08-25 02:48:49 +04:00
/* we only want to index by the first part of original_path,
2003-07-02 04:08:29 +04:00
up to the length of translated_path */
original_path [ translated_path_length ] = ' \0 ' ;
original_path_length = translated_path_length ;
}
2007-09-08 00:57:01 +04:00
/* Ensure we're null terminated. */
saved_char = translated_path [ translated_path_length ] ;
translated_path [ translated_path_length ] = ' \0 ' ;
2007-08-31 03:07:10 +04:00
2007-09-08 00:57:01 +04:00
/*
* New entry or replace old entry .
*/
2007-12-18 11:41:03 +03:00
memcache_add (
smbd_memcache ( ) , STAT_CACHE ,
data_blob_const ( original_path , original_path_length ) ,
data_blob_const ( translated_path , translated_path_length + 1 ) ) ;
DEBUG ( 5 , ( " stat_cache_add: Added entry (%lx:size %x) %s -> %s \n " ,
( unsigned long ) translated_path ,
( unsigned int ) translated_path_length ,
original_path ,
translated_path ) ) ;
2003-07-02 04:08:29 +04:00
2007-09-08 00:57:01 +04:00
translated_path [ translated_path_length ] = saved_char ;
TALLOC_FREE ( original_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
*
* @ param conn A connection struct to do the stat ( ) with .
2011-10-22 04:46:12 +04:00
* @ param posix_paths Whether to lookup using stat ( ) or lstat ( )
2003-03-18 01:23:51 +03:00
* @ param name The path we are attempting to cache , modified by this routine
2007-07-12 02:39:11 +04:00
* to be correct as far as the cache can tell us . We assume that
2007-09-08 00:57:01 +04:00
* it is a talloc ' ed string from top of stack , we free it if
* necessary .
* @ param dirpath The path as far as the stat cache told us . Also talloced
* from top of stack .
2007-08-31 03:07:10 +04:00
* @ param start A pointer into name , for where to ' start ' in fixing the rest
* of the name up .
2003-03-18 01:23:51 +03:00
* @ param psd A stat buffer , NOT from the cache , but just a side - effect .
*
2007-08-31 03:07:10 +04:00
* @ return True if we translated ( and did a scuccessful stat on ) the entire
* name .
2003-03-18 01:23:51 +03:00
*
*/
2000-04-30 15:04:28 +04:00
2007-10-19 04:40:25 +04:00
bool stat_cache_lookup ( connection_struct * conn ,
2011-10-22 04:46:12 +04:00
bool posix_paths ,
2007-09-08 00:57:01 +04:00
char * * pp_name ,
char * * pp_dirpath ,
char * * pp_start ,
SMB_STRUCT_STAT * pst )
2000-04-30 15:04:28 +04:00
{
2003-07-27 07:40:45 +04:00
char * chk_name ;
2003-07-02 04:08:29 +04:00
size_t namelen ;
2007-10-19 04:40:25 +04:00
bool sizechanged = False ;
2003-07-03 00:01:51 +04:00
unsigned int num_components = 0 ;
2007-07-07 13:57:27 +04:00
char * translated_path ;
size_t translated_path_length ;
2007-12-18 11:41:03 +03:00
DATA_BLOB data_val ;
2007-07-12 02:39:11 +04:00
char * name ;
2007-09-08 00:57:01 +04:00
TALLOC_CTX * ctx = talloc_tos ( ) ;
2009-11-15 12:46:23 +03:00
struct smb_filename smb_fname ;
2011-10-22 04:46:12 +04:00
int ret ;
2007-09-08 00:57:01 +04:00
* pp_dirpath = NULL ;
* pp_start = * pp_name ;
2003-07-02 04:08:29 +04:00
2007-09-08 00:57:01 +04:00
if ( ! lp_stat_cache ( ) ) {
2003-07-02 04:08:29 +04:00
return False ;
2007-09-08 00:57:01 +04:00
}
2007-08-31 03:07:10 +04:00
2007-09-08 00:57:01 +04:00
name = * pp_name ;
2003-07-02 04:08:29 +04:00
namelen = strlen ( name ) ;
DO_PROFILE_INC ( statcache_lookups ) ;
/*
* Don ' t lookup trivial valid directory entries .
*/
2007-07-12 02:39:11 +04:00
if ( ( * name = = ' \0 ' ) | | ISDOT ( name ) | | ISDOTDOT ( name ) ) {
2003-07-02 04:08:29 +04:00
return False ;
2007-07-12 02:39:11 +04:00
}
2003-07-02 04:08:29 +04:00
2004-05-07 22:37:47 +04:00
if ( conn - > case_sensitive ) {
2007-09-08 00:57:01 +04:00
chk_name = talloc_strdup ( ctx , name ) ;
2003-07-27 07:40:45 +04:00
if ( ! chk_name ) {
DEBUG ( 0 , ( " stat_cache_lookup: strdup failed! \n " ) ) ;
return False ;
}
} else {
2007-09-08 00:57:01 +04:00
chk_name = talloc_strdup_upper ( ctx , name ) ;
2003-07-27 07:40:45 +04:00
if ( ! chk_name ) {
2009-03-19 04:20:11 +03:00
DEBUG ( 0 , ( " stat_cache_lookup: talloc_strdup_upper failed! \n " ) ) ;
2003-07-27 07:40:45 +04:00
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 .
*/
2007-09-08 00:57:01 +04:00
if ( strlen ( chk_name ) ! = namelen ) {
2003-07-03 00:01:51 +04:00
sizechanged = True ;
2007-09-08 00:57:01 +04:00
}
2003-07-03 00:01:51 +04:00
}
2003-07-02 04:08:29 +04:00
while ( 1 ) {
2004-08-25 02:48:49 +04:00
char * sp ;
2007-12-18 11:41:03 +03:00
data_val = data_blob_null ;
2007-07-07 13:57:27 +04:00
2007-12-18 11:41:03 +03:00
if ( memcache_lookup (
smbd_memcache ( ) , STAT_CACHE ,
data_blob_const ( chk_name , strlen ( chk_name ) ) ,
& data_val ) ) {
2007-07-07 13:57:27 +04:00
break ;
}
2007-08-31 03:07:10 +04:00
DEBUG ( 10 , ( " stat_cache_lookup: lookup failed for name [%s] \n " ,
chk_name ) ) ;
2007-07-07 13:57:27 +04:00
/*
* Didn ' t find it - remove last component for next try .
*/
if ( ! ( sp = strrchr_m ( chk_name , ' / ' ) ) ) {
2003-07-02 04:08:29 +04:00
/*
2007-07-07 13:57:27 +04:00
* We reached the end of the name - no match .
2003-07-02 04:08:29 +04:00
*/
2007-07-07 13:57:27 +04:00
DO_PROFILE_INC ( statcache_misses ) ;
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( chk_name ) ;
2007-07-07 13:57:27 +04:00
return False ;
}
* sp = ' \0 ' ;
/*
* Count the number of times we have done this , we ' ll
* need it when reconstructing the string .
*/
2007-09-08 00:57:01 +04:00
if ( sizechanged ) {
2007-07-07 13:57:27 +04:00
num_components + + ;
2007-09-08 00:57:01 +04:00
}
2007-07-07 13:57:27 +04:00
if ( ( * chk_name = = ' \0 ' )
| | ISDOT ( chk_name ) | | ISDOTDOT ( chk_name ) ) {
DO_PROFILE_INC ( statcache_misses ) ;
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( chk_name ) ;
2007-07-07 13:57:27 +04:00
return False ;
}
}
2007-12-18 11:41:03 +03:00
translated_path = talloc_strdup ( ctx , ( char * ) data_val . data ) ;
2007-09-08 00:57:01 +04:00
if ( ! translated_path ) {
smb_panic ( " talloc failed " ) ;
}
2007-12-18 11:41:03 +03:00
translated_path_length = data_val . length - 1 ;
2007-07-12 02:39:11 +04:00
2007-07-07 13:57:27 +04:00
DEBUG ( 10 , ( " stat_cache_lookup: lookup succeeded for name [%s] "
" -> [%s] \n " , chk_name , translated_path ) ) ;
DO_PROFILE_INC ( statcache_hits ) ;
2009-11-15 12:46:23 +03:00
ZERO_STRUCT ( smb_fname ) ;
smb_fname . base_name = translated_path ;
2009-07-22 20:52:09 +04:00
2011-10-22 04:46:12 +04:00
if ( posix_paths ) {
ret = SMB_VFS_LSTAT ( conn , & smb_fname ) ;
} else {
ret = SMB_VFS_STAT ( conn , & smb_fname ) ;
}
if ( ret ! = 0 ) {
2007-07-07 13:57:27 +04:00
/* Discard this entry - it doesn't exist in the filesystem. */
2007-12-18 11:41:03 +03:00
memcache_delete ( smbd_memcache ( ) , STAT_CACHE ,
data_blob_const ( chk_name , strlen ( chk_name ) ) ) ;
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( chk_name ) ;
TALLOC_FREE ( translated_path ) ;
2007-07-07 13:57:27 +04:00
return False ;
}
2009-11-15 12:46:23 +03:00
* pst = smb_fname . st ;
2007-07-07 13:57:27 +04:00
if ( ! sizechanged ) {
2007-09-08 00:57:01 +04:00
memcpy ( * pp_name , translated_path ,
2007-07-12 02:39:11 +04:00
MIN ( namelen , translated_path_length ) ) ;
2007-09-08 00:57:01 +04:00
} else {
2007-07-12 02:39:11 +04:00
if ( num_components = = 0 ) {
2007-09-08 00:57:01 +04:00
name = talloc_strndup ( ctx , translated_path ,
2007-07-12 02:39:11 +04:00
translated_path_length ) ;
2007-07-07 13:57:27 +04:00
} else {
2007-07-12 02:39:11 +04:00
char * sp ;
sp = strnrchr_m ( name , ' / ' , num_components ) ;
if ( sp ) {
2007-09-08 00:57:01 +04:00
name = talloc_asprintf ( ctx , " %.*s%s " ,
2007-07-12 02:39:11 +04:00
( int ) translated_path_length ,
translated_path , sp ) ;
} else {
2007-09-08 00:57:01 +04:00
name = talloc_strndup ( ctx ,
translated_path ,
translated_path_length ) ;
2007-07-12 02:39:11 +04:00
}
2003-07-02 04:08:29 +04:00
}
2007-07-12 02:39:11 +04:00
if ( name = = NULL ) {
/*
* TODO : Get us out of here with a real error message
*/
2007-09-08 00:57:01 +04:00
smb_panic ( " talloc failed " ) ;
2007-07-12 02:39:11 +04:00
}
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( * pp_name ) ;
* pp_name = name ;
2003-07-02 04:08:29 +04:00
}
2007-07-07 13:57:27 +04:00
2007-07-12 02:39:11 +04:00
2007-07-07 13:57:27 +04:00
/* set pointer for 'where to start' on fixing the rest of the name */
2007-09-08 00:57:01 +04:00
* pp_start = & name [ translated_path_length ] ;
if ( * * pp_start = = ' / ' ) {
+ + * pp_start ;
}
2007-07-07 13:57:27 +04:00
2007-09-08 00:57:01 +04:00
* pp_dirpath = translated_path ;
TALLOC_FREE ( chk_name ) ;
2007-07-07 13:57:27 +04:00
return ( namelen = = translated_path_length ) ;
2000-04-30 15:04:28 +04:00
}
2007-01-20 00:46:12 +03:00
/***************************************************************************
Tell all smbd ' s to delete an entry .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-05-31 07:18:37 +04:00
void smbd_send_stat_cache_delete_message ( struct messaging_context * msg_ctx ,
const char * name )
2007-01-20 00:46:12 +03:00
{
# ifdef DEVELOPER
2010-07-07 00:43:14 +04:00
message_send_all ( msg_ctx ,
2007-05-15 19:49:55 +04:00
MSG_SMB_STAT_CACHE_DELETE ,
2007-01-20 00:46:12 +03:00
name ,
strlen ( name ) + 1 ,
NULL ) ;
# endif
}
/***************************************************************************
Delete an entry .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void stat_cache_delete ( const char * name )
{
2007-09-08 00:57:01 +04:00
char * lname = talloc_strdup_upper ( talloc_tos ( ) , name ) ;
2007-01-20 00:46:12 +03:00
if ( ! lname ) {
return ;
}
DEBUG ( 10 , ( " stat_cache_delete: deleting name [%s] -> %s \n " ,
lname , name ) ) ;
2007-12-18 11:41:03 +03:00
memcache_delete ( smbd_memcache ( ) , STAT_CACHE ,
data_blob_const ( lname , talloc_get_size ( lname ) - 1 ) ) ;
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( lname ) ;
2007-01-20 00:46:12 +03:00
}
2004-08-25 05:04:02 +04:00
/***************************************************************
Compute a hash value based on a string key value .
The function returns the bucket index number for the hashed key .
JRA . Use a djb - algorithm hash for speed .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-08-31 03:07:10 +04:00
2011-06-20 13:10:32 +04:00
unsigned int fast_string_hash ( TDB_DATA * key )
2004-08-25 02:48:49 +04:00
{
2006-07-11 22:01:26 +04:00
unsigned int n = 0 ;
2004-08-25 02:48:49 +04:00
const char * p ;
2007-03-29 13:35:51 +04:00
for ( p = ( const char * ) key - > dptr ; * p ! = ' \0 ' ; p + + ) {
2006-07-11 22:01:26 +04:00
n = ( ( n < < 5 ) + n ) ^ ( unsigned int ) ( * p ) ;
2004-08-25 02:48:49 +04:00
}
return n ;
}
2004-08-25 05:04:02 +04:00
/***************************************************************************
Initializes or clears the stat cache .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-19 04:40:25 +04:00
bool reset_stat_cache ( void )
2000-04-30 15:04:28 +04:00
{
2003-07-02 04:08:29 +04:00
if ( ! lp_stat_cache ( ) )
return True ;
2000-05-04 10:28:38 +04:00
2007-12-18 11:41:03 +03:00
memcache_flush ( smbd_memcache ( ) , STAT_CACHE ) ;
2000-10-05 02:37:33 +04:00
2004-08-25 02:48:49 +04:00
return True ;
2003-07-02 04:08:29 +04:00
}