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_key_value/ldb_kv.h"
61
62 #define TEST_BE "tdb"
63
64 struct ldbtest_ctx {
65 struct tevent_context *ev;
66 struct ldb_context *ldb;
67
68 const char *dbfile;
69
70 const char *dbpath;
71 };
72
unlink_old_db(struct ldbtest_ctx * test_ctx)73 static void unlink_old_db(struct ldbtest_ctx *test_ctx)
74 {
75 int ret;
76
77 errno = 0;
78 ret = unlink(test_ctx->dbfile);
79 if (ret == -1 && errno != ENOENT) {
80 fail();
81 }
82 }
83
ldbtest_noconn_setup(void ** state)84 static int ldbtest_noconn_setup(void **state)
85 {
86 struct ldbtest_ctx *test_ctx;
87
88 test_ctx = talloc_zero(NULL, struct ldbtest_ctx);
89 assert_non_null(test_ctx);
90
91 test_ctx->ev = tevent_context_init(test_ctx);
92 assert_non_null(test_ctx->ev);
93
94 test_ctx->ldb = ldb_init(test_ctx, test_ctx->ev);
95 assert_non_null(test_ctx->ldb);
96
97 test_ctx->dbfile = talloc_strdup(test_ctx, "apitest.ldb");
98 assert_non_null(test_ctx->dbfile);
99
100 test_ctx->dbpath = talloc_asprintf(test_ctx,
101 TEST_BE"://%s", test_ctx->dbfile);
102 assert_non_null(test_ctx->dbpath);
103
104 unlink_old_db(test_ctx);
105 *state = test_ctx;
106 return 0;
107 }
108
ldbtest_noconn_teardown(void ** state)109 static int ldbtest_noconn_teardown(void **state)
110 {
111 struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
112 struct ldbtest_ctx);
113
114 unlink_old_db(test_ctx);
115 talloc_free(test_ctx);
116 return 0;
117 }
118
ldbtest_setup(void ** state)119 static int ldbtest_setup(void **state)
120 {
121 struct ldbtest_ctx *test_ctx;
122 int ret;
123 struct ldb_ldif *ldif;
124 const char *index_ldif = \
125 "dn: @INDEXLIST\n"
126 "@IDXGUID: objectUUID\n"
127 "@IDX_DN_GUID: GUID\n"
128 "\n";
129
130 ldbtest_noconn_setup((void **) &test_ctx);
131
132 ret = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, NULL);
133 assert_int_equal(ret, 0);
134
135 while ((ldif = ldb_ldif_read_string(test_ctx->ldb, &index_ldif))) {
136 ret = ldb_add(test_ctx->ldb, ldif->msg);
137 assert_int_equal(ret, LDB_SUCCESS);
138 }
139 *state = test_ctx;
140 return 0;
141 }
142
ldbtest_teardown(void ** state)143 static int ldbtest_teardown(void **state)
144 {
145 struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
146 struct ldbtest_ctx);
147 ldbtest_noconn_teardown((void **) &test_ctx);
148 return 0;
149 }
150
151
get_tdb_context(struct ldb_context * ldb)152 static TDB_CONTEXT *get_tdb_context(struct ldb_context *ldb)
153 {
154 void *data = NULL;
155 struct ldb_kv_private *ldb_kv = NULL;
156 TDB_CONTEXT *tdb = NULL;
157
158 data = ldb_module_get_private(ldb->modules);
159 assert_non_null(data);
160
161 ldb_kv = talloc_get_type(data, struct ldb_kv_private);
162 assert_non_null(ldb_kv);
163
164 tdb = ldb_kv->tdb;
165 assert_non_null(tdb);
166
167 return tdb;
168 }
169
test_multiple_opens(void ** state)170 static void test_multiple_opens(void **state)
171 {
172 struct ldb_context *ldb1 = NULL;
173 struct ldb_context *ldb2 = NULL;
174 struct ldb_context *ldb3 = NULL;
175 TDB_CONTEXT *tdb1 = NULL;
176 TDB_CONTEXT *tdb2 = NULL;
177 TDB_CONTEXT *tdb3 = NULL;
178 int ret;
179 struct ldbtest_ctx *test_ctx = NULL;
180
181 test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx);
182
183 /*
184 * Open the database again
185 */
186 ldb1 = ldb_init(test_ctx, test_ctx->ev);
187 ret = ldb_connect(ldb1, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
188 assert_int_equal(ret, 0);
189
190 ldb2 = ldb_init(test_ctx, test_ctx->ev);
191 ret = ldb_connect(ldb2, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
192 assert_int_equal(ret, 0);
193
194 ldb3 = ldb_init(test_ctx, test_ctx->ev);
195 ret = ldb_connect(ldb3, test_ctx->dbpath, 0, NULL);
196 assert_int_equal(ret, 0);
197 /*
198 * We now have 3 ldb's open pointing to the same on disk database
199 * they should all share the same MDB_env
200 */
201 tdb1 = get_tdb_context(ldb1);
202 tdb2 = get_tdb_context(ldb2);
203 tdb3 = get_tdb_context(ldb3);
204
205 assert_ptr_equal(tdb1, tdb2);
206 assert_ptr_equal(tdb1, tdb3);
207 }
208
test_multiple_opens_across_fork(void ** state)209 static void test_multiple_opens_across_fork(void **state)
210 {
211 struct ldb_context *ldb1 = NULL;
212 struct ldb_context *ldb2 = NULL;
213 TDB_CONTEXT *tdb1 = NULL;
214 TDB_CONTEXT *tdb2 = NULL;
215 int ret;
216 struct ldbtest_ctx *test_ctx = NULL;
217 int pipes[2];
218 char buf[2];
219 int wstatus;
220 pid_t pid, child_pid;
221
222 test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx);
223
224 /*
225 * Open the database again
226 */
227 ldb1 = ldb_init(test_ctx, test_ctx->ev);
228 ret = ldb_connect(ldb1, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
229 assert_int_equal(ret, 0);
230
231 ldb2 = ldb_init(test_ctx, test_ctx->ev);
232 ret = ldb_connect(ldb2, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
233 assert_int_equal(ret, 0);
234
235 tdb1 = get_tdb_context(ldb1);
236 tdb2 = get_tdb_context(ldb2);
237
238 ret = pipe(pipes);
239 assert_int_equal(ret, 0);
240
241 child_pid = fork();
242 if (child_pid == 0) {
243 struct ldb_context *ldb3 = NULL;
244 TDB_CONTEXT *tdb3 = NULL;
245
246 close(pipes[0]);
247 ldb3 = ldb_init(test_ctx, test_ctx->ev);
248 ret = ldb_connect(ldb3, test_ctx->dbpath, 0, NULL);
249 if (ret != 0) {
250 print_error(__location__": ldb_connect returned (%d)\n",
251 ret);
252 exit(ret);
253 }
254 tdb3 = get_tdb_context(ldb3);
255 if (tdb1 != tdb2) {
256 print_error(__location__": tdb1 != tdb2\n");
257 exit(LDB_ERR_OPERATIONS_ERROR);
258 }
259 if (tdb1 != tdb3) {
260 print_error(__location__": tdb1 != tdb3\n");
261 exit(LDB_ERR_OPERATIONS_ERROR);
262 }
263 ret = write(pipes[1], "GO", 2);
264 if (ret != 2) {
265 print_error(__location__
266 " write returned (%d)",
267 ret);
268 exit(LDB_ERR_OPERATIONS_ERROR);
269 }
270 exit(LDB_SUCCESS);
271 }
272 close(pipes[1]);
273 ret = read(pipes[0], buf, 2);
274 assert_int_equal(ret, 2);
275
276 pid = waitpid(child_pid, &wstatus, 0);
277 assert_int_equal(pid, child_pid);
278
279 assert_true(WIFEXITED(wstatus));
280
281 assert_int_equal(WEXITSTATUS(wstatus), 0);
282 }
283
test_multiple_opens_across_fork_triggers_reopen(void ** state)284 static void test_multiple_opens_across_fork_triggers_reopen(void **state)
285 {
286 struct ldb_context *ldb1 = NULL;
287 struct ldb_context *ldb2 = NULL;
288 TDB_CONTEXT *tdb1 = NULL;
289 TDB_CONTEXT *tdb2 = NULL;
290 int ret;
291 struct ldbtest_ctx *test_ctx = NULL;
292 int pipes[2];
293 char buf[2];
294 int wstatus;
295 pid_t pid, child_pid;
296
297 test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx);
298
299 /*
300 * Open the database again
301 */
302 ldb1 = ldb_init(test_ctx, test_ctx->ev);
303 ret = ldb_connect(ldb1, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
304 assert_int_equal(ret, 0);
305
306 ldb2 = ldb_init(test_ctx, test_ctx->ev);
307 ret = ldb_connect(ldb2, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
308 assert_int_equal(ret, 0);
309
310 tdb1 = get_tdb_context(ldb1);
311 tdb2 = get_tdb_context(ldb2);
312 assert_ptr_equal(tdb1, tdb2);
313
314 /*
315 * Break the internal tdb_reopen() by making a
316 * transaction
317 *
318 * This shows that the tdb_reopen() is called, which is
319 * essential if the host OS does not have pread()
320 */
321 ret = tdb_transaction_start(tdb1);
322 assert_int_equal(ret, 0);
323
324 ret = pipe(pipes);
325 assert_int_equal(ret, 0);
326
327 child_pid = fork();
328 if (child_pid == 0) {
329 struct ldb_context *ldb3 = NULL;
330
331 close(pipes[0]);
332 ldb3 = ldb_init(test_ctx, test_ctx->ev);
333
334 /*
335 * This should fail as we have taken out a lock
336 * against the raw TDB above, and tdb_reopen()
337 * will fail in that state.
338 *
339 * This check matters as tdb_reopen() is important
340 * if the host does not have pread()
341 */
342 ret = ldb_connect(ldb3, test_ctx->dbpath, 0, NULL);
343 if (ret == 0) {
344 print_error(__location__": ldb_connect expected "
345 "LDB_ERR_OPERATIONS_ERROR "
346 "returned (%d)\n",
347 ret);
348 exit(5000);
349 }
350 ret = write(pipes[1], "GO", 2);
351 if (ret != 2) {
352 print_error(__location__
353 " write returned (%d)",
354 ret);
355 exit(LDB_ERR_OPERATIONS_ERROR);
356 }
357 exit(LDB_SUCCESS);
358 }
359 close(pipes[1]);
360 ret = read(pipes[0], buf, 2);
361 assert_int_equal(ret, 2);
362
363 pid = waitpid(child_pid, &wstatus, 0);
364 assert_int_equal(pid, child_pid);
365
366 assert_true(WIFEXITED(wstatus));
367
368 assert_int_equal(WEXITSTATUS(wstatus), 0);
369 }
370
main(int argc,const char ** argv)371 int main(int argc, const char **argv)
372 {
373 const struct CMUnitTest tests[] = {
374 cmocka_unit_test_setup_teardown(
375 test_multiple_opens,
376 ldbtest_setup,
377 ldbtest_teardown),
378 cmocka_unit_test_setup_teardown(
379 test_multiple_opens_across_fork,
380 ldbtest_setup,
381 ldbtest_teardown),
382 cmocka_unit_test_setup_teardown(
383 test_multiple_opens_across_fork_triggers_reopen,
384 ldbtest_setup,
385 ldbtest_teardown),
386 };
387
388 return cmocka_run_group_tests(tests, NULL, NULL);
389 }
390