1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-30 06:50:24 +03:00

Add a in-memory cache

This is a more general API that caches data with a LRU scheme. See
include/cache.h. No comments yet, I'm still working on it. But Jeremy has given
me a hint in one of his checkins that he would like to make use of this now.

The idea is that we get rid of all our silly little caches and merge them all
into one cache that we can then very easily trim, for example even with a
smbcontrol message if someone decides memory is tight. The main user is the
stat cache, this patch also converts the getwd cache. More caches to come.
(This used to be commit 7a911b35713538d82001a3c9f34152e293fe1943)
This commit is contained in:
Volker Lendecke 2007-12-18 09:41:03 +01:00
parent 68e65b2981
commit f427d4ce65
10 changed files with 513 additions and 212 deletions

View File

@ -284,7 +284,7 @@ TALLOC_OBJ = lib/talloc/talloc.o
LIB_WITHOUT_PROTO_OBJ = $(LIBREPLACE_OBJ) $(SOCKET_WRAPPER_OBJ) $(NSS_WRAPPER_OBJ) $(TALLOC_OBJ) \
lib/messages.o librpc/gen_ndr/ndr_messaging.o lib/messages_local.o \
lib/messages_ctdbd.o lib/packet.o lib/ctdbd_conn.o lib/talloc_stack.o \
lib/interfaces.o lib/rbtree.o
lib/interfaces.o lib/rbtree.o lib/cache.o
LIB_WITH_PROTO_OBJ = $(VERSION_OBJ) lib/charcnv.o lib/debug.o lib/fault.o \
lib/interface.o lib/md4.o \

View File

@ -719,6 +719,7 @@ typedef char fstring[FSTRING_LEN];
#include "packet.h"
#include "ctdbd_conn.h"
#include "talloc_stack.h"
#include "cache.h"
/* used in net.c */
struct functable {

298
source3/lib/cache.c Normal file
View File

@ -0,0 +1,298 @@
/*
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/>.
*/
#include "cache.h"
#include "rbtree.h"
struct memcache_element {
struct rb_node rb_node;
struct memcache_element *prev, *next;
size_t keylength, valuelength;
uint8 n; /* This is really an enum, but save memory */
char data[1]; /* placeholder for offsetof */
};
struct memcache {
struct memcache_element *mru, *lru;
struct rb_root tree;
size_t size;
size_t max_size;
};
static int memcache_destructor(struct memcache *cache) {
struct memcache_element *e, *next;
for (e = cache->mru; e != NULL; e = next) {
next = e->next;
SAFE_FREE(e);
}
return 0;
}
struct memcache *memcache_init(TALLOC_CTX *mem_ctx, size_t max_size)
{
struct memcache *result;
result = TALLOC_ZERO_P(mem_ctx, struct memcache);
if (result == NULL) {
return NULL;
}
result->max_size = max_size;
talloc_set_destructor(result, memcache_destructor);
return result;
}
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)
{
key->data = ((uint8 *)e) + offsetof(struct memcache_element, data);
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;
if ((int)e->n < (int)n) return -1;
if ((int)e->n > (int)n) return 1;
if (e->keylength < key.length) return -1;
if (e->keylength > key.length) return 1;
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;
e = memcache_find(cache, n, key);
if (e == NULL) {
return false;
}
if (cache->size != 0) {
/*
* Do LRU promotion only when we will ever shrink
*/
if (e == cache->lru) {
cache->lru = e->prev;
}
DLIST_PROMOTE(cache->mru, e);
if (cache->mru == NULL) {
cache->mru = e;
}
}
memcache_element_parse(e, &key, value);
return true;
}
static void memcache_delete_element(struct memcache *cache,
struct memcache_element *e)
{
rb_erase(&e->rb_node, &cache->tree);
if (e == cache->lru) {
cache->lru = e->prev;
}
DLIST_REMOVE(cache->mru, e);
cache->size -= memcache_element_size(e->keylength, e->valuelength);
SAFE_FREE(e);
}
static void memcache_trim(struct memcache *cache)
{
if (cache->max_size == 0) {
return;
}
while ((cache->size > cache->max_size) && (cache->lru != NULL)) {
memcache_delete_element(cache, cache->lru);
}
}
void memcache_delete(struct memcache *cache, enum memcache_number n,
DATA_BLOB key)
{
struct memcache_element *e;
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;
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) {
/*
* 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);
e = (struct memcache_element *)SMB_MALLOC(element_size);
if (e == NULL) {
DEBUG(0, ("malloc failed\n"));
return;
}
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);
if (cache->lru == NULL) {
cache->lru = e;
}
cache->size += element_size;
memcache_trim(cache);
}
void memcache_flush(struct memcache *cache, enum memcache_number n)
{
struct rb_node *node;
/*
* Find the smallest element of number n
*/
node = cache->tree.rb_node;
if (node == NULL) {
return;
}
while (true) {
struct memcache_element *elem = memcache_node2elem(node);
struct rb_node *next;
if ((int)elem->n < (int)n) {
next = node->rb_right;
}
else {
next = node->rb_left;
}
if (next == NULL) {
break;
}
node = next;
}
node = rb_next(node);
if (node == NULL) {
return;
}
while (node != NULL) {
struct memcache_element *e = memcache_node2elem(node);
struct rb_node *next = rb_next(node);
memcache_delete_element(cache, e);
node = next;
}
}

View File

@ -88,8 +88,6 @@ static bool include_registry_globals = False;
#define USERSHARE_VALID 1
#define USERSHARE_PENDING_DELETE 2
bool use_getwd_cache = True;
extern int extra_time_offset;
static bool defaults_saved = False;
@ -215,6 +213,7 @@ typedef struct {
int pwordlevel;
int unamelevel;
int deadtime;
bool getwd_cache;
int maxprotocol;
int minprotocol;
int security;
@ -1037,7 +1036,7 @@ static struct parm_struct parm_table[] = {
{"block size", P_INTEGER, P_LOCAL, &sDefault.iBlock_size, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL},
{"deadtime", P_INTEGER, P_GLOBAL, &Globals.deadtime, NULL, NULL, FLAG_ADVANCED},
{"getwd cache", P_BOOL, P_GLOBAL, &use_getwd_cache, NULL, NULL, FLAG_ADVANCED},
{"getwd cache", P_BOOL, P_GLOBAL, &Globals.getwd_cache, NULL, NULL, FLAG_ADVANCED},
{"keepalive", P_INTEGER, P_GLOBAL, &Globals.iKeepalive, NULL, NULL, FLAG_ADVANCED},
{"change notify", P_BOOL, P_LOCAL, &sDefault.bChangeNotify, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE },
{"directory name cache size", P_INTEGER, P_LOCAL, &sDefault.iDirectoryNameCacheSize, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE },
@ -1535,6 +1534,7 @@ static void init_globals(bool first_time_only)
Globals.pwordlevel = 0;
Globals.unamelevel = 0;
Globals.deadtime = 0;
Globals.getwd_cache = true;
Globals.bLargeReadwrite = True;
Globals.max_log_size = 5000;
Globals.max_open_files = MAX_OPEN_FILES;
@ -2023,6 +2023,7 @@ FN_GLOBAL_INTEGER(lp_maxmux, &Globals.max_mux)
FN_GLOBAL_INTEGER(lp_passwordlevel, &Globals.pwordlevel)
FN_GLOBAL_INTEGER(lp_usernamelevel, &Globals.unamelevel)
FN_GLOBAL_INTEGER(lp_deadtime, &Globals.deadtime)
FN_GLOBAL_BOOL(lp_getwd_cache, &Globals.getwd_cache)
FN_GLOBAL_INTEGER(lp_maxprotocol, &Globals.maxprotocol)
FN_GLOBAL_INTEGER(lp_minprotocol, &Globals.minprotocol)
FN_GLOBAL_INTEGER(lp_security, &Globals.security)

View File

@ -93,15 +93,6 @@ static unsigned char char_flags[256];
*/
static unsigned mangle_prefix;
/* we will use a very simple direct mapped prefix cache. The big
advantage of this cache structure is speed and low memory usage
The cache is indexed by the low-order bits of the hash, and confirmed by
hashing the resulting cache entry to match the known hash
*/
static char **prefix_cache;
static unsigned int *prefix_cache_hashes;
/* these are the characters we use in the 8.3 hash. Must be 36 chars long */
static const char *basechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static unsigned char base_reverse[256];
@ -147,57 +138,39 @@ static unsigned int mangle_hash(const char *key, unsigned int length)
return value & ~0x80000000;
}
/*
initialise (ie. allocate) the prefix cache
*/
static bool cache_init(void)
{
if (prefix_cache) {
return True;
}
prefix_cache = SMB_CALLOC_ARRAY(char *,MANGLE_CACHE_SIZE);
if (!prefix_cache) {
return False;
}
prefix_cache_hashes = SMB_CALLOC_ARRAY(unsigned int, MANGLE_CACHE_SIZE);
if (!prefix_cache_hashes) {
SAFE_FREE(prefix_cache);
return False;
}
return True;
}
/*
insert an entry into the prefix cache. The string might not be null
terminated */
static void cache_insert(const char *prefix, int length, unsigned int hash)
{
int i = hash % MANGLE_CACHE_SIZE;
char *str = SMB_STRNDUP(prefix, length);
if (prefix_cache[i]) {
free(prefix_cache[i]);
if (str == NULL) {
return;
}
prefix_cache[i] = SMB_STRNDUP(prefix, length);
prefix_cache_hashes[i] = hash;
memcache_add(smbd_memcache(), MANGLE_HASH2_CACHE,
data_blob_const(&hash, sizeof(hash)),
data_blob_const(str, length+1));
SAFE_FREE(str);
}
/*
lookup an entry in the prefix cache. Return NULL if not found.
*/
static const char *cache_lookup(unsigned int hash)
static char *cache_lookup(TALLOC_CTX *mem_ctx, unsigned int hash)
{
int i = hash % MANGLE_CACHE_SIZE;
DATA_BLOB value;
if (!prefix_cache[i] || hash != prefix_cache_hashes[i]) {
if (!memcache_lookup(smbd_memcache(), MANGLE_HASH2_CACHE,
data_blob_const(&hash, sizeof(hash)), &value)) {
return NULL;
}
/* yep, it matched */
return prefix_cache[i];
SMB_ASSERT((value.length > 0)
&& (value.data[value.length-1] == '\0'));
return talloc_strdup(mem_ctx, (char *)value.data);
}
@ -377,7 +350,7 @@ static bool lookup_name_from_8_3(TALLOC_CTX *ctx,
{
unsigned int hash, multiplier;
unsigned int i;
const char *prefix;
char *prefix;
char extension[4];
*pp_out = NULL;
@ -397,7 +370,7 @@ static bool lookup_name_from_8_3(TALLOC_CTX *ctx,
}
/* now look in the prefix cache for that hash */
prefix = cache_lookup(hash);
prefix = cache_lookup(ctx, hash);
if (!prefix) {
M_DEBUG(10,("lookup_name_from_8_3: %s -> %08X -> not found\n",
name, hash));
@ -421,6 +394,8 @@ static bool lookup_name_from_8_3(TALLOC_CTX *ctx,
*pp_out = talloc_strdup(ctx, prefix);
}
TALLOC_FREE(prefix);
if (!pp_out) {
M_DEBUG(0,("talloc_fail"));
return False;
@ -728,10 +703,6 @@ struct mangle_fns *mangle_hash2_init(void)
init_tables();
mangle_reset();
if (!cache_init()) {
return NULL;
}
return &mangle_fns;
}

View File

@ -86,6 +86,19 @@ struct messaging_context *smbd_messaging_context(void)
return ctx;
}
struct memcache *smbd_memcache(void)
{
static struct memcache *cache;
if (!cache
&& !(cache = memcache_init(NULL,
lp_max_stat_cache_size()*1024))) {
smb_panic("Could not init smbd memcache");
}
return cache;
}
/*******************************************************************
What to do when smb.conf is updated.
********************************************************************/

View File

@ -26,8 +26,6 @@
Stat cache code used in unix_convert.
*****************************************************************************/
static TDB_CONTEXT *tdb_stat_cache;
/**
* Add an entry into the stat cache.
*
@ -45,10 +43,8 @@ void stat_cache_add( const char *full_orig_name,
bool case_sensitive)
{
size_t translated_path_length;
TDB_DATA data_val;
char *original_path;
size_t original_path_length;
size_t sc_size = lp_max_stat_cache_size();
char saved_char;
TALLOC_CTX *ctx = talloc_tos();
@ -56,12 +52,6 @@ void stat_cache_add( const char *full_orig_name,
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 ..
*/
@ -132,24 +122,20 @@ void stat_cache_add( const char *full_orig_name,
saved_char = translated_path[translated_path_length];
translated_path[translated_path_length] = '\0';
data_val.dsize = translated_path_length + 1;
data_val.dptr = (uint8 *)translated_path;
/*
* New entry or replace old entry.
*/
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));
}
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));
translated_path[translated_path_length] = saved_char;
TALLOC_FREE(original_path);
@ -186,7 +172,7 @@ bool stat_cache_lookup(connection_struct *conn,
unsigned int num_components = 0;
char *translated_path;
size_t translated_path_length;
TDB_DATA data_val;
DATA_BLOB data_val;
char *name;
TALLOC_CTX *ctx = talloc_tos();
@ -236,9 +222,12 @@ bool stat_cache_lookup(connection_struct *conn,
while (1) {
char *sp;
data_val = tdb_fetch_bystring(tdb_stat_cache, chk_name);
data_val = data_blob_null;
if (data_val.dptr != NULL && data_val.dsize != 0) {
if (memcache_lookup(
smbd_memcache(), STAT_CACHE,
data_blob_const(chk_name, strlen(chk_name)),
&data_val)) {
break;
}
@ -275,12 +264,11 @@ bool stat_cache_lookup(connection_struct *conn,
}
}
translated_path = talloc_strdup(ctx,(char *)data_val.dptr);
translated_path = talloc_strdup(ctx,(char *)data_val.data);
if (!translated_path) {
smb_panic("talloc failed");
}
translated_path_length = data_val.dsize - 1;
SAFE_FREE(data_val.dptr);
translated_path_length = data_val.length - 1;
DEBUG(10,("stat_cache_lookup: lookup succeeded for name [%s] "
"-> [%s]\n", chk_name, translated_path ));
@ -288,7 +276,8 @@ bool stat_cache_lookup(connection_struct *conn,
if (SMB_VFS_STAT(conn, translated_path, pst) != 0) {
/* Discard this entry - it doesn't exist in the filesystem. */
tdb_delete_bystring(tdb_stat_cache, chk_name);
memcache_delete(smbd_memcache(), STAT_CACHE,
data_blob_const(chk_name, strlen(chk_name)));
TALLOC_FREE(chk_name);
TALLOC_FREE(translated_path);
return False;
@ -366,7 +355,8 @@ void stat_cache_delete(const char *name)
DEBUG(10,("stat_cache_delete: deleting name [%s] -> %s\n",
lname, name ));
tdb_delete_bystring(tdb_stat_cache, lname);
memcache_delete(smbd_memcache(), STAT_CACHE,
data_blob_const(lname, talloc_get_size(lname)-1));
TALLOC_FREE(lname);
}
@ -395,15 +385,7 @@ bool reset_stat_cache( void )
if (!lp_stat_cache())
return True;
if (tdb_stat_cache) {
tdb_close(tdb_stat_cache);
}
memcache_flush(smbd_memcache(), STAT_CACHE);
/* 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;
}

View File

@ -731,152 +731,98 @@ int vfs_ChDir(connection_struct *conn, const char *path)
return(res);
}
/* number of list structures for a caching GetWd function. */
#define MAX_GETWDCACHE (50)
static struct {
SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
char *path; /* The pathname. */
bool valid;
} ino_list[MAX_GETWDCACHE];
extern bool use_getwd_cache;
/****************************************************************************
Prompte a ptr (to make it recently used)
****************************************************************************/
static void array_promote(char *array,int elsize,int element)
{
char *p;
if (element == 0)
return;
p = (char *)SMB_MALLOC(elsize);
if (!p) {
DEBUG(5,("array_promote: malloc fail\n"));
return;
}
memcpy(p,array + element * elsize, elsize);
memmove(array + elsize,array,elsize*element);
memcpy(array,p,elsize);
SAFE_FREE(p);
}
/*******************************************************************
Return the absolute current directory path - given a UNIX pathname.
Note that this path is returned in DOS format, not UNIX
format. Note this can be called with conn == NULL.
********************************************************************/
struct getwd_cache_key {
SMB_DEV_T dev;
SMB_INO_T ino;
};
char *vfs_GetWd(TALLOC_CTX *ctx, connection_struct *conn)
{
char s[PATH_MAX+1];
static bool getwd_cache_init = False;
SMB_STRUCT_STAT st, st2;
int i;
char *ret = NULL;
char *result;
DATA_BLOB cache_value;
struct getwd_cache_key key;
*s = 0;
if (!use_getwd_cache) {
nocache:
ret = SMB_VFS_GETWD(conn,s);
if (!ret) {
DEBUG(0,("vfs_GetWd: SMB_VFS_GETWD call failed, "
"errno %s\n",strerror(errno)));
return NULL;
}
return talloc_strdup(ctx, ret);
}
/* init the cache */
if (!getwd_cache_init) {
getwd_cache_init = True;
for (i=0;i<MAX_GETWDCACHE;i++) {
string_set(&ino_list[i].path,"");
ino_list[i].valid = False;
}
}
/* Get the inode of the current directory, if this doesn't work we're
in trouble :-) */
if (SMB_VFS_STAT(conn, ".",&st) == -1) {
/* Known to fail for root: the directory may be
* NFS-mounted and exported with root_squash (so has no root access). */
DEBUG(1,("vfs_GetWd: couldn't stat \".\" error %s "
"(NFS problem ?)\n",
strerror(errno) ));
if (!lp_getwd_cache()) {
goto nocache;
}
SET_STAT_INVALID(st);
for (i=0; i<MAX_GETWDCACHE; i++) {
if (ino_list[i].valid) {
/* If we have found an entry with a matching inode and dev number
then find the inode number for the directory in the cached string.
If this agrees with that returned by the stat for the current
directory then all is o.k. (but make sure it is a directory all
the same...) */
if (st.st_ino == ino_list[i].inode && st.st_dev == ino_list[i].dev) {
if (SMB_VFS_STAT(conn,ino_list[i].path,&st2) == 0) {
if (st.st_ino == st2.st_ino && st.st_dev == st2.st_dev &&
(st2.st_mode & S_IFMT) == S_IFDIR) {
ret = talloc_strdup(ctx,
ino_list[i].path);
/* promote it for future use */
array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
if (ret == NULL) {
errno = ENOMEM;
}
return ret;
} else {
/* If the inode is different then something's changed,
scrub the entry and start from scratch. */
ino_list[i].valid = False;
}
}
}
}
if (SMB_VFS_STAT(conn, ".",&st) == -1) {
/*
* Known to fail for root: the directory may be NFS-mounted
* and exported with root_squash (so has no root access).
*/
DEBUG(1,("vfs_GetWd: couldn't stat \".\" error %s "
"(NFS problem ?)\n", strerror(errno) ));
goto nocache;
}
/* We don't have the information to hand so rely on traditional
* methods. The very slow getcwd, which spawns a process on some
* systems, or the not quite so bad getwd. */
ZERO_STRUCT(key); /* unlikely, but possible padding */
key.dev = st.st_dev;
key.ino = st.st_ino;
if (!memcache_lookup(smbd_memcache(), GETWD_CACHE,
data_blob_const(&key, sizeof(key)),
&cache_value)) {
goto nocache;
}
SMB_ASSERT((cache_value.length > 0)
&& (cache_value.data[cache_value.length-1] == '\0'));
if ((SMB_VFS_STAT(conn, (char *)cache_value.data, &st2) == 0)
&& (st.st_dev == st2.st_dev) && (st.st_ino == st2.st_ino)
&& (S_ISDIR(st.st_mode))) {
/*
* Ok, we're done
*/
result = talloc_strdup(ctx, (char *)cache_value.data);
if (result == NULL) {
errno = ENOMEM;
}
return result;
}
nocache:
/*
* We don't have the information to hand so rely on traditional
* methods. The very slow getcwd, which spawns a process on some
* systems, or the not quite so bad getwd.
*/
if (!SMB_VFS_GETWD(conn,s)) {
DEBUG(0,("vfs_GetWd: SMB_VFS_GETWD call failed, errno %s\n",
strerror(errno)));
return (NULL);
DEBUG(0, ("vfs_GetWd: SMB_VFS_GETWD call failed: %s\n",
strerror(errno)));
return NULL;
}
ret = talloc_strdup(ctx,s);
if (lp_getwd_cache() && VALID_STAT(st)) {
ZERO_STRUCT(key); /* unlikely, but possible padding */
key.dev = st.st_dev;
key.ino = st.st_ino;
DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",
s,(double)st.st_ino,(double)st.st_dev));
memcache_add(smbd_memcache(), GETWD_CACHE,
data_blob_const(&key, sizeof(key)),
data_blob_const(s, strlen(s)+1));
}
/* add it to the cache */
i = MAX_GETWDCACHE - 1;
string_set(&ino_list[i].path,s);
ino_list[i].dev = st.st_dev;
ino_list[i].inode = st.st_ino;
ino_list[i].valid = True;
/* put it at the top of the list */
array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
if (ret == NULL) {
result = talloc_strdup(ctx, s);
if (result == NULL) {
errno = ENOMEM;
}
return ret;
return result;
}
/*******************************************************************

View File

@ -5042,6 +5042,81 @@ static bool run_local_rbtree(int dummy)
return ret;
}
static bool data_blob_equal(DATA_BLOB a, DATA_BLOB b)
{
if (a.length != b.length) {
printf("a.length=%d != b.length=%d\n",
(int)a.length, (int)b.length);
return false;
}
if (memcmp(a.data, b.data, a.length) != 0) {
printf("a.data and b.data differ\n");
return false;
}
return true;
}
static bool run_local_memcache(int dummy)
{
struct memcache *cache;
DATA_BLOB k1, k2;
DATA_BLOB d1, d2, d3;
DATA_BLOB v1, v2, v3;
cache = memcache_init(NULL, 100);
if (cache == NULL) {
printf("memcache_init failed\n");
return false;
}
d1 = data_blob_const("d1", 2);
d2 = data_blob_const("d2", 2);
d3 = data_blob_const("d3", 2);
k1 = data_blob_const("d1", 2);
k2 = data_blob_const("d2", 2);
memcache_add(cache, STAT_CACHE, k1, d1);
memcache_add(cache, GETWD_CACHE, k2, d2);
if (!memcache_lookup(cache, STAT_CACHE, k1, &v1)) {
printf("could not find k1\n");
return false;
}
if (!data_blob_equal(d1, v1)) {
return false;
}
if (!memcache_lookup(cache, GETWD_CACHE, k2, &v2)) {
printf("could not find k2\n");
return false;
}
if (!data_blob_equal(d2, v2)) {
return false;
}
memcache_add(cache, STAT_CACHE, k1, d3);
if (!memcache_lookup(cache, STAT_CACHE, k1, &v3)) {
printf("could not find replaced k1\n");
return false;
}
if (!data_blob_equal(d3, v3)) {
return false;
}
memcache_add(cache, GETWD_CACHE, k1, d1);
if (memcache_lookup(cache, GETWD_CACHE, k2, &v2)) {
printf("Did find k2, should have been purged\n");
return false;
}
TALLOC_FREE(cache);
return true;
}
static double create_procs(bool (*fn)(int), bool *result)
{
int i, status;
@ -5196,6 +5271,7 @@ static struct {
{ "LOCAL-SUBSTITUTE", run_local_substitute, 0},
{ "LOCAL-GENCACHE", run_local_gencache, 0},
{ "LOCAL-RBTREE", run_local_rbtree, 0},
{ "LOCAL-MEMCACHE", run_local_memcache, 0},
{NULL, NULL, 0}};

View File

@ -495,6 +495,19 @@ struct messaging_context *smbd_messaging_context(void)
return ctx;
}
struct memcache *smbd_memcache(void)
{
static struct memcache *cache;
if (!cache
&& !(cache = memcache_init(NULL,
lp_max_stat_cache_size()*1024))) {
smb_panic("Could not init smbd memcache");
}
return cache;
}
/* Main function */
int main(int argc, char *argv[])