76b78dd13fa0edb543854a5f3de5c6422eb6352a
[ddiss/samba.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_compat(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_compat(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                 talloc_free(tmp_ctx);
133                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
134                                         tdb_errorstr_compat(tdb));
135         }
136
137         talloc_free(tmp_ctx);
138
139         return LDB_SUCCESS;
140 }
141
142
143 /*
144  * Open sam.ldb.d/metadata.tdb.
145  */
146 static int partition_metadata_open(struct ldb_module *module, bool create)
147 {
148         struct ldb_context *ldb = ldb_module_get_ctx(module);
149         TALLOC_CTX *tmp_ctx;
150         struct partition_private_data *data;
151         struct loadparm_context *lp_ctx;
152         const char *sam_name;
153         char *filename, *dirname;
154         int open_flags;
155         struct stat statbuf;
156
157         data = talloc_get_type_abort(ldb_module_get_private(module),
158                                      struct partition_private_data);
159         if (!data || !data->metadata) {
160                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
161                                         "partition_metadata: metadata not initialized");
162         }
163
164         tmp_ctx = talloc_new(NULL);
165         if (tmp_ctx == NULL) {
166                 return ldb_module_oom(module);
167         }
168
169         sam_name = (const char *)ldb_get_opaque(ldb, "ldb_url");
170         if (strncmp("tdb://", sam_name, 6) == 0) {
171                 sam_name += 6;
172         }
173         if (!sam_name) {
174                 talloc_free(tmp_ctx);
175                 return ldb_operr(ldb);
176         }
177         filename = talloc_asprintf(tmp_ctx, "%s.d/metadata.tdb", sam_name);
178         if (!filename) {
179                 talloc_free(tmp_ctx);
180                 return ldb_oom(ldb);
181         }
182
183         open_flags = O_RDWR;
184         if (create) {
185                 open_flags |= O_CREAT;
186
187                 /* While provisioning, sam.ldb.d directory may not exist,
188                  * so create it. Ignore errors, if it already exists. */
189                 dirname = talloc_asprintf(tmp_ctx, "%s.d", sam_name);
190                 if (!dirname) {
191                         talloc_free(tmp_ctx);
192                         return ldb_oom(ldb);
193                 }
194
195                 mkdir(dirname, 0700);
196                 talloc_free(dirname);
197         } else {
198                 if (stat(filename, &statbuf) != 0) {
199                         talloc_free(tmp_ctx);
200                         return LDB_ERR_OPERATIONS_ERROR;
201                 }
202         }
203
204         lp_ctx = talloc_get_type_abort(ldb_get_opaque(ldb, "loadparm"),
205                                        struct loadparm_context);
206
207         data->metadata->db = tdb_wrap_open(data->metadata, filename, 10,
208                                               TDB_DEFAULT, open_flags, 0660,
209                                               lp_ctx);
210         if (data->metadata->db == NULL) {
211                 talloc_free(tmp_ctx);
212                 if (create) {
213                         ldb_debug(ldb, LDB_DEBUG_ERROR,
214                                   "partition_metadata: Unable to create %s",
215                                   filename);
216                 }
217                 return LDB_ERR_OPERATIONS_ERROR;
218         }
219
220         talloc_free(tmp_ctx);
221         return LDB_SUCCESS;
222 }
223
224
225 /*
226  * Set the sequence number calculated from older logic (sum of primary sequence
227  * numbers for each partition) as LDB_METADATA_SEQ_NUM key.
228  */
229 static int partition_metadata_set_sequence_number(struct ldb_module *module)
230 {
231         struct partition_private_data *data;
232         struct ldb_result *res;
233         struct ldb_request *req;
234         struct ldb_seqnum_request *seq;
235         struct ldb_seqnum_result *seqr;
236         struct ldb_extended *ext;
237         TALLOC_CTX *tmp_ctx;
238         int ret;
239         uint64_t seq_number;
240
241         data = talloc_get_type_abort(ldb_module_get_private(module),
242                                     struct partition_private_data);
243         if (!data || !data->metadata) {
244                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
245                                         "partition_metadata: metadata not initialized");
246         }
247
248         tmp_ctx = talloc_new(data->metadata);
249         if (tmp_ctx == NULL) {
250                 return ldb_module_oom(module);
251         }
252
253         res = talloc_zero(tmp_ctx, struct ldb_result);
254         if (res == NULL) {
255                 talloc_free(tmp_ctx);
256                 return ldb_module_oom(module);
257         }
258
259         seq = talloc_zero(tmp_ctx, struct ldb_seqnum_request);
260         if (seq == NULL) {
261                 talloc_free(tmp_ctx);
262                 return ldb_module_oom(module);
263         }
264         seq->type = LDB_SEQ_HIGHEST_SEQ;
265
266         /* Build an extended request, so it can be passed to each partition in
267            partition_sequence_number_from_partitions() */
268         ret = ldb_build_extended_req(&req,
269                                      ldb_module_get_ctx(module),
270                                      tmp_ctx,
271                                      LDB_EXTENDED_SEQUENCE_NUMBER,
272                                      seq,
273                                      NULL,
274                                      res,
275                                      ldb_extended_default_callback,
276                                      NULL);
277         LDB_REQ_SET_LOCATION(req);
278         if (ret != LDB_SUCCESS) {
279                 talloc_free(tmp_ctx);
280                 return ret;
281         }
282
283         ret = partition_sequence_number_from_partitions(module, req, &ext);
284         if (ret != LDB_SUCCESS) {
285                 talloc_free(tmp_ctx);
286                 return ret;
287         }
288
289         seqr = talloc_get_type_abort(ext->data, struct ldb_seqnum_result);
290         seq_number = seqr->seq_num;
291
292         talloc_free(tmp_ctx);
293
294         return partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, seq_number, true);
295 }
296
297
298 /*
299  * Initialize metadata. Load metadata.tdb.
300  * If missing, create it and fill in sequence number
301  */
302 int partition_metadata_init(struct ldb_module *module)
303 {
304         struct partition_private_data *data;
305         int ret;
306
307         data = talloc_get_type_abort(ldb_module_get_private(module),
308                                      struct partition_private_data);
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                 goto end;
318         }
319
320         /* metadata.tdb does not exist, create it */
321         DEBUG(2, ("partition_metadata: Migrating partition metadata\n"));
322         ret = partition_metadata_open(module, true);
323         if (ret != LDB_SUCCESS) {
324                 talloc_free(data->metadata);
325                 data->metadata = NULL;
326                 goto end;
327         }
328
329         ret = partition_metadata_set_sequence_number(module);
330         if (ret != LDB_SUCCESS) {
331                 talloc_free(data->metadata);
332                 data->metadata = NULL;
333         }
334
335 end:
336         return ret;
337 }
338
339
340 /*
341  * Read the sequence number, default to 0 if LDB_METADATA_SEQ_NUM key is missing
342  */
343 int partition_metadata_sequence_number(struct ldb_module *module, uint64_t *value)
344 {
345         return partition_metadata_get_uint64(module,
346                                              LDB_METADATA_SEQ_NUM,
347                                              value,
348                                              0);
349 }
350
351
352 /*
353  * Increment the sequence number, returning the new sequence number
354  */
355 int partition_metadata_sequence_number_increment(struct ldb_module *module, uint64_t *value)
356 {
357         struct partition_private_data *data;
358         int ret;
359
360         data = talloc_get_type_abort(ldb_module_get_private(module),
361                                     struct partition_private_data);
362         if (!data || !data->metadata) {
363                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
364                                         "partition_metadata: metadata not initialized");
365         }
366
367         if (data->metadata->in_transaction == 0) {
368                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
369                                         "partition_metadata: increment sequence number without transaction");
370         }
371
372         ret = partition_metadata_get_uint64(module, LDB_METADATA_SEQ_NUM, value, 0);
373         if (ret != LDB_SUCCESS) {
374                 return ret;
375         }
376
377         (*value)++;
378         ret = partition_metadata_set_uint64(module, LDB_METADATA_SEQ_NUM, *value, false);
379         return ret;
380 }
381
382
383 /*
384  * Transaction start
385  */
386 int partition_metadata_start_trans(struct ldb_module *module)
387 {
388         struct partition_private_data *data;
389         struct tdb_context *tdb;
390
391         data = talloc_get_type_abort(ldb_module_get_private(module),
392                                      struct partition_private_data);
393         if (!data || !data->metadata || !data->metadata->db) {
394                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
395                                         "partition_metadata: metadata not initialized");
396         }
397         tdb = data->metadata->db->tdb;
398
399         if (tdb_transaction_start(tdb) != 0) {
400                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
401                                         tdb_errorstr_compat(tdb));
402         }
403
404         data->metadata->in_transaction++;
405
406         return LDB_SUCCESS;
407 }
408
409
410 /*
411  * Transaction prepare commit
412  */
413 int partition_metadata_prepare_commit(struct ldb_module *module)
414 {
415         struct partition_private_data *data;
416         struct tdb_context *tdb;
417
418         data = talloc_get_type_abort(ldb_module_get_private(module),
419                                      struct partition_private_data);
420         if (!data || !data->metadata || !data->metadata->db) {
421                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
422                                         "partition_metadata: metadata not initialized");
423         }
424         tdb = data->metadata->db->tdb;
425
426         if (data->metadata->in_transaction == 0) {
427                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
428                                         "partition_metadata: not in transaction");
429         }
430
431         if (tdb_transaction_prepare_commit(tdb) != 0) {
432                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
433                                         tdb_errorstr_compat(tdb));
434         }
435
436         return LDB_SUCCESS;
437 }
438
439
440 /*
441  * Transaction end
442  */
443 int partition_metadata_end_trans(struct ldb_module *module)
444 {
445         struct partition_private_data *data;
446         struct tdb_context *tdb;
447
448         data = talloc_get_type_abort(ldb_module_get_private(module),
449                                      struct partition_private_data);
450         if (!data || !data->metadata || !data->metadata->db) {
451                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
452                                         "partition_metadata: metadata not initialized");
453         }
454         tdb = data->metadata->db->tdb;
455
456         if (data->metadata->in_transaction == 0) {
457                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
458                                         "partition_metadata: not in transaction");
459         }
460
461         data->metadata->in_transaction--;
462
463         if (tdb_transaction_commit(tdb) != 0) {
464                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
465                                         tdb_errorstr_compat(tdb));
466         }
467
468         return LDB_SUCCESS;
469 }
470
471
472 /*
473  * Transaction delete
474  */
475 int partition_metadata_del_trans(struct ldb_module *module)
476 {
477         struct partition_private_data *data;
478         struct tdb_context *tdb;
479
480         data = talloc_get_type_abort(ldb_module_get_private(module),
481                                      struct partition_private_data);
482         if (!data || !data->metadata || !data->metadata->db) {
483                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
484                                         "partition_metadata: metadata not initialized");
485         }
486         tdb = data->metadata->db->tdb;
487
488         if (data->metadata->in_transaction == 0) {
489                 return ldb_module_error(module, LDB_ERR_OPERATIONS_ERROR,
490                                         "partition_metadata: not in transaction");
491         }
492
493         data->metadata->in_transaction--;
494
495         tdb_transaction_cancel(tdb);
496
497         return LDB_SUCCESS;
498 }