Line data Source code
1 : /*
2 : * tdb 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 : * tdb 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 :
73 6 : static void unlink_old_db(struct ldbtest_ctx *test_ctx)
74 : {
75 6 : int ret;
76 :
77 6 : errno = 0;
78 6 : ret = unlink(test_ctx->dbfile);
79 6 : if (ret == -1 && errno != ENOENT) {
80 0 : fail();
81 : }
82 6 : }
83 :
84 3 : static int ldbtest_noconn_setup(void **state)
85 : {
86 3 : struct ldbtest_ctx *test_ctx;
87 :
88 3 : test_ctx = talloc_zero(NULL, struct ldbtest_ctx);
89 3 : assert_non_null(test_ctx);
90 :
91 3 : test_ctx->ev = tevent_context_init(test_ctx);
92 3 : assert_non_null(test_ctx->ev);
93 :
94 3 : test_ctx->ldb = ldb_init(test_ctx, test_ctx->ev);
95 3 : assert_non_null(test_ctx->ldb);
96 :
97 3 : test_ctx->dbfile = talloc_strdup(test_ctx, "apitest.ldb");
98 3 : assert_non_null(test_ctx->dbfile);
99 :
100 3 : test_ctx->dbpath = talloc_asprintf(test_ctx,
101 : TEST_BE"://%s", test_ctx->dbfile);
102 3 : assert_non_null(test_ctx->dbpath);
103 :
104 3 : unlink_old_db(test_ctx);
105 3 : *state = test_ctx;
106 3 : return 0;
107 : }
108 :
109 3 : static int ldbtest_noconn_teardown(void **state)
110 : {
111 3 : struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
112 : struct ldbtest_ctx);
113 :
114 3 : unlink_old_db(test_ctx);
115 3 : talloc_free(test_ctx);
116 3 : return 0;
117 : }
118 :
119 3 : static int ldbtest_setup(void **state)
120 : {
121 3 : struct ldbtest_ctx *test_ctx;
122 3 : int ret;
123 3 : struct ldb_ldif *ldif;
124 3 : const char *index_ldif = \
125 : "dn: @INDEXLIST\n"
126 : "@IDXGUID: objectUUID\n"
127 : "@IDX_DN_GUID: GUID\n"
128 : "\n";
129 :
130 3 : ldbtest_noconn_setup((void **) &test_ctx);
131 :
132 3 : ret = ldb_connect(test_ctx->ldb, test_ctx->dbpath, 0, NULL);
133 3 : assert_int_equal(ret, 0);
134 :
135 6 : while ((ldif = ldb_ldif_read_string(test_ctx->ldb, &index_ldif))) {
136 3 : ret = ldb_add(test_ctx->ldb, ldif->msg);
137 3 : assert_int_equal(ret, LDB_SUCCESS);
138 : }
139 3 : *state = test_ctx;
140 3 : return 0;
141 : }
142 :
143 3 : static int ldbtest_teardown(void **state)
144 : {
145 3 : struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
146 : struct ldbtest_ctx);
147 3 : ldbtest_noconn_teardown((void **) &test_ctx);
148 3 : return 0;
149 : }
150 :
151 :
152 8 : static TDB_CONTEXT *get_tdb_context(struct ldb_context *ldb)
153 : {
154 8 : void *data = NULL;
155 8 : struct ldb_kv_private *ldb_kv = NULL;
156 8 : TDB_CONTEXT *tdb = NULL;
157 :
158 8 : data = ldb_module_get_private(ldb->modules);
159 8 : assert_non_null(data);
160 :
161 8 : ldb_kv = talloc_get_type(data, struct ldb_kv_private);
162 8 : assert_non_null(ldb_kv);
163 :
164 8 : tdb = ldb_kv->tdb;
165 8 : assert_non_null(tdb);
166 :
167 8 : return tdb;
168 : }
169 :
170 1 : static void test_multiple_opens(void **state)
171 : {
172 1 : struct ldb_context *ldb1 = NULL;
173 1 : struct ldb_context *ldb2 = NULL;
174 1 : struct ldb_context *ldb3 = NULL;
175 1 : TDB_CONTEXT *tdb1 = NULL;
176 1 : TDB_CONTEXT *tdb2 = NULL;
177 1 : TDB_CONTEXT *tdb3 = NULL;
178 1 : int ret;
179 1 : struct ldbtest_ctx *test_ctx = NULL;
180 :
181 1 : test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx);
182 :
183 : /*
184 : * Open the database again
185 : */
186 1 : ldb1 = ldb_init(test_ctx, test_ctx->ev);
187 1 : ret = ldb_connect(ldb1, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
188 1 : assert_int_equal(ret, 0);
189 :
190 1 : ldb2 = ldb_init(test_ctx, test_ctx->ev);
191 1 : ret = ldb_connect(ldb2, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
192 1 : assert_int_equal(ret, 0);
193 :
194 1 : ldb3 = ldb_init(test_ctx, test_ctx->ev);
195 1 : ret = ldb_connect(ldb3, test_ctx->dbpath, 0, NULL);
196 1 : 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 1 : tdb1 = get_tdb_context(ldb1);
202 1 : tdb2 = get_tdb_context(ldb2);
203 1 : tdb3 = get_tdb_context(ldb3);
204 :
205 1 : assert_ptr_equal(tdb1, tdb2);
206 1 : assert_ptr_equal(tdb1, tdb3);
207 1 : }
208 :
209 1 : static void test_multiple_opens_across_fork(void **state)
210 : {
211 1 : struct ldb_context *ldb1 = NULL;
212 1 : struct ldb_context *ldb2 = NULL;
213 1 : TDB_CONTEXT *tdb1 = NULL;
214 1 : TDB_CONTEXT *tdb2 = NULL;
215 1 : int ret;
216 1 : struct ldbtest_ctx *test_ctx = NULL;
217 1 : int pipes[2];
218 1 : char buf[2];
219 1 : int wstatus;
220 1 : pid_t pid, child_pid;
221 :
222 1 : test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx);
223 :
224 : /*
225 : * Open the database again
226 : */
227 1 : ldb1 = ldb_init(test_ctx, test_ctx->ev);
228 1 : ret = ldb_connect(ldb1, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
229 1 : assert_int_equal(ret, 0);
230 :
231 1 : ldb2 = ldb_init(test_ctx, test_ctx->ev);
232 1 : ret = ldb_connect(ldb2, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
233 1 : assert_int_equal(ret, 0);
234 :
235 1 : tdb1 = get_tdb_context(ldb1);
236 1 : tdb2 = get_tdb_context(ldb2);
237 :
238 1 : ret = pipe(pipes);
239 1 : assert_int_equal(ret, 0);
240 :
241 1 : child_pid = fork();
242 2 : if (child_pid == 0) {
243 1 : struct ldb_context *ldb3 = NULL;
244 1 : TDB_CONTEXT *tdb3 = NULL;
245 :
246 1 : close(pipes[0]);
247 1 : ldb3 = ldb_init(test_ctx, test_ctx->ev);
248 1 : ret = ldb_connect(ldb3, test_ctx->dbpath, 0, NULL);
249 1 : if (ret != 0) {
250 0 : print_error(__location__": ldb_connect returned (%d)\n",
251 : ret);
252 0 : exit(ret);
253 : }
254 1 : tdb3 = get_tdb_context(ldb3);
255 1 : if (tdb1 != tdb2) {
256 0 : print_error(__location__": tdb1 != tdb2\n");
257 0 : exit(LDB_ERR_OPERATIONS_ERROR);
258 : }
259 1 : if (tdb1 != tdb3) {
260 0 : print_error(__location__": tdb1 != tdb3\n");
261 0 : exit(LDB_ERR_OPERATIONS_ERROR);
262 : }
263 1 : ret = write(pipes[1], "GO", 2);
264 1 : if (ret != 2) {
265 0 : print_error(__location__
266 : " write returned (%d)",
267 : ret);
268 0 : exit(LDB_ERR_OPERATIONS_ERROR);
269 : }
270 1 : exit(LDB_SUCCESS);
271 : }
272 1 : close(pipes[1]);
273 1 : ret = read(pipes[0], buf, 2);
274 1 : assert_int_equal(ret, 2);
275 :
276 1 : pid = waitpid(child_pid, &wstatus, 0);
277 1 : assert_int_equal(pid, child_pid);
278 :
279 1 : assert_true(WIFEXITED(wstatus));
280 :
281 1 : assert_int_equal(WEXITSTATUS(wstatus), 0);
282 1 : }
283 :
284 1 : static void test_multiple_opens_across_fork_triggers_reopen(void **state)
285 : {
286 1 : struct ldb_context *ldb1 = NULL;
287 1 : struct ldb_context *ldb2 = NULL;
288 1 : TDB_CONTEXT *tdb1 = NULL;
289 1 : TDB_CONTEXT *tdb2 = NULL;
290 1 : int ret;
291 1 : struct ldbtest_ctx *test_ctx = NULL;
292 1 : int pipes[2];
293 1 : char buf[2];
294 1 : int wstatus;
295 1 : pid_t pid, child_pid;
296 :
297 1 : test_ctx = talloc_get_type_abort(*state, struct ldbtest_ctx);
298 :
299 : /*
300 : * Open the database again
301 : */
302 1 : ldb1 = ldb_init(test_ctx, test_ctx->ev);
303 1 : ret = ldb_connect(ldb1, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
304 1 : assert_int_equal(ret, 0);
305 :
306 1 : ldb2 = ldb_init(test_ctx, test_ctx->ev);
307 1 : ret = ldb_connect(ldb2, test_ctx->dbpath, LDB_FLG_RDONLY, NULL);
308 1 : assert_int_equal(ret, 0);
309 :
310 1 : tdb1 = get_tdb_context(ldb1);
311 1 : tdb2 = get_tdb_context(ldb2);
312 1 : 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 1 : ret = tdb_transaction_start(tdb1);
322 1 : assert_int_equal(ret, 0);
323 :
324 1 : ret = pipe(pipes);
325 1 : assert_int_equal(ret, 0);
326 :
327 1 : child_pid = fork();
328 2 : if (child_pid == 0) {
329 1 : struct ldb_context *ldb3 = NULL;
330 :
331 1 : close(pipes[0]);
332 1 : 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 1 : ret = ldb_connect(ldb3, test_ctx->dbpath, 0, NULL);
343 1 : if (ret == 0) {
344 0 : print_error(__location__": ldb_connect expected "
345 : "LDB_ERR_OPERATIONS_ERROR "
346 : "returned (%d)\n",
347 : ret);
348 0 : exit(5000);
349 : }
350 1 : ret = write(pipes[1], "GO", 2);
351 1 : if (ret != 2) {
352 0 : print_error(__location__
353 : " write returned (%d)",
354 : ret);
355 0 : exit(LDB_ERR_OPERATIONS_ERROR);
356 : }
357 1 : exit(LDB_SUCCESS);
358 : }
359 1 : close(pipes[1]);
360 1 : ret = read(pipes[0], buf, 2);
361 1 : assert_int_equal(ret, 2);
362 :
363 1 : pid = waitpid(child_pid, &wstatus, 0);
364 1 : assert_int_equal(pid, child_pid);
365 :
366 1 : assert_true(WIFEXITED(wstatus));
367 :
368 1 : assert_int_equal(WEXITSTATUS(wstatus), 0);
369 1 : }
370 :
371 1 : int main(int argc, const char **argv)
372 : {
373 1 : 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 1 : cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
389 :
390 1 : return cmocka_run_group_tests(tests, NULL, NULL);
391 : }
|