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