LCOV - code coverage report
Current view: top level - lib/ldb/tests - ldb_tdb_test.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 170 183 92.9 %
Date: 2024-04-21 15:09:00 Functions: 10 10 100.0 %

          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             : }

Generated by: LCOV version 1.14