summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/Makefile.in2
-rw-r--r--source3/include/includes.h1
-rw-r--r--source3/lib/cache.c298
-rw-r--r--source3/param/loadparm.c7
-rw-r--r--source3/smbd/mangle_hash2.c67
-rw-r--r--source3/smbd/server.c13
-rw-r--r--source3/smbd/statcache.c64
-rw-r--r--source3/smbd/vfs.c168
-rw-r--r--source3/torture/torture.c76
-rw-r--r--source3/torture/vfstest.c13
10 files changed, 505 insertions, 204 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in
index eda3297d23..81c8330216 100644
--- a/source3/Makefile.in
+++ b/source3/Makefile.in
@@ -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 \
diff --git a/source3/include/includes.h b/source3/include/includes.h
index 22451741a1..a45176aba3 100644
--- a/source3/include/includes.h
+++ b/source3/include/includes.h
@@ -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 {
diff --git a/source3/lib/cache.c b/source3/lib/cache.c
new file mode 100644
index 0000000000..baf2fe3984
--- /dev/null
+++ b/source3/lib/cache.c
@@ -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;
+ }
+}
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index 881bcece7c..eea3ecec2b 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -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)
diff --git a/source3/smbd/mangle_hash2.c b/source3/smbd/mangle_hash2.c
index 7066c2a4e5..9643506aea 100644
--- a/source3/smbd/mangle_hash2.c
+++ b/source3/smbd/mangle_hash2.c
@@ -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;
}
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 41d036a8b9..574197d711 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -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.
********************************************************************/
diff --git a/source3/smbd/statcache.c b/source3/smbd/statcache.c
index 8f1e008985..72fed008a2 100644
--- a/source3/smbd/statcache.c
+++ b/source3/smbd/statcache.c
@@ -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;
}
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index 628d2eec4b..45d0788117 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -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;
- }
+ if (!lp_getwd_cache()) {
+ goto nocache;
}
- /* Get the inode of the current directory, if this doesn't work we're
- in trouble :-) */
+ SET_STAT_INVALID(st);
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). */
+ /*
+ * 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) ));
+ "(NFS problem ?)\n", strerror(errno) ));
goto nocache;
}
+ ZERO_STRUCT(key); /* unlikely, but possible padding */
+ key.dev = st.st_dev;
+ key.ino = st.st_ino;
- 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) {
+ if (!memcache_lookup(smbd_memcache(), GETWD_CACHE,
+ data_blob_const(&key, sizeof(key)),
+ &cache_value)) {
+ goto nocache;
+ }
- ret = talloc_strdup(ctx,
- ino_list[i].path);
+ SMB_ASSERT((cache_value.length > 0)
+ && (cache_value.data[cache_value.length-1] == '\0'));
- /* 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, (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;
}
- /* 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. */
+ 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);
-
- DEBUG(5,("vfs_GetWd %s, inode %.0f, dev %.0f\n",
- s,(double)st.st_ino,(double)st.st_dev));
-
- /* 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;
+ if (lp_getwd_cache() && VALID_STAT(st)) {
+ ZERO_STRUCT(key); /* unlikely, but possible padding */
+ key.dev = st.st_dev;
+ key.ino = st.st_ino;
- /* put it at the top of the list */
- array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
+ memcache_add(smbd_memcache(), GETWD_CACHE,
+ data_blob_const(&key, sizeof(key)),
+ data_blob_const(s, strlen(s)+1));
+ }
- if (ret == NULL) {
+ result = talloc_strdup(ctx, s);
+ if (result == NULL) {
errno = ENOMEM;
}
- return ret;
+ return result;
}
/*******************************************************************
diff --git a/source3/torture/torture.c b/source3/torture/torture.c
index ad57470c61..082949e0af 100644
--- a/source3/torture/torture.c
+++ b/source3/torture/torture.c
@@ -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}};
diff --git a/source3/torture/vfstest.c b/source3/torture/vfstest.c
index 1436ecc022..7e4ee624a1 100644
--- a/source3/torture/vfstest.c
+++ b/source3/torture/vfstest.c
@@ -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[])