tdb_wrap: Remove tdb_wrap_open_ again
[kai/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 "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), 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                 goto end;
304         }
305
306         /* metadata.tdb does not exist, create it */
307         DEBUG(2, ("partition_metadata: Migrating partition metadata: "
308                   "open of metadata.tdb gave: %s\n",
309                   ldb_errstring(ldb_module_get_ctx(module))));
310         ret = partition_metadata_open(module, true);
311         if (ret != LDB_SUCCESS) {
312                 ldb_asprintf_errstring(ldb_module_get_ctx(module),
313                                        "partition_metadata: "
314                                        "Migrating partition metadata: "
315                                        "create of metadata.tdb gave: %s\n",
316                                        ldb_errstring(ldb_module_get_ctx(module)));
317                 talloc_free(data->metadata);
318                 data->metadata = NULL;
319                 goto end;
320         }
321
322         ret = partition_metadata_set_sequence_number(module);
323         if (ret != LDB_SUCCESS) {
324                 talloc_free(data->metadata);
325                 data->metadata = NULL;
326         }
327
328 end:
329         return ret;
330 }
331
332
333 /*
334  * Read the sequence number, default to 0 if LDB_METADATA_SEQ_NUM key is missing
335  */
336 int partition_metadata_sequence_number(struct ldb_module *module, uint64_t *value)
337 {
338         return partition_metadata_get_uint64(module,
339                                              LDB_METADATA_SEQ_NUM,
340                                              value,
341                                              0);
342 }
343
344
345 /*
346  * Increment the sequence number, returning the new sequence number
347  */
348 int partition_metadata_sequence_number_increment(struct ldb_module *module, uint64_t *value)
349 {
350         struct partition_private_data *data;
351         int ret;
352
353         data = talloc_get_type_abort(ldb_module_get_private(module),
354                                     struct partition_private_data);
355         if (!data || !data->metadata) {
356                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
357                                         "partition_metadata: metadata not initialized");
358         }
359
360         if (data->metadata->in_transaction == 0) {
361                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
362                                         "partition_metadata: increment sequence number without transaction");
363         }
364
365         ret = partition_metadata_get_uint64(module, LDB_METADATA_SEQ_NUM, value, 0);
366         if (ret != LDB_SUCCESS) {
367                 return ret;
368         }
369
370         (*value)++;
371         ret = partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, *value, false);
372         return ret;
373 }
374
375
376 /*
377  * Transaction start
378  */
379 int partition_metadata_start_trans(struct ldb_module *module)
380 {
381         struct partition_private_data *data;
382         struct tdb_context *tdb;
383
384         data = talloc_get_type_abort(ldb_module_get_private(module),
385                                      struct partition_private_data);
386         if (!data || !data->metadata || !data->metadata->db) {
387                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
388                                         "partition_metadata: metadata not initialized");
389         }
390         tdb = data->metadata->db->tdb;
391
392         if (tdb_transaction_start(tdb) != 0) {
393                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
394                                         tdb_errorstr(tdb));
395         }
396
397         data->metadata->in_transaction++;
398
399         return LDB_SUCCESS;
400 }
401
402
403 /*
404  * Transaction prepare commit
405  */
406 int partition_metadata_prepare_commit(struct ldb_module *module)
407 {
408         struct partition_private_data *data;
409         struct tdb_context *tdb;
410
411         data = talloc_get_type_abort(ldb_module_get_private(module),
412                                      struct partition_private_data);
413         if (!data || !data->metadata || !data->metadata->db) {
414                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
415                                         "partition_metadata: metadata not initialized");
416         }
417         tdb = data->metadata->db->tdb;
418
419         if (data->metadata->in_transaction == 0) {
420                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
421                                         "partition_metadata: not in transaction");
422         }
423
424         if (tdb_transaction_prepare_commit(tdb) != 0) {
425                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
426                                         tdb_errorstr(tdb));
427         }
428
429         return LDB_SUCCESS;
430 }
431
432
433 /*
434  * Transaction end
435  */
436 int partition_metadata_end_trans(struct ldb_module *module)
437 {
438         struct partition_private_data *data;
439         struct tdb_context *tdb;
440
441         data = talloc_get_type_abort(ldb_module_get_private(module),
442                                      struct partition_private_data);
443         if (!data || !data->metadata || !data->metadata->db) {
444                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
445                                         "partition_metadata: metadata not initialized");
446         }
447         tdb = data->metadata->db->tdb;
448
449         if (data->metadata->in_transaction == 0) {
450                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
451                                         "partition_metadata: not in transaction");
452         }
453
454         data->metadata->in_transaction--;
455
456         if (tdb_transaction_commit(tdb) != 0) {
457                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
458                                         tdb_errorstr(tdb));
459         }
460
461         return LDB_SUCCESS;
462 }
463
464
465 /*
466  * Transaction delete
467  */
468 int partition_metadata_del_trans(struct ldb_module *module)
469 {
470         struct partition_private_data *data;
471         struct tdb_context *tdb;
472
473         data = talloc_get_type_abort(ldb_module_get_private(module),
474                                      struct partition_private_data);
475         if (!data || !data->metadata || !data->metadata->db) {
476                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
477                                         "partition_metadata: metadata not initialized");
478         }
479         tdb = data->metadata->db->tdb;
480
481         if (data->metadata->in_transaction == 0) {
482                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
483                                         "partition_metadata: not in transaction");
484         }
485
486         data->metadata->in_transaction--;
487
488         tdb_transaction_cancel(tdb);
489
490         return LDB_SUCCESS;
491 }