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
+ 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,
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 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#define TIMEOUT_LEN 12
#define CACHE_DATA_FMT "%12u/%s"
+#define READ_CACHE_DATA_FMT_TEMPLATE "%%12u/%%%us"
static TDB_CONTEXT *cache;
+static BOOL cache_readonly;
/**
* @file gencache.c
/* skip file open if it's already opened */
if (cache) return True;
- asprintf(&cache_fname, "%s/%s", lp_lockdir(), "gencache.tdb");
- if (cache_fname)
- DEBUG(5, ("Opening cache file at %s\n", cache_fname));
- else {
- DEBUG(0, ("Filename allocation failed.\n"));
- return False;
- }
+ cache_fname = lock_path("gencache.tdb");
+
+ DEBUG(5, ("Opening cache file at %s\n", cache_fname));
cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT,
O_RDWR|O_CREAT, 0644);
- SAFE_FREE(cache_fname);
+ if (!cache && (errno == EACCES)) {
+ cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT, O_RDONLY, 0644);
+ if (cache) {
+ cache_readonly = True;
+ DEBUG(5, ("gencache_init: Opening cache file %s read-only.\n", cache_fname));
+ }
+ }
+
if (!cache) {
- DEBUG(0, ("Attempt to open the cache file has failed.\n"));
+ DEBUG(5, ("Attempt to open gencache.tdb has failed.\n"));
return False;
}
return True;
BOOL gencache_shutdown(void)
{
- /* tdb_close routine returns 0 on successful close */
+ int ret;
+ /* tdb_close routine returns -1 on error */
if (!cache) return False;
DEBUG(5, ("Closing cache file\n"));
- return tdb_close(cache) ? False : True;
+ ret = tdb_close(cache);
+ cache = NULL;
+ cache_readonly = False;
+ return ret != -1;
}
* Set an entry in the cache file. If there's no such
* one, then add it.
*
- * @param key string that represents a key of this entry
+ * @param keystr string that represents a key of this entry
* @param value text representation value being cached
* @param timeout time when the value is expired
*
- * @return true when entry is successfuly stored or
- * false on the attempt's failure
+ * @retval true when entry is successfuly stored
+ * @retval false on failure
**/
BOOL gencache_set(const char *keystr, const char *value, time_t timeout)
{
int ret;
- TDB_DATA keybuf, databuf;
+ TDB_DATA databuf;
char* valstr = NULL;
/* fail completely if get null pointers passed */
if (!gencache_init()) return False;
- asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value);
- keybuf.dptr = strdup(keystr);
- keybuf.dsize = strlen(keystr);
- databuf.dptr = strdup(valstr);
- databuf.dsize = strlen(valstr);
- DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout \
- = %s (%d seconds %s)\n", keybuf.dptr, value, ctime(&timeout),
- (int)(timeout - time(NULL)), timeout > time(NULL) ? "ahead" : "in the past"));
-
- ret = tdb_store(cache, keybuf, databuf, 0);
- SAFE_FREE(valstr);
- SAFE_FREE(keybuf.dptr);
- SAFE_FREE(databuf.dptr);
-
- return ret == 0 ? True : False;
-}
-
-
-/**
- * Set existing entry to the cache file.
- *
- * @param key string that represents a key of this entry
- * @param value text representation value being cached
- * @param timeout time when the value is expired
- *
- * @return true when entry is successfuly set or
- * false on the attempt's failure
- **/
-
-BOOL gencache_set_only(const char *keystr, const char *valstr, time_t timeout)
-{
- int ret = -1;
- TDB_DATA keybuf, databuf;
- char *old_valstr, *datastr;
- time_t old_timeout;
-
- /* fail completely if get null pointers passed */
- SMB_ASSERT(keystr && valstr);
+ if (cache_readonly) {
+ return False;
+ }
- if (!gencache_init()) return False;
-
- /*
- * Check whether entry exists in the cache
- * Don't verify gencache_get exit code, since the entry may be expired
- */
- gencache_get(keystr, &old_valstr, &old_timeout);
-
- if (!(old_valstr && old_timeout)) return False;
-
- DEBUG(10, ("Setting cache entry with key = %s; old value = %s and old timeout \
- = %s\n", keystr, old_valstr, ctime(&old_timeout)));
-
- asprintf(&datastr, CACHE_DATA_FMT, (int)timeout, valstr);
- keybuf.dptr = strdup(keystr);
- keybuf.dsize = strlen(keystr);
- databuf.dptr = strdup(datastr);
- databuf.dsize = strlen(datastr);
- DEBUGADD(10, ("New value = %s, new timeout = %s (%d seconds %s)", valstr,
- ctime(&timeout), (int)(timeout - time(NULL)),
- timeout > time(NULL) ? "ahead" : "in the past"));
+ asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value);
+ if (!valstr)
+ return False;
-
- ret = tdb_store(cache, keybuf, databuf, TDB_REPLACE);
+ databuf = string_term_tdb_data(valstr);
+ DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout ="
+ " %s (%d seconds %s)\n", keystr, value,ctime(&timeout),
+ (int)(timeout - time(NULL)),
+ timeout > time(NULL) ? "ahead" : "in the past"));
- SAFE_FREE(datastr);
- SAFE_FREE(old_valstr);
- SAFE_FREE(keybuf.dptr);
- SAFE_FREE(databuf.dptr);
+ ret = tdb_store_bystring(cache, keystr, databuf, 0);
+ SAFE_FREE(valstr);
- return ret == 0 ? True : False;
+ return ret == 0;
}
-
/**
* Delete one entry from the cache file.
*
- * @param key string that represents a key of this entry
+ * @param keystr string that represents a key of this entry
*
- * @return true upon successful deletion or
- * false in case of failure
+ * @retval true upon successful deletion
+ * @retval false in case of failure
**/
BOOL gencache_del(const char *keystr)
{
int ret;
- TDB_DATA keybuf;
/* fail completely if get null pointers passed */
SMB_ASSERT(keystr);
if (!gencache_init()) return False;
- keybuf.dptr = strdup(keystr);
- keybuf.dsize = strlen(keystr);
+ if (cache_readonly) {
+ return False;
+ }
+
DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr));
- ret = tdb_delete(cache, keybuf);
+ ret = tdb_delete_bystring(cache, keystr);
- SAFE_FREE(keybuf.dptr);
- return ret == 0 ? True : False;
+ return ret == 0;
}
/**
* Get existing entry from the cache file.
*
- * @param key string that represents a key of this entry
- * @param value buffer that is allocated and filled with the entry value
+ * @param keystr string that represents a key of this entry
+ * @param valstr buffer that is allocated and filled with the entry value
* buffer's disposing must be done outside
* @param timeout pointer to a time_t that is filled with entry's
* timeout
*
- * @return true when entry is successfuly fetched or
- * false on the failure
+ * @retval true when entry is successfuly fetched
+ * @retval False for failure
**/
BOOL gencache_get(const char *keystr, char **valstr, time_t *timeout)
{
- TDB_DATA keybuf, databuf;
+ TDB_DATA databuf;
+ time_t t;
+ char *endptr;
/* fail completely if get null pointers passed */
SMB_ASSERT(keystr);
- if (!gencache_init())
+ if (!gencache_init()) {
return False;
-
- keybuf.dptr = strdup(keystr);
- keybuf.dsize = strlen(keystr);
- databuf = tdb_fetch(cache, keybuf);
-
- if (databuf.dptr && databuf.dsize > TIMEOUT_LEN) {
- char* entry_buf = strndup(databuf.dptr, databuf.dsize);
- char *v;
- time_t t;
-
- v = (char*)malloc(sizeof(char) *
- (databuf.dsize - TIMEOUT_LEN));
-
- SAFE_FREE(databuf.dptr);
- sscanf(entry_buf, CACHE_DATA_FMT, (int*)&t, v);
- SAFE_FREE(entry_buf);
-
- DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
- "timeout = %s\n", t > time(NULL) ? "valid" :
- "expired", keystr, v, ctime(&t)));
+ }
- if (valstr)
- *valstr = v;
- else
- SAFE_FREE(v);
+ databuf = tdb_fetch_bystring(cache, keystr);
- if (timeout)
- *timeout = t;
+ if (databuf.dptr == NULL) {
+ DEBUG(10, ("Cache entry with key = %s couldn't be found\n",
+ keystr));
+ return False;
+ }
- return t > time(NULL);
+ t = strtol((const char *)databuf.dptr, &endptr, 10);
- } else {
+ if ((endptr == NULL) || (*endptr != '/')) {
+ DEBUG(2, ("Invalid gencache data format: %s\n", databuf.dptr));
SAFE_FREE(databuf.dptr);
+ return False;
+ }
- if (valstr)
- *valstr = NULL;
+ DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
+ "timeout = %s", t > time(NULL) ? "valid" :
+ "expired", keystr, endptr+1, ctime(&t)));
- if (timeout)
- timeout = NULL;
+ if (t <= time(NULL)) {
- DEBUG(10, ("Cache entry with key = %s couldn't be found\n",
- keystr));
+ /* We're expired, delete the entry */
+ tdb_delete_bystring(cache, keystr);
+ SAFE_FREE(databuf.dptr);
return False;
}
-}
+
+ if (valstr) {
+ *valstr = SMB_STRDUP(endptr+1);
+ if (*valstr == NULL) {
+ SAFE_FREE(databuf.dptr);
+ DEBUG(0, ("strdup failed\n"));
+ return False;
+ }
+ }
+
+ SAFE_FREE(databuf.dptr);
+
+ if (timeout) {
+ *timeout = t;
+ }
+
+ return True;
+}
/**
TDB_DATA databuf;
char *keystr = NULL, *valstr = NULL, *entry = NULL;
time_t timeout = 0;
+ int status;
+ unsigned u;
/* fail completely if get null pointers passed */
SMB_ASSERT(fn && keystr_pattern);
first_node = node;
while (node) {
- /* ensure null termination of the key string */
- node->node_key.dptr[node->node_key.dsize] = '\0';
- keystr = node->node_key.dptr;
+ char *fmt;
+ /* ensure null termination of the key string */
+ keystr = SMB_STRNDUP((const char *)node->node_key.dptr, node->node_key.dsize);
+ if (!keystr) {
+ break;
+ }
+
/*
* We don't use gencache_get function, because we need to iterate through
* all of the entries. Validity verification is up to fn routine.
databuf = tdb_fetch(cache, node->node_key);
if (!databuf.dptr || databuf.dsize <= TIMEOUT_LEN) {
SAFE_FREE(databuf.dptr);
+ SAFE_FREE(keystr);
+ node = node->next;
continue;
}
- entry = strndup(databuf.dptr, databuf.dsize);
+ entry = SMB_STRNDUP((const char *)databuf.dptr, databuf.dsize);
+ if (!entry) {
+ SAFE_FREE(databuf.dptr);
+ SAFE_FREE(keystr);
+ break;
+ }
+
SAFE_FREE(databuf.dptr);
- valstr = (char*)malloc(sizeof(char) * (databuf.dsize - TIMEOUT_LEN));
- sscanf(entry, CACHE_DATA_FMT, (int*)(&timeout), valstr);
+
+ valstr = (char *)SMB_MALLOC(databuf.dsize + 1 - TIMEOUT_LEN);
+ if (!valstr) {
+ SAFE_FREE(entry);
+ SAFE_FREE(keystr);
+ break;
+ }
+
+ asprintf(&fmt, READ_CACHE_DATA_FMT_TEMPLATE, (unsigned int)databuf.dsize - TIMEOUT_LEN);
+ if (!fmt) {
+ SAFE_FREE(valstr);
+ SAFE_FREE(entry);
+ SAFE_FREE(keystr);
+ break;
+ }
+ status = sscanf(entry, fmt, &u, valstr);
+ SAFE_FREE(fmt);
+
+ if ( status != 2 ) {
+ DEBUG(0,("gencache_iterate: invalid return from sscanf %d\n",status));
+ }
+ timeout = u;
DEBUG(10, ("Calling function with arguments (key = %s, value = %s, timeout = %s)\n",
keystr, valstr, ctime(&timeout)));
SAFE_FREE(valstr);
SAFE_FREE(entry);
+ SAFE_FREE(keystr);
node = node->next;
}
tdb_search_list_free(first_node);
}
+
+/********************************************************************
+ lock a key
+********************************************************************/
+
+int gencache_lock_entry( const char *key )
+{
+ if (!gencache_init())
+ return -1;
+
+ return tdb_lock_bystring(cache, key);
+}
+
+/********************************************************************
+ unlock a key
+********************************************************************/
+
+void gencache_unlock_entry( const char *key )
+{
+ if (!gencache_init())
+ return;
+
+ tdb_unlock_bystring(cache, key);
+ return;
+}