X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source3%2Fsmbd%2Fstatcache.c;h=b63dd165a7627934bfd3a6031ca78af86395ad4e;hb=ac3f08ddbe0b484375624db0e35999a8584b57f4;hp=948173687d9ebcb9516a738ab1a4210bb04ea164;hpb=4093bf7ff8c8861cf7b941945ede53a8ec5bb6c8;p=vlendec%2Fsamba-autobuild%2F.git diff --git a/source3/smbd/statcache.c b/source3/smbd/statcache.c index 948173687d9..b63dd165a76 100644 --- a/source3/smbd/statcache.c +++ b/source3/smbd/statcache.c @@ -2,7 +2,7 @@ Unix SMB/CIFS implementation. stat cache code Copyright (C) Andrew Tridgell 1992-2000 - Copyright (C) Jeremy Allison 1999-2000 + Copyright (C) Jeremy Allison 1999-2004 Copyright (C) Andrew Bartlett 2003 This program is free software; you can redistribute it and/or modify @@ -22,21 +22,11 @@ #include "includes.h" -extern BOOL case_sensitive; - /**************************************************************************** Stat cache code used in unix_convert. *****************************************************************************/ -typedef struct { - char *original_path; - char *translated_path; - size_t translated_path_length; - char names[2]; /* This is extended via malloc... */ -} stat_cache_entry; - -#define INIT_STAT_CACHE_SIZE 512 -static hash_table stat_cache; +static TDB_CONTEXT *tdb_stat_cache; /** * Add an entry into the stat cache. @@ -50,28 +40,31 @@ static hash_table stat_cache; * */ -void stat_cache_add( const char *full_orig_name, const char *orig_translated_path) +void stat_cache_add( const char *full_orig_name, const char *orig_translated_path, BOOL case_sensitive) { - stat_cache_entry *scp; - stat_cache_entry *found_scp; char *translated_path; size_t translated_path_length; - + TDB_DATA data_val; char *original_path; size_t original_path_length; - - hash_element *hash_elem; + size_t sc_size = lp_max_stat_cache_size(); if (!lp_stat_cache()) return; + if (sc_size && (tdb_map_size(tdb_stat_cache) > sc_size*1024)) { + reset_stat_cache(); + } + + ZERO_STRUCT(data_val); + /* * Don't cache trivial valid directory entries such as . and .. */ if((*full_orig_name == '\0') || (full_orig_name[0] == '.' && ((full_orig_name[1] == '\0') || - (full_orig_name[1] == '.' && full_orig_name[1] == '\0')))) + (full_orig_name[1] == '.' && full_orig_name[2] == '\0')))) return; /* @@ -88,7 +81,7 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat * translated path. */ - translated_path = strdup(orig_translated_path); + translated_path = SMB_STRDUP(orig_translated_path); if (!translated_path) return; @@ -100,7 +93,7 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat } if(case_sensitive) { - original_path = strdup(full_orig_name); + original_path = SMB_STRDUP(full_orig_name); } else { original_path = strdup_upper(full_orig_name); } @@ -119,14 +112,14 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat if (original_path_length != translated_path_length) { if (original_path_length < translated_path_length) { - DEBUG(0, ("OOPS - tried to store stat cache entry for werid length paths [%s] %u and [%s] %u)!\n", - original_path, original_path_length, translated_path, translated_path_length)); + 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)); SAFE_FREE(original_path); SAFE_FREE(translated_path); return; } - /* we only want to store the first part of original_path, + /* we only want to index by the first part of original_path, up to the length of translated_path */ original_path[translated_path_length] = '\0'; @@ -134,55 +127,26 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat } /* - * 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); - } - - /* - * New entry. + * New entry or replace old entry. */ - 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; - /* pointer into the structure... */ - 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; + data_val.dsize = translated_path_length + 1; + data_val.dptr = (uint8 *)translated_path; - hash_insert(&stat_cache, (char *)scp, original_path); + if (tdb_store_bystring(tdb_stat_cache, original_path, data_val, TDB_REPLACE) != 0) { + DEBUG(0,("stat_cache_add: Error storing entry %s -> %s\n", original_path, translated_path)); + } else { + DEBUG(5,("stat_cache_add: Added entry (%lx:size%x) %s -> %s\n", + (unsigned long)data_val.dptr, (unsigned int)data_val.dsize, original_path, translated_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)); } /** * 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 @@ -197,11 +161,8 @@ void stat_cache_add( const char *full_orig_name, const char *orig_translated_pat BOOL stat_cache_lookup(connection_struct *conn, pstring name, pstring dirpath, char **start, SMB_STRUCT_STAT *pst) { - stat_cache_entry *scp; char *chk_name; size_t namelen; - hash_element *hash_elem; - char *sp; BOOL sizechanged = False; unsigned int num_components = 0; @@ -222,8 +183,8 @@ BOOL stat_cache_lookup(connection_struct *conn, pstring name, pstring dirpath, (name[1] == '.' && name[1] == '\0')))) return False; - if (case_sensitive) { - chk_name = strdup(name); + if (conn->case_sensitive) { + chk_name = SMB_STRDUP(name); if (!chk_name) { DEBUG(0, ("stat_cache_lookup: strdup failed!\n")); return False; @@ -246,8 +207,11 @@ BOOL stat_cache_lookup(connection_struct *conn, pstring name, pstring dirpath, } while (1) { - hash_elem = hash_lookup(&stat_cache, chk_name); - if(hash_elem == NULL) { + TDB_DATA data_val; + char *sp; + + data_val = tdb_fetch_bystring(tdb_stat_cache, chk_name); + if(data_val.dptr == NULL || data_val.dsize == 0) { DEBUG(10,("stat_cache_lookup: lookup failed for name [%s]\n", chk_name )); /* * Didn't find it - remove last component for next try. @@ -277,63 +241,117 @@ BOOL stat_cache_lookup(connection_struct *conn, pstring name, pstring dirpath, return False; } } else { - scp = (stat_cache_entry *)(hash_elem->value); - DEBUG(10,("stat_cache_lookup: lookup succeeded for name [%s] -> [%s]\n", chk_name, scp->translated_path )); + BOOL retval; + char *translated_path = (char *)data_val.dptr; + size_t translated_path_length = data_val.dsize - 1; + + DEBUG(10,("stat_cache_lookup: lookup succeeded for name [%s] -> [%s]\n", chk_name, translated_path )); DO_PROFILE_INC(statcache_hits); - if(SMB_VFS_STAT(conn,scp->translated_path, pst) != 0) { + if(SMB_VFS_STAT(conn,translated_path, pst) != 0) { /* Discard this entry - it doesn't exist in the filesystem. */ - hash_remove(&stat_cache, hash_elem); + tdb_delete_bystring(tdb_stat_cache, chk_name); SAFE_FREE(chk_name); + SAFE_FREE(data_val.dptr); return False; } if (!sizechanged) { - memcpy(name, scp->translated_path, MIN(sizeof(pstring)-1, scp->translated_path_length)); + memcpy(name, translated_path, MIN(sizeof(pstring)-1, translated_path_length)); } else if (num_components == 0) { - pstrcpy(name, scp->translated_path); + pstrcpy(name, translated_path); } else { sp = strnrchr_m(name, '/', num_components); if (sp) { pstring last_component; pstrcpy(last_component, sp); - pstrcpy(name, scp->translated_path); + pstrcpy(name, translated_path); pstrcat(name, last_component); } else { - pstrcpy(name, scp->translated_path); + pstrcpy(name, translated_path); } } /* set pointer for 'where to start' on fixing the rest of the name */ - *start = &name[scp->translated_path_length]; + *start = &name[translated_path_length]; if(**start == '/') ++*start; - pstrcpy(dirpath, scp->translated_path); + pstrcpy(dirpath, translated_path); + retval = (namelen == translated_path_length) ? True : False; SAFE_FREE(chk_name); - return (namelen == scp->translated_path_length); + SAFE_FREE(data_val.dptr); + return retval; } } } -/*************************************************************************** ** - * Initializes or clears the stat cache. - * - * Input: none. - * Output: none. - * - * ************************************************************************** ** - */ +/*************************************************************************** + Tell all smbd's to delete an entry. +**************************************************************************/ + +void send_stat_cache_delete_message(const char *name) +{ +#ifdef DEVELOPER + message_send_all(smbd_messaging_context(), + MSG_SMB_STAT_CACHE_DELETE, + name, + strlen(name)+1, + NULL); +#endif +} + +/*************************************************************************** + Delete an entry. +**************************************************************************/ + +void stat_cache_delete(const char *name) +{ + char *lname = strdup_upper(name); + + if (!lname) { + return; + } + DEBUG(10,("stat_cache_delete: deleting name [%s] -> %s\n", + lname, name )); + + tdb_delete_bystring(tdb_stat_cache, lname); + SAFE_FREE(lname); +} + +/*************************************************************** + 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. +***************************************************************/ + +unsigned int fast_string_hash(TDB_DATA *key) +{ + unsigned int n = 0; + const char *p; + for (p = (const char *)key->dptr; *p != '\0'; p++) { + n = ((n << 5) + n) ^ (unsigned int)(*p); + } + return n; +} + +/*************************************************************************** + Initializes or clears the stat cache. +**************************************************************************/ + BOOL reset_stat_cache( void ) { - static BOOL initialised; if (!lp_stat_cache()) return True; - if (initialised) { - hash_clear(&stat_cache); + if (tdb_stat_cache) { + tdb_close(tdb_stat_cache); } - initialised = hash_table_init( &stat_cache, INIT_STAT_CACHE_SIZE, - (compare_function)(strcmp)); - return initialised; + /* Create the in-memory tdb using our custom hash function. */ + tdb_stat_cache = tdb_open_ex("statcache", 1031, TDB_INTERNAL, + (O_RDWR|O_CREAT), 0644, NULL, fast_string_hash); + + if (!tdb_stat_cache) + return False; + return True; }