LCOV - code coverage report
Current view: top level - source3/lib - gencache.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 189 271 69.7 %
Date: 2021-09-23 10:06:22 Functions: 17 17 100.0 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    Generic, persistent and shared between processes cache mechanism for use
       5             :    by various parts of the Samba code
       6             : 
       7             :    Copyright (C) Rafal Szczesniak    2002
       8             :    Copyright (C) Volker Lendecke     2009
       9             : 
      10             :    This program is free software; you can redistribute it and/or modify
      11             :    it under the terms of the GNU General Public License as published by
      12             :    the Free Software Foundation; either version 3 of the License, or
      13             :    (at your option) any later version.
      14             : 
      15             :    This program is distributed in the hope that it will be useful,
      16             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :    GNU General Public License for more details.
      19             : 
      20             :    You should have received a copy of the GNU General Public License
      21             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      22             : */
      23             : 
      24             : #include "includes.h"
      25             : #include "lib/gencache.h"
      26             : #include "system/filesys.h"
      27             : #include "system/glob.h"
      28             : #include "util_tdb.h"
      29             : #include "tdb_wrap/tdb_wrap.h"
      30             : #include "zlib.h"
      31             : #include "lib/util/strv.h"
      32             : #include "lib/util/util_paths.h"
      33             : 
      34             : #undef  DBGC_CLASS
      35             : #define DBGC_CLASS DBGC_TDB
      36             : 
      37             : #define GENCACHE_USER_PATH "~/.cache/samba/gencache.tdb"
      38             : 
      39             : static struct tdb_wrap *cache;
      40             : 
      41             : /**
      42             :  * @file gencache.c
      43             :  * @brief Generic, persistent and shared between processes cache mechanism
      44             :  *        for use by various parts of the Samba code
      45             :  *
      46             :  **/
      47             : 
      48             : static bool gencache_pull_timeout(TDB_DATA key,
      49             :                                   TDB_DATA data,
      50             :                                   time_t *pres,
      51             :                                   DATA_BLOB *payload);
      52             : 
      53             : struct gencache_timeout {
      54             :         time_t timeout;
      55             : };
      56             : 
      57     1518844 : bool gencache_timeout_expired(const struct gencache_timeout *t)
      58             : {
      59     1518844 :         return t->timeout <= time(NULL);
      60             : }
      61             : 
      62             : /**
      63             :  * Cache initialisation function. Opens cache tdb file or creates
      64             :  * it if does not exist.
      65             :  *
      66             :  * @return true on successful initialisation of the cache or
      67             :  *         false on failure
      68             :  **/
      69             : 
      70     3715782 : static bool gencache_init(void)
      71             : {
      72     3715782 :         char* cache_fname = NULL;
      73     3715782 :         int open_flags = O_RDWR|O_CREAT;
      74     3715782 :         int tdb_flags = TDB_INCOMPATIBLE_HASH|TDB_NOSYNC|TDB_MUTEX_LOCKING;
      75             :         int hash_size;
      76             : 
      77             :         /* skip file open if it's already opened */
      78     3715782 :         if (cache) {
      79     2701020 :                 return true;
      80             :         }
      81             : 
      82        7509 :         hash_size = lp_parm_int(-1, "gencache", "hash_size", 10000);
      83             : 
      84        7509 :         cache_fname = lock_path(talloc_tos(), "gencache.tdb");
      85        7509 :         if (cache_fname == NULL) {
      86           0 :                 return false;
      87             :         }
      88             : 
      89        7509 :         DEBUG(5, ("Opening cache file at %s\n", cache_fname));
      90             : 
      91        7509 :         cache = tdb_wrap_open(NULL, cache_fname, hash_size,
      92             :                               tdb_flags,
      93             :                               open_flags, 0644);
      94             :         /*
      95             :          * Allow client tools to create a gencache in the home directory
      96             :          * as a normal user.
      97             :          */
      98        7509 :         if (cache == NULL && errno == EACCES && geteuid() != 0) {
      99           0 :                 char *cache_dname = NULL, *tmp = NULL;
     100             :                 bool ok;
     101             : 
     102           0 :                 TALLOC_FREE(cache_fname);
     103             : 
     104           0 :                 cache_fname = path_expand_tilde(talloc_tos(),
     105             :                                                 GENCACHE_USER_PATH);
     106           0 :                 if (cache_fname == NULL) {
     107           0 :                         DBG_ERR("Failed to expand path: %s\n",
     108             :                                 GENCACHE_USER_PATH);
     109           0 :                         return false;
     110             :                 }
     111             : 
     112           0 :                 tmp = talloc_strdup(talloc_tos(), cache_fname);
     113           0 :                 if (tmp == NULL) {
     114           0 :                         DBG_ERR("No memory!\n");
     115           0 :                         TALLOC_FREE(cache_fname);
     116           0 :                         return false;
     117             :                 }
     118             : 
     119           0 :                 cache_dname = dirname(tmp);
     120           0 :                 if (cache_dname == NULL) {
     121           0 :                         DBG_ERR("Invalid path: %s\n", cache_fname);
     122           0 :                         TALLOC_FREE(tmp);
     123           0 :                         TALLOC_FREE(cache_fname);
     124           0 :                         return false;
     125             :                 }
     126             : 
     127           0 :                 ok = directory_create_or_exists_recursive(cache_dname, 0700);
     128           0 :                 if (!ok) {
     129           0 :                         DBG_ERR("Failed to create directory: %s - %s\n",
     130             :                                 cache_dname, strerror(errno));
     131           0 :                         TALLOC_FREE(tmp);
     132           0 :                         TALLOC_FREE(cache_fname);
     133           0 :                         return false;
     134             :                 }
     135           0 :                 TALLOC_FREE(tmp);
     136             : 
     137           0 :                 cache = tdb_wrap_open(NULL,
     138             :                                       cache_fname,
     139             :                                       hash_size,
     140             :                                       tdb_flags,
     141             :                                       open_flags,
     142             :                                       0644);
     143           0 :                 if (cache != NULL) {
     144           0 :                         DBG_INFO("Opening user cache file %s.\n",
     145             :                                  cache_fname);
     146             :                 }
     147             :         }
     148             : 
     149        7509 :         if (cache == NULL) {
     150           0 :                 DEBUG(5, ("Opening %s failed: %s\n", cache_fname,
     151             :                           strerror(errno)));
     152           0 :                 TALLOC_FREE(cache_fname);
     153           0 :                 return false;
     154             :         }
     155        7509 :         TALLOC_FREE(cache_fname);
     156             : 
     157        7490 :         return true;
     158             : }
     159             : 
     160             : /*
     161             :  * Walk the hash chain for "key", deleting all expired entries for
     162             :  * that hash chain
     163             :  */
     164             : struct gencache_prune_expired_state {
     165             :         TALLOC_CTX *mem_ctx;
     166             :         char *keys;
     167             : };
     168             : 
     169       12981 : static int gencache_prune_expired_fn(struct tdb_context *tdb,
     170             :                                      TDB_DATA key,
     171             :                                      TDB_DATA data,
     172             :                                      void *private_data)
     173             : {
     174       12981 :         struct gencache_prune_expired_state *state = private_data;
     175             :         struct gencache_timeout t;
     176       12981 :         bool ok = false;
     177       12981 :         bool expired = false;
     178             : 
     179       12981 :         if ((key.dsize == 0) || (key.dptr[key.dsize-1] != '\0')) {
     180             :                 /* not a valid record, should never happen */
     181           0 :                 return 0;
     182             :         }
     183             : 
     184       12981 :         ok = gencache_pull_timeout(key, data, &t.timeout, NULL);
     185       12981 :         if (ok) {
     186       12981 :                 expired = gencache_timeout_expired(&t);
     187             :         }
     188             : 
     189       12981 :         if (!ok || expired) {
     190             :                 int ret;
     191             : 
     192         571 :                 ret = strv_add(state->mem_ctx, &state->keys, (char *)key.dptr);
     193         571 :                 if (ret != 0) {
     194             :                         /*
     195             :                          * Exit the loop. It's unlikely that it will
     196             :                          * succeed next time.
     197             :                          */
     198           0 :                         return -1;
     199             :                 }
     200             :         }
     201             : 
     202       12958 :         return 0;
     203             : }
     204             : 
     205       30355 : static void gencache_prune_expired(struct tdb_context *tdb,
     206             :                                    TDB_DATA chain_key)
     207             : {
     208       60710 :         struct gencache_prune_expired_state state = {
     209       30355 :                 .mem_ctx = talloc_tos(),
     210             :         };
     211       30355 :         char *keystr = NULL;
     212             :         int ret;
     213             : 
     214       30355 :         ret = tdb_traverse_key_chain(
     215             :                 tdb, chain_key, gencache_prune_expired_fn, &state);
     216       30355 :         if (ret == -1) {
     217           0 :                 DBG_DEBUG("tdb_traverse_key_chain failed: %s\n",
     218             :                           tdb_errorstr(tdb));
     219           0 :                 return;
     220             :         }
     221             : 
     222       56422 :         while ((keystr = strv_next(state.keys, keystr)) != NULL) {
     223         571 :                 TDB_DATA key = string_term_tdb_data(keystr);
     224             : 
     225             :                 /*
     226             :                  * We expect the hash chain of "chain_key" to be
     227             :                  * locked. So between gencache_prune_expired_fn
     228             :                  * figuring out "keystr" is expired and the
     229             :                  * tdb_delete, nobody can have reset the timeout.
     230             :                  */
     231         571 :                 tdb_delete(tdb, key);
     232             :         }
     233             : 
     234       30355 :         TALLOC_FREE(state.keys);
     235             : }
     236             : 
     237             : /**
     238             :  * Set an entry in the cache file. If there's no such
     239             :  * one, then add it.
     240             :  *
     241             :  * @param keystr string that represents a key of this entry
     242             :  * @param blob DATA_BLOB value being cached
     243             :  * @param timeout time when the value is expired
     244             :  *
     245             :  * @retval true when entry is successfully stored
     246             :  * @retval false on failure
     247             :  **/
     248             : 
     249       30355 : bool gencache_set_data_blob(const char *keystr, DATA_BLOB blob,
     250             :                             time_t timeout)
     251             : {
     252             :         TDB_DATA key;
     253             :         int ret;
     254             :         TDB_DATA dbufs[3];
     255             :         uint32_t crc;
     256             : 
     257       30355 :         if ((keystr == NULL) || (blob.data == NULL)) {
     258           0 :                 return false;
     259             :         }
     260             : 
     261       30355 :         key = string_term_tdb_data(keystr);
     262             : 
     263       30355 :         if (!gencache_init()) {
     264           0 :                 return false;
     265             :         }
     266             : 
     267       30355 :         dbufs[0] = (TDB_DATA) { .dptr = (uint8_t *)&timeout,
     268             :                                 .dsize = sizeof(time_t) };
     269       30355 :         dbufs[1] = (TDB_DATA) { .dptr = blob.data, .dsize = blob.length };
     270             : 
     271       30355 :         crc = crc32(0, Z_NULL, 0);
     272       30355 :         crc = crc32(crc, key.dptr, key.dsize);
     273       30355 :         crc = crc32(crc, dbufs[0].dptr, dbufs[0].dsize);
     274       30355 :         crc = crc32(crc, dbufs[1].dptr, dbufs[1].dsize);
     275             : 
     276       30355 :         dbufs[2] = (TDB_DATA) { .dptr = (uint8_t *)&crc,
     277             :                                 .dsize = sizeof(crc) };
     278             : 
     279       30355 :         DBG_DEBUG("Adding cache entry with key=[%s] and timeout="
     280             :                    "[%s] (%ld seconds %s)\n", keystr,
     281             :                    timestring(talloc_tos(), timeout),
     282             :                    ((long int)timeout) - time(NULL),
     283             :                    timeout > time(NULL) ? "ahead" : "in the past");
     284             : 
     285       30355 :         ret = tdb_chainlock(cache->tdb, key);
     286       30355 :         if (ret == -1) {
     287           0 :                 DBG_WARNING("tdb_chainlock for key [%s] failed: %s\n",
     288             :                             keystr, tdb_errorstr(cache->tdb));
     289           0 :                 return false;
     290             :         }
     291             : 
     292       30355 :         gencache_prune_expired(cache->tdb, key);
     293             : 
     294       30355 :         ret = tdb_storev(cache->tdb, key, dbufs, ARRAY_SIZE(dbufs), 0);
     295             : 
     296       30355 :         tdb_chainunlock(cache->tdb, key);
     297             : 
     298       30355 :         if (ret == 0) {
     299       29740 :                 return true;
     300             :         }
     301           0 :         if (tdb_error(cache->tdb) != TDB_ERR_CORRUPT) {
     302           0 :                 return false;
     303             :         }
     304             : 
     305           0 :         ret = tdb_wipe_all(cache->tdb);
     306           0 :         SMB_ASSERT(ret == 0);
     307             : 
     308           0 :         return false;
     309             : }
     310             : 
     311             : /**
     312             :  * Delete one entry from the cache file.
     313             :  *
     314             :  * @param keystr string that represents a key of this entry
     315             :  *
     316             :  * @retval true upon successful deletion
     317             :  * @retval false in case of failure
     318             :  **/
     319             : 
     320        1858 : bool gencache_del(const char *keystr)
     321             : {
     322        1858 :         TDB_DATA key = string_term_tdb_data(keystr);
     323             :         int ret;
     324             : 
     325        1858 :         if (keystr == NULL) {
     326           0 :                 return false;
     327             :         }
     328             : 
     329        1858 :         if (!gencache_init()) {
     330           0 :                 return false;
     331             :         }
     332             : 
     333        1858 :         DEBUG(10, ("Deleting cache entry (key=[%s])\n", keystr));
     334             : 
     335        1858 :         ret = tdb_delete(cache->tdb, key);
     336             : 
     337        1858 :         if (ret == 0) {
     338        1207 :                 return true;
     339             :         }
     340         624 :         if (tdb_error(cache->tdb) != TDB_ERR_CORRUPT) {
     341         608 :                 return false;
     342             :         }
     343             : 
     344           0 :         ret = tdb_wipe_all(cache->tdb);
     345           0 :         SMB_ASSERT(ret == 0);
     346             : 
     347           0 :         return true;            /* We've deleted a bit more... */
     348             : }
     349             : 
     350     6852746 : static bool gencache_pull_timeout(TDB_DATA key,
     351             :                                   TDB_DATA data,
     352             :                                   time_t *pres,
     353             :                                   DATA_BLOB *payload)
     354             : {
     355             :         size_t crc_ofs;
     356             :         uint32_t crc, stored_crc;
     357             : 
     358    12276012 :         if ((data.dptr == NULL) ||
     359     5846211 :             (data.dsize < (sizeof(time_t) + sizeof(uint32_t)))) {
     360           0 :                 return false;
     361             :         }
     362             : 
     363     6852746 :         crc_ofs = data.dsize - sizeof(uint32_t);
     364             : 
     365     6852746 :         crc = crc32(0, Z_NULL, 0);
     366     6852746 :         crc = crc32(crc, key.dptr, key.dsize);
     367     6852746 :         crc = crc32(crc, data.dptr, crc_ofs);
     368             : 
     369     6852746 :         memcpy(&stored_crc, data.dptr + crc_ofs, sizeof(uint32_t));
     370             : 
     371     6852746 :         if (stored_crc != crc) {
     372           0 :                 return false;
     373             :         }
     374             : 
     375     6852746 :         if (pres != NULL) {
     376     6852746 :                 memcpy(pres, data.dptr, sizeof(time_t));
     377             :         }
     378     6852746 :         if (payload != NULL) {
     379     6839765 :                 *payload = (DATA_BLOB) {
     380     6839765 :                         .data = data.dptr+sizeof(time_t),
     381     6839765 :                         .length = data.dsize-sizeof(time_t)-sizeof(uint32_t),
     382             :                 };
     383             :         }
     384     5846211 :         return true;
     385             : }
     386             : 
     387             : struct gencache_parse_state {
     388             :         void (*parser)(const struct gencache_timeout *timeout,
     389             :                        DATA_BLOB blob,
     390             :                        void *private_data);
     391             :         void *private_data;
     392             :         bool format_error;
     393             : };
     394             : 
     395     3446545 : static int gencache_parse_fn(TDB_DATA key, TDB_DATA data, void *private_data)
     396             : {
     397     3446545 :         struct gencache_parse_state *state = private_data;
     398             :         struct gencache_timeout t;
     399             :         DATA_BLOB payload;
     400             :         bool ret;
     401             : 
     402     3446545 :         ret = gencache_pull_timeout(key, data, &t.timeout, &payload);
     403     3446545 :         if (!ret) {
     404           0 :                 state->format_error = true;
     405           0 :                 return 0;
     406             :         }
     407     3446545 :         state->parser(&t, payload, state->private_data);
     408             : 
     409     3446545 :         return 0;
     410             : }
     411             : 
     412     3681045 : bool gencache_parse(const char *keystr,
     413             :                     void (*parser)(const struct gencache_timeout *timeout,
     414             :                                    DATA_BLOB blob,
     415             :                                    void *private_data),
     416             :                     void *private_data)
     417             : {
     418     3681045 :         struct gencache_parse_state state = {
     419             :                 .parser = parser, .private_data = private_data
     420             :         };
     421     3681045 :         TDB_DATA key = string_term_tdb_data(keystr);
     422             :         int ret;
     423             : 
     424     3681045 :         if (keystr == NULL) {
     425           0 :                 return false;
     426             :         }
     427     3681045 :         if (!gencache_init()) {
     428           0 :                 return false;
     429             :         }
     430             : 
     431     3681045 :         ret = tdb_parse_record(cache->tdb, key,
     432             :                                gencache_parse_fn, &state);
     433     3681045 :         if ((ret == -1) && (tdb_error(cache->tdb) == TDB_ERR_CORRUPT)) {
     434           0 :                 goto wipe;
     435             :         }
     436     3681045 :         if (ret == -1) {
     437      234398 :                 return false;
     438             :         }
     439     3446545 :         if (state.format_error) {
     440           0 :                 ret = tdb_delete(cache->tdb, key);
     441           0 :                 if (ret == -1) {
     442           0 :                         goto wipe;
     443             :                 }
     444           0 :                 return false;
     445             :         }
     446     2440033 :         return true;
     447             : 
     448           0 : wipe:
     449           0 :         ret = tdb_wipe_all(cache->tdb);
     450           0 :         SMB_ASSERT(ret == 0);
     451           0 :         return false;
     452             : }
     453             : 
     454             : struct gencache_get_data_blob_state {
     455             :         TALLOC_CTX *mem_ctx;
     456             :         DATA_BLOB *blob;
     457             :         time_t timeout;
     458             :         bool result;
     459             : };
     460             : 
     461      940681 : static void gencache_get_data_blob_parser(const struct gencache_timeout *t,
     462             :                                           DATA_BLOB blob,
     463             :                                           void *private_data)
     464             : {
     465      940681 :         struct gencache_get_data_blob_state *state =
     466             :                 (struct gencache_get_data_blob_state *)private_data;
     467             : 
     468      940681 :         if (t->timeout == 0) {
     469           8 :                 state->result = false;
     470           8 :                 return;
     471             :         }
     472      940673 :         state->timeout = t->timeout;
     473             : 
     474      940673 :         if (state->blob == NULL) {
     475           0 :                 state->result = true;
     476           0 :                 return;
     477             :         }
     478             : 
     479      940673 :         *state->blob = data_blob_talloc(state->mem_ctx, blob.data,
     480             :                                         blob.length);
     481      940673 :         if (state->blob->data == NULL) {
     482           0 :                 state->result = false;
     483           0 :                 return;
     484             :         }
     485      940673 :         state->result = true;
     486             : }
     487             : 
     488             : /**
     489             :  * Get existing entry from the cache file.
     490             :  *
     491             :  * @param keystr string that represents a key of this entry
     492             :  * @param blob DATA_BLOB that is filled with entry's blob
     493             :  * @param timeout pointer to a time_t that is filled with entry's
     494             :  *        timeout
     495             :  *
     496             :  * @retval true when entry is successfully fetched
     497             :  * @retval false for failure
     498             :  **/
     499             : 
     500      984493 : bool gencache_get_data_blob(const char *keystr, TALLOC_CTX *mem_ctx,
     501             :                             DATA_BLOB *blob,
     502             :                             time_t *timeout, bool *was_expired)
     503             : {
     504             :         struct gencache_get_data_blob_state state;
     505      984493 :         bool expired = false;
     506             : 
     507      984493 :         state.result = false;
     508      984493 :         state.mem_ctx = mem_ctx;
     509      984493 :         state.blob = blob;
     510             : 
     511      984493 :         if (!gencache_parse(keystr, gencache_get_data_blob_parser, &state)) {
     512       43729 :                 goto fail;
     513             :         }
     514      940681 :         if (!state.result) {
     515           8 :                 goto fail;
     516             :         }
     517      940673 :         if (state.timeout <= time(NULL)) {
     518             :                 /*
     519             :                  * We're expired, delete the entry. We can't use gencache_del
     520             :                  * here, because that uses gencache_get_data_blob for checking
     521             :                  * the existence of a record. We know the thing exists and
     522             :                  * directly store an empty value with 0 timeout.
     523             :                  */
     524         106 :                 gencache_set(keystr, "", 0);
     525         106 :                 expired = true;
     526         106 :                 goto fail;
     527             :         }
     528      940567 :         if (timeout) {
     529      940080 :                 *timeout = state.timeout;
     530             :         }
     531             : 
     532      938654 :         return true;
     533             : 
     534       43926 : fail:
     535       43926 :         if (was_expired != NULL) {
     536           0 :                 *was_expired = expired;
     537             :         }
     538       43926 :         if (state.result && state.blob) {
     539         106 :                 data_blob_free(state.blob);
     540             :         }
     541       43843 :         return false;
     542             : } 
     543             : 
     544             : /**
     545             :  * Get existing entry from the cache file.
     546             :  *
     547             :  * @param keystr string that represents a key of this entry
     548             :  * @param valstr buffer that is allocated and filled with the entry value
     549             :  *        buffer's disposing must be done outside
     550             :  * @param timeout pointer to a time_t that is filled with entry's
     551             :  *        timeout
     552             :  *
     553             :  * @retval true when entry is successfully fetched
     554             :  * @retval false for failure
     555             :  **/
     556             : 
     557      984461 : bool gencache_get(const char *keystr, TALLOC_CTX *mem_ctx, char **value,
     558             :                   time_t *ptimeout)
     559             : {
     560             :         DATA_BLOB blob;
     561      984461 :         bool ret = false;
     562             : 
     563      984461 :         ret = gencache_get_data_blob(keystr, mem_ctx, &blob, ptimeout, NULL);
     564      984461 :         if (!ret) {
     565       43838 :                 return false;
     566             :         }
     567      940541 :         if ((blob.data == NULL) || (blob.length == 0)) {
     568           0 :                 data_blob_free(&blob);
     569           0 :                 return false;
     570             :         }
     571      940541 :         if (blob.data[blob.length-1] != '\0') {
     572             :                 /* Not NULL terminated, can't be a string */
     573           1 :                 data_blob_free(&blob);
     574           1 :                 return false;
     575             :         }
     576      940540 :         if (value) {
     577             :                 /*
     578             :                  * talloc_move generates a type-punned warning here. As we
     579             :                  * leave the function immediately, do a simple talloc_steal.
     580             :                  */
     581      940539 :                 *value = (char *)talloc_steal(mem_ctx, blob.data);
     582      940539 :                 return true;
     583             :         }
     584           1 :         data_blob_free(&blob);
     585           1 :         return true;
     586             : }
     587             : 
     588             : /**
     589             :  * Set an entry in the cache file. If there's no such
     590             :  * one, then add it.
     591             :  *
     592             :  * @param keystr string that represents a key of this entry
     593             :  * @param value text representation value being cached
     594             :  * @param timeout time when the value is expired
     595             :  *
     596             :  * @retval true when entry is successfully stored
     597             :  * @retval false on failure
     598             :  **/
     599             : 
     600       29743 : bool gencache_set(const char *keystr, const char *value, time_t timeout)
     601             : {
     602       29743 :         DATA_BLOB blob = data_blob_const(value, strlen(value)+1);
     603       29743 :         return gencache_set_data_blob(keystr, blob, timeout);
     604             : }
     605             : 
     606             : struct gencache_iterate_blobs_state {
     607             :         void (*fn)(const char *key, DATA_BLOB value,
     608             :                    time_t timeout, void *private_data);
     609             :         const char *pattern;
     610             :         void *private_data;
     611             : };
     612             : 
     613     3393220 : static int gencache_iterate_blobs_fn(struct tdb_context *tdb, TDB_DATA key,
     614             :                                      TDB_DATA data, void *priv)
     615             : {
     616     3393220 :         struct gencache_iterate_blobs_state *state =
     617             :                 (struct gencache_iterate_blobs_state *)priv;
     618             :         char *keystr;
     619     3393220 :         char *free_key = NULL;
     620             :         time_t timeout;
     621             :         DATA_BLOB payload;
     622             : 
     623     3393220 :         if (key.dptr[key.dsize-1] == '\0') {
     624     3393220 :                 keystr = (char *)key.dptr;
     625             :         } else {
     626             :                 /* ensure 0-termination */
     627           0 :                 keystr = talloc_strndup(talloc_tos(), (char *)key.dptr, key.dsize);
     628           0 :                 free_key = keystr;
     629           0 :                 if (keystr == NULL) {
     630           0 :                         goto done;
     631             :                 }
     632             :         }
     633             : 
     634     3393220 :         if (!gencache_pull_timeout(key, data, &timeout, &payload)) {
     635           0 :                 goto done;
     636             :         }
     637             : 
     638     3393220 :         if (timeout == 0) {
     639             :                 /* delete marker */
     640           0 :                 goto done;
     641             :         }
     642             : 
     643     3393220 :         if (fnmatch(state->pattern, keystr, 0) != 0) {
     644     3392468 :                 goto done;
     645             :         }
     646             : 
     647         752 :         DEBUG(10, ("Calling function with arguments "
     648             :                    "(key=[%s], timeout=[%s])\n",
     649             :                    keystr, timestring(talloc_tos(), timeout)));
     650             : 
     651         752 :         state->fn(keystr, payload, timeout, state->private_data);
     652             : 
     653     3393220 :  done:
     654     3393220 :         TALLOC_FREE(free_key);
     655     3393220 :         return 0;
     656             : }
     657             : 
     658        2524 : void gencache_iterate_blobs(void (*fn)(const char *key, DATA_BLOB value,
     659             :                                        time_t timeout, void *private_data),
     660             :                             void *private_data, const char *pattern)
     661             : {
     662             :         struct gencache_iterate_blobs_state state;
     663             :         int ret;
     664             : 
     665        2524 :         if ((fn == NULL) || (pattern == NULL) || !gencache_init()) {
     666           0 :                 return;
     667             :         }
     668             : 
     669        2524 :         DEBUG(5, ("Searching cache keys with pattern %s\n", pattern));
     670             : 
     671        2524 :         state.fn = fn;
     672        2524 :         state.pattern = pattern;
     673        2524 :         state.private_data = private_data;
     674             : 
     675        2524 :         ret = tdb_traverse(cache->tdb, gencache_iterate_blobs_fn, &state);
     676             : 
     677        2524 :         if ((ret == -1) && (tdb_error(cache->tdb) == TDB_ERR_CORRUPT)) {
     678           0 :                 ret = tdb_wipe_all(cache->tdb);
     679           0 :                 SMB_ASSERT(ret == 0);
     680             :         }
     681             : }
     682             : 
     683             : /**
     684             :  * Iterate through all entries which key matches to specified pattern
     685             :  *
     686             :  * @param fn pointer to the function that will be supplied with each single
     687             :  *        matching cache entry (key, value and timeout) as an arguments
     688             :  * @param data void pointer to an arbitrary data that is passed directly to the fn
     689             :  *        function on each call
     690             :  * @param keystr_pattern pattern the existing entries' keys are matched to
     691             :  *
     692             :  **/
     693             : 
     694             : struct gencache_iterate_state {
     695             :         void (*fn)(const char *key, const char *value, time_t timeout,
     696             :                    void *priv);
     697             :         void *private_data;
     698             : };
     699             : 
     700         752 : static void gencache_iterate_fn(const char *key, DATA_BLOB value,
     701             :                                 time_t timeout, void *private_data)
     702             : {
     703         752 :         struct gencache_iterate_state *state =
     704             :                 (struct gencache_iterate_state *)private_data;
     705             :         char *valstr;
     706         752 :         char *free_val = NULL;
     707             : 
     708         752 :         if (value.data[value.length-1] == '\0') {
     709         750 :                 valstr = (char *)value.data;
     710             :         } else {
     711             :                 /* ensure 0-termination */
     712           2 :                 valstr = talloc_strndup(talloc_tos(), (char *)value.data, value.length);
     713           2 :                 free_val = valstr;
     714           2 :                 if (valstr == NULL) {
     715           0 :                         goto done;
     716             :                 }
     717             :         }
     718             : 
     719         752 :         DEBUG(10, ("Calling function with arguments "
     720             :                    "(key=[%s], value=[%s], timeout=[%s])\n",
     721             :                    key, valstr, timestring(talloc_tos(), timeout)));
     722             : 
     723         752 :         state->fn(key, valstr, timeout, state->private_data);
     724             : 
     725         752 :   done:
     726             : 
     727         752 :         TALLOC_FREE(free_val);
     728         752 : }
     729             : 
     730        2524 : void gencache_iterate(void (*fn)(const char *key, const char *value,
     731             :                                  time_t timeout, void *dptr),
     732             :                       void *private_data, const char *pattern)
     733             : {
     734             :         struct gencache_iterate_state state;
     735             : 
     736        2524 :         if (fn == NULL) {
     737           0 :                 return;
     738             :         }
     739        2524 :         state.fn = fn;
     740        2524 :         state.private_data = private_data;
     741        2524 :         gencache_iterate_blobs(gencache_iterate_fn, &state, pattern);
     742             : }

Generated by: LCOV version 1.13