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;
40 data = talloc_get_type_abort(ldb_module_get_private(module),
41 struct partition_private_data);
43 if (!data || !data->metadata || !data->metadata->db) {
44 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
45 "partition_metadata: metadata tdb not initialized");
48 tmp_ctx = talloc_new(NULL);
49 if (tmp_ctx == NULL) {
50 return ldb_module_oom(module);
53 tdb = data->metadata->db->tdb;
55 tdb_key.dptr = (uint8_t *)discard_const_p(char, key);
56 tdb_key.dsize = strlen(key);
58 tdb_data = tdb_fetch(tdb, tdb_key);
60 if (tdb_error(tdb) == TDB_ERR_NOEXIST) {
61 *value = default_value;
64 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
69 value_str = talloc_strndup(tmp_ctx, (char *)tdb_data.dptr, tdb_data.dsize);
70 if (value_str == NULL) {
71 SAFE_FREE(tdb_data.dptr);
73 return ldb_module_oom(module);
76 *value = strtoull(value_str, NULL, 10);
78 SAFE_FREE(tdb_data.dptr);
86 * Write a key with uin64 value
88 static int partition_metadata_set_uint64(struct ldb_module *module,
89 const char *key, uint64_t value,
92 struct partition_private_data *data;
93 struct tdb_context *tdb;
94 TDB_DATA tdb_key, tdb_data;
99 data = talloc_get_type_abort(ldb_module_get_private(module),
100 struct partition_private_data);
102 if (!data || !data->metadata || !data->metadata->db) {
103 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
104 "partition_metadata: metadata tdb not initialized");
107 tmp_ctx = talloc_new(NULL);
108 if (tmp_ctx == NULL) {
109 return ldb_module_oom(module);
112 tdb = data->metadata->db->tdb;
114 value_str = talloc_asprintf(tmp_ctx, "%llu", (unsigned long long)value);
115 if (value_str == NULL) {
116 talloc_free(tmp_ctx);
117 return ldb_module_oom(module);
120 tdb_key.dptr = (uint8_t *)discard_const_p(char, key);
121 tdb_key.dsize = strlen(key);
123 tdb_data.dptr = (uint8_t *)value_str;
124 tdb_data.dsize = strlen(value_str);
127 tdb_flag = TDB_INSERT;
129 tdb_flag = TDB_MODIFY;
132 if (tdb_store(tdb, tdb_key, tdb_data, tdb_flag) != 0) {
134 char *error_string = talloc_asprintf(tmp_ctx, "%s: tdb_store of key %s failed: %s",
135 tdb_name(tdb), key, tdb_errorstr(tdb));
136 ret = ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
138 talloc_free(tmp_ctx);
142 talloc_free(tmp_ctx);
147 int partition_metadata_inc_schema_sequence(struct ldb_module *module)
149 struct partition_private_data *data;
153 data = talloc_get_type_abort(ldb_module_get_private(module),
154 struct partition_private_data);
155 if (!data || !data->metadata) {
156 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
157 "partition_metadata: metadata not initialized");
160 if (data->metadata->in_transaction == 0) {
161 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
162 "partition_metadata: increment sequence number without transaction");
164 ret = partition_metadata_get_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, &value, 0);
165 if (ret != LDB_SUCCESS) {
170 ret = partition_metadata_set_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, value, false);
171 if (ret == LDB_ERR_OPERATIONS_ERROR) {
172 /* Modify failed, let's try the add */
173 ret = partition_metadata_set_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, value, true);
181 * Open sam.ldb.d/metadata.tdb.
183 static int partition_metadata_open(struct ldb_module *module, bool create)
185 struct ldb_context *ldb = ldb_module_get_ctx(module);
187 struct partition_private_data *data;
188 struct loadparm_context *lp_ctx;
189 char *filename, *dirname;
193 data = talloc_get_type_abort(ldb_module_get_private(module),
194 struct partition_private_data);
195 if (!data || !data->metadata) {
196 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
197 "partition_metadata: metadata not initialized");
200 tmp_ctx = talloc_new(NULL);
201 if (tmp_ctx == NULL) {
202 return ldb_module_oom(module);
205 filename = ldb_relative_path(ldb,
207 "sam.ldb.d/metadata.tdb");
210 talloc_free(tmp_ctx);
216 open_flags |= O_CREAT;
218 /* While provisioning, sam.ldb.d directory may not exist,
219 * so create it. Ignore errors, if it already exists. */
220 dirname = ldb_relative_path(ldb,
224 talloc_free(tmp_ctx);
228 mkdir(dirname, 0700);
229 talloc_free(dirname);
231 if (stat(filename, &statbuf) != 0) {
232 talloc_free(tmp_ctx);
233 return LDB_ERR_OPERATIONS_ERROR;
237 lp_ctx = talloc_get_type_abort(ldb_get_opaque(ldb, "loadparm"),
238 struct loadparm_context);
240 data->metadata->db = tdb_wrap_open(
241 data->metadata, filename, 10,
242 lpcfg_tdb_flags(lp_ctx, TDB_DEFAULT|TDB_SEQNUM), open_flags, 0660);
243 if (data->metadata->db == NULL) {
244 talloc_free(tmp_ctx);
246 ldb_asprintf_errstring(ldb, "partition_metadata: Unable to create %s: %s",
247 filename, strerror(errno));
249 ldb_asprintf_errstring(ldb, "partition_metadata: Unable to open %s: %s",
250 filename, strerror(errno));
252 if (errno == EACCES || errno == EPERM) {
253 return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
255 return LDB_ERR_OPERATIONS_ERROR;
258 talloc_free(tmp_ctx);
264 * Set the sequence number calculated from older logic (sum of primary sequence
265 * numbers for each partition) as LDB_METADATA_SEQ_NUM key.
267 static int partition_metadata_set_sequence_number(struct ldb_module *module)
272 ret = partition_sequence_number_from_partitions(module, &seq_number);
273 if (ret != LDB_SUCCESS) {
277 return partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, seq_number, true);
282 * Initialize metadata. Load metadata.tdb.
283 * If missing, create it and fill in sequence number
285 int partition_metadata_init(struct ldb_module *module)
287 struct partition_private_data *data;
290 data = talloc_get_type_abort(ldb_module_get_private(module),
291 struct partition_private_data);
293 data->metadata = talloc_zero(data, struct partition_metadata);
294 if (data->metadata == NULL) {
295 return ldb_module_oom(module);
298 ret = partition_metadata_open(module, false);
299 if (ret == LDB_SUCCESS) {
300 /* Great, we got the DB open */
304 /* metadata.tdb does not exist, create it */
305 DEBUG(2, ("partition_metadata: Migrating partition metadata: "
306 "open of metadata.tdb gave: %s\n",
307 ldb_errstring(ldb_module_get_ctx(module))));
308 ret = partition_metadata_open(module, true);
309 if (ret != LDB_SUCCESS) {
310 ldb_asprintf_errstring(ldb_module_get_ctx(module),
311 "partition_metadata: "
312 "Migrating partition metadata: "
313 "create of metadata.tdb gave: %s\n",
314 ldb_errstring(ldb_module_get_ctx(module)));
315 TALLOC_FREE(data->metadata);
324 * Read the sequence number, default to 0 if LDB_METADATA_SEQ_NUM key is missing
326 int partition_metadata_sequence_number(struct ldb_module *module, uint64_t *value)
329 /* We have to lock all the databases as otherwise we can
330 * return a sequence number that is higher than the DB values
331 * that we can see, as those transactions close after the
332 * metadata.tdb transaction closes */
333 int ret = partition_read_lock(module);
334 if (ret != LDB_SUCCESS) {
339 * This means we will give a 0 until the first write
340 * tranaction, which is actually pretty reasonable.
342 * All modern databases will have the metadata.tdb from
343 * the time of the first transaction in provision anyway.
345 ret = partition_metadata_get_uint64(module,
346 LDB_METADATA_SEQ_NUM,
349 if (ret == LDB_SUCCESS) {
350 ret = partition_read_unlock(module);
352 /* Don't overwrite the error code */
353 partition_read_unlock(module);
361 * Increment the sequence number, returning the new sequence number
363 int partition_metadata_sequence_number_increment(struct ldb_module *module, uint64_t *value)
365 struct partition_private_data *data;
368 data = talloc_get_type_abort(ldb_module_get_private(module),
369 struct partition_private_data);
370 if (!data || !data->metadata) {
371 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
372 "partition_metadata: metadata not initialized");
375 if (data->metadata->in_transaction == 0) {
376 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
377 "partition_metadata: increment sequence number without transaction");
380 ret = partition_metadata_get_uint64(module, LDB_METADATA_SEQ_NUM, value, 0);
381 if (ret != LDB_SUCCESS) {
387 * We are in a transaction now, so we can get the
388 * sequence number from the partitions.
390 ret = partition_metadata_set_sequence_number(module);
391 if (ret != LDB_SUCCESS) {
392 TALLOC_FREE(data->metadata);
393 partition_del_trans(module);
397 ret = partition_metadata_get_uint64(module,
398 LDB_METADATA_SEQ_NUM,
400 if (ret != LDB_SUCCESS) {
406 ret = partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, *value, false);
414 int partition_metadata_start_trans(struct ldb_module *module)
416 struct partition_private_data *data;
417 struct tdb_context *tdb;
419 data = talloc_get_type_abort(ldb_module_get_private(module),
420 struct partition_private_data);
421 if (!data || !data->metadata || !data->metadata->db) {
422 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
423 "partition_metadata: metadata not initialized");
425 tdb = data->metadata->db->tdb;
427 if (tdb_transaction_start(tdb) != 0) {
428 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
432 data->metadata->in_transaction++;
439 * Transaction prepare commit
441 int partition_metadata_prepare_commit(struct ldb_module *module)
443 struct partition_private_data *data;
444 struct tdb_context *tdb;
446 data = talloc_get_type_abort(ldb_module_get_private(module),
447 struct partition_private_data);
448 if (!data || !data->metadata || !data->metadata->db) {
449 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
450 "partition_metadata: metadata not initialized");
452 tdb = data->metadata->db->tdb;
454 if (data->metadata->in_transaction == 0) {
455 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
456 "partition_metadata: not in transaction");
459 if (tdb_transaction_prepare_commit(tdb) != 0) {
460 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
471 int partition_metadata_end_trans(struct ldb_module *module)
473 struct partition_private_data *data;
474 struct tdb_context *tdb;
476 data = talloc_get_type_abort(ldb_module_get_private(module),
477 struct partition_private_data);
478 if (!data || !data->metadata || !data->metadata->db) {
479 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
480 "partition_metadata: metadata not initialized");
482 tdb = data->metadata->db->tdb;
484 if (data->metadata->in_transaction == 0) {
485 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
486 "partition_metadata: not in transaction");
489 data->metadata->in_transaction--;
491 if (tdb_transaction_commit(tdb) != 0) {
492 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
503 int partition_metadata_del_trans(struct ldb_module *module)
505 struct partition_private_data *data;
506 struct tdb_context *tdb;
508 data = talloc_get_type_abort(ldb_module_get_private(module),
509 struct partition_private_data);
510 if (!data || !data->metadata || !data->metadata->db) {
511 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
512 "partition_metadata: metadata not initialized");
514 tdb = data->metadata->db->tdb;
516 if (data->metadata->in_transaction == 0) {
517 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
518 "partition_metadata: not in transaction");
521 data->metadata->in_transaction--;
523 tdb_transaction_cancel(tdb);