2 Partitions ldb module - management of metadata.tdb for sequence number
4 Copyright (C) Amitay Isaacs <amitay@samba.org> 2011
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "dsdb/samdb/ldb_modules/partition.h"
21 #include "lib/ldb-samba/ldb_wrap.h"
22 #include "system/filesys.h"
24 #define LDB_METADATA_SEQ_NUM "SEQ_NUM"
28 * Read a key with uint64 value
30 static int partition_metadata_get_uint64(struct ldb_module *module,
31 const char *key, uint64_t *value,
32 uint64_t default_value)
34 struct partition_private_data *data;
35 struct tdb_context *tdb;
36 TDB_DATA tdb_key, tdb_data;
41 data = talloc_get_type_abort(ldb_module_get_private(module),
42 struct partition_private_data);
44 if (!data || !data->metadata || !data->metadata->db) {
45 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
46 "partition_metadata: metadata tdb not initialized");
49 tmp_ctx = talloc_new(NULL);
50 if (tmp_ctx == NULL) {
51 return ldb_module_oom(module);
54 tdb = data->metadata->db->tdb;
56 tdb_key.dptr = (uint8_t *)discard_const_p(char, key);
57 tdb_key.dsize = strlen(key);
59 tdb_data = tdb_fetch(tdb, tdb_key);
61 if (tdb_error(tdb) == TDB_ERR_NOEXIST) {
62 *value = default_value;
65 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
70 value_str = talloc_strndup(tmp_ctx, (char *)tdb_data.dptr, tdb_data.dsize);
71 if (value_str == NULL) {
72 SAFE_FREE(tdb_data.dptr);
74 return ldb_module_oom(module);
77 *value = strtoull_err(value_str, NULL, 10, &error);
79 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
80 "partition_metadata: converision failed");
83 SAFE_FREE(tdb_data.dptr);
91 * Write a key with uin64 value
93 static int partition_metadata_set_uint64(struct ldb_module *module,
94 const char *key, uint64_t value,
97 struct partition_private_data *data;
98 struct tdb_context *tdb;
99 TDB_DATA tdb_key, tdb_data;
104 data = talloc_get_type_abort(ldb_module_get_private(module),
105 struct partition_private_data);
107 if (!data || !data->metadata || !data->metadata->db) {
108 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
109 "partition_metadata: metadata tdb not initialized");
112 tmp_ctx = talloc_new(NULL);
113 if (tmp_ctx == NULL) {
114 return ldb_module_oom(module);
117 tdb = data->metadata->db->tdb;
119 value_str = talloc_asprintf(tmp_ctx, "%llu", (unsigned long long)value);
120 if (value_str == NULL) {
121 talloc_free(tmp_ctx);
122 return ldb_module_oom(module);
125 tdb_key.dptr = (uint8_t *)discard_const_p(char, key);
126 tdb_key.dsize = strlen(key);
128 tdb_data.dptr = (uint8_t *)value_str;
129 tdb_data.dsize = strlen(value_str);
132 tdb_flag = TDB_INSERT;
134 tdb_flag = TDB_MODIFY;
137 if (tdb_store(tdb, tdb_key, tdb_data, tdb_flag) != 0) {
139 char *error_string = talloc_asprintf(tmp_ctx, "%s: tdb_store of key %s failed: %s",
140 tdb_name(tdb), key, tdb_errorstr(tdb));
141 ret = ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
143 talloc_free(tmp_ctx);
147 talloc_free(tmp_ctx);
152 int partition_metadata_inc_schema_sequence(struct ldb_module *module)
154 struct partition_private_data *data;
158 data = talloc_get_type_abort(ldb_module_get_private(module),
159 struct partition_private_data);
160 if (!data || !data->metadata) {
161 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
162 "partition_metadata: metadata not initialized");
165 if (data->metadata->in_transaction == 0) {
166 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
167 "partition_metadata: increment sequence number without transaction");
169 ret = partition_metadata_get_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, &value, 0);
170 if (ret != LDB_SUCCESS) {
175 ret = partition_metadata_set_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, value, false);
176 if (ret == LDB_ERR_OPERATIONS_ERROR) {
177 /* Modify failed, let's try the add */
178 ret = partition_metadata_set_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, value, true);
186 * Open sam.ldb.d/metadata.tdb.
188 static int partition_metadata_open(struct ldb_module *module, bool create)
190 struct ldb_context *ldb = ldb_module_get_ctx(module);
192 struct partition_private_data *data;
193 struct loadparm_context *lp_ctx;
194 char *filename, *dirname;
195 int open_flags, tdb_flags, ldb_flags;
198 data = talloc_get_type_abort(ldb_module_get_private(module),
199 struct partition_private_data);
200 if (!data || !data->metadata) {
201 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
202 "partition_metadata: metadata not initialized");
205 tmp_ctx = talloc_new(NULL);
206 if (tmp_ctx == NULL) {
207 return ldb_module_oom(module);
210 filename = ldb_relative_path(ldb,
212 "sam.ldb.d/metadata.tdb");
215 talloc_free(tmp_ctx);
221 open_flags |= O_CREAT;
223 /* While provisioning, sam.ldb.d directory may not exist,
224 * so create it. Ignore errors, if it already exists. */
225 dirname = ldb_relative_path(ldb,
229 talloc_free(tmp_ctx);
233 mkdir(dirname, 0700);
234 talloc_free(dirname);
236 if (stat(filename, &statbuf) != 0) {
237 talloc_free(tmp_ctx);
238 return LDB_ERR_OPERATIONS_ERROR;
242 lp_ctx = talloc_get_type_abort(ldb_get_opaque(ldb, "loadparm"),
243 struct loadparm_context);
245 tdb_flags = lpcfg_tdb_flags(lp_ctx, TDB_DEFAULT|TDB_SEQNUM);
247 ldb_flags = ldb_module_flags(ldb);
249 if (ldb_flags & LDB_FLG_NOSYNC) {
250 tdb_flags |= TDB_NOSYNC;
253 data->metadata->db = tdb_wrap_open(
254 data->metadata, filename, 10,
255 tdb_flags, open_flags, 0660);
256 if (data->metadata->db == NULL) {
257 talloc_free(tmp_ctx);
259 ldb_asprintf_errstring(ldb, "partition_metadata: Unable to create %s: %s",
260 filename, strerror(errno));
262 ldb_asprintf_errstring(ldb, "partition_metadata: Unable to open %s: %s",
263 filename, strerror(errno));
265 if (errno == EACCES || errno == EPERM) {
266 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
268 return LDB_ERR_OPERATIONS_ERROR;
271 talloc_free(tmp_ctx);
277 * Set the sequence number calculated from older logic (sum of primary sequence
278 * numbers for each partition) as LDB_METADATA_SEQ_NUM key.
280 static int partition_metadata_set_sequence_number(struct ldb_module *module)
285 ret = partition_sequence_number_from_partitions(module, &seq_number);
286 if (ret != LDB_SUCCESS) {
290 return partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, seq_number, true);
295 * Initialize metadata. Load metadata.tdb.
296 * If missing, create it and fill in sequence number
298 int partition_metadata_init(struct ldb_module *module)
300 struct partition_private_data *data;
303 data = talloc_get_type_abort(ldb_module_get_private(module),
304 struct partition_private_data);
306 data->metadata = talloc_zero(data, struct partition_metadata);
307 if (data->metadata == NULL) {
308 return ldb_module_oom(module);
311 ret = partition_metadata_open(module, false);
312 if (ret == LDB_SUCCESS) {
313 /* Great, we got the DB open */
317 /* metadata.tdb does not exist, create it */
318 DEBUG(2, ("partition_metadata: Migrating partition metadata: "
319 "open of metadata.tdb gave: %s\n",
320 ldb_errstring(ldb_module_get_ctx(module))));
321 ret = partition_metadata_open(module, true);
322 if (ret != LDB_SUCCESS) {
323 ldb_asprintf_errstring(ldb_module_get_ctx(module),
324 "partition_metadata: "
325 "Migrating partition metadata: "
326 "create of metadata.tdb gave: %s\n",
327 ldb_errstring(ldb_module_get_ctx(module)));
328 TALLOC_FREE(data->metadata);
337 * Read the sequence number, default to 0 if LDB_METADATA_SEQ_NUM key is missing
339 int partition_metadata_sequence_number(struct ldb_module *module, uint64_t *value)
342 /* We have to lock all the databases as otherwise we can
343 * return a sequence number that is higher than the DB values
344 * that we can see, as those transactions close after the
345 * metadata.tdb transaction closes */
346 int ret = partition_read_lock(module);
347 if (ret != LDB_SUCCESS) {
352 * This means we will give a 0 until the first write
353 * tranaction, which is actually pretty reasonable.
355 * All modern databases will have the metadata.tdb from
356 * the time of the first transaction in provision anyway.
358 ret = partition_metadata_get_uint64(module,
359 LDB_METADATA_SEQ_NUM,
362 if (ret == LDB_SUCCESS) {
363 ret = partition_read_unlock(module);
365 /* Don't overwrite the error code */
366 partition_read_unlock(module);
374 * Increment the sequence number, returning the new sequence number
376 int partition_metadata_sequence_number_increment(struct ldb_module *module, uint64_t *value)
378 struct partition_private_data *data;
381 data = talloc_get_type_abort(ldb_module_get_private(module),
382 struct partition_private_data);
383 if (!data || !data->metadata) {
384 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
385 "partition_metadata: metadata not initialized");
388 if (data->metadata->in_transaction == 0) {
389 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
390 "partition_metadata: increment sequence number without transaction");
393 ret = partition_metadata_get_uint64(module, LDB_METADATA_SEQ_NUM, value, 0);
394 if (ret != LDB_SUCCESS) {
400 * We are in a transaction now, so we can get the
401 * sequence number from the partitions.
403 ret = partition_metadata_set_sequence_number(module);
404 if (ret != LDB_SUCCESS) {
405 TALLOC_FREE(data->metadata);
406 partition_del_trans(module);
410 ret = partition_metadata_get_uint64(module,
411 LDB_METADATA_SEQ_NUM,
413 if (ret != LDB_SUCCESS) {
419 ret = partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, *value, false);
427 int partition_metadata_start_trans(struct ldb_module *module)
429 struct partition_private_data *data;
430 struct tdb_context *tdb;
432 data = talloc_get_type_abort(ldb_module_get_private(module),
433 struct partition_private_data);
434 if (!data || !data->metadata || !data->metadata->db) {
435 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
436 "partition_metadata: metadata not initialized");
438 tdb = data->metadata->db->tdb;
440 if (tdb_transaction_start(tdb) != 0) {
441 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
445 data->metadata->in_transaction++;
452 * Transaction prepare commit
454 int partition_metadata_prepare_commit(struct ldb_module *module)
456 struct partition_private_data *data;
457 struct tdb_context *tdb;
459 data = talloc_get_type_abort(ldb_module_get_private(module),
460 struct partition_private_data);
461 if (!data || !data->metadata || !data->metadata->db) {
462 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
463 "partition_metadata: metadata not initialized");
465 tdb = data->metadata->db->tdb;
467 if (data->metadata->in_transaction == 0) {
468 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
469 "partition_metadata: not in transaction");
472 if (tdb_transaction_prepare_commit(tdb) != 0) {
473 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
484 int partition_metadata_end_trans(struct ldb_module *module)
486 struct partition_private_data *data;
487 struct tdb_context *tdb;
489 data = talloc_get_type_abort(ldb_module_get_private(module),
490 struct partition_private_data);
491 if (!data || !data->metadata || !data->metadata->db) {
492 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
493 "partition_metadata: metadata not initialized");
495 tdb = data->metadata->db->tdb;
497 if (data->metadata->in_transaction == 0) {
498 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
499 "partition_metadata: not in transaction");
502 data->metadata->in_transaction--;
504 if (tdb_transaction_commit(tdb) != 0) {
505 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
516 int partition_metadata_del_trans(struct ldb_module *module)
518 struct partition_private_data *data;
519 struct tdb_context *tdb;
521 data = talloc_get_type_abort(ldb_module_get_private(module),
522 struct partition_private_data);
523 if (!data || !data->metadata || !data->metadata->db) {
524 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
525 "partition_metadata: metadata not initialized");
527 tdb = data->metadata->db->tdb;
529 if (data->metadata->in_transaction == 0) {
530 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
531 "partition_metadata: not in transaction");
534 data->metadata->in_transaction--;
536 tdb_transaction_cancel(tdb);