Line data Source code
1 : /*
2 : * lmdb backend specific tests for ldb
3 : *
4 : * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2018
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 :
21 : /*
22 : * lmdb backend specific tests for ldb
23 : *
24 : * Setup and tear down code copied from ldb_mod_op_test.c
25 : */
26 :
27 : /*
28 : * from cmocka.c:
29 : * These headers or their equivalents should be included prior to
30 : * including
31 : * this header file.
32 : *
33 : * #include <stdarg.h>
34 : * #include <stddef.h>
35 : * #include <setjmp.h>
36 : *
37 : * This allows test applications to use custom definitions of C standard
38 : * library functions and types.
39 : *
40 : */
41 : #include <stdarg.h>
42 : #include <stddef.h>
43 : #include <stdint.h>
44 : #include <setjmp.h>
45 : #include <cmocka.h>
46 :
47 : #include <errno.h>
48 : #include <unistd.h>
49 : #include <talloc.h>
50 : #include <tevent.h>
51 : #include <ldb.h>
52 : #include <ldb_module.h>
53 : #include <ldb_private.h>
54 : #include <string.h>
55 : #include <ctype.h>
56 :
57 : #include <sys/wait.h>
58 :
59 : #include "../ldb_tdb/ldb_tdb.h"
60 : #include "../ldb_mdb/ldb_mdb.h"
61 : #include "../ldb_key_value/ldb_kv.h"
62 :
63 : #define TEST_BE "mdb"
64 :
65 : #define LMDB_MAX_KEY_SIZE 511
66 :
67 : struct ldbtest_ctx {
68 : struct tevent_context *ev;
69 : struct ldb_context *ldb;
70 :
71 : const char *dbfile;
72 : const char *lockfile; /* lockfile is separate */
73 :
74 : const char *dbpath;
75 : };
76 :
77 16 : static void unlink_old_db(struct ldbtest_ctx *test_ctx)
78 : {
79 16 : int ret;
80 :
81 16 : errno = 0;
82 16 : ret = unlink(test_ctx->lockfile);
83 16 : if (ret == -1 && errno != ENOENT) {
84 0 : fail();
85 : }
86 :
87 16 : errno = 0;
88 16 : ret = unlink(test_ctx->dbfile);
89 16 : if (ret == -1 && errno != ENOENT) {
90 0 : fail();
91 : }
92 16 : }
93 :
94 8 : static int ldbtest_noconn_setup(void **state)
95 : {
96 8 : struct ldbtest_ctx *test_ctx;
97 :
98 8 : test_ctx = talloc_zero(NULL, struct ldbtest_ctx);
99 8 : assert_non_null(test_ctx);
100 :
101 8 : test_ctx->ev = tevent_context_init(test_ctx);
102 8 : assert_non_null(test_ctx->ev);
103 :
104 8 : test_ctx->ldb = ldb_init(test_ctx, test_ctx->ev);
105 8 : assert_non_null(test_ctx->ldb);
106 :
107 8 : test_ctx->dbfile = talloc_strdup(test_ctx, "apitest.ldb");
108 8 : assert_non_null(test_ctx->dbfile);
109 :
110 8 : test_ctx->lockfile = talloc_asprintf(test_ctx, "%s-lock",
111 : test_ctx->dbfile);
112 8 : assert_non_null(test_ctx->lockfile);
113 :
114 8 : test_ctx->dbpath = talloc_asprintf(test_ctx,
115 : TEST_BE"://%s", test_ctx->dbfile);
116 8 : assert_non_null(test_ctx->dbpath);
117 :
118 8 : unlink_old_db(test_ctx);
119 8 : *state = test_ctx;
120 8 : return 0;
121 : }
122 :
123 8 : static int ldbtest_noconn_teardown(void **state)
124 : {
125 8 : struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
126 : struct ldbtest_ctx);
127 :
128 8 : unlink_old_db(test_ctx);
129 8 : talloc_free(test_ctx);
130 8 : return 0;
131 : }
132 :
133 5 : static int ldbtest_setup(void **state)
134 : {
135 5 : struct ldbtest_ctx *test_ctx;
136 5 : int ret;
137 5 : struct ldb_ldif *ldif;
138 5 : const char *index_ldif = \
139 : "dn: @INDEXLIST\n"
140 : "@IDXGUID: objectUUID\n"
141 : "@IDX_DN_GUID: GUID\n"
142 : "\n";
143 :
144 5 : ldbtest_noconn_setup((void **) &test_ctx);
145 :
146 5 : ret = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, NULL);
147 5 : assert_int_equal(ret, 0);
148 :
149 10 : while ((ldif = ldb_ldif_read_string(test_ctx->ldb, &index_ldif))) {
150 5 : ret = ldb_add(test_ctx->ldb, ldif->msg);
151 5 : assert_int_equal(ret, LDB_SUCCESS);
152 : }
153 5 : *state = test_ctx;
154 5 : return 0;
155 : }
156 :
157 8 : static int ldbtest_teardown(void **state)
158 : {
159 8 : struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
160 : struct ldbtest_ctx);
161 8 : ldbtest_noconn_teardown((void **) &test_ctx);
162 8 : return 0;
163 : }
164 :
165 1 : static void test_ldb_add_key_len_gt_max(void **state)
166 : {
167 1 : int ret;
168 1 : int xs_size = 0;
169 1 : struct ldb_message *msg;
170 1 : struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
171 : struct ldbtest_ctx);
172 1 : char *xs = NULL;
173 1 : TALLOC_CTX *tmp_ctx;
174 :
175 1 : tmp_ctx = talloc_new(test_ctx);
176 1 : assert_non_null(tmp_ctx);
177 :
178 1 : msg = ldb_msg_new(tmp_ctx);
179 1 : assert_non_null(msg);
180 :
181 : /*
182 : * The zero terminator is part of the key if we were not in
183 : * GUID mode
184 : */
185 :
186 1 : xs_size = LMDB_MAX_KEY_SIZE - 7; /* "dn=dc=" and the zero terminator */
187 1 : xs_size += 1; /* want key on char too long */
188 1 : xs = talloc_zero_size(tmp_ctx, (xs_size + 1));
189 1 : memset(xs, 'x', xs_size);
190 :
191 1 : msg->dn = ldb_dn_new_fmt(msg, test_ctx->ldb, "dc=%s", xs);
192 1 : assert_non_null(msg->dn);
193 :
194 1 : ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
195 1 : assert_int_equal(ret, 0);
196 :
197 1 : ret = ldb_msg_add_string(msg, "objectUUID", "0123456789abcdef");
198 1 : assert_int_equal(ret, 0);
199 :
200 1 : ret = ldb_add(test_ctx->ldb, msg);
201 1 : assert_int_equal(ret, LDB_SUCCESS);
202 :
203 1 : talloc_free(tmp_ctx);
204 1 : }
205 :
206 1 : static void test_ldb_add_key_len_2x_gt_max(void **state)
207 : {
208 1 : int ret;
209 1 : int xs_size = 0;
210 1 : struct ldb_message *msg;
211 1 : struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
212 : struct ldbtest_ctx);
213 1 : char *xs = NULL;
214 1 : TALLOC_CTX *tmp_ctx;
215 :
216 1 : tmp_ctx = talloc_new(test_ctx);
217 1 : assert_non_null(tmp_ctx);
218 :
219 1 : msg = ldb_msg_new(tmp_ctx);
220 1 : assert_non_null(msg);
221 :
222 : /*
223 : * The zero terminator is part of the key if we were not in
224 : * GUID mode
225 : */
226 :
227 1 : xs_size = 2 * LMDB_MAX_KEY_SIZE;
228 1 : xs = talloc_zero_size(tmp_ctx, (xs_size + 1));
229 1 : memset(xs, 'x', xs_size);
230 :
231 1 : msg->dn = ldb_dn_new_fmt(msg, test_ctx->ldb, "dc=%s", xs);
232 1 : assert_non_null(msg->dn);
233 :
234 1 : ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
235 1 : assert_int_equal(ret, 0);
236 :
237 1 : ret = ldb_msg_add_string(msg, "objectUUID", "0123456789abcdef");
238 1 : assert_int_equal(ret, 0);
239 :
240 1 : ret = ldb_add(test_ctx->ldb, msg);
241 1 : assert_int_equal(ret, LDB_SUCCESS);
242 :
243 1 : talloc_free(tmp_ctx);
244 1 : }
245 :
246 1 : static void test_ldb_add_key_len_eq_max(void **state)
247 : {
248 1 : int ret;
249 1 : int xs_size = 0;
250 1 : struct ldb_message *msg;
251 1 : struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
252 : struct ldbtest_ctx);
253 1 : char *xs = NULL;
254 1 : TALLOC_CTX *tmp_ctx;
255 :
256 1 : tmp_ctx = talloc_new(test_ctx);
257 1 : assert_non_null(tmp_ctx);
258 :
259 1 : msg = ldb_msg_new(tmp_ctx);
260 1 : assert_non_null(msg);
261 :
262 : /*
263 : * The zero terminator is part of the key if we were not in
264 : * GUID mode
265 : */
266 :
267 1 : xs_size = LMDB_MAX_KEY_SIZE - 7; /* "dn=dc=" and the zero terminator */
268 1 : xs = talloc_zero_size(tmp_ctx, (xs_size + 1));
269 1 : memset(xs, 'x', xs_size);
270 :
271 1 : msg->dn = ldb_dn_new_fmt(msg, test_ctx->ldb, "dc=%s", xs);
272 1 : assert_non_null(msg->dn);
273 :
274 1 : ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
275 1 : assert_int_equal(ret, 0);
276 :
277 1 : ret = ldb_msg_add_string(msg, "objectUUID", "0123456789abcdef");
278 1 : assert_int_equal(ret, 0);
279 :
280 1 : ret = ldb_add(test_ctx->ldb, msg);
281 1 : assert_int_equal(ret, 0);
282 :
283 1 : talloc_free(tmp_ctx);
284 1 : }
285 :
286 3 : static int ldbtest_setup_noguid(void **state)
287 : {
288 3 : struct ldbtest_ctx *test_ctx;
289 3 : int ret;
290 :
291 3 : ldbtest_noconn_setup((void **) &test_ctx);
292 :
293 3 : ret = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, NULL);
294 3 : assert_int_equal(ret, 0);
295 :
296 3 : *state = test_ctx;
297 3 : return 0;
298 : }
299 :
300 1 : static void test_ldb_add_special_key_len_gt_max(void **state)
301 : {
302 1 : int ret;
303 1 : int xs_size = 0;
304 1 : struct ldb_message *msg;
305 1 : struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
306 : struct ldbtest_ctx);
307 1 : char *xs = NULL;
308 1 : TALLOC_CTX *tmp_ctx;
309 :
310 1 : tmp_ctx = talloc_new(test_ctx);
311 1 : assert_non_null(tmp_ctx);
312 :
313 1 : msg = ldb_msg_new(tmp_ctx);
314 1 : assert_non_null(msg);
315 :
316 : /*
317 : * The zero terminator is part of the key if we were not in
318 : * GUID mode
319 : */
320 :
321 1 : xs_size = LMDB_MAX_KEY_SIZE - 5; /* "dn=@" and the zero terminator */
322 1 : xs_size += 1; /* want key on char too long */
323 1 : xs = talloc_zero_size(tmp_ctx, (xs_size + 1));
324 1 : memset(xs, 'x', xs_size);
325 :
326 1 : msg->dn = ldb_dn_new_fmt(msg, test_ctx->ldb, "@%s", xs);
327 1 : assert_non_null(msg->dn);
328 :
329 1 : ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
330 1 : assert_int_equal(ret, 0);
331 :
332 1 : ret = ldb_add(test_ctx->ldb, msg);
333 1 : assert_int_equal(ret, LDB_ERR_PROTOCOL_ERROR);
334 :
335 1 : talloc_free(tmp_ctx);
336 1 : }
337 :
338 1 : static void test_ldb_add_special_key_len_eq_max(void **state)
339 : {
340 1 : int ret;
341 1 : int xs_size = 0;
342 1 : struct ldb_message *msg;
343 1 : struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
344 : struct ldbtest_ctx);
345 1 : char *xs = NULL;
346 1 : TALLOC_CTX *tmp_ctx;
347 :
348 1 : tmp_ctx = talloc_new(test_ctx);
349 1 : assert_non_null(tmp_ctx);
350 :
351 1 : msg = ldb_msg_new(tmp_ctx);
352 1 : assert_non_null(msg);
353 :
354 : /*
355 : * The zero terminator is part of the key if we were not in
356 : * GUID mode
357 : */
358 :
359 1 : xs_size = LMDB_MAX_KEY_SIZE - 5; /* "dn=@" and the zero terminator */
360 1 : xs = talloc_zero_size(tmp_ctx, (xs_size + 1));
361 1 : memset(xs, 'x', xs_size);
362 :
363 1 : msg->dn = ldb_dn_new_fmt(msg, test_ctx->ldb, "@%s", xs);
364 1 : assert_non_null(msg->dn);
365 :
366 1 : ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
367 1 : assert_int_equal(ret, 0);
368 :
369 1 : ret = ldb_add(test_ctx->ldb, msg);
370 1 : assert_int_equal(ret, LDB_SUCCESS);
371 :
372 1 : talloc_free(tmp_ctx);
373 1 : }
374 :
375 1 : static void test_ldb_add_dn_no_guid_mode(void **state)
376 : {
377 1 : int ret;
378 1 : int xs_size = 0;
379 1 : struct ldb_message *msg;
380 1 : struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
381 : struct ldbtest_ctx);
382 1 : char *xs = NULL;
383 1 : TALLOC_CTX *tmp_ctx;
384 :
385 1 : tmp_ctx = talloc_new(test_ctx);
386 1 : assert_non_null(tmp_ctx);
387 :
388 1 : msg = ldb_msg_new(tmp_ctx);
389 1 : assert_non_null(msg);
390 :
391 : /*
392 : * The zero terminator is part of the key if we were not in
393 : * GUID mode
394 : */
395 :
396 1 : xs_size = LMDB_MAX_KEY_SIZE - 7; /* "dn=dc=" and the zero terminator */
397 1 : xs_size += 1; /* want key on char too long */
398 1 : xs = talloc_zero_size(tmp_ctx, (xs_size + 1));
399 1 : memset(xs, 'x', xs_size);
400 :
401 1 : msg->dn = ldb_dn_new_fmt(msg, test_ctx->ldb, "dc=%s", xs);
402 1 : assert_non_null(msg->dn);
403 :
404 1 : ret = ldb_msg_add_string(msg, "cn", "test_cn_val");
405 1 : assert_int_equal(ret, 0);
406 :
407 1 : ret = ldb_msg_add_string(msg, "objectUUID", "0123456789abcdef");
408 1 : assert_int_equal(ret, 0);
409 :
410 1 : ret = ldb_add(test_ctx->ldb, msg);
411 1 : assert_int_equal(ret, LDB_ERR_UNWILLING_TO_PERFORM);
412 :
413 1 : talloc_free(tmp_ctx);
414 1 : }
415 :
416 6 : static struct MDB_env *get_mdb_env(struct ldb_context *ldb)
417 : {
418 6 : void *data = NULL;
419 6 : struct ldb_kv_private *ldb_kv = NULL;
420 6 : struct lmdb_private *lmdb = NULL;
421 6 : struct MDB_env *env = NULL;
422 :
423 6 : data = ldb_module_get_private(ldb->modules);
424 6 : assert_non_null(data);
425 :
426 6 : ldb_kv = talloc_get_type(data, struct ldb_kv_private);
427 6 : assert_non_null(ldb_kv);
428 :
429 6 : lmdb = ldb_kv->lmdb_private;
430 6 : assert_non_null(lmdb);
431 :
432 6 : env = lmdb->env;
433 6 : assert_non_null(env);
434 :
435 6 : return env;
436 : }
437 :
438 1 : static void test_multiple_opens(void **state)
439 : {
440 1 : struct ldb_context *ldb1 = NULL;
441 1 : struct ldb_context *ldb2 = NULL;
442 1 : struct ldb_context *ldb3 = NULL;
443 1 : struct MDB_env *env1 = NULL;
444 1 : struct MDB_env *env2 = NULL;
445 1 : struct MDB_env *env3 = NULL;
446 1 : int ret;
447 1 : struct ldbtest_ctx *test_ctx = NULL;
448 :
449 1 : test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx);
450 :
451 : /*
452 : * Open the database again
453 : */
454 1 : ldb1 = ldb_init(test_ctx, test_ctx->ev);
455 1 : ret = ldb_connect(ldb1, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
456 1 : assert_int_equal(ret, 0);
457 :
458 1 : ldb2 = ldb_init(test_ctx, test_ctx->ev);
459 1 : ret = ldb_connect(ldb2, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
460 1 : assert_int_equal(ret, 0);
461 :
462 1 : ldb3 = ldb_init(test_ctx, test_ctx->ev);
463 1 : ret = ldb_connect(ldb3, test_ctx->dbpath, 0, NULL);
464 1 : assert_int_equal(ret, 0);
465 : /*
466 : * We now have 3 ldb's open pointing to the same on disk database
467 : * they should all share the same MDB_env
468 : */
469 1 : env1 = get_mdb_env(ldb1);
470 1 : env2 = get_mdb_env(ldb2);
471 1 : env3 = get_mdb_env(ldb3);
472 :
473 1 : assert_ptr_equal(env1, env2);
474 1 : assert_ptr_equal(env1, env3);
475 1 : }
476 :
477 1 : static void test_multiple_opens_across_fork(void **state)
478 : {
479 1 : struct ldb_context *ldb1 = NULL;
480 1 : struct ldb_context *ldb2 = NULL;
481 1 : struct MDB_env *env1 = NULL;
482 1 : struct MDB_env *env2 = NULL;
483 1 : int ret;
484 1 : struct ldbtest_ctx *test_ctx = NULL;
485 1 : int pipes[2];
486 1 : char buf[2];
487 1 : int wstatus;
488 1 : pid_t pid, child_pid;
489 :
490 1 : test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx);
491 :
492 : /*
493 : * Open the database again
494 : */
495 1 : ldb1 = ldb_init(test_ctx, test_ctx->ev);
496 1 : ret = ldb_connect(ldb1, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
497 1 : assert_int_equal(ret, 0);
498 :
499 1 : ldb2 = ldb_init(test_ctx, test_ctx->ev);
500 1 : ret = ldb_connect(ldb2, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
501 1 : assert_int_equal(ret, 0);
502 :
503 1 : env1 = get_mdb_env(ldb1);
504 1 : env2 = get_mdb_env(ldb2);
505 :
506 1 : ret = pipe(pipes);
507 1 : assert_int_equal(ret, 0);
508 :
509 1 : child_pid = fork();
510 2 : if (child_pid == 0) {
511 1 : struct ldb_context *ldb3 = NULL;
512 1 : struct MDB_env *env3 = NULL;
513 :
514 1 : close(pipes[0]);
515 1 : ldb3 = ldb_init(test_ctx, test_ctx->ev);
516 1 : ret = ldb_connect(ldb3, test_ctx->dbpath, 0, NULL);
517 1 : if (ret != 0) {
518 0 : print_error(__location__": ldb_connect returned (%d)\n",
519 : ret);
520 0 : exit(ret);
521 : }
522 1 : env3 = get_mdb_env(ldb3);
523 1 : if (env1 != env2) {
524 0 : print_error(__location__": env1 != env2\n");
525 0 : exit(LDB_ERR_OPERATIONS_ERROR);
526 : }
527 1 : if (env1 == env3) {
528 0 : print_error(__location__": env1 == env3\n");
529 0 : exit(LDB_ERR_OPERATIONS_ERROR);
530 : }
531 1 : ret = write(pipes[1], "GO", 2);
532 1 : if (ret != 2) {
533 0 : print_error(__location__
534 : " write returned (%d)",
535 : ret);
536 0 : exit(LDB_ERR_OPERATIONS_ERROR);
537 : }
538 1 : exit(LDB_SUCCESS);
539 : }
540 1 : close(pipes[1]);
541 1 : ret = read(pipes[0], buf, 2);
542 1 : assert_int_equal(ret, 2);
543 :
544 1 : pid = waitpid(child_pid, &wstatus, 0);
545 1 : assert_int_equal(pid, child_pid);
546 :
547 1 : assert_true(WIFEXITED(wstatus));
548 :
549 1 : assert_int_equal(WEXITSTATUS(wstatus), 0);
550 1 : }
551 :
552 1 : int main(int argc, const char **argv)
553 : {
554 1 : const struct CMUnitTest tests[] = {
555 : cmocka_unit_test_setup_teardown(
556 : test_ldb_add_key_len_eq_max,
557 : ldbtest_setup,
558 : ldbtest_teardown),
559 : cmocka_unit_test_setup_teardown(
560 : test_ldb_add_key_len_gt_max,
561 : ldbtest_setup,
562 : ldbtest_teardown),
563 : cmocka_unit_test_setup_teardown(
564 : test_ldb_add_key_len_2x_gt_max,
565 : ldbtest_setup,
566 : ldbtest_teardown),
567 : cmocka_unit_test_setup_teardown(
568 : test_ldb_add_special_key_len_eq_max,
569 : ldbtest_setup_noguid,
570 : ldbtest_teardown),
571 : cmocka_unit_test_setup_teardown(
572 : test_ldb_add_special_key_len_gt_max,
573 : ldbtest_setup_noguid,
574 : ldbtest_teardown),
575 : cmocka_unit_test_setup_teardown(
576 : test_ldb_add_dn_no_guid_mode,
577 : ldbtest_setup_noguid,
578 : ldbtest_teardown),
579 : cmocka_unit_test_setup_teardown(
580 : test_multiple_opens,
581 : ldbtest_setup,
582 : ldbtest_teardown),
583 : cmocka_unit_test_setup_teardown(
584 : test_multiple_opens_across_fork,
585 : ldbtest_setup,
586 : ldbtest_teardown),
587 : };
588 :
589 1 : cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
590 :
591 1 : return cmocka_run_group_tests(tests, NULL, NULL);
592 : }
|