dsdb:partition_metadata: make use of ldb_relative_path() in partition_metadata_open()
[metze/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / partition_metadata.c
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  */
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
40         data = talloc_get_type_abort(ldb_module_get_private(module),
41                                      struct partition_private_data);
42
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");
46         }
47
48         tmp_ctx = talloc_new(NULL);
49         if (tmp_ctx == NULL) {
50                 return ldb_module_oom(module);
51         }
52
53         tdb = data->metadata->db->tdb;
54
55         tdb_key.dptr = (uint8_t *)discard_const_p(char, key);
56         tdb_key.dsize = strlen(key);
57
58         tdb_data = tdb_fetch(tdb, tdb_key);
59         if (!tdb_data.dptr) {
60                 if (tdb_error(tdb) == TDB_ERR_NOEXIST) {
61                         *value = default_value;
62                         return LDB_SUCCESS;
63                 } else {
64                         return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
65                                                 tdb_errorstr(tdb));
66                 }
67         }
68
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);
72                 talloc_free(tmp_ctx);
73                 return ldb_module_oom(module);
74         }
75
76         *value = strtoull(value_str, NULL, 10);
77
78         SAFE_FREE(tdb_data.dptr);
79         talloc_free(tmp_ctx);
80
81         return LDB_SUCCESS;
82 }
83
84
85 /*
86  * Write a key with uin64 value
87  */
88 static int partition_metadata_set_uint64(struct ldb_module *module,
89                                          const char *key, uint64_t value,
90                                          bool insert)
91 {
92         struct partition_private_data *data;
93         struct tdb_context *tdb;
94         TDB_DATA tdb_key, tdb_data;
95         int tdb_flag;
96         char *value_str;
97         TALLOC_CTX *tmp_ctx;
98
99         data = talloc_get_type_abort(ldb_module_get_private(module),
100                                      struct partition_private_data);
101
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");
105         }
106
107         tmp_ctx = talloc_new(NULL);
108         if (tmp_ctx == NULL) {
109                 return ldb_module_oom(module);
110         }
111
112         tdb = data->metadata->db->tdb;
113
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);
118         }
119
120         tdb_key.dptr = (uint8_t *)discard_const_p(char, key);
121         tdb_key.dsize = strlen(key);
122
123         tdb_data.dptr = (uint8_t *)value_str;
124         tdb_data.dsize = strlen(value_str);
125
126         if (insert) {
127                 tdb_flag = TDB_INSERT;
128         } else {
129                 tdb_flag = TDB_MODIFY;
130         }
131
132         if (tdb_store(tdb, tdb_key, tdb_data, tdb_flag) != 0) {
133                 int ret;
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,
137                                        error_string);
138                 talloc_free(tmp_ctx);
139                 return ret;
140         }
141
142         talloc_free(tmp_ctx);
143
144         return LDB_SUCCESS;
145 }
146
147 int partition_metadata_inc_schema_sequence(struct ldb_module *module)
148 {
149         struct partition_private_data *data;
150         int ret;
151         uint64_t value;
152
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");
158         }
159
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");
163         }
164         ret = partition_metadata_get_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, &value, 0);
165         if (ret != LDB_SUCCESS) {
166                 return ret;
167         }
168
169         value++;
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);
174         }
175         return ret;
176 }
177
178
179
180 /*
181  * Open sam.ldb.d/metadata.tdb.
182  */
183 static int partition_metadata_open(struct ldb_module *module, bool create)
184 {
185         struct ldb_context *ldb = ldb_module_get_ctx(module);
186         TALLOC_CTX *tmp_ctx;
187         struct partition_private_data *data;
188         struct loadparm_context *lp_ctx;
189         char *filename, *dirname;
190         int open_flags;
191         struct stat statbuf;
192
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");
198         }
199
200         tmp_ctx = talloc_new(NULL);
201         if (tmp_ctx == NULL) {
202                 return ldb_module_oom(module);
203         }
204
205         filename = ldb_relative_path(ldb,
206                                      tmp_ctx,
207                                      "sam.ldb.d/metadata.tdb");
208
209         if (!filename) {
210                 talloc_free(tmp_ctx);
211                 return ldb_oom(ldb);
212         }
213
214         open_flags = O_RDWR;
215         if (create) {
216                 open_flags |= O_CREAT;
217
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,
221                                             tmp_ctx,
222                                             "sam.ldb.d");
223                 if (!dirname) {
224                         talloc_free(tmp_ctx);
225                         return ldb_oom(ldb);
226                 }
227
228                 mkdir(dirname, 0700);
229                 talloc_free(dirname);
230         } else {
231                 if (stat(filename, &statbuf) != 0) {
232                         talloc_free(tmp_ctx);
233                         return LDB_ERR_OPERATIONS_ERROR;
234                 }
235         }
236
237         lp_ctx = talloc_get_type_abort(ldb_get_opaque(ldb, "loadparm"),
238                                        struct loadparm_context);
239
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);
245                 if (create) {
246                         ldb_asprintf_errstring(ldb, "partition_metadata: Unable to create %s: %s",
247                                                filename, strerror(errno));
248                 } else {
249                         ldb_asprintf_errstring(ldb, "partition_metadata: Unable to open %s: %s",
250                                                filename, strerror(errno));
251                 }
252                 if (errno == EACCES || errno == EPERM) {
253                         return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
254                 }
255                 return LDB_ERR_OPERATIONS_ERROR;
256         }
257
258         talloc_free(tmp_ctx);
259         return LDB_SUCCESS;
260 }
261
262
263 /*
264  * Set the sequence number calculated from older logic (sum of primary sequence
265  * numbers for each partition) as LDB_METADATA_SEQ_NUM key.
266  */
267 static int partition_metadata_set_sequence_number(struct ldb_module *module)
268 {
269         int ret;
270         uint64_t seq_number;
271
272         ret = partition_sequence_number_from_partitions(module, &seq_number);
273         if (ret != LDB_SUCCESS) {
274                 return ret;
275         }
276
277         return partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, seq_number, true);
278 }
279
280
281 /*
282  * Initialize metadata. Load metadata.tdb.
283  * If missing, create it and fill in sequence number
284  */
285 int partition_metadata_init(struct ldb_module *module)
286 {
287         struct partition_private_data *data;
288         int ret;
289
290         data = talloc_get_type_abort(ldb_module_get_private(module),
291                                      struct partition_private_data);
292
293         data->metadata = talloc_zero(data, struct partition_metadata);
294         if (data->metadata == NULL) {
295                 return ldb_module_oom(module);
296         }
297
298         ret = partition_metadata_open(module, false);
299         if (ret == LDB_SUCCESS) {
300                 /* Great, we got the DB open */
301                 return LDB_SUCCESS;
302         }
303
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);
316                 return ret;
317         }
318
319         return ret;
320 }
321
322
323 /*
324  * Read the sequence number, default to 0 if LDB_METADATA_SEQ_NUM key is missing
325  */
326 int partition_metadata_sequence_number(struct ldb_module *module, uint64_t *value)
327 {
328
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) {
335                 return ret;
336         }
337
338         /*
339          * This means we will give a 0 until the first write
340          * tranaction, which is actually pretty reasonable.
341          *
342          * All modern databases will have the metadata.tdb from
343          * the time of the first transaction in provision anyway.
344          */
345         ret = partition_metadata_get_uint64(module,
346                                             LDB_METADATA_SEQ_NUM,
347                                             value,
348                                             0);
349         if (ret == LDB_SUCCESS) {
350                 ret = partition_read_unlock(module);
351         } else {
352                 /* Don't overwrite the error code */
353                 partition_read_unlock(module);
354         }
355         return ret;
356
357 }
358
359
360 /*
361  * Increment the sequence number, returning the new sequence number
362  */
363 int partition_metadata_sequence_number_increment(struct ldb_module *module, uint64_t *value)
364 {
365         struct partition_private_data *data;
366         int ret;
367
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");
373         }
374
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");
378         }
379
380         ret = partition_metadata_get_uint64(module, LDB_METADATA_SEQ_NUM, value, 0);
381         if (ret != LDB_SUCCESS) {
382                 return ret;
383         }
384
385         if (*value == 0) {
386                 /*
387                  * We are in a transaction now, so we can get the
388                  * sequence number from the partitions.
389                  */
390                 ret = partition_metadata_set_sequence_number(module);
391                 if (ret != LDB_SUCCESS) {
392                         TALLOC_FREE(data->metadata);
393                         partition_del_trans(module);
394                         return ret;
395                 }
396
397                 ret = partition_metadata_get_uint64(module,
398                                                     LDB_METADATA_SEQ_NUM,
399                                                     value, 0);
400                 if (ret != LDB_SUCCESS) {
401                         return ret;
402                 }
403         }
404
405         (*value)++;
406         ret = partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, *value, false);
407         return ret;
408 }
409
410
411 /*
412  * Transaction start
413  */
414 int partition_metadata_start_trans(struct ldb_module *module)
415 {
416         struct partition_private_data *data;
417         struct tdb_context *tdb;
418
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");
424         }
425         tdb = data->metadata->db->tdb;
426
427         if (tdb_transaction_start(tdb) != 0) {
428                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
429                                         tdb_errorstr(tdb));
430         }
431
432         data->metadata->in_transaction++;
433
434         return LDB_SUCCESS;
435 }
436
437
438 /*
439  * Transaction prepare commit
440  */
441 int partition_metadata_prepare_commit(struct ldb_module *module)
442 {
443         struct partition_private_data *data;
444         struct tdb_context *tdb;
445
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");
451         }
452         tdb = data->metadata->db->tdb;
453
454         if (data->metadata->in_transaction == 0) {
455                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
456                                         "partition_metadata: not in transaction");
457         }
458
459         if (tdb_transaction_prepare_commit(tdb) != 0) {
460                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
461                                         tdb_errorstr(tdb));
462         }
463
464         return LDB_SUCCESS;
465 }
466
467
468 /*
469  * Transaction end
470  */
471 int partition_metadata_end_trans(struct ldb_module *module)
472 {
473         struct partition_private_data *data;
474         struct tdb_context *tdb;
475
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");
481         }
482         tdb = data->metadata->db->tdb;
483
484         if (data->metadata->in_transaction == 0) {
485                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
486                                         "partition_metadata: not in transaction");
487         }
488
489         data->metadata->in_transaction--;
490
491         if (tdb_transaction_commit(tdb) != 0) {
492                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
493                                         tdb_errorstr(tdb));
494         }
495
496         return LDB_SUCCESS;
497 }
498
499
500 /*
501  * Transaction delete
502  */
503 int partition_metadata_del_trans(struct ldb_module *module)
504 {
505         struct partition_private_data *data;
506         struct tdb_context *tdb;
507
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");
513         }
514         tdb = data->metadata->db->tdb;
515
516         if (data->metadata->in_transaction == 0) {
517                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
518                                         "partition_metadata: not in transaction");
519         }
520
521         data->metadata->in_transaction--;
522
523         tdb_transaction_cancel(tdb);
524
525         return LDB_SUCCESS;
526 }