diff options
Diffstat (limited to 'source4/dsdb/samdb/ldb_modules/partition_metadata.c')
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/partition_metadata.c | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/partition_metadata.c b/source4/dsdb/samdb/ldb_modules/partition_metadata.c new file mode 100644 index 0000000..7763e53 --- /dev/null +++ b/source4/dsdb/samdb/ldb_modules/partition_metadata.c @@ -0,0 +1,605 @@ +/* + Partitions ldb module - management of metadata.tdb for sequence number + + Copyright (C) Amitay Isaacs <amitay@samba.org> 2011 + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "dsdb/samdb/ldb_modules/partition.h" +#include "lib/ldb-samba/ldb_wrap.h" +#include "system/filesys.h" +#include "lib/util/smb_strtox.h" + +#define LDB_METADATA_SEQ_NUM "SEQ_NUM" + + +/* + * Read a key with uint64 value + */ +static int partition_metadata_get_uint64(struct ldb_module *module, + const char *key, uint64_t *value, + uint64_t default_value) +{ + struct partition_private_data *data; + struct tdb_context *tdb; + TDB_DATA tdb_key, tdb_data; + char *value_str; + TALLOC_CTX *tmp_ctx; + int error = 0; + + data = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + + if (!data || !data->metadata || !data->metadata->db) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: metadata tdb not initialized"); + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ldb_module_oom(module); + } + + tdb = data->metadata->db->tdb; + + tdb_key.dptr = (uint8_t *)discard_const_p(char, key); + tdb_key.dsize = strlen(key); + + tdb_data = tdb_fetch(tdb, tdb_key); + if (!tdb_data.dptr) { + if (tdb_error(tdb) == TDB_ERR_NOEXIST) { + *value = default_value; + return LDB_SUCCESS; + } else { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + tdb_errorstr(tdb)); + } + } + + value_str = talloc_strndup(tmp_ctx, (char *)tdb_data.dptr, tdb_data.dsize); + if (value_str == NULL) { + SAFE_FREE(tdb_data.dptr); + talloc_free(tmp_ctx); + return ldb_module_oom(module); + } + + *value = smb_strtoull(value_str, NULL, 10, &error, SMB_STR_STANDARD); + if (error != 0) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: converision failed"); + } + + SAFE_FREE(tdb_data.dptr); + talloc_free(tmp_ctx); + + return LDB_SUCCESS; +} + + +/* + * Write a key with uin64 value + */ +static int partition_metadata_set_uint64(struct ldb_module *module, + const char *key, uint64_t value, + bool insert) +{ + struct partition_private_data *data; + struct tdb_context *tdb; + TDB_DATA tdb_key, tdb_data; + int tdb_flag; + char *value_str; + TALLOC_CTX *tmp_ctx; + + data = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + + if (!data || !data->metadata || !data->metadata->db) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: metadata tdb not initialized"); + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ldb_module_oom(module); + } + + tdb = data->metadata->db->tdb; + + value_str = talloc_asprintf(tmp_ctx, "%llu", (unsigned long long)value); + if (value_str == NULL) { + talloc_free(tmp_ctx); + return ldb_module_oom(module); + } + + tdb_key.dptr = (uint8_t *)discard_const_p(char, key); + tdb_key.dsize = strlen(key); + + tdb_data.dptr = (uint8_t *)value_str; + tdb_data.dsize = strlen(value_str); + + if (insert) { + tdb_flag = TDB_INSERT; + } else { + tdb_flag = TDB_MODIFY; + } + + if (tdb_store(tdb, tdb_key, tdb_data, tdb_flag) != 0) { + int ret; + char *error_string = talloc_asprintf(tmp_ctx, "%s: tdb_store of key %s failed: %s", + tdb_name(tdb), key, tdb_errorstr(tdb)); + ret = ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + error_string); + talloc_free(tmp_ctx); + return ret; + } + + talloc_free(tmp_ctx); + + return LDB_SUCCESS; +} + +int partition_metadata_inc_schema_sequence(struct ldb_module *module) +{ + struct partition_private_data *data; + int ret; + uint64_t value = 0; + + data = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + if (!data || !data->metadata) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: metadata not initialized"); + } + + if (data->metadata->in_transaction == 0) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: increment sequence number without transaction"); + } + ret = partition_metadata_get_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, &value, 0); + if (ret != LDB_SUCCESS) { + return ret; + } + + value++; + ret = partition_metadata_set_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, value, false); + if (ret == LDB_ERR_OPERATIONS_ERROR) { + /* Modify failed, let's try the add */ + ret = partition_metadata_set_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, value, true); + } + return ret; +} + + + +/* + * Open sam.ldb.d/metadata.tdb. + */ +static int partition_metadata_open(struct ldb_module *module, bool create) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + TALLOC_CTX *tmp_ctx; + struct partition_private_data *data; + struct loadparm_context *lp_ctx; + char *filename, *dirname; + int open_flags, tdb_flags, ldb_flags; + struct stat statbuf; + + data = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + if (!data || !data->metadata) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: metadata not initialized"); + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ldb_module_oom(module); + } + + filename = ldb_relative_path(ldb, + tmp_ctx, + "sam.ldb.d/metadata.tdb"); + + if (!filename) { + talloc_free(tmp_ctx); + return ldb_oom(ldb); + } + + open_flags = O_RDWR; + if (create) { + open_flags |= O_CREAT; + + /* While provisioning, sam.ldb.d directory may not exist, + * so create it. Ignore errors, if it already exists. */ + dirname = ldb_relative_path(ldb, + tmp_ctx, + "sam.ldb.d"); + if (!dirname) { + talloc_free(tmp_ctx); + return ldb_oom(ldb); + } + + mkdir(dirname, 0700); + talloc_free(dirname); + } else { + if (stat(filename, &statbuf) != 0) { + talloc_free(tmp_ctx); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + lp_ctx = talloc_get_type_abort(ldb_get_opaque(ldb, "loadparm"), + struct loadparm_context); + + tdb_flags = lpcfg_tdb_flags(lp_ctx, TDB_DEFAULT|TDB_SEQNUM); + + ldb_flags = ldb_module_flags(ldb); + + if (ldb_flags & LDB_FLG_NOSYNC) { + tdb_flags |= TDB_NOSYNC; + } + + data->metadata->db = tdb_wrap_open( + data->metadata, filename, 10, + tdb_flags, open_flags, 0660); + if (data->metadata->db == NULL) { + talloc_free(tmp_ctx); + if (create) { + ldb_asprintf_errstring(ldb, "partition_metadata: Unable to create %s: %s", + filename, strerror(errno)); + } else { + ldb_asprintf_errstring(ldb, "partition_metadata: Unable to open %s: %s", + filename, strerror(errno)); + } + if (errno == EACCES || errno == EPERM) { + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } + return LDB_ERR_OPERATIONS_ERROR; + } + + talloc_free(tmp_ctx); + return LDB_SUCCESS; +} + + +/* + * Set the sequence number calculated from older logic (sum of primary sequence + * numbers for each partition) as LDB_METADATA_SEQ_NUM key. + */ +static int partition_metadata_set_sequence_number(struct ldb_module *module) +{ + int ret; + uint64_t seq_number; + + ret = partition_sequence_number_from_partitions(module, &seq_number); + if (ret != LDB_SUCCESS) { + return ret; + } + + return partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, seq_number, true); +} + + +/* + * Initialize metadata. Load metadata.tdb. + * If missing, create it and fill in sequence number + */ +int partition_metadata_init(struct ldb_module *module) +{ + struct partition_private_data *data; + int ret; + + data = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + + if (data->metadata != NULL && data->metadata->db != NULL) { + return LDB_SUCCESS; + } + + data->metadata = talloc_zero(data, struct partition_metadata); + if (data->metadata == NULL) { + return ldb_module_oom(module); + } + + ret = partition_metadata_open(module, false); + if (ret == LDB_SUCCESS) { + /* Great, we got the DB open */ + return LDB_SUCCESS; + } + + /* metadata.tdb does not exist, create it */ + DEBUG(2, ("partition_metadata: Migrating partition metadata: " + "open of metadata.tdb gave: %s\n", + ldb_errstring(ldb_module_get_ctx(module)))); + ret = partition_metadata_open(module, true); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), + "partition_metadata: " + "Migrating partition metadata: " + "create of metadata.tdb gave: %s\n", + ldb_errstring(ldb_module_get_ctx(module))); + TALLOC_FREE(data->metadata); + return ret; + } + + return ret; +} + + +/* + * Read the sequence number, default to 0 if LDB_METADATA_SEQ_NUM key is missing + */ +int partition_metadata_sequence_number(struct ldb_module *module, uint64_t *value) +{ + + /* We have to lock all the databases as otherwise we can + * return a sequence number that is higher than the DB values + * that we can see, as those transactions close after the + * metadata.tdb transaction closes */ + int ret = partition_read_lock(module); + if (ret != LDB_SUCCESS) { + return ret; + } + + /* + * This means we will give a 0 until the first write + * transaction, which is actually pretty reasonable. + * + * All modern databases will have the metadata.tdb from + * the time of the first transaction in provision anyway. + */ + ret = partition_metadata_get_uint64(module, + LDB_METADATA_SEQ_NUM, + value, + 0); + if (ret == LDB_SUCCESS) { + ret = partition_read_unlock(module); + } else { + /* Don't overwrite the error code */ + partition_read_unlock(module); + } + return ret; + +} + + +/* + * Increment the sequence number, returning the new sequence number + */ +int partition_metadata_sequence_number_increment(struct ldb_module *module, uint64_t *value) +{ + struct partition_private_data *data; + int ret; + + data = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + if (!data || !data->metadata) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: metadata not initialized"); + } + + if (data->metadata->in_transaction == 0) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: increment sequence number without transaction"); + } + + ret = partition_metadata_get_uint64(module, LDB_METADATA_SEQ_NUM, value, 0); + if (ret != LDB_SUCCESS) { + return ret; + } + + if (*value == 0) { + /* + * We are in a transaction now, so we can get the + * sequence number from the partitions. + */ + ret = partition_metadata_set_sequence_number(module); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = partition_metadata_get_uint64(module, + LDB_METADATA_SEQ_NUM, + value, 0); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + (*value)++; + ret = partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, *value, false); + return ret; +} +/* + lock the database for read - use by partition_lock_read +*/ +int partition_metadata_read_lock(struct ldb_module *module) +{ + struct partition_private_data *data + = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + struct tdb_context *tdb = NULL; + int tdb_ret = 0; + int ret; + + if (!data || !data->metadata || !data->metadata->db) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: metadata not initialized"); + } + tdb = data->metadata->db->tdb; + + if (tdb_transaction_active(tdb) == false && + data->metadata->read_lock_count == 0) { + tdb_ret = tdb_lockall_read(tdb); + } + if (tdb_ret == 0) { + data->metadata->read_lock_count++; + return LDB_SUCCESS; + } else { + /* Sadly we can't call ltdb_err_map(tdb_error(tdb)); */ + ret = LDB_ERR_BUSY; + } + ldb_debug_set(ldb_module_get_ctx(module), + LDB_DEBUG_FATAL, + "Failure during partition_metadata_read_lock(): %s", + tdb_errorstr(tdb)); + return ret; +} + +/* + unlock the database after a partition_metadata_lock_read() +*/ +int partition_metadata_read_unlock(struct ldb_module *module) +{ + struct partition_private_data *data + = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + struct tdb_context *tdb = NULL; + + data = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + if (!data || !data->metadata || !data->metadata->db) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: metadata not initialized"); + } + tdb = data->metadata->db->tdb; + + if (!tdb_transaction_active(tdb) && + data->metadata->read_lock_count == 1) { + tdb_unlockall_read(tdb); + data->metadata->read_lock_count--; + return 0; + } + data->metadata->read_lock_count--; + return 0; +} + + +/* + * Transaction start + */ +int partition_metadata_start_trans(struct ldb_module *module) +{ + struct partition_private_data *data; + struct tdb_context *tdb; + + data = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + if (!data || !data->metadata || !data->metadata->db) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: metadata not initialized"); + } + tdb = data->metadata->db->tdb; + + if (tdb_transaction_start(tdb) != 0) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + tdb_errorstr(tdb)); + } + + data->metadata->in_transaction++; + + return LDB_SUCCESS; +} + + +/* + * Transaction prepare commit + */ +int partition_metadata_prepare_commit(struct ldb_module *module) +{ + struct partition_private_data *data; + struct tdb_context *tdb; + + data = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + if (!data || !data->metadata || !data->metadata->db) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: metadata not initialized"); + } + tdb = data->metadata->db->tdb; + + if (data->metadata->in_transaction == 0) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: not in transaction"); + } + + if (tdb_transaction_prepare_commit(tdb) != 0) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + tdb_errorstr(tdb)); + } + + return LDB_SUCCESS; +} + + +/* + * Transaction end + */ +int partition_metadata_end_trans(struct ldb_module *module) +{ + struct partition_private_data *data; + struct tdb_context *tdb; + + data = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + if (!data || !data->metadata || !data->metadata->db) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: metadata not initialized"); + } + tdb = data->metadata->db->tdb; + + if (data->metadata->in_transaction == 0) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: not in transaction"); + } + + data->metadata->in_transaction--; + + if (tdb_transaction_commit(tdb) != 0) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + tdb_errorstr(tdb)); + } + + return LDB_SUCCESS; +} + + +/* + * Transaction delete + */ +int partition_metadata_del_trans(struct ldb_module *module) +{ + struct partition_private_data *data; + struct tdb_context *tdb; + + data = talloc_get_type_abort(ldb_module_get_private(module), + struct partition_private_data); + if (!data || !data->metadata || !data->metadata->db) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: metadata not initialized"); + } + tdb = data->metadata->db->tdb; + + if (data->metadata->in_transaction == 0) { + return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR, + "partition_metadata: not in transaction"); + } + + data->metadata->in_transaction--; + + tdb_transaction_cancel(tdb); + + return LDB_SUCCESS; +} |