LCOV - code coverage report
Current view: top level - lib/dbwrap - dbwrap_tdb.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 186 237 78.5 %
Date: 2024-04-21 15:09:00 Functions: 21 26 80.8 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Database interface wrapper around tdb
       4             :    Copyright (C) Volker Lendecke 2005-2007
       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             : #include "replace.h"
      21             : #include "dbwrap/dbwrap.h"
      22             : #include "dbwrap/dbwrap_private.h"
      23             : #include "dbwrap/dbwrap_tdb.h"
      24             : #include "lib/tdb_wrap/tdb_wrap.h"
      25             : #include "lib/util/util_tdb.h"
      26             : #include "lib/util/debug.h"
      27             : #include "lib/util/samba_util.h"
      28             : #include "system/filesys.h"
      29             : #include "lib/param/param.h"
      30             : #include "libcli/util/error.h"
      31             : 
      32             : struct db_tdb_ctx {
      33             :         struct tdb_wrap *wtdb;
      34             : 
      35             :         struct {
      36             :                 dev_t dev;
      37             :                 ino_t ino;
      38             :         } id;
      39             : };
      40             : 
      41             : static NTSTATUS db_tdb_storev(struct db_record *rec,
      42             :                               const TDB_DATA *dbufs, int num_dbufs, int flag);
      43             : static NTSTATUS db_tdb_delete(struct db_record *rec);
      44             : 
      45     4959665 : static void db_tdb_log_key(const char *prefix, TDB_DATA key)
      46             : {
      47     4959665 :         if (DEBUGLEVEL < 10) {
      48     4934766 :                 return;
      49             :         }
      50           0 :         if (DEBUGLEVEL == 10) {
      51             :                 /*
      52             :                  * Only fully spam at debuglevel > 10
      53             :                  */
      54           0 :                 key.dsize = MIN(10, key.dsize);
      55             :         }
      56             : 
      57           0 :         if (key.dsize < 1024) {
      58           0 :                 char keystr[key.dsize*2+1];
      59           0 :                 hex_encode_buf(keystr, key.dptr, key.dsize);
      60           0 :                 DBG_DEBUG("%s key %s\n", prefix, keystr);
      61           0 :                 return;
      62             :         }
      63             : 
      64           0 :         dump_data(DEBUGLEVEL, key.dptr, key.dsize);
      65             : }
      66             : 
      67     2479832 : static int db_tdb_record_destr(struct db_record* data)
      68             : {
      69       12449 :         struct db_tdb_ctx *ctx =
      70     2479832 :                 talloc_get_type_abort(data->private_data, struct db_tdb_ctx);
      71             : 
      72     2479832 :         db_tdb_log_key("Unlocking", data->key);
      73     2479832 :         tdb_chainunlock(ctx->wtdb->tdb, data->key);
      74     2479832 :         return 0;
      75             : }
      76             : 
      77             : struct tdb_fetch_locked_state {
      78             :         TALLOC_CTX *mem_ctx;
      79             :         struct db_record *result;
      80             : };
      81             : 
      82     2479833 : static int db_tdb_fetchlock_parse(TDB_DATA key, TDB_DATA data,
      83             :                                   void *private_data)
      84             : {
      85     2479833 :         struct tdb_fetch_locked_state *state =
      86             :                 (struct tdb_fetch_locked_state *)private_data;
      87       12450 :         struct db_record *result;
      88             : 
      89     2479833 :         result = (struct db_record *)talloc_size(
      90             :                 state->mem_ctx,
      91             :                 sizeof(struct db_record) + key.dsize + data.dsize);
      92             : 
      93     2479833 :         if (result == NULL) {
      94           0 :                 return 0;
      95             :         }
      96     2479833 :         state->result = result;
      97             : 
      98     2479833 :         result->key.dsize = key.dsize;
      99     2479833 :         result->key.dptr = ((uint8_t *)result) + sizeof(struct db_record);
     100     2479833 :         memcpy(result->key.dptr, key.dptr, key.dsize);
     101             : 
     102     2479833 :         result->value.dsize = data.dsize;
     103             : 
     104     2479833 :         if (data.dsize > 0) {
     105     1394650 :                 result->value.dptr = result->key.dptr+key.dsize;
     106     1394650 :                 memcpy(result->value.dptr, data.dptr, data.dsize);
     107             :         }
     108             :         else {
     109     1085183 :                 result->value.dptr = NULL;
     110             :         }
     111     2479833 :         result->value_valid = true;
     112             : 
     113     2479833 :         return 0;
     114             : }
     115             : 
     116     2479833 : static struct db_record *db_tdb_fetch_locked_internal(
     117             :         struct db_context *db,
     118             :         struct db_tdb_ctx *ctx,
     119             :         TALLOC_CTX *mem_ctx,
     120             :         TDB_DATA key)
     121             : {
     122       12450 :         struct tdb_fetch_locked_state state;
     123       12450 :         int ret;
     124             : 
     125     2479833 :         state = (struct tdb_fetch_locked_state) {
     126             :                 .mem_ctx = mem_ctx,
     127             :         };
     128             : 
     129     2479833 :         ret = tdb_parse_record(ctx->wtdb->tdb,
     130             :                                key,
     131             :                                db_tdb_fetchlock_parse,
     132             :                                &state);
     133     2479833 :         if ((ret < 0) && (tdb_error(ctx->wtdb->tdb) != TDB_ERR_NOEXIST)) {
     134           0 :                 tdb_chainunlock(ctx->wtdb->tdb, key);
     135           0 :                 return NULL;
     136             :         }
     137             : 
     138     2479833 :         if (state.result == NULL) {
     139     1085183 :                 db_tdb_fetchlock_parse(key, tdb_null, &state);
     140             :         }
     141             : 
     142     2479833 :         if (state.result == NULL) {
     143           0 :                 tdb_chainunlock(ctx->wtdb->tdb, key);
     144           0 :                 return NULL;
     145             :         }
     146             : 
     147     2479833 :         talloc_set_destructor(state.result, db_tdb_record_destr);
     148             : 
     149     2479833 :         state.result->private_data = ctx;
     150     2479833 :         state.result->storev = db_tdb_storev;
     151     2479833 :         state.result->delete_rec = db_tdb_delete;
     152             : 
     153     2479833 :         DBG_DEBUG("Allocated locked data %p\n", state.result);
     154             : 
     155     2479833 :         return state.result;
     156             : }
     157             : 
     158     2479833 : static struct db_record *db_tdb_fetch_locked(
     159             :         struct db_context *db, TALLOC_CTX *mem_ctx, TDB_DATA key)
     160             : {
     161     2479833 :         struct db_tdb_ctx *ctx = talloc_get_type_abort(db->private_data,
     162             :                                                        struct db_tdb_ctx);
     163             : 
     164     2479833 :         db_tdb_log_key("Locking", key);
     165     2479833 :         if (tdb_chainlock(ctx->wtdb->tdb, key) != 0) {
     166           0 :                 DEBUG(3, ("tdb_chainlock failed\n"));
     167           0 :                 return NULL;
     168             :         }
     169     2479833 :         return db_tdb_fetch_locked_internal(db, ctx, mem_ctx, key);
     170             : }
     171             : 
     172     2879452 : static NTSTATUS db_tdb_do_locked(struct db_context *db, TDB_DATA key,
     173             :                                  void (*fn)(struct db_record *rec,
     174             :                                             TDB_DATA value,
     175             :                                             void *private_data),
     176             :                                  void *private_data)
     177             : {
     178     2879452 :         struct db_tdb_ctx *ctx = talloc_get_type_abort(
     179             :                 db->private_data, struct db_tdb_ctx);
     180     2879452 :         uint8_t *buf = NULL;
     181       13134 :         struct db_record rec;
     182       13134 :         int ret;
     183             : 
     184     2879452 :         ret = tdb_chainlock(ctx->wtdb->tdb, key);
     185     2879452 :         if (ret == -1) {
     186           0 :                 enum TDB_ERROR err = tdb_error(ctx->wtdb->tdb);
     187           0 :                 DBG_DEBUG("tdb_chainlock failed: %s\n",
     188             :                           tdb_errorstr(ctx->wtdb->tdb));
     189           0 :                 return map_nt_error_from_tdb(err);
     190             :         }
     191             : 
     192     2879452 :         ret = tdb_fetch_talloc(ctx->wtdb->tdb, key, ctx, &buf);
     193             : 
     194     2879452 :         if ((ret != 0) && (ret != ENOENT)) {
     195           0 :                 DBG_DEBUG("tdb_fetch_talloc failed: %s\n",
     196             :                           strerror(errno));
     197           0 :                 tdb_chainunlock(ctx->wtdb->tdb, key);
     198           0 :                 return map_nt_error_from_unix_common(ret);
     199             :         }
     200             : 
     201     2879452 :         rec = (struct db_record) {
     202             :                 .db = db, .key = key,
     203             :                 .value_valid = false,
     204             :                 .storev = db_tdb_storev, .delete_rec = db_tdb_delete,
     205             :                 .private_data = ctx
     206             :         };
     207             : 
     208     2892586 :         fn(&rec,
     209     2879452 :            (TDB_DATA) { .dptr = buf, .dsize = talloc_get_size(buf) },
     210             :            private_data);
     211             : 
     212     2879452 :         tdb_chainunlock(ctx->wtdb->tdb, key);
     213             : 
     214     2879452 :         talloc_free(buf);
     215             : 
     216     2879452 :         return NT_STATUS_OK;
     217             : }
     218             : 
     219       55244 : static int db_tdb_exists(struct db_context *db, TDB_DATA key)
     220             : {
     221       55244 :         struct db_tdb_ctx *ctx = talloc_get_type_abort(
     222             :                 db->private_data, struct db_tdb_ctx);
     223       55244 :         return tdb_exists(ctx->wtdb->tdb, key);
     224             : }
     225             : 
     226          45 : static int db_tdb_wipe(struct db_context *db)
     227             : {
     228          45 :         struct db_tdb_ctx *ctx = talloc_get_type_abort(
     229             :                 db->private_data, struct db_tdb_ctx);
     230          45 :         return tdb_wipe_all(ctx->wtdb->tdb);
     231             : }
     232             : 
     233           0 : static int db_tdb_check(struct db_context *db)
     234             : {
     235           0 :         struct db_tdb_ctx *ctx = talloc_get_type_abort(
     236             :                 db->private_data, struct db_tdb_ctx);
     237           0 :         return tdb_check(ctx->wtdb->tdb, NULL, NULL);
     238             : }
     239             : 
     240             : struct db_tdb_parse_state {
     241             :         void (*parser)(TDB_DATA key, TDB_DATA data,
     242             :                        void *private_data);
     243             :         void *private_data;
     244             : };
     245             : 
     246             : /*
     247             :  * tdb_parse_record expects a parser returning int, mixing up tdb and
     248             :  * parser errors. Wrap around that by always returning 0 and have
     249             :  * dbwrap_parse_record expect a parser returning void.
     250             :  */
     251             : 
     252    22383865 : static int db_tdb_parser(TDB_DATA key, TDB_DATA data, void *private_data)
     253             : {
     254    22383865 :         struct db_tdb_parse_state *state =
     255             :                 (struct db_tdb_parse_state *)private_data;
     256    22383865 :         state->parser(key, data, state->private_data);
     257    22383865 :         return 0;
     258             : }
     259             : 
     260    31490784 : static NTSTATUS db_tdb_parse(struct db_context *db, TDB_DATA key,
     261             :                              void (*parser)(TDB_DATA key, TDB_DATA data,
     262             :                                            void *private_data),
     263             :                              void *private_data)
     264             : {
     265    31490784 :         struct db_tdb_ctx *ctx = talloc_get_type_abort(
     266             :                 db->private_data, struct db_tdb_ctx);
     267      108506 :         struct db_tdb_parse_state state;
     268      108506 :         int ret;
     269             : 
     270    31490784 :         state.parser = parser;
     271    31490784 :         state.private_data = private_data;
     272             : 
     273    31490784 :         ret = tdb_parse_record(ctx->wtdb->tdb, key, db_tdb_parser, &state);
     274             : 
     275    31490784 :         if (ret != 0) {
     276     9106919 :                 return map_nt_error_from_tdb(tdb_error(ctx->wtdb->tdb));
     277             :         }
     278    22383865 :         return NT_STATUS_OK;
     279             : }
     280             : 
     281     3129349 : static NTSTATUS db_tdb_storev(struct db_record *rec,
     282             :                               const TDB_DATA *dbufs, int num_dbufs, int flag)
     283             : {
     284     3129349 :         struct db_tdb_ctx *ctx = talloc_get_type_abort(rec->private_data,
     285             :                                                        struct db_tdb_ctx);
     286     3129349 :         struct tdb_context *tdb = ctx->wtdb->tdb;
     287     3129349 :         NTSTATUS status = NT_STATUS_OK;
     288       15516 :         int ret;
     289             : 
     290             :         /*
     291             :          * This has a bug: We need to replace rec->value for correct
     292             :          * operation, but right now brlock and locking don't use the value
     293             :          * anymore after it was stored.
     294             :          */
     295             : 
     296     3129349 :         ret = tdb_storev(tdb, rec->key, dbufs, num_dbufs, flag);
     297     3129349 :         if (ret == -1) {
     298           0 :                 enum TDB_ERROR err = tdb_error(tdb);
     299           0 :                 status = map_nt_error_from_tdb(err);
     300             :         }
     301     3129349 :         return status;
     302             : }
     303             : 
     304     1671935 : static NTSTATUS db_tdb_delete(struct db_record *rec)
     305             : {
     306     1671935 :         struct db_tdb_ctx *ctx = talloc_get_type_abort(rec->private_data,
     307             :                                                        struct db_tdb_ctx);
     308             : 
     309     1671935 :         if (tdb_delete(ctx->wtdb->tdb, rec->key) == 0) {
     310     1454618 :                 return NT_STATUS_OK;
     311             :         }
     312             : 
     313      217317 :         if (tdb_error(ctx->wtdb->tdb) == TDB_ERR_NOEXIST) {
     314      217317 :                 return NT_STATUS_NOT_FOUND;
     315             :         }
     316             : 
     317           0 :         return NT_STATUS_UNSUCCESSFUL;
     318             : }
     319             : 
     320             : struct db_tdb_traverse_ctx {
     321             :         struct db_context *db;
     322             :         int (*f)(struct db_record *rec, void *private_data);
     323             :         void *private_data;
     324             : };
     325             : 
     326         730 : static int db_tdb_traverse_func(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
     327             :                                 void *private_data)
     328             : {
     329         730 :         struct db_tdb_traverse_ctx *ctx =
     330             :                 (struct db_tdb_traverse_ctx *)private_data;
     331          35 :         struct db_record rec;
     332             : 
     333         730 :         rec.key = kbuf;
     334         730 :         rec.value = dbuf;
     335         730 :         rec.value_valid = true;
     336         730 :         rec.storev = db_tdb_storev;
     337         730 :         rec.delete_rec = db_tdb_delete;
     338         730 :         rec.private_data = ctx->db->private_data;
     339         730 :         rec.db = ctx->db;
     340             : 
     341         730 :         return ctx->f(&rec, ctx->private_data);
     342             : }
     343             : 
     344         342 : static int db_tdb_traverse(struct db_context *db,
     345             :                            int (*f)(struct db_record *rec, void *private_data),
     346             :                            void *private_data)
     347             : {
     348          14 :         struct db_tdb_ctx *db_ctx =
     349         342 :                 talloc_get_type_abort(db->private_data, struct db_tdb_ctx);
     350          14 :         struct db_tdb_traverse_ctx ctx;
     351             : 
     352         342 :         ctx.db = db;
     353         342 :         ctx.f = f;
     354         342 :         ctx.private_data = private_data;
     355         342 :         return tdb_traverse(db_ctx->wtdb->tdb, db_tdb_traverse_func, &ctx);
     356             : }
     357             : 
     358           0 : static NTSTATUS db_tdb_storev_deny(struct db_record *rec,
     359             :                                    const TDB_DATA *dbufs, int num_dbufs,
     360             :                                    int flag)
     361             : {
     362           0 :         return NT_STATUS_MEDIA_WRITE_PROTECTED;
     363             : }
     364             : 
     365           0 : static NTSTATUS db_tdb_delete_deny(struct db_record *rec)
     366             : {
     367           0 :         return NT_STATUS_MEDIA_WRITE_PROTECTED;
     368             : }
     369             : 
     370     3449592 : static int db_tdb_traverse_read_func(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
     371             :                                 void *private_data)
     372             : {
     373     3449592 :         struct db_tdb_traverse_ctx *ctx =
     374             :                 (struct db_tdb_traverse_ctx *)private_data;
     375         681 :         struct db_record rec;
     376             : 
     377     3449592 :         rec.key = kbuf;
     378     3449592 :         rec.value = dbuf;
     379     3449592 :         rec.value_valid = true;
     380     3449592 :         rec.storev = db_tdb_storev_deny;
     381     3449592 :         rec.delete_rec = db_tdb_delete_deny;
     382     3449592 :         rec.private_data = ctx->db->private_data;
     383     3449592 :         rec.db = ctx->db;
     384             : 
     385     3449592 :         return ctx->f(&rec, ctx->private_data);
     386             : }
     387             : 
     388      231734 : static int db_tdb_traverse_read(struct db_context *db,
     389             :                            int (*f)(struct db_record *rec, void *private_data),
     390             :                            void *private_data)
     391             : {
     392          53 :         struct db_tdb_ctx *db_ctx =
     393      231734 :                 talloc_get_type_abort(db->private_data, struct db_tdb_ctx);
     394          53 :         struct db_tdb_traverse_ctx ctx;
     395             : 
     396      231734 :         ctx.db = db;
     397      231734 :         ctx.f = f;
     398      231734 :         ctx.private_data = private_data;
     399      231734 :         return tdb_traverse_read(db_ctx->wtdb->tdb, db_tdb_traverse_read_func, &ctx);
     400             : }
     401             : 
     402     2477033 : static int db_tdb_get_seqnum(struct db_context *db)
     403             : 
     404             : {
     405        1967 :         struct db_tdb_ctx *db_ctx =
     406     2477033 :                 talloc_get_type_abort(db->private_data, struct db_tdb_ctx);
     407     2477033 :         return tdb_get_seqnum(db_ctx->wtdb->tdb);
     408             : }
     409             : 
     410      117485 : static int db_tdb_transaction_start(struct db_context *db)
     411             : {
     412         625 :         struct db_tdb_ctx *db_ctx =
     413      117485 :                 talloc_get_type_abort(db->private_data, struct db_tdb_ctx);
     414      117485 :         return tdb_transaction_start(db_ctx->wtdb->tdb) ? -1 : 0;
     415             : }
     416             : 
     417           0 : static NTSTATUS db_tdb_transaction_start_nonblock(struct db_context *db)
     418             : {
     419           0 :         struct db_tdb_ctx *db_ctx =
     420           0 :                 talloc_get_type_abort(db->private_data, struct db_tdb_ctx);
     421           0 :         int ret;
     422             : 
     423           0 :         ret = tdb_transaction_start_nonblock(db_ctx->wtdb->tdb);
     424           0 :         if (ret != 0) {
     425           0 :                 return map_nt_error_from_tdb(tdb_error(db_ctx->wtdb->tdb));
     426             :         }
     427           0 :         return NT_STATUS_OK;
     428             : }
     429             : 
     430      117257 : static int db_tdb_transaction_commit(struct db_context *db)
     431             : {
     432         618 :         struct db_tdb_ctx *db_ctx =
     433      117257 :                 talloc_get_type_abort(db->private_data, struct db_tdb_ctx);
     434      117257 :         return tdb_transaction_commit(db_ctx->wtdb->tdb) ? -1 : 0;
     435             : }
     436             : 
     437         227 : static int db_tdb_transaction_cancel(struct db_context *db)
     438             : {
     439           6 :         struct db_tdb_ctx *db_ctx =
     440         227 :                 talloc_get_type_abort(db->private_data, struct db_tdb_ctx);
     441         227 :         tdb_transaction_cancel(db_ctx->wtdb->tdb);
     442         227 :         return 0;
     443             : }
     444             : 
     445           0 : static size_t db_tdb_id(struct db_context *db, uint8_t *id, size_t idlen)
     446             : {
     447           0 :         struct db_tdb_ctx *db_ctx =
     448           0 :                 talloc_get_type_abort(db->private_data, struct db_tdb_ctx);
     449             : 
     450           0 :         if (idlen >= sizeof(db_ctx->id)) {
     451           0 :                 memcpy(id, &db_ctx->id, sizeof(db_ctx->id));
     452             :         }
     453             : 
     454           0 :         return sizeof(db_ctx->id);
     455             : }
     456             : 
     457      310251 : struct db_context *db_open_tdb(TALLOC_CTX *mem_ctx,
     458             :                                const char *name,
     459             :                                int hash_size, int tdb_flags,
     460             :                                int open_flags, mode_t mode,
     461             :                                enum dbwrap_lock_order lock_order,
     462             :                                uint64_t dbwrap_flags)
     463             : {
     464      310251 :         struct db_context *result = NULL;
     465       11757 :         struct db_tdb_ctx *db_tdb;
     466       11757 :         struct stat st;
     467             : 
     468      310251 :         result = talloc_zero(mem_ctx, struct db_context);
     469      310251 :         if (result == NULL) {
     470           0 :                 DEBUG(0, ("talloc failed\n"));
     471           0 :                 goto fail;
     472             :         }
     473             : 
     474      310251 :         result->private_data = db_tdb = talloc(result, struct db_tdb_ctx);
     475      310251 :         if (db_tdb == NULL) {
     476           0 :                 DEBUG(0, ("talloc failed\n"));
     477           0 :                 goto fail;
     478             :         }
     479      310251 :         result->lock_order = lock_order;
     480             : 
     481      310251 :         db_tdb->wtdb = tdb_wrap_open(db_tdb, name, hash_size, tdb_flags,
     482             :                                      open_flags, mode);
     483      310251 :         if (db_tdb->wtdb == NULL) {
     484         829 :                 DEBUG(3, ("Could not open tdb: %s\n", strerror(errno)));
     485         829 :                 goto fail;
     486             :         }
     487             : 
     488      309422 :         ZERO_STRUCT(db_tdb->id);
     489             : 
     490      309422 :         if (fstat(tdb_fd(db_tdb->wtdb->tdb), &st) == -1) {
     491           0 :                 DEBUG(3, ("fstat failed: %s\n", strerror(errno)));
     492           0 :                 goto fail;
     493             :         }
     494      309422 :         db_tdb->id.dev = st.st_dev;
     495      309422 :         db_tdb->id.ino = st.st_ino;
     496             : 
     497      309422 :         result->fetch_locked = db_tdb_fetch_locked;
     498      309422 :         result->do_locked = db_tdb_do_locked;
     499      309422 :         result->traverse = db_tdb_traverse;
     500      309422 :         result->traverse_read = db_tdb_traverse_read;
     501      309422 :         result->parse_record = db_tdb_parse;
     502      309422 :         result->get_seqnum = db_tdb_get_seqnum;
     503      309422 :         result->persistent = ((tdb_flags & TDB_CLEAR_IF_FIRST) == 0);
     504      309422 :         result->transaction_start = db_tdb_transaction_start;
     505      309422 :         result->transaction_start_nonblock = db_tdb_transaction_start_nonblock;
     506      309422 :         result->transaction_commit = db_tdb_transaction_commit;
     507      309422 :         result->transaction_cancel = db_tdb_transaction_cancel;
     508      309422 :         result->exists = db_tdb_exists;
     509      309422 :         result->wipe = db_tdb_wipe;
     510      309422 :         result->id = db_tdb_id;
     511      309422 :         result->check = db_tdb_check;
     512      309422 :         result->name = tdb_name(db_tdb->wtdb->tdb);
     513      309422 :         return result;
     514             : 
     515         829 :  fail:
     516         829 :         TALLOC_FREE(result);
     517         824 :         return NULL;
     518             : }

Generated by: LCOV version 1.14