Line data Source code
1 : /*
2 : Partitions ldb module - management of metadata.tdb for sequence number
3 :
4 : Copyright (C) Amitay Isaacs <amitay@samba.org> 2011
5 :
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.
10 :
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.
15 :
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/>.
18 : */
19 :
20 : #include "dsdb/samdb/ldb_modules/partition.h"
21 : #include "lib/ldb-samba/ldb_wrap.h"
22 : #include "system/filesys.h"
23 : #include "lib/util/smb_strtox.h"
24 :
25 : #define LDB_METADATA_SEQ_NUM "SEQ_NUM"
26 :
27 :
28 : /*
29 : * Read a key with uint64 value
30 : */
31 920540 : static int partition_metadata_get_uint64(struct ldb_module *module,
32 : const char *key, uint64_t *value,
33 : uint64_t default_value)
34 : {
35 : struct partition_private_data *data;
36 : struct tdb_context *tdb;
37 : TDB_DATA tdb_key, tdb_data;
38 : char *value_str;
39 : TALLOC_CTX *tmp_ctx;
40 920540 : int error = 0;
41 :
42 920540 : data = talloc_get_type_abort(ldb_module_get_private(module),
43 : struct partition_private_data);
44 :
45 920540 : if (!data || !data->metadata || !data->metadata->db) {
46 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
47 : "partition_metadata: metadata tdb not initialized");
48 : }
49 :
50 920540 : tmp_ctx = talloc_new(NULL);
51 920540 : if (tmp_ctx == NULL) {
52 0 : return ldb_module_oom(module);
53 : }
54 :
55 920540 : tdb = data->metadata->db->tdb;
56 :
57 920540 : tdb_key.dptr = (uint8_t *)discard_const_p(char, key);
58 920540 : tdb_key.dsize = strlen(key);
59 :
60 920540 : tdb_data = tdb_fetch(tdb, tdb_key);
61 920540 : if (!tdb_data.dptr) {
62 303 : if (tdb_error(tdb) == TDB_ERR_NOEXIST) {
63 303 : *value = default_value;
64 303 : return LDB_SUCCESS;
65 : } else {
66 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
67 : tdb_errorstr(tdb));
68 : }
69 : }
70 :
71 920237 : value_str = talloc_strndup(tmp_ctx, (char *)tdb_data.dptr, tdb_data.dsize);
72 920237 : if (value_str == NULL) {
73 0 : SAFE_FREE(tdb_data.dptr);
74 0 : talloc_free(tmp_ctx);
75 0 : return ldb_module_oom(module);
76 : }
77 :
78 920237 : *value = smb_strtoull(value_str, NULL, 10, &error, SMB_STR_STANDARD);
79 920237 : if (error != 0) {
80 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
81 : "partition_metadata: converision failed");
82 : }
83 :
84 920237 : SAFE_FREE(tdb_data.dptr);
85 920237 : talloc_free(tmp_ctx);
86 :
87 920237 : return LDB_SUCCESS;
88 : }
89 :
90 :
91 : /*
92 : * Write a key with uin64 value
93 : */
94 901946 : static int partition_metadata_set_uint64(struct ldb_module *module,
95 : const char *key, uint64_t value,
96 : bool insert)
97 : {
98 : struct partition_private_data *data;
99 : struct tdb_context *tdb;
100 : TDB_DATA tdb_key, tdb_data;
101 : int tdb_flag;
102 : char *value_str;
103 : TALLOC_CTX *tmp_ctx;
104 :
105 901946 : data = talloc_get_type_abort(ldb_module_get_private(module),
106 : struct partition_private_data);
107 :
108 901946 : if (!data || !data->metadata || !data->metadata->db) {
109 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
110 : "partition_metadata: metadata tdb not initialized");
111 : }
112 :
113 901946 : tmp_ctx = talloc_new(NULL);
114 901946 : if (tmp_ctx == NULL) {
115 0 : return ldb_module_oom(module);
116 : }
117 :
118 901946 : tdb = data->metadata->db->tdb;
119 :
120 901946 : value_str = talloc_asprintf(tmp_ctx, "%llu", (unsigned long long)value);
121 901946 : if (value_str == NULL) {
122 0 : talloc_free(tmp_ctx);
123 0 : return ldb_module_oom(module);
124 : }
125 :
126 901946 : tdb_key.dptr = (uint8_t *)discard_const_p(char, key);
127 901946 : tdb_key.dsize = strlen(key);
128 :
129 901946 : tdb_data.dptr = (uint8_t *)value_str;
130 901946 : tdb_data.dsize = strlen(value_str);
131 :
132 901946 : if (insert) {
133 303 : tdb_flag = TDB_INSERT;
134 : } else {
135 901643 : tdb_flag = TDB_MODIFY;
136 : }
137 :
138 901946 : if (tdb_store(tdb, tdb_key, tdb_data, tdb_flag) != 0) {
139 : int ret;
140 152 : char *error_string = talloc_asprintf(tmp_ctx, "%s: tdb_store of key %s failed: %s",
141 : tdb_name(tdb), key, tdb_errorstr(tdb));
142 152 : ret = ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
143 : error_string);
144 152 : talloc_free(tmp_ctx);
145 152 : return ret;
146 : }
147 :
148 901794 : talloc_free(tmp_ctx);
149 :
150 901794 : return LDB_SUCCESS;
151 : }
152 :
153 1808 : int partition_metadata_inc_schema_sequence(struct ldb_module *module)
154 : {
155 : struct partition_private_data *data;
156 : int ret;
157 1808 : uint64_t value = 0;
158 :
159 1808 : data = talloc_get_type_abort(ldb_module_get_private(module),
160 : struct partition_private_data);
161 1808 : if (!data || !data->metadata) {
162 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
163 : "partition_metadata: metadata not initialized");
164 : }
165 :
166 1808 : if (data->metadata->in_transaction == 0) {
167 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
168 : "partition_metadata: increment sequence number without transaction");
169 : }
170 1808 : ret = partition_metadata_get_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, &value, 0);
171 1808 : if (ret != LDB_SUCCESS) {
172 0 : return ret;
173 : }
174 :
175 1808 : value++;
176 1808 : ret = partition_metadata_set_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, value, false);
177 1808 : if (ret == LDB_ERR_OPERATIONS_ERROR) {
178 : /* Modify failed, let's try the add */
179 152 : ret = partition_metadata_set_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, value, true);
180 : }
181 1808 : return ret;
182 : }
183 :
184 :
185 :
186 : /*
187 : * Open sam.ldb.d/metadata.tdb.
188 : */
189 117607 : static int partition_metadata_open(struct ldb_module *module, bool create)
190 : {
191 117607 : struct ldb_context *ldb = ldb_module_get_ctx(module);
192 : TALLOC_CTX *tmp_ctx;
193 : struct partition_private_data *data;
194 : struct loadparm_context *lp_ctx;
195 : char *filename, *dirname;
196 : int open_flags, tdb_flags, ldb_flags;
197 : struct stat statbuf;
198 :
199 117607 : data = talloc_get_type_abort(ldb_module_get_private(module),
200 : struct partition_private_data);
201 117607 : if (!data || !data->metadata) {
202 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
203 : "partition_metadata: metadata not initialized");
204 : }
205 :
206 117607 : tmp_ctx = talloc_new(NULL);
207 117607 : if (tmp_ctx == NULL) {
208 0 : return ldb_module_oom(module);
209 : }
210 :
211 117607 : filename = ldb_relative_path(ldb,
212 : tmp_ctx,
213 : "sam.ldb.d/metadata.tdb");
214 :
215 117607 : if (!filename) {
216 0 : talloc_free(tmp_ctx);
217 0 : return ldb_oom(ldb);
218 : }
219 :
220 117607 : open_flags = O_RDWR;
221 117607 : if (create) {
222 152 : open_flags |= O_CREAT;
223 :
224 : /* While provisioning, sam.ldb.d directory may not exist,
225 : * so create it. Ignore errors, if it already exists. */
226 152 : dirname = ldb_relative_path(ldb,
227 : tmp_ctx,
228 : "sam.ldb.d");
229 152 : if (!dirname) {
230 0 : talloc_free(tmp_ctx);
231 0 : return ldb_oom(ldb);
232 : }
233 :
234 152 : mkdir(dirname, 0700);
235 152 : talloc_free(dirname);
236 : } else {
237 117455 : if (stat(filename, &statbuf) != 0) {
238 152 : talloc_free(tmp_ctx);
239 152 : return LDB_ERR_OPERATIONS_ERROR;
240 : }
241 : }
242 :
243 117455 : lp_ctx = talloc_get_type_abort(ldb_get_opaque(ldb, "loadparm"),
244 : struct loadparm_context);
245 :
246 117455 : tdb_flags = lpcfg_tdb_flags(lp_ctx, TDB_DEFAULT|TDB_SEQNUM);
247 :
248 117455 : ldb_flags = ldb_module_flags(ldb);
249 :
250 117455 : if (ldb_flags & LDB_FLG_NOSYNC) {
251 116930 : tdb_flags |= TDB_NOSYNC;
252 : }
253 :
254 234910 : data->metadata->db = tdb_wrap_open(
255 117455 : data->metadata, filename, 10,
256 : tdb_flags, open_flags, 0660);
257 117455 : if (data->metadata->db == NULL) {
258 0 : talloc_free(tmp_ctx);
259 0 : if (create) {
260 0 : ldb_asprintf_errstring(ldb, "partition_metadata: Unable to create %s: %s",
261 0 : filename, strerror(errno));
262 : } else {
263 0 : ldb_asprintf_errstring(ldb, "partition_metadata: Unable to open %s: %s",
264 0 : filename, strerror(errno));
265 : }
266 0 : if (errno == EACCES || errno == EPERM) {
267 0 : return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
268 : }
269 0 : return LDB_ERR_OPERATIONS_ERROR;
270 : }
271 :
272 117455 : talloc_free(tmp_ctx);
273 117455 : return LDB_SUCCESS;
274 : }
275 :
276 :
277 : /*
278 : * Set the sequence number calculated from older logic (sum of primary sequence
279 : * numbers for each partition) as LDB_METADATA_SEQ_NUM key.
280 : */
281 151 : static int partition_metadata_set_sequence_number(struct ldb_module *module)
282 : {
283 : int ret;
284 : uint64_t seq_number;
285 :
286 151 : ret = partition_sequence_number_from_partitions(module, &seq_number);
287 151 : if (ret != LDB_SUCCESS) {
288 0 : return ret;
289 : }
290 :
291 151 : return partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, seq_number, true);
292 : }
293 :
294 :
295 : /*
296 : * Initialize metadata. Load metadata.tdb.
297 : * If missing, create it and fill in sequence number
298 : */
299 119141 : int partition_metadata_init(struct ldb_module *module)
300 : {
301 : struct partition_private_data *data;
302 : int ret;
303 :
304 119141 : data = talloc_get_type_abort(ldb_module_get_private(module),
305 : struct partition_private_data);
306 :
307 119141 : if (data->metadata != NULL && data->metadata->db != NULL) {
308 1686 : return LDB_SUCCESS;
309 : }
310 :
311 117455 : data->metadata = talloc_zero(data, struct partition_metadata);
312 117455 : if (data->metadata == NULL) {
313 0 : return ldb_module_oom(module);
314 : }
315 :
316 117455 : ret = partition_metadata_open(module, false);
317 117455 : if (ret == LDB_SUCCESS) {
318 : /* Great, we got the DB open */
319 117303 : return LDB_SUCCESS;
320 : }
321 :
322 : /* metadata.tdb does not exist, create it */
323 152 : DEBUG(2, ("partition_metadata: Migrating partition metadata: "
324 : "open of metadata.tdb gave: %s\n",
325 : ldb_errstring(ldb_module_get_ctx(module))));
326 152 : ret = partition_metadata_open(module, true);
327 152 : if (ret != LDB_SUCCESS) {
328 0 : ldb_asprintf_errstring(ldb_module_get_ctx(module),
329 : "partition_metadata: "
330 : "Migrating partition metadata: "
331 : "create of metadata.tdb gave: %s\n",
332 : ldb_errstring(ldb_module_get_ctx(module)));
333 0 : TALLOC_FREE(data->metadata);
334 0 : return ret;
335 : }
336 :
337 152 : return ret;
338 : }
339 :
340 :
341 : /*
342 : * Read the sequence number, default to 0 if LDB_METADATA_SEQ_NUM key is missing
343 : */
344 18746 : int partition_metadata_sequence_number(struct ldb_module *module, uint64_t *value)
345 : {
346 :
347 : /* We have to lock all the databases as otherwise we can
348 : * return a sequence number that is higher than the DB values
349 : * that we can see, as those transactions close after the
350 : * metadata.tdb transaction closes */
351 18746 : int ret = partition_read_lock(module);
352 18746 : if (ret != LDB_SUCCESS) {
353 0 : return ret;
354 : }
355 :
356 : /*
357 : * This means we will give a 0 until the first write
358 : * transaction, which is actually pretty reasonable.
359 : *
360 : * All modern databases will have the metadata.tdb from
361 : * the time of the first transaction in provision anyway.
362 : */
363 18746 : ret = partition_metadata_get_uint64(module,
364 : LDB_METADATA_SEQ_NUM,
365 : value,
366 : 0);
367 18746 : if (ret == LDB_SUCCESS) {
368 18746 : ret = partition_read_unlock(module);
369 : } else {
370 : /* Don't overwrite the error code */
371 0 : partition_read_unlock(module);
372 : }
373 18746 : return ret;
374 :
375 : }
376 :
377 :
378 : /*
379 : * Increment the sequence number, returning the new sequence number
380 : */
381 899835 : int partition_metadata_sequence_number_increment(struct ldb_module *module, uint64_t *value)
382 : {
383 : struct partition_private_data *data;
384 : int ret;
385 :
386 899835 : data = talloc_get_type_abort(ldb_module_get_private(module),
387 : struct partition_private_data);
388 899835 : if (!data || !data->metadata) {
389 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
390 : "partition_metadata: metadata not initialized");
391 : }
392 :
393 899835 : if (data->metadata->in_transaction == 0) {
394 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
395 : "partition_metadata: increment sequence number without transaction");
396 : }
397 :
398 899835 : ret = partition_metadata_get_uint64(module, LDB_METADATA_SEQ_NUM, value, 0);
399 899835 : if (ret != LDB_SUCCESS) {
400 0 : return ret;
401 : }
402 :
403 899835 : if (*value == 0) {
404 : /*
405 : * We are in a transaction now, so we can get the
406 : * sequence number from the partitions.
407 : */
408 151 : ret = partition_metadata_set_sequence_number(module);
409 151 : if (ret != LDB_SUCCESS) {
410 0 : return ret;
411 : }
412 :
413 151 : ret = partition_metadata_get_uint64(module,
414 : LDB_METADATA_SEQ_NUM,
415 : value, 0);
416 151 : if (ret != LDB_SUCCESS) {
417 0 : return ret;
418 : }
419 : }
420 :
421 899835 : (*value)++;
422 899835 : ret = partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, *value, false);
423 899835 : return ret;
424 : }
425 : /*
426 : lock the database for read - use by partition_lock_read
427 : */
428 11677684 : int partition_metadata_read_lock(struct ldb_module *module)
429 : {
430 : struct partition_private_data *data
431 11677684 : = talloc_get_type_abort(ldb_module_get_private(module),
432 : struct partition_private_data);
433 11677684 : struct tdb_context *tdb = NULL;
434 11677684 : int tdb_ret = 0;
435 : int ret;
436 :
437 11677684 : if (!data || !data->metadata || !data->metadata->db) {
438 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
439 : "partition_metadata: metadata not initialized");
440 : }
441 11677684 : tdb = data->metadata->db->tdb;
442 :
443 11677684 : if (tdb_transaction_active(tdb) == false &&
444 7387786 : data->metadata->read_lock_count == 0) {
445 5826739 : tdb_ret = tdb_lockall_read(tdb);
446 : }
447 11677684 : if (tdb_ret == 0) {
448 11677684 : data->metadata->read_lock_count++;
449 11677684 : return LDB_SUCCESS;
450 : } else {
451 : /* Sadly we can't call ltdb_err_map(tdb_error(tdb)); */
452 0 : ret = LDB_ERR_BUSY;
453 : }
454 0 : ldb_debug_set(ldb_module_get_ctx(module),
455 : LDB_DEBUG_FATAL,
456 : "Failure during partition_metadata_read_lock(): %s",
457 : tdb_errorstr(tdb));
458 0 : return ret;
459 : }
460 :
461 : /*
462 : unlock the database after a partition_metadata_lock_read()
463 : */
464 11677684 : int partition_metadata_read_unlock(struct ldb_module *module)
465 : {
466 : struct partition_private_data *data
467 11677684 : = talloc_get_type_abort(ldb_module_get_private(module),
468 : struct partition_private_data);
469 11677684 : struct tdb_context *tdb = NULL;
470 :
471 11677684 : data = talloc_get_type_abort(ldb_module_get_private(module),
472 : struct partition_private_data);
473 11677684 : if (!data || !data->metadata || !data->metadata->db) {
474 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
475 : "partition_metadata: metadata not initialized");
476 : }
477 11677684 : tdb = data->metadata->db->tdb;
478 :
479 11677684 : if (!tdb_transaction_active(tdb) &&
480 7387786 : data->metadata->read_lock_count == 1) {
481 5826739 : tdb_unlockall_read(tdb);
482 5826739 : data->metadata->read_lock_count--;
483 5826739 : return 0;
484 : }
485 5850945 : data->metadata->read_lock_count--;
486 5850945 : return 0;
487 : }
488 :
489 :
490 : /*
491 : * Transaction start
492 : */
493 272407 : int partition_metadata_start_trans(struct ldb_module *module)
494 : {
495 : struct partition_private_data *data;
496 : struct tdb_context *tdb;
497 :
498 272407 : data = talloc_get_type_abort(ldb_module_get_private(module),
499 : struct partition_private_data);
500 272407 : if (!data || !data->metadata || !data->metadata->db) {
501 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
502 : "partition_metadata: metadata not initialized");
503 : }
504 272407 : tdb = data->metadata->db->tdb;
505 :
506 272407 : if (tdb_transaction_start(tdb) != 0) {
507 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
508 : tdb_errorstr(tdb));
509 : }
510 :
511 272407 : data->metadata->in_transaction++;
512 :
513 272407 : return LDB_SUCCESS;
514 : }
515 :
516 :
517 : /*
518 : * Transaction prepare commit
519 : */
520 233323 : int partition_metadata_prepare_commit(struct ldb_module *module)
521 : {
522 : struct partition_private_data *data;
523 : struct tdb_context *tdb;
524 :
525 233323 : data = talloc_get_type_abort(ldb_module_get_private(module),
526 : struct partition_private_data);
527 233323 : if (!data || !data->metadata || !data->metadata->db) {
528 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
529 : "partition_metadata: metadata not initialized");
530 : }
531 233323 : tdb = data->metadata->db->tdb;
532 :
533 233323 : if (data->metadata->in_transaction == 0) {
534 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
535 : "partition_metadata: not in transaction");
536 : }
537 :
538 233323 : if (tdb_transaction_prepare_commit(tdb) != 0) {
539 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
540 : tdb_errorstr(tdb));
541 : }
542 :
543 233323 : return LDB_SUCCESS;
544 : }
545 :
546 :
547 : /*
548 : * Transaction end
549 : */
550 233591 : int partition_metadata_end_trans(struct ldb_module *module)
551 : {
552 : struct partition_private_data *data;
553 : struct tdb_context *tdb;
554 :
555 233591 : data = talloc_get_type_abort(ldb_module_get_private(module),
556 : struct partition_private_data);
557 233591 : if (!data || !data->metadata || !data->metadata->db) {
558 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
559 : "partition_metadata: metadata not initialized");
560 : }
561 233591 : tdb = data->metadata->db->tdb;
562 :
563 233591 : if (data->metadata->in_transaction == 0) {
564 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
565 : "partition_metadata: not in transaction");
566 : }
567 :
568 233591 : data->metadata->in_transaction--;
569 :
570 233591 : if (tdb_transaction_commit(tdb) != 0) {
571 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
572 : tdb_errorstr(tdb));
573 : }
574 :
575 233591 : return LDB_SUCCESS;
576 : }
577 :
578 :
579 : /*
580 : * Transaction delete
581 : */
582 38815 : int partition_metadata_del_trans(struct ldb_module *module)
583 : {
584 : struct partition_private_data *data;
585 : struct tdb_context *tdb;
586 :
587 38815 : data = talloc_get_type_abort(ldb_module_get_private(module),
588 : struct partition_private_data);
589 38815 : if (!data || !data->metadata || !data->metadata->db) {
590 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
591 : "partition_metadata: metadata not initialized");
592 : }
593 38815 : tdb = data->metadata->db->tdb;
594 :
595 38815 : if (data->metadata->in_transaction == 0) {
596 0 : return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
597 : "partition_metadata: not in transaction");
598 : }
599 :
600 38815 : data->metadata->in_transaction--;
601 :
602 38815 : tdb_transaction_cancel(tdb);
603 :
604 38815 : return LDB_SUCCESS;
605 : }
|