+ state.result = false;
+ state.blob = blob;
+
+ if (!gencache_parse(keystr, gencache_get_data_blob_parser, &state)) {
+ goto fail;
+ }
+ if (!state.result) {
+ goto fail;
+ }
+ if (state.timeout <= time(NULL)) {
+ /*
+ * We're expired, delete the entry. We can't use gencache_del
+ * here, because that uses gencache_get_data_blob for checking
+ * the existence of a record. We know the thing exists and
+ * directly store an empty value with 0 timeout.
+ */
+ gencache_set(keystr, "", 0);
+ expired = true;
+ goto fail;
+ }
+ if (timeout) {
+ *timeout = state.timeout;
+ }
+
+ return True;
+
+fail:
+ if (was_expired != NULL) {
+ *was_expired = expired;
+ }
+ if (state.result && state.blob) {
+ data_blob_free(state.blob);
+ }
+ return false;
+}
+
+struct stabilize_state {
+ bool written;
+ bool error;
+};
+static int stabilize_fn(struct tdb_context *tdb, TDB_DATA key, TDB_DATA val,
+ void *priv);
+
+/**
+ * Stabilize gencache
+ *
+ * Migrate the clear-if-first gencache data to the stable,
+ * transaction-based gencache.tdb
+ */
+
+bool gencache_stabilize(void)
+{
+ struct stabilize_state state;
+ int res;
+ char *now;
+
+ if (!gencache_init()) {
+ return false;
+ }
+
+ res = tdb_transaction_start_nonblock(cache);
+ if (res == -1) {
+
+ if (tdb_error(cache) == TDB_ERR_NOLOCK) {
+ /*
+ * Someone else already does the stabilize,
+ * this does not have to be done twice
+ */
+ return true;
+ }
+
+ DEBUG(10, ("Could not start transaction on gencache.tdb: "
+ "%s\n", tdb_errorstr(cache)));
+ return false;
+ }
+ res = tdb_transaction_start(cache_notrans);
+ if (res == -1) {
+ tdb_transaction_cancel(cache);
+ DEBUG(10, ("Could not start transaction on "
+ "gencache_notrans.tdb: %s\n",
+ tdb_errorstr(cache_notrans)));
+ return false;
+ }
+
+ state.error = 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");
+ }
+ return false;
+ }
+
+ if (!state.written) {
+ if ((tdb_transaction_cancel(cache_notrans) == -1)
+ || (tdb_transaction_cancel(cache) == -1)) {
+ smb_panic("tdb_transaction_cancel failed\n");
+ }
+ return true;
+ }
+
+ res = tdb_transaction_commit(cache);
+ if (res == -1) {
+ 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");
+ }
+ return false;
+ }
+
+ res = tdb_transaction_commit(cache_notrans);
+ if (res == -1) {
+ DEBUG(10, ("tdb_transaction_commit on gencache.tdb failed: "
+ "%s\n", tdb_errorstr(cache)));
+ return false;
+ }
+
+ now = talloc_asprintf(talloc_tos(), "%d", (int)time(NULL));
+ if (now != NULL) {
+ tdb_store(cache_notrans, last_stabilize_key(),
+ string_term_tdb_data(now), 0);
+ TALLOC_FREE(now);
+ }
+
+ return true;