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