return LDB_SUCCESS;
}
+/*
+ * Called after modifies and when starting a transaction. Checks target pack
+ * format version and current pack format version, which are set by cache_load,
+ * and repacks if necessary.
+ */
+static int ldb_kv_maybe_repack(struct ldb_kv_private *ldb_kv) {
+ if (ldb_kv->pack_format_version !=
+ ldb_kv->target_pack_format_version) {
+ int r;
+ struct ldb_context *ldb = ldb_module_get_ctx(ldb_kv->module);
+ ldb_kv->pack_format_version =
+ ldb_kv->target_pack_format_version;
+ r = ldb_kv_repack(ldb_kv->module);
+ if (r != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Database repack failed.");
+ }
+ return r;
+ }
+
+ return LDB_SUCCESS;
+}
/*
we've made a modification to a dn - possibly reindex and
return ret;
}
+ /*
+ * If GUID indexing was toggled in this transaction, we repack at
+ * format version 2 if GUID indexing was enabled, or version 1 if
+ * it was disabled.
+ */
+ ret = ldb_kv_maybe_repack(ldb_kv);
+ if (ret != LDB_SUCCESS) {
+ ldb_kv_del_trans(module);
+ ldb_set_errstring(ldb_module_get_ctx(module),
+ "Failure during re-pack, so "
+ "transaction must be aborted.");
+ return ret;
+ }
+
if (ldb_kv->kv_ops->prepare_write(ldb_kv) != 0) {
ret = ldb_kv->kv_ops->error(ldb_kv);
ldb_debug_set(ldb_module_get_ctx(module),
ldb_kv->sequence_number = 0;
- ldb_kv->pack_format_version = LDB_PACKING_FORMAT;
-
ldb_kv->pid = getpid();
ldb_kv->module = ldb_module_new(ldb, ldb, name, &ldb_kv_ops);
unsigned long long sequence_number;
uint32_t pack_format_version;
+ uint32_t target_pack_format_version;
/* the low level tdb seqnum - used to avoid loading BASEINFO when
possible */
uint32_t count;
};
+struct ldb_kv_repack_context {
+ int error;
+ uint32_t count;
+ bool normal_record_seen;
+};
+
/* special record types */
#define LDB_KV_INDEX "@INDEX"
struct ldb_message_element *el,
unsigned int v_idx);
int ldb_kv_reindex(struct ldb_module *module);
+int ldb_kv_repack(struct ldb_module *module);
int ldb_kv_index_transaction_start(
struct ldb_module *module,
size_t cache_size);
const struct ldb_schema_attribute *a;
bool have_write_txn = false;
int r;
- uint32_t pack_format_version;
struct ldb_val key;
ldb = ldb_module_get_ctx(module);
/* Read packing format from first 4 bytes of @BASEINFO record */
r = ldb_kv->kv_ops->fetch_and_parse(ldb_kv, key,
get_pack_format_version,
- &pack_format_version);
-
- if (r != LDB_ERR_NO_SUCH_OBJECT) {
- if (r != LDB_SUCCESS) {
- goto failed_and_unlock;
- }
-
- /* Make sure the database has the right format */
- if (pack_format_version != ldb_kv->pack_format_version) {
- ldb_debug(ldb, LDB_DEBUG_ERROR,
- "Unexpected packing format. "
- "Expected: %#010x, Got: %#010x",
- pack_format_version,
- ldb_kv->pack_format_version);
- goto failed_and_unlock;
- }
- }
-
- /* Now fetch the whole @BASEINFO record */
- r = ldb_kv_search_dn1(module, baseinfo_dn, baseinfo, 0);
- if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) {
- goto failed_and_unlock;
- }
+ &ldb_kv->pack_format_version);
/* possibly initialise the baseinfo */
if (r == LDB_ERR_NO_SUCH_OBJECT) {
have_write_txn = true;
+ /*
+ * We need to write but haven't figured out packing format yet.
+ * Just go with version 1 and we'll repack if we got it wrong.
+ */
+ ldb_kv->pack_format_version = LDB_PACKING_FORMAT;
+ ldb_kv->target_pack_format_version = LDB_PACKING_FORMAT;
+
/* error handling for ltdb_baseinfo_init() is by
looking for the record again. */
ldb_kv_baseinfo_init(module);
- if (ldb_kv_search_dn1(module, baseinfo_dn, baseinfo, 0) !=
- LDB_SUCCESS) {
- goto failed_and_unlock;
- }
+ } else if (r != LDB_SUCCESS) {
+ goto failed_and_unlock;
+ }
+ /* OK now we definitely have a @BASEINFO record so fetch it */
+ r = ldb_kv_search_dn1(module, baseinfo_dn, baseinfo, 0);
+ if (r != LDB_SUCCESS) {
+ goto failed_and_unlock;
}
/* Ignore the result, and update the sequence number */
goto failed_and_unlock;
}
+ /*
+ * Initialise packing version and GUID index syntax, and force the
+ * two to travel together, ie a GUID indexed database must use V2
+ * packing format and a DN indexed database must use V1.
+ */
ldb_kv->GUID_index_syntax = NULL;
if (ldb_kv->cache->GUID_index_attribute != NULL) {
+ ldb_kv->target_pack_format_version = LDB_PACKING_FORMAT_V2;
+
/*
* Now the attributes are loaded, set the guid_index_syntax.
* This can't fail, it will return a default at worst
a = ldb_schema_attribute_by_name(
ldb, ldb_kv->cache->GUID_index_attribute);
ldb_kv->GUID_index_syntax = a->syntax;
+ } else {
+ ldb_kv->target_pack_format_version = LDB_PACKING_FORMAT;
}
done:
return 0;
}
+static int re_pack(struct ldb_kv_private *ldb_kv,
+ struct ldb_val key,
+ struct ldb_val val,
+ void *state)
+{
+ struct ldb_context *ldb;
+ struct ldb_message *msg;
+ struct ldb_module *module = ldb_kv->module;
+ struct ldb_kv_repack_context *ctx =
+ (struct ldb_kv_repack_context *)state;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ msg = ldb_msg_new(module);
+ if (msg == NULL) {
+ return -1;
+ }
+
+ ret = ldb_unpack_data(ldb, &val, msg);
+ if (ret != 0) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Repack: unpack failed: %s\n",
+ ldb_dn_get_linearized(msg->dn));
+ ctx->error = ret;
+ talloc_free(msg);
+ return -1;
+ }
+
+ ret = ldb_kv_store(module, msg, TDB_MODIFY);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Repack: store failed: %s\n",
+ ldb_dn_get_linearized(msg->dn));
+ ctx->error = ret;
+ talloc_free(msg);
+ return -1;
+ }
+
+ /*
+ * Warn the user that we're repacking the first time we see a normal
+ * record. This means we never warn if we're repacking a database with
+ * only @ records. This is because during database initialisation,
+ * we might repack as initial settings are written out, and we don't
+ * want to spam the log.
+ */
+ if ((!ctx->normal_record_seen) && (!ldb_dn_is_special(msg->dn))) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "Repacking database with format %#010x",
+ ldb_kv->pack_format_version);
+ ctx->normal_record_seen = true;
+ }
+
+ ctx->count++;
+ if (ctx->count % 10000 == 0) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "Repack: re-packed %u records so far",
+ ctx->count);
+ }
+
+ return 0;
+}
+
+int ldb_kv_repack(struct ldb_module *module)
+{
+ struct ldb_kv_private *ldb_kv = talloc_get_type(
+ ldb_module_get_private(module), struct ldb_kv_private);
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_kv_repack_context ctx;
+ int ret;
+
+ ctx.count = 0;
+ ctx.error = LDB_SUCCESS;
+ ctx.normal_record_seen = false;
+
+ /* Iterate all database records and repack them in the new format */
+ ret = ldb_kv->kv_ops->iterate(ldb_kv, re_pack, &ctx);
+ if (ret < 0) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Repack traverse failed: %s",
+ ldb_errstring(ldb));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (ctx.error != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "Repack failed: %s",
+ ldb_errstring(ldb));
+ return ctx.error;
+ }
+
+ return LDB_SUCCESS;
+}
+
/*
force a complete reindex of the database
*/