dsdb: Do not create a transaction in partition_init()
[metze/samba/wip.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 "system/filesys.h"
22
23 #define LDB_METADATA_SEQ_NUM    "SEQ_NUM"
24
25
26 /*
27  * Read a key with uint64 value
28  */
29 static int partition_metadata_get_uint64(struct ldb_module *module,
30                                          const char *key, uint64_t *value,
31                                          uint64_t default_value)
32 {
33         struct partition_private_data *data;
34         struct tdb_context *tdb;
35         TDB_DATA tdb_key, tdb_data;
36         char *value_str;
37         TALLOC_CTX *tmp_ctx;
38
39         data = talloc_get_type_abort(ldb_module_get_private(module),
40                                      struct partition_private_data);
41
42         if (!data || !data->metadata || !data->metadata->db) {
43                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
44                                         "partition_metadata: metadata tdb not initialized");
45         }
46
47         tmp_ctx = talloc_new(NULL);
48         if (tmp_ctx == NULL) {
49                 return ldb_module_oom(module);
50         }
51
52         tdb = data->metadata->db->tdb;
53
54         tdb_key.dptr = (uint8_t *)discard_const_p(char, key);
55         tdb_key.dsize = strlen(key);
56
57         tdb_data = tdb_fetch(tdb, tdb_key);
58         if (!tdb_data.dptr) {
59                 if (tdb_error(tdb) == TDB_ERR_NOEXIST) {
60                         *value = default_value;
61                         return LDB_SUCCESS;
62                 } else {
63                         return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
64                                                 tdb_errorstr(tdb));
65                 }
66         }
67
68         value_str = talloc_strndup(tmp_ctx, (char *)tdb_data.dptr, tdb_data.dsize);
69         if (value_str == NULL) {
70                 SAFE_FREE(tdb_data.dptr);
71                 talloc_free(tmp_ctx);
72                 return ldb_module_oom(module);
73         }
74
75         *value = strtoull(value_str, NULL, 10);
76
77         SAFE_FREE(tdb_data.dptr);
78         talloc_free(tmp_ctx);
79
80         return LDB_SUCCESS;
81 }
82
83
84 /*
85  * Write a key with uin64 value
86  */
87 static int partition_metadata_set_uint64(struct ldb_module *module,
88                                          const char *key, uint64_t value,
89                                          bool insert)
90 {
91         struct partition_private_data *data;
92         struct tdb_context *tdb;
93         TDB_DATA tdb_key, tdb_data;
94         int tdb_flag;
95         char *value_str;
96         TALLOC_CTX *tmp_ctx;
97
98         data = talloc_get_type_abort(ldb_module_get_private(module),
99                                      struct partition_private_data);
100
101         if (!data || !data->metadata || !data->metadata->db) {
102                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
103                                         "partition_metadata: metadata tdb not initialized");
104         }
105
106         tmp_ctx = talloc_new(NULL);
107         if (tmp_ctx == NULL) {
108                 return ldb_module_oom(module);
109         }
110
111         tdb = data->metadata->db->tdb;
112
113         value_str = talloc_asprintf(tmp_ctx, "%llu", (unsigned long long)value);
114         if (value_str == NULL) {
115                 talloc_free(tmp_ctx);
116                 return ldb_module_oom(module);
117         }
118
119         tdb_key.dptr = (uint8_t *)discard_const_p(char, key);
120         tdb_key.dsize = strlen(key);
121
122         tdb_data.dptr = (uint8_t *)value_str;
123         tdb_data.dsize = strlen(value_str);
124
125         if (insert) {
126                 tdb_flag = TDB_INSERT;
127         } else {
128                 tdb_flag = TDB_MODIFY;
129         }
130
131         if (tdb_store(tdb, tdb_key, tdb_data, tdb_flag) != 0) {
132                 int ret;
133                 char *error_string = talloc_asprintf(tmp_ctx, "%s: tdb_store of key %s failed: %s",
134                                                      tdb_name(tdb), key, tdb_errorstr(tdb));
135                 ret = ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
136                                        error_string);
137                 talloc_free(tmp_ctx);
138                 return ret;
139         }
140
141         talloc_free(tmp_ctx);
142
143         return LDB_SUCCESS;
144 }
145
146 int partition_metadata_inc_schema_sequence(struct ldb_module *module)
147 {
148         struct partition_private_data *data;
149         int ret;
150         uint64_t value;
151
152         data = talloc_get_type_abort(ldb_module_get_private(module),
153                                     struct partition_private_data);
154         if (!data || !data->metadata) {
155                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
156                                         "partition_metadata: metadata not initialized");
157         }
158
159         if (data->metadata->in_transaction == 0) {
160                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
161                                         "partition_metadata: increment sequence number without transaction");
162         }
163         ret = partition_metadata_get_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, &value, 0);
164         if (ret != LDB_SUCCESS) {
165                 return ret;
166         }
167
168         value++;
169         ret = partition_metadata_set_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, value, false);
170         if (ret == LDB_ERR_OPERATIONS_ERROR) {
171                 /* Modify failed, let's try the add */
172                 ret = partition_metadata_set_uint64(module, DSDB_METADATA_SCHEMA_SEQ_NUM, value, true);
173         }
174         return ret;
175 }
176
177
178
179 /*
180  * Open sam.ldb.d/metadata.tdb.
181  */
182 static int partition_metadata_open(struct ldb_module *module, bool create)
183 {
184         struct ldb_context *ldb = ldb_module_get_ctx(module);
185         TALLOC_CTX *tmp_ctx;
186         struct partition_private_data *data;
187         struct loadparm_context *lp_ctx;
188         const char *sam_name;
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         sam_name = (const char *)ldb_get_opaque(ldb, "ldb_url");
206         if (!sam_name) {
207                 talloc_free(tmp_ctx);
208                 return ldb_operr(ldb);
209         }
210         if (strncmp("tdb://", sam_name, 6) == 0) {
211                 sam_name += 6;
212         }
213         filename = talloc_asprintf(tmp_ctx, "%s.d/metadata.tdb", sam_name);
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 = talloc_asprintf(tmp_ctx, "%s.d", sam_name);
226                 if (!dirname) {
227                         talloc_free(tmp_ctx);
228                         return ldb_oom(ldb);
229                 }
230
231                 mkdir(dirname, 0700);
232                 talloc_free(dirname);
233         } else {
234                 if (stat(filename, &statbuf) != 0) {
235                         talloc_free(tmp_ctx);
236                         return LDB_ERR_OPERATIONS_ERROR;
237                 }
238         }
239
240         lp_ctx = talloc_get_type_abort(ldb_get_opaque(ldb, "loadparm"),
241                                        struct loadparm_context);
242
243         data->metadata->db = tdb_wrap_open(
244                 data->metadata, filename, 10,
245                 lpcfg_tdb_flags(lp_ctx, TDB_DEFAULT|TDB_SEQNUM), open_flags, 0660);
246         if (data->metadata->db == NULL) {
247                 talloc_free(tmp_ctx);
248                 if (create) {
249                         ldb_asprintf_errstring(ldb, "partition_metadata: Unable to create %s: %s",
250                                                filename, strerror(errno));
251                 } else {
252                         ldb_asprintf_errstring(ldb, "partition_metadata: Unable to open %s: %s",
253                                                filename, strerror(errno));
254                 }
255                 if (errno == EACCES || errno == EPERM) {
256                         return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
257                 }
258                 return LDB_ERR_OPERATIONS_ERROR;
259         }
260
261         talloc_free(tmp_ctx);
262         return LDB_SUCCESS;
263 }
264
265
266 /*
267  * Set the sequence number calculated from older logic (sum of primary sequence
268  * numbers for each partition) as LDB_METADATA_SEQ_NUM key.
269  */
270 static int partition_metadata_set_sequence_number(struct ldb_module *module)
271 {
272         int ret;
273         uint64_t seq_number;
274
275         ret = partition_sequence_number_from_partitions(module, &seq_number);
276         if (ret != LDB_SUCCESS) {
277                 return ret;
278         }
279
280         return partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, seq_number, true);
281 }
282
283
284 /*
285  * Initialize metadata. Load metadata.tdb.
286  * If missing, create it and fill in sequence number
287  */
288 int partition_metadata_init(struct ldb_module *module)
289 {
290         struct partition_private_data *data;
291         int ret;
292
293         data = talloc_get_type_abort(ldb_module_get_private(module),
294                                      struct partition_private_data);
295
296         data->metadata = talloc_zero(data, struct partition_metadata);
297         if (data->metadata == NULL) {
298                 return ldb_module_oom(module);
299         }
300
301         ret = partition_metadata_open(module, false);
302         if (ret == LDB_SUCCESS) {
303                 /* Great, we got the DB open */
304                 return LDB_SUCCESS;
305         }
306
307         /* metadata.tdb does not exist, create it */
308         DEBUG(2, ("partition_metadata: Migrating partition metadata: "
309                   "open of metadata.tdb gave: %s\n",
310                   ldb_errstring(ldb_module_get_ctx(module))));
311         ret = partition_metadata_open(module, true);
312         if (ret != LDB_SUCCESS) {
313                 ldb_asprintf_errstring(ldb_module_get_ctx(module),
314                                        "partition_metadata: "
315                                        "Migrating partition metadata: "
316                                        "create of metadata.tdb gave: %s\n",
317                                        ldb_errstring(ldb_module_get_ctx(module)));
318                 TALLOC_FREE(data->metadata);
319                 return ret;
320         }
321
322         return ret;
323 }
324
325
326 /*
327  * Read the sequence number, default to 0 if LDB_METADATA_SEQ_NUM key is missing
328  */
329 int partition_metadata_sequence_number(struct ldb_module *module, uint64_t *value)
330 {
331
332         /* We have to lock all the databases as otherwise we can
333          * return a sequence number that is higher than the DB values
334          * that we can see, as those transactions close after the
335          * metadata.tdb transaction closes */
336         int ret = partition_read_lock(module);
337         if (ret != LDB_SUCCESS) {
338                 return ret;
339         }
340
341         /*
342          * This means we will give a 0 until the first write
343          * tranaction, which is actually pretty reasonable.
344          *
345          * All modern databases will have the metadata.tdb from
346          * the time of the first transaction in provision anyway.
347          */
348         ret = partition_metadata_get_uint64(module,
349                                             LDB_METADATA_SEQ_NUM,
350                                             value,
351                                             0);
352         if (ret == LDB_SUCCESS) {
353                 ret = partition_read_unlock(module);
354         } else {
355                 /* Don't overwrite the error code */
356                 partition_read_unlock(module);
357         }
358         return ret;
359
360 }
361
362
363 /*
364  * Increment the sequence number, returning the new sequence number
365  */
366 int partition_metadata_sequence_number_increment(struct ldb_module *module, uint64_t *value)
367 {
368         struct partition_private_data *data;
369         int ret;
370
371         data = talloc_get_type_abort(ldb_module_get_private(module),
372                                     struct partition_private_data);
373         if (!data || !data->metadata) {
374                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
375                                         "partition_metadata: metadata not initialized");
376         }
377
378         if (data->metadata->in_transaction == 0) {
379                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
380                                         "partition_metadata: increment sequence number without transaction");
381         }
382
383         ret = partition_metadata_get_uint64(module, LDB_METADATA_SEQ_NUM, value, 0);
384         if (ret != LDB_SUCCESS) {
385                 return ret;
386         }
387
388         if (*value == 0) {
389                 /*
390                  * We are in a transaction now, so we can get the
391                  * sequence number from the partitions.
392                  */
393                 ret = partition_metadata_set_sequence_number(module);
394                 if (ret != LDB_SUCCESS) {
395                         TALLOC_FREE(data->metadata);
396                         partition_del_trans(module);
397                         return ret;
398                 }
399
400                 ret = partition_metadata_get_uint64(module,
401                                                     LDB_METADATA_SEQ_NUM,
402                                                     value, 0);
403                 if (ret != LDB_SUCCESS) {
404                         return ret;
405                 }
406         }
407
408         (*value)++;
409         ret = partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, *value, false);
410         return ret;
411 }
412
413
414 /*
415  * Transaction start
416  */
417 int partition_metadata_start_trans(struct ldb_module *module)
418 {
419         struct partition_private_data *data;
420         struct tdb_context *tdb;
421
422         data = talloc_get_type_abort(ldb_module_get_private(module),
423                                      struct partition_private_data);
424         if (!data || !data->metadata || !data->metadata->db) {
425                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
426                                         "partition_metadata: metadata not initialized");
427         }
428         tdb = data->metadata->db->tdb;
429
430         if (tdb_transaction_start(tdb) != 0) {
431                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
432                                         tdb_errorstr(tdb));
433         }
434
435         data->metadata->in_transaction++;
436
437         return LDB_SUCCESS;
438 }
439
440
441 /*
442  * Transaction prepare commit
443  */
444 int partition_metadata_prepare_commit(struct ldb_module *module)
445 {
446         struct partition_private_data *data;
447         struct tdb_context *tdb;
448
449         data = talloc_get_type_abort(ldb_module_get_private(module),
450                                      struct partition_private_data);
451         if (!data || !data->metadata || !data->metadata->db) {
452                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
453                                         "partition_metadata: metadata not initialized");
454         }
455         tdb = data->metadata->db->tdb;
456
457         if (data->metadata->in_transaction == 0) {
458                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
459                                         "partition_metadata: not in transaction");
460         }
461
462         if (tdb_transaction_prepare_commit(tdb) != 0) {
463                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
464                                         tdb_errorstr(tdb));
465         }
466
467         return LDB_SUCCESS;
468 }
469
470
471 /*
472  * Transaction end
473  */
474 int partition_metadata_end_trans(struct ldb_module *module)
475 {
476         struct partition_private_data *data;
477         struct tdb_context *tdb;
478
479         data = talloc_get_type_abort(ldb_module_get_private(module),
480                                      struct partition_private_data);
481         if (!data || !data->metadata || !data->metadata->db) {
482                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
483                                         "partition_metadata: metadata not initialized");
484         }
485         tdb = data->metadata->db->tdb;
486
487         if (data->metadata->in_transaction == 0) {
488                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
489                                         "partition_metadata: not in transaction");
490         }
491
492         data->metadata->in_transaction--;
493
494         if (tdb_transaction_commit(tdb) != 0) {
495                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
496                                         tdb_errorstr(tdb));
497         }
498
499         return LDB_SUCCESS;
500 }
501
502
503 /*
504  * Transaction delete
505  */
506 int partition_metadata_del_trans(struct ldb_module *module)
507 {
508         struct partition_private_data *data;
509         struct tdb_context *tdb;
510
511         data = talloc_get_type_abort(ldb_module_get_private(module),
512                                      struct partition_private_data);
513         if (!data || !data->metadata || !data->metadata->db) {
514                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
515                                         "partition_metadata: metadata not initialized");
516         }
517         tdb = data->metadata->db->tdb;
518
519         if (data->metadata->in_transaction == 0) {
520                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
521                                         "partition_metadata: not in transaction");
522         }
523
524         data->metadata->in_transaction--;
525
526         tdb_transaction_cancel(tdb);
527
528         return LDB_SUCCESS;
529 }