#include "includes.h"
#include "system/filesys.h"
#include "system/glob.h"
+#include "util_tdb.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_TDB
{
char* cache_fname = NULL;
int open_flags = O_RDWR|O_CREAT;
- bool first_try = true;
/* skip file open if it's already opened */
if (cache) return True;
- cache_fname = lock_path("gencache.tdb");
+ cache_fname = cache_path("gencache.tdb");
DEBUG(5, ("Opening cache file at %s\n", cache_fname));
-again:
cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT|TDB_INCOMPATIBLE_HASH, open_flags, 0644);
if (cache) {
int ret;
ret = tdb_check(cache, NULL, NULL);
if (ret != 0) {
tdb_close(cache);
- cache = NULL;
- if (!first_try) {
- DEBUG(0, ("gencache_init: tdb_check(%s) failed\n",
- cache_fname));
- return false;
- }
- first_try = false;
- DEBUG(0, ("gencache_init: tdb_check(%s) failed - retry after CLEAR_IF_FIRST\n",
- cache_fname));
- cache = tdb_open_log(cache_fname, 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, open_flags, 0644);
- if (cache) {
- tdb_close(cache);
- cache = NULL;
- goto again;
- }
+
+ /*
+ * Retry with CLEAR_IF_FIRST.
+ *
+ * Warning: Converting this to dbwrap won't work
+ * directly. gencache.c does transactions on this tdb,
+ * and dbwrap forbids this for CLEAR_IF_FIRST
+ * databases. tdb does allow transactions on
+ * CLEAR_IF_FIRST databases, so lets use it here to
+ * clean up a broken database.
+ */
+ cache = tdb_open_log(cache_fname, 0,
+ TDB_DEFAULT|
+ TDB_INCOMPATIBLE_HASH|
+ TDB_CLEAR_IF_FIRST,
+ open_flags, 0644);
}
}
DEBUG(5, ("Opening cache file at %s\n", cache_fname));
- cache_notrans = tdb_open_log(cache_fname, 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
+ cache_notrans = tdb_open_log(cache_fname, 0,
+ TDB_CLEAR_IF_FIRST|
+ TDB_INCOMPATIBLE_HASH|
+ TDB_NOSYNC,
open_flags, 0644);
if (cache_notrans == NULL) {
DEBUG(5, ("Opening %s failed: %s\n", cache_fname,
static TDB_DATA last_stabilize_key(void)
{
TDB_DATA result;
- result.dptr = (uint8_t *)"@LAST_STABILIZED";
+ result.dptr = discard_const_p(uint8_t, "@LAST_STABILIZED");
result.dsize = 17;
return result;
}
+struct gencache_have_val_state {
+ time_t new_timeout;
+ const DATA_BLOB *data;
+ bool gotit;
+};
+
+static void gencache_have_val_parser(time_t old_timeout, DATA_BLOB data,
+ void *private_data)
+{
+ struct gencache_have_val_state *state =
+ (struct gencache_have_val_state *)private_data;
+ time_t now = time(NULL);
+ int cache_time_left, new_time_left, additional_time;
+
+ /*
+ * Excuse the many variables, but these time calculations are
+ * confusing to me. We do not want to write to gencache with a
+ * possibly expensive transaction if we are about to write the same
+ * value, just extending the remaining timeout by less than 10%.
+ */
+
+ cache_time_left = old_timeout - now;
+ if (cache_time_left <= 0) {
+ /*
+ * timed out, write new value
+ */
+ return;
+ }
+
+ new_time_left = state->new_timeout - now;
+ if (new_time_left <= 0) {
+ /*
+ * Huh -- no new timeout?? Write it.
+ */
+ return;
+ }
+
+ if (new_time_left < cache_time_left) {
+ /*
+ * Someone wants to shorten the timeout. Let it happen.
+ */
+ return;
+ }
+
+ /*
+ * By how much does the new timeout extend the remaining cache time?
+ */
+ additional_time = new_time_left - cache_time_left;
+
+ if (additional_time * 10 < 0) {
+ /*
+ * Integer overflow. We extend by so much that we have to write it.
+ */
+ return;
+ }
+
+ /*
+ * The comparison below is essentially equivalent to
+ *
+ * new_time_left > cache_time_left * 1.10
+ *
+ * but without floating point calculations.
+ */
+
+ if (additional_time * 10 > cache_time_left) {
+ /*
+ * We extend the cache timeout by more than 10%. Do it.
+ */
+ return;
+ }
+
+ /*
+ * Now the more expensive data compare.
+ */
+ if (data_blob_cmp(state->data, &data) != 0) {
+ /*
+ * Write a new value. Certainly do it.
+ */
+ return;
+ }
+
+ /*
+ * Extending the timeout by less than 10% for the same cache value is
+ * not worth the trouble writing a value into gencache under a
+ * possibly expensive transaction.
+ */
+ state->gotit = true;
+}
+
+static bool gencache_have_val(const char *keystr, const DATA_BLOB *data,
+ time_t timeout)
+{
+ struct gencache_have_val_state state;
+
+ state.new_timeout = timeout;
+ state.data = data;
+ state.gotit = false;
+
+ if (!gencache_parse(keystr, gencache_have_val_parser, &state)) {
+ return false;
+ }
+ return state.gotit;
+}
+
/**
* Set an entry in the cache file. If there's no such
* one, then add it.
if (!gencache_init()) return False;
+ if (gencache_have_val(keystr, blob, timeout)) {
+ DEBUG(10, ("Did not store value for %s, we already got it\n",
+ keystr));
+ return true;
+ }
+
val = talloc_asprintf(talloc_tos(), CACHE_DATA_FMT, (int)timeout);
if (val == NULL) {
return False;
return false;
}
- DEBUG(10, ("Adding cache entry with key = %s and timeout ="
- " %s (%d seconds %s)\n", keystr, ctime(&timeout),
+ DEBUG(10, ("Adding cache entry with key=[%s] and timeout="
+ "[%s] (%d seconds %s)\n", keystr,
+ timestring(talloc_tos(), timeout),
(int)(timeout - time(NULL)),
timeout > time(NULL) ? "ahead" : "in the past"));
*/
last_stabilize = 0;
- databuf = tdb_fetch(cache_notrans, last_stabilize_key());
+ databuf = tdb_fetch_compat(cache_notrans, last_stabilize_key());
if ((databuf.dptr != NULL)
&& (databuf.dptr[databuf.dsize-1] == '\0')) {
last_stabilize = atoi((char *)databuf.dptr);
if (!gencache_init()) return False;
- DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr));
+ DEBUG(10, ("Deleting cache entry (key=[%s])\n", keystr));
/*
* We delete an element by setting its timeout to 0. This way we don't
* element.
*/
- exists = gencache_get_data_blob(keystr, &value, NULL, &was_expired);
+ exists = gencache_get_data_blob(keystr, NULL, &value, NULL,
+ &was_expired);
if (!exists && was_expired) {
/*
state.private_data = private_data;
ret = tdb_parse_record(cache_notrans, key, gencache_parse_fn, &state);
- if (ret != -1) {
+ if (ret == 0) {
return true;
}
ret = tdb_parse_record(cache, key, gencache_parse_fn, &state);
- return (ret != -1);
+ return (ret == 0);
}
struct gencache_get_data_blob_state {
+ TALLOC_CTX *mem_ctx;
DATA_BLOB *blob;
time_t timeout;
bool result;
return;
}
- *state->blob = data_blob(blob.data, blob.length);
+ *state->blob = data_blob_talloc(state->mem_ctx, blob.data,
+ blob.length);
if (state->blob->data == NULL) {
state->result = false;
return;
* @retval False for failure
**/
-bool gencache_get_data_blob(const char *keystr, DATA_BLOB *blob,
+bool gencache_get_data_blob(const char *keystr, TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob,
time_t *timeout, bool *was_expired)
{
struct gencache_get_data_blob_state state;
bool expired = false;
state.result = false;
+ state.mem_ctx = mem_ctx;
state.blob = blob;
if (!gencache_parse(keystr, gencache_get_data_blob_parser, &state)) {
}
res = tdb_transaction_start_nonblock(cache);
- if (res == -1) {
-
- if (tdb_error(cache) == TDB_ERR_NOLOCK) {
+ if (res != 0) {
+ if (tdb_error(cache) == TDB_ERR_NOLOCK)
+ {
/*
* Someone else already does the stabilize,
* this does not have to be done twice
}
DEBUG(10, ("Could not start transaction on gencache.tdb: "
- "%s\n", tdb_errorstr(cache)));
+ "%s\n", tdb_errorstr_compat(cache)));
return false;
}
res = tdb_transaction_start(cache_notrans);
- if (res == -1) {
+ if (res != 0) {
tdb_transaction_cancel(cache);
DEBUG(10, ("Could not start transaction on "
"gencache_notrans.tdb: %s\n",
- tdb_errorstr(cache_notrans)));
+ tdb_errorstr_compat(cache_notrans)));
return false;
}
state.written = false;
res = tdb_traverse(cache_notrans, stabilize_fn, &state);
- if ((res == -1) || state.error) {
- if ((tdb_transaction_cancel(cache_notrans) == -1)
- || (tdb_transaction_cancel(cache) == -1)) {
- smb_panic("tdb_transaction_cancel failed\n");
- }
+ if ((res < 0) || state.error) {
+ tdb_transaction_cancel(cache_notrans);
+ tdb_transaction_cancel(cache);
return false;
}
if (!state.written) {
- if ((tdb_transaction_cancel(cache_notrans) == -1)
- || (tdb_transaction_cancel(cache) == -1)) {
- smb_panic("tdb_transaction_cancel failed\n");
- }
+ tdb_transaction_cancel(cache_notrans);
+ tdb_transaction_cancel(cache);
return true;
}
res = tdb_transaction_commit(cache);
- if (res == -1) {
+ if (res != 0) {
DEBUG(10, ("tdb_transaction_commit on gencache.tdb failed: "
- "%s\n", tdb_errorstr(cache)));
- if (tdb_transaction_cancel(cache_notrans) == -1) {
- smb_panic("tdb_transaction_cancel failed\n");
- }
+ "%s\n", tdb_errorstr_compat(cache)));
+ tdb_transaction_cancel(cache_notrans);
return false;
}
res = tdb_transaction_commit(cache_notrans);
- if (res == -1) {
+ if (res != 0) {
DEBUG(10, ("tdb_transaction_commit on gencache.tdb failed: "
- "%s\n", tdb_errorstr(cache)));
+ "%s\n", tdb_errorstr_compat(cache)));
return false;
}
}
if ((timeout < time(NULL)) || (val.dsize == 0)) {
res = tdb_delete(cache, key);
- if ((res == -1) && (tdb_error(cache) == TDB_ERR_NOEXIST)) {
+ if ((res != 0) && (tdb_error(cache) == TDB_ERR_NOEXIST)) {
res = 0;
} else {
state->written = true;
}
}
- if (res == -1) {
+ if (res != 0) {
DEBUG(10, ("Transfer to gencache.tdb failed: %s\n",
- tdb_errorstr(cache)));
+ tdb_errorstr_compat(cache)));
state->error = true;
return -1;
}
- if (tdb_delete(cache_notrans, key) == -1) {
+ if (tdb_delete(cache_notrans, key) != 0) {
DEBUG(10, ("tdb_delete from gencache_notrans.tdb failed: "
- "%s\n", tdb_errorstr(cache_notrans)));
+ "%s\n", tdb_errorstr_compat(cache_notrans)));
state->error = true;
return -1;
}
DATA_BLOB blob;
bool ret = False;
- ret = gencache_get_data_blob(keystr, &blob, ptimeout, NULL);
+ ret = gencache_get_data_blob(keystr, NULL, &blob, ptimeout, NULL);
if (!ret) {
return false;
}
goto done;
}
- DEBUG(10, ("Calling function with arguments (key=%s, timeout=%s)\n",
- keystr, ctime(&timeout)));
+ DEBUG(10, ("Calling function with arguments "
+ "(key=[%s], timeout=[%s])\n",
+ keystr, timestring(talloc_tos(), timeout)));
state->fn(keystr,
data_blob_const(endptr,
}
DEBUG(10, ("Calling function with arguments "
- "(key = %s, value = %s, timeout = %s)\n",
- key, valstr, ctime(&timeout)));
+ "(key=[%s], value=[%s], timeout=[%s])\n",
+ key, valstr, timestring(talloc_tos(), timeout)));
state->fn(key, valstr, timeout, state->private_data);