1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-01 04:58:35 +03:00

ldb: repack old format database if GUID indexing enabled

VERY IMPORTANT PATCH
Now that we have a new packing format, we need to enable it by repacking
the database. We've decided to link all new database features together,
so once GUID indexing is enabled, the database will be repacked with
version 2 format. Repacking is done following the same iterate pattern as
reindexing.

Signed-off-by: Aaron Haslett <aaronhaslett@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
This commit is contained in:
Aaron Haslett 2019-05-13 16:37:25 +12:00 committed by Andrew Bartlett
parent d6ded22cb6
commit 73763acf49
4 changed files with 158 additions and 30 deletions

View File

@ -301,6 +301,28 @@ static int ldb_kv_check_special_dn(struct ldb_module *module,
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
@ -1447,6 +1469,20 @@ static int ldb_kv_prepare_commit(struct ldb_module *module)
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),
@ -1895,8 +1931,6 @@ int ldb_kv_init_store(struct ldb_kv_private *ldb_kv,
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);

View File

@ -64,6 +64,7 @@ struct ldb_kv_private {
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 */
@ -141,6 +142,12 @@ struct ldb_kv_reindex_context {
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"
@ -226,6 +233,7 @@ int ldb_kv_index_del_value(struct ldb_module *module,
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);

View File

@ -418,7 +418,6 @@ int ldb_kv_cache_load(struct ldb_module *module)
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);
@ -453,29 +452,7 @@ int ldb_kv_cache_load(struct ldb_module *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) {
@ -492,15 +469,25 @@ int ldb_kv_cache_load(struct ldb_module *module)
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 */
@ -562,8 +549,15 @@ int ldb_kv_cache_load(struct ldb_module *module)
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
@ -571,6 +565,8 @@ int ldb_kv_cache_load(struct ldb_module *module)
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:

View File

@ -3412,6 +3412,96 @@ static int re_index(struct ldb_kv_private *ldb_kv,
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
*/