LCOV - code coverage report
Current view: top level - source3/lib - tdb_validate.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 154 264 58.3 %
Date: 2024-04-21 15:09:00 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Unix SMB/CIFS implementation.
       3             :  *
       4             :  * A general tdb content validation mechanism
       5             :  *
       6             :  * Copyright (C) Michael Adam      2007
       7             :  *
       8             :  * This program is free software; you can redistribute it and/or modify
       9             :  * it under the terms of the GNU General Public License as published by
      10             :  * the Free Software Foundation; either version 3 of the License, or
      11             :  * (at your option) any later version.
      12             :  *
      13             :  * This program is distributed in the hope that it will be useful,
      14             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :  * GNU General Public License for more details.
      17             :  *
      18             :  * You should have received a copy of the GNU General Public License
      19             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             :  */
      21             : 
      22             : #include "includes.h"
      23             : #include "system/filesys.h"
      24             : #include "util_tdb.h"
      25             : #include "tdb_validate.h"
      26             : 
      27             : /*
      28             :  * internal validation function, executed by the child.
      29             :  */
      30          13 : static int tdb_validate_child(struct tdb_context *tdb,
      31             :                               tdb_validate_data_func validate_fn)
      32             : {
      33          13 :         int ret = 1;
      34           1 :         int check_rc;
      35          13 :         int num_entries = 0;
      36           1 :         struct tdb_validation_status v_status;
      37             : 
      38          13 :         v_status.tdb_error = False;
      39          13 :         v_status.bad_freelist = False;
      40          13 :         v_status.bad_entry = False;
      41          13 :         v_status.unknown_key = False;
      42          13 :         v_status.success = True;
      43             : 
      44          13 :         if (!tdb) {
      45           0 :                 v_status.tdb_error = True;
      46           0 :                 v_status.success = False;
      47           0 :                 goto out;
      48             :         }
      49             : 
      50             :         /*
      51             :          * we can simplify this by passing a check function,
      52             :          * but I don't want to change all the callers...
      53             :          */
      54          13 :         check_rc = tdb_check(tdb, NULL, NULL);
      55          13 :         if (check_rc != 0) {
      56           0 :                 v_status.tdb_error = True;
      57           0 :                 v_status.success = False;
      58           0 :                 goto out;
      59             :         }
      60             : 
      61             :         /* Check if the tdb's freelist is good. */
      62          13 :         if (tdb_validate_freelist(tdb, &num_entries) == -1) {
      63           0 :                 v_status.bad_freelist = True;
      64           0 :                 v_status.success = False;
      65           0 :                 goto out;
      66             :         }
      67             : 
      68          13 :         DEBUG(10,("tdb_validate_child: tdb %s freelist has %d entries\n",
      69             :                   tdb_name(tdb), num_entries));
      70             : 
      71             :         /* Now traverse the tdb to validate it. */
      72          13 :         num_entries = tdb_traverse(tdb, validate_fn, (void *)&v_status);
      73          13 :         if (!v_status.success) {
      74           1 :                 goto out;
      75          12 :         } else if (num_entries < 0) {
      76           0 :                 v_status.tdb_error = True;
      77           0 :                 v_status.success = False;
      78           0 :                 goto out;
      79             :         }
      80             : 
      81          12 :         DEBUG(10,("tdb_validate_child: tdb %s is good with %d entries\n",
      82             :                   tdb_name(tdb), num_entries));
      83          12 :         ret = 0; /* Cache is good. */
      84             : 
      85          13 : out:
      86          13 :         DEBUG(10,   ("tdb_validate_child: summary of validation status:\n"));
      87          13 :         DEBUGADD(10,(" * tdb error: %s\n", v_status.tdb_error ? "yes" : "no"));
      88          13 :         DEBUGADD(10,(" * bad freelist: %s\n",v_status.bad_freelist?"yes":"no"));
      89          13 :         DEBUGADD(10,(" * bad entry: %s\n", v_status.bad_entry ? "yes" : "no"));
      90          13 :         DEBUGADD(10,(" * unknown key: %s\n", v_status.unknown_key?"yes":"no"));
      91          13 :         DEBUGADD(10,(" => overall success: %s\n", v_status.success?"yes":"no"));
      92             : 
      93          13 :         return ret;
      94             : }
      95             : 
      96             : /*
      97             :  * tdb validation function.
      98             :  * returns 0 if tdb is ok, != 0 if it isn't.
      99             :  * this function expects an opened tdb.
     100             :  */
     101           9 : int tdb_validate(struct tdb_context *tdb, tdb_validate_data_func validate_fn)
     102             : {
     103           9 :         pid_t child_pid = -1;
     104           9 :         int child_status = 0;
     105           9 :         int wait_pid = 0;
     106           9 :         int ret = 1;
     107             : 
     108           9 :         if (tdb == NULL) {
     109           0 :                 DEBUG(1, ("Error: tdb_validate called with tdb == NULL\n"));
     110           0 :                 return ret;
     111             :         }
     112             : 
     113           9 :         DEBUG(5, ("tdb_validate called for tdb '%s'\n", tdb_name(tdb)));
     114             : 
     115             :         /* fork and let the child do the validation.
     116             :          * benefit: no need to twist signal handlers and panic functions.
     117             :          * just let the child panic. we catch the signal. */
     118             : 
     119           9 :         DEBUG(10, ("tdb_validate: forking to let child do validation.\n"));
     120           9 :         child_pid = fork();
     121          22 :         if (child_pid == 0) {
     122             :                 /* child code */
     123          13 :                 DEBUG(10, ("tdb_validate (validation child): created\n"));
     124          13 :                 DEBUG(10, ("tdb_validate (validation child): "
     125             :                            "calling tdb_validate_child\n"));
     126          13 :                 exit(tdb_validate_child(tdb, validate_fn));
     127             :         }
     128           9 :         else if (child_pid < 0) {
     129           0 :                 DEBUG(1, ("tdb_validate: fork for validation failed.\n"));
     130           0 :                 goto done;
     131             :         }
     132             : 
     133             :         /* parent */
     134             : 
     135           9 :         DEBUG(10, ("tdb_validate: fork succeeded, child PID = %u\n",
     136             :                 (unsigned int)child_pid));
     137             : 
     138           9 :         DEBUG(10, ("tdb_validate: waiting for child to finish...\n"));
     139           9 :         while  ((wait_pid = waitpid(child_pid, &child_status, 0)) < 0) {
     140           0 :                 if (errno == EINTR) {
     141           0 :                         DEBUG(10, ("tdb_validate: got signal during waitpid, "
     142             :                                    "retrying\n"));
     143           0 :                         errno = 0;
     144           0 :                         continue;
     145             :                 }
     146           0 :                 DEBUG(1, ("tdb_validate: waitpid failed with error '%s'.\n",
     147             :                           strerror(errno)));
     148           0 :                 goto done;
     149             :         }
     150           9 :         if (wait_pid != child_pid) {
     151           0 :                 DEBUG(1, ("tdb_validate: waitpid returned pid %d, "
     152             :                           "but %u was expected\n", wait_pid, (unsigned int)child_pid));
     153           0 :                 goto done;
     154             :         }
     155             : 
     156           9 :         DEBUG(10, ("tdb_validate: validating child returned.\n"));
     157           9 :         if (WIFEXITED(child_status)) {
     158           9 :                 DEBUG(10, ("tdb_validate: child exited, code %d.\n",
     159             :                            WEXITSTATUS(child_status)));
     160           9 :                 ret = WEXITSTATUS(child_status);
     161             :         }
     162           9 :         if (WIFSIGNALED(child_status)) {
     163           0 :                 DEBUG(10, ("tdb_validate: child terminated by signal %d\n",
     164             :                            WTERMSIG(child_status)));
     165             : #ifdef WCOREDUMP
     166           0 :                 if (WCOREDUMP(child_status)) {
     167           0 :                         DEBUGADD(10, ("core dumped\n"));
     168             :                 }
     169             : #endif
     170           0 :                 ret = WTERMSIG(child_status);
     171             :         }
     172           9 :         if (WIFSTOPPED(child_status)) {
     173           0 :                 DEBUG(10, ("tdb_validate: child was stopped by signal %d\n",
     174             :                            WSTOPSIG(child_status)));
     175           0 :                 ret = WSTOPSIG(child_status);
     176             :         }
     177             : 
     178           9 : done:
     179           9 :         DEBUG(5, ("tdb_validate returning code '%d' for tdb '%s'\n", ret,
     180             :                   tdb_name(tdb)));
     181             : 
     182           8 :         return ret;
     183             : }
     184             : 
     185             : /*
     186             :  * tdb validation function.
     187             :  * returns 0 if tdb is ok, != 0 if it isn't.
     188             :  * this is a wrapper around the actual validation function that opens and closes
     189             :  * the tdb.
     190             :  */
     191           8 : int tdb_validate_open(const char *tdb_path, tdb_validate_data_func validate_fn)
     192             : {
     193           8 :         TDB_CONTEXT *tdb = NULL;
     194           8 :         int ret = 1;
     195             : 
     196           8 :         DEBUG(5, ("tdb_validate_open called for tdb '%s'\n", tdb_path));
     197             : 
     198           8 :         tdb = tdb_open_log(tdb_path, 0, TDB_DEFAULT, O_RDWR, 0);
     199           8 :         if (!tdb) {
     200           0 :                 DEBUG(1, ("Error opening tdb %s\n", tdb_path));
     201           0 :                 return ret;
     202             :         }
     203             : 
     204           8 :         ret = tdb_validate(tdb, validate_fn);
     205           8 :         tdb_close(tdb);
     206           8 :         return ret;
     207             : }
     208             : 
     209             : /*
     210             :  * tdb backup function and helpers for tdb_validate wrapper with backup
     211             :  * handling.
     212             :  */
     213             : 
     214             : /* this structure eliminates the need for a global overall status for
     215             :  * the traverse-copy */
     216             : struct tdb_copy_data {
     217             :         struct tdb_context *dst;
     218             :         bool success;
     219             : };
     220             : 
     221          32 : static int traverse_copy_fn(struct tdb_context *tdb, TDB_DATA key,
     222             :                             TDB_DATA dbuf, void *private_data)
     223             : {
     224          32 :         struct tdb_copy_data *data = (struct tdb_copy_data *)private_data;
     225             : 
     226          32 :         if (tdb_store(data->dst, key, dbuf, TDB_INSERT) != 0) {
     227           0 :                 DEBUG(4, ("Failed to insert into %s: %s\n", tdb_name(data->dst),
     228             :                           strerror(errno)));
     229           0 :                 data->success = False;
     230           0 :                 return 1;
     231             :         }
     232          32 :         return 0;
     233             : }
     234             : 
     235           8 : static int tdb_copy(struct tdb_context *src, struct tdb_context *dst)
     236             : {
     237           0 :         struct tdb_copy_data data;
     238           0 :         int count;
     239             : 
     240           8 :         data.dst = dst;
     241           8 :         data.success = True;
     242             : 
     243           8 :         count = tdb_traverse(src, traverse_copy_fn, (void *)(&data));
     244           8 :         if ((count < 0) || (data.success == False)) {
     245           0 :                 return -1;
     246             :         }
     247           8 :         return count;
     248             : }
     249             : 
     250           8 : static int tdb_verify_basic(struct tdb_context *tdb)
     251             : {
     252           8 :         return tdb_traverse(tdb, NULL, NULL);
     253             : }
     254             : 
     255             : /* this backup function is essentially taken from lib/tdb/tools/tdbbackup.tdb
     256             :  */
     257           8 : static int tdb_backup(TALLOC_CTX *ctx, const char *src_path,
     258             :                       const char *dst_path, int hash_size)
     259             : {
     260           8 :         struct tdb_context *src_tdb = NULL;
     261           8 :         struct tdb_context *dst_tdb = NULL;
     262           8 :         char *tmp_path = NULL;
     263           0 :         struct stat st;
     264           0 :         int count1, count2;
     265           8 :         int saved_errno = 0;
     266           8 :         int ret = -1;
     267             : 
     268           8 :         if (stat(src_path, &st) != 0) {
     269           0 :                 DEBUG(3, ("Could not stat '%s': %s\n", src_path,
     270             :                           strerror(errno)));
     271           0 :                 goto done;
     272             :         }
     273             : 
     274             :         /* open old tdb RDWR - so we can lock it */
     275           8 :         src_tdb = tdb_open_log(src_path, 0, TDB_DEFAULT, O_RDWR, 0);
     276           8 :         if (src_tdb == NULL) {
     277           0 :                 DEBUG(3, ("Failed to open tdb '%s'\n", src_path));
     278           0 :                 goto done;
     279             :         }
     280             : 
     281           8 :         if (tdb_lockall(src_tdb) != 0) {
     282           0 :                 DEBUG(3, ("Failed to lock tdb '%s'\n", src_path));
     283           0 :                 goto done;
     284             :         }
     285             : 
     286           8 :         tmp_path = talloc_asprintf(ctx, "%s%s", dst_path, ".tmp");
     287           8 :         if (!tmp_path) {
     288           0 :                 DEBUG(3, ("talloc fail\n"));
     289           0 :                 goto done;
     290             :         }
     291             : 
     292           8 :         unlink(tmp_path);
     293             : 
     294           8 :         if (!hash_size) {
     295           8 :                 hash_size = tdb_hash_size(src_tdb);
     296             :         }
     297             : 
     298           8 :         dst_tdb = tdb_open_log(tmp_path, hash_size,
     299             :                                TDB_DEFAULT, O_RDWR | O_CREAT | O_EXCL,
     300           8 :                                st.st_mode & 0777);
     301           8 :         if (dst_tdb == NULL) {
     302           0 :                 DEBUG(3, ("Error creating tdb '%s': %s\n", tmp_path,
     303             :                           strerror(errno)));
     304           0 :                 saved_errno = errno;
     305           0 :                 unlink(tmp_path);
     306           0 :                 goto done;
     307             :         }
     308             : 
     309           8 :         count1 = tdb_copy(src_tdb, dst_tdb);
     310           8 :         if (count1 < 0) {
     311           0 :                 DEBUG(3, ("Failed to copy tdb '%s': %s\n", src_path,
     312             :                           strerror(errno)));
     313           0 :                 tdb_close(dst_tdb);
     314           0 :                 goto done;
     315             :         }
     316             : 
     317             :         /* reopen ro and do basic verification */
     318           8 :         tdb_close(dst_tdb);
     319           8 :         dst_tdb = tdb_open_log(tmp_path, 0, TDB_DEFAULT, O_RDONLY, 0);
     320           8 :         if (!dst_tdb) {
     321           0 :                 DEBUG(3, ("Failed to reopen tdb '%s': %s\n", tmp_path,
     322             :                           strerror(errno)));
     323           0 :                 goto done;
     324             :         }
     325           8 :         count2 = tdb_verify_basic(dst_tdb);
     326           8 :         if (count2 != count1) {
     327           0 :                 DEBUG(3, ("Failed to verify result of copying tdb '%s'.\n",
     328             :                           src_path));
     329           0 :                 tdb_close(dst_tdb);
     330           0 :                 goto done;
     331             :         }
     332             : 
     333           8 :         DEBUG(10, ("tdb_backup: successfully copied %d entries\n", count1));
     334             : 
     335             :         /* make sure the new tdb has reached stable storage
     336             :          * then rename it to its destination */
     337           8 :         fsync(tdb_fd(dst_tdb));
     338           8 :         tdb_close(dst_tdb);
     339           8 :         unlink(dst_path);
     340           8 :         if (rename(tmp_path, dst_path) != 0) {
     341           0 :                 DEBUG(3, ("Failed to rename '%s' to '%s': %s\n",
     342             :                           tmp_path, dst_path, strerror(errno)));
     343           0 :                 goto done;
     344             :         }
     345             : 
     346             :         /* success */
     347           8 :         ret = 0;
     348             : 
     349           8 : done:
     350           8 :         if (src_tdb != NULL) {
     351           8 :                 tdb_close(src_tdb);
     352             :         }
     353           8 :         if (tmp_path != NULL) {
     354           8 :                 unlink(tmp_path);
     355           8 :                 TALLOC_FREE(tmp_path);
     356             :         }
     357           8 :         if (saved_errno != 0) {
     358           0 :                 errno = saved_errno;
     359             :         }
     360           8 :         return ret;
     361             : }
     362             : 
     363           8 : static int rename_file_with_suffix(TALLOC_CTX *ctx, const char *path,
     364             :                                    const char *suffix)
     365             : {
     366           8 :         int ret = -1;
     367           0 :         char *dst_path;
     368             : 
     369           8 :         dst_path = talloc_asprintf(ctx, "%s%s", path, suffix);
     370           8 :         if (dst_path == NULL) {
     371           0 :                 DEBUG(3, ("error out of memory\n"));
     372           0 :                 return ret;
     373             :         }
     374             : 
     375           8 :         ret = (rename(path, dst_path) != 0);
     376             : 
     377           8 :         if (ret == 0) {
     378           4 :                 DEBUG(5, ("moved '%s' to '%s'\n", path, dst_path));
     379           4 :         } else if (errno == ENOENT) {
     380           4 :                 DEBUG(3, ("file '%s' does not exist - so not moved\n", path));
     381           4 :                 ret = 0;
     382             :         } else {
     383           0 :                 DEBUG(3, ("error renaming %s to %s: %s\n", path, dst_path,
     384             :                           strerror(errno)));
     385             :         }
     386             : 
     387           8 :         TALLOC_FREE(dst_path);
     388           8 :         return ret;
     389             : }
     390             : 
     391             : /*
     392             :  * do a backup of a tdb, moving the destination out of the way first
     393             :  */
     394           8 : static int tdb_backup_with_rotate(TALLOC_CTX *ctx, const char *src_path,
     395             :                                   const char *dst_path, int hash_size,
     396             :                                   const char *rotate_suffix,
     397             :                                   bool retry_norotate_if_nospc,
     398             :                                   bool rename_as_last_resort_if_nospc)
     399             : {
     400           0 :         int ret;
     401             : 
     402           8 :         rename_file_with_suffix(ctx, dst_path, rotate_suffix);
     403             : 
     404           8 :         ret = tdb_backup(ctx, src_path, dst_path, hash_size);
     405             : 
     406           8 :         if (ret != 0) {
     407           0 :                 DEBUG(10, ("backup of %s failed: %s\n", src_path, strerror(errno)));
     408             :         }
     409           8 :         if ((ret != 0) && (errno == ENOSPC) && retry_norotate_if_nospc)
     410             :         {
     411           0 :                 char *rotate_path = talloc_asprintf(ctx, "%s%s", dst_path,
     412             :                                                     rotate_suffix);
     413           0 :                 if (rotate_path == NULL) {
     414           0 :                         DEBUG(10, ("talloc fail\n"));
     415           0 :                         return -1;
     416             :                 }
     417           0 :                 DEBUG(10, ("backup of %s failed due to lack of space\n",
     418             :                            src_path));
     419           0 :                 DEBUGADD(10, ("trying to free some space by removing rotated "
     420             :                               "dst %s\n", rotate_path));
     421           0 :                 if (unlink(rotate_path) == -1) {
     422           0 :                         DEBUG(10, ("unlink of %s failed: %s\n", rotate_path,
     423             :                                    strerror(errno)));
     424             :                 } else {
     425           0 :                         ret = tdb_backup(ctx, src_path, dst_path, hash_size);
     426             :                 }
     427           0 :                 TALLOC_FREE(rotate_path);
     428             :         }
     429             : 
     430           8 :         if ((ret != 0) && (errno == ENOSPC) && rename_as_last_resort_if_nospc)
     431             :         {
     432           0 :                 DEBUG(10, ("backup of %s failed due to lack of space\n", 
     433             :                            src_path));
     434           0 :                 DEBUGADD(10, ("using 'rename' as a last resort\n"));
     435           0 :                 ret = rename(src_path, dst_path);
     436             :         }
     437             : 
     438           8 :         return ret;
     439             : }
     440             : 
     441             : /*
     442             :  * validation function with backup handling:
     443             :  *
     444             :  *  - calls tdb_validate
     445             :  *  - if the tdb is ok, create a backup "name.bak", possibly moving
     446             :  *    existing backup to name.bak.old,
     447             :  *    return 0 (success) even if the backup fails
     448             :  *  - if the tdb is corrupt:
     449             :  *    - move the tdb to "name.corrupt"
     450             :  *    - check if there is valid backup.
     451             :  *      if so, restore the backup.
     452             :  *      if restore is successful, return 0 (success),
     453             :  *    - otherwise return -1 (failure)
     454             :  */
     455           8 : int tdb_validate_and_backup(const char *tdb_path,
     456             :                             tdb_validate_data_func validate_fn)
     457             : {
     458           8 :         int ret = -1;
     459           8 :         const char *backup_suffix = ".bak";
     460           8 :         const char *corrupt_suffix = ".corrupt";
     461           8 :         const char *rotate_suffix = ".old";
     462           0 :         char *tdb_path_backup;
     463           0 :         struct stat st;
     464           8 :         TALLOC_CTX *ctx = NULL;
     465             : 
     466           8 :         ctx = talloc_new(NULL);
     467           8 :         if (ctx == NULL) {
     468           0 :                 DEBUG(0, ("tdb_validate_and_backup: out of memory\n"));
     469           0 :                 goto done;
     470             :         }
     471             : 
     472           8 :         tdb_path_backup = talloc_asprintf(ctx, "%s%s", tdb_path, backup_suffix);
     473           8 :         if (!tdb_path_backup) {
     474           0 :                 DEBUG(0, ("tdb_validate_and_backup: out of memory\n"));
     475           0 :                 goto done;
     476             :         }
     477             : 
     478           8 :         ret = tdb_validate_open(tdb_path, validate_fn);
     479             : 
     480           8 :         if (ret == 0) {
     481           8 :                 DEBUG(1, ("tdb '%s' is valid\n", tdb_path));
     482           8 :                 ret = tdb_backup_with_rotate(ctx, tdb_path, tdb_path_backup, 0,
     483             :                                              rotate_suffix, True, False);
     484           8 :                 if (ret != 0) {
     485           0 :                         DEBUG(1, ("Error creating backup of tdb '%s'\n",
     486             :                                   tdb_path));
     487             :                         /* the actual validation was successful: */
     488           0 :                         ret = 0;
     489             :                 } else {
     490           8 :                         DEBUG(1, ("Created backup '%s' of tdb '%s'\n",
     491             :                                   tdb_path_backup, tdb_path));
     492             :                 }
     493             :         } else {
     494           0 :                 DEBUG(1, ("tdb '%s' is invalid\n", tdb_path));
     495             : 
     496           0 :                 ret =stat(tdb_path_backup, &st);
     497           0 :                 if (ret != 0) {
     498           0 :                         DEBUG(5, ("Could not stat '%s': %s\n", tdb_path_backup,
     499             :                                   strerror(errno)));
     500           0 :                         DEBUG(1, ("No backup found.\n"));
     501             :                 } else {
     502           0 :                         DEBUG(1, ("backup '%s' found.\n", tdb_path_backup));
     503           0 :                         ret = tdb_validate_open(tdb_path_backup, validate_fn);
     504           0 :                         if (ret != 0) {
     505           0 :                                 DEBUG(1, ("Backup '%s' is invalid.\n",
     506             :                                           tdb_path_backup));
     507             :                         }
     508             :                 }
     509             : 
     510           0 :                 if (ret != 0) {
     511           0 :                         int renamed = rename_file_with_suffix(ctx, tdb_path,
     512             :                                                               corrupt_suffix);
     513           0 :                         if (renamed != 0) {
     514           0 :                                 DEBUG(1, ("Error moving tdb to '%s%s'\n",
     515             :                                           tdb_path, corrupt_suffix));
     516             :                         } else {
     517           0 :                                 DEBUG(1, ("Corrupt tdb stored as '%s%s'\n",
     518             :                                           tdb_path, corrupt_suffix));
     519             :                         }
     520           0 :                         goto done;
     521             :                 }
     522             : 
     523           0 :                 DEBUG(1, ("valid backup '%s' found\n", tdb_path_backup));
     524           0 :                 ret = tdb_backup_with_rotate(ctx, tdb_path_backup, tdb_path, 0,
     525             :                                              corrupt_suffix, True, True);
     526           0 :                 if (ret != 0) {
     527           0 :                         DEBUG(1, ("Error restoring backup from '%s'\n",
     528             :                                   tdb_path_backup));
     529             :                 } else {
     530           0 :                         DEBUG(1, ("Restored tdb backup from '%s'\n",
     531             :                                   tdb_path_backup));
     532             :                 }
     533             :         }
     534             : 
     535           8 : done:
     536           8 :         TALLOC_FREE(ctx);
     537           8 :         return ret;
     538             : }

Generated by: LCOV version 1.14