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