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