LCOV - code coverage report
Current view: top level - feature/dircache - conscache.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 187 201 93.0 %
Date: 2021-11-24 03:28:48 Functions: 24 27 88.9 %

          Line data    Source code
       1             : /* Copyright (c) 2017-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * @file conscache.c
       6             :  * @brief Consensus and diff on-disk cache.
       7             :  **/
       8             : 
       9             : #include "core/or/or.h"
      10             : 
      11             : #include "app/config/config.h"
      12             : #include "feature/dircache/conscache.h"
      13             : #include "lib/crypt_ops/crypto_util.h"
      14             : #include "lib/fs/storagedir.h"
      15             : #include "lib/encoding/confline.h"
      16             : 
      17             : #define CCE_MAGIC 0x17162253
      18             : 
      19             : #ifdef _WIN32
      20             : /* On Windows, unlink won't work on a file if the file is actively mmap()ed.
      21             :  * That forces us to be less aggressive about unlinking files, and causes other
      22             :  * changes throughout our logic.
      23             :  */
      24             : #define MUST_UNMAP_TO_UNLINK
      25             : #endif /* defined(_WIN32) */
      26             : 
      27             : /**
      28             :  * A consensus_cache_entry_t is a reference-counted handle to an
      29             :  * item in a consensus_cache_t.  It can be mmapped into RAM, or not,
      30             :  * depending whether it's currently in use.
      31             :  */
      32             : struct consensus_cache_entry_t {
      33             :   uint32_t magic; /**< Must be set to CCE_MAGIC */
      34             :   HANDLE_ENTRY(consensus_cache_entry, consensus_cache_entry_t);
      35             :   int32_t refcnt; /**< Reference count. */
      36             :   unsigned can_remove : 1; /**< If true, we want to delete this file. */
      37             :   /** If true, we intend to unmap this file as soon as we're done with it. */
      38             :   unsigned release_aggressively : 1;
      39             : 
      40             :   /** Filename for this object within the storage_dir_t */
      41             :   char *fname;
      42             :   /** Labels associated with this object. Immutable once the object
      43             :    * is created. */
      44             :   config_line_t *labels;
      45             :   /** Pointer to the cache that includes this entry (if any). */
      46             :   consensus_cache_t *in_cache;
      47             : 
      48             :   /** Since what time has this object been mapped into RAM, but with the cache
      49             :    * being the only having a reference to it? */
      50             :   time_t unused_since;
      51             :   /** mmaped contents of the underlying file.  May be NULL */
      52             :   tor_mmap_t *map;
      53             :   /** Length of the body within <b>map</b>. */
      54             :   size_t bodylen;
      55             :   /** Pointer to the body within <b>map</b>. */
      56             :   const uint8_t *body;
      57             : };
      58             : 
      59             : /**
      60             :  * A consensus_cache_t holds a directory full of labeled items.
      61             :  */
      62             : struct consensus_cache_t {
      63             :   /** Underling storage_dir_t to handle persistence */
      64             :   storage_dir_t *dir;
      65             :   /** List of all the entries in the directory. */
      66             :   smartlist_t *entries;
      67             : 
      68             :   /** The maximum number of entries that we'd like to allow in this cache.
      69             :    * This is the same as the storagedir limit when MUST_UNMAP_TO_UNLINK is
      70             :    * not defined. */
      71             :   unsigned max_entries;
      72             : };
      73             : 
      74             : static void consensus_cache_clear(consensus_cache_t *cache);
      75             : static void consensus_cache_rescan(consensus_cache_t *);
      76             : static void consensus_cache_entry_map(consensus_cache_t *,
      77             :                                       consensus_cache_entry_t *);
      78             : static void consensus_cache_entry_unmap(consensus_cache_entry_t *ent);
      79             : 
      80             : /**
      81             :  * Helper: Open a consensus cache in subdirectory <b>subdir</b> of the
      82             :  * data directory, to hold up to <b>max_entries</b> of data.
      83             :  */
      84             : consensus_cache_t *
      85          27 : consensus_cache_open(const char *subdir, int max_entries)
      86             : {
      87          27 :   int storagedir_max_entries;
      88          27 :   consensus_cache_t *cache = tor_malloc_zero(sizeof(consensus_cache_t));
      89          27 :   char *directory = get_cachedir_fname(subdir);
      90          27 :   cache->max_entries = max_entries;
      91             : 
      92             : #ifdef MUST_UNMAP_TO_UNLINK
      93             :   /* If we can't unlink the files that we're still using, then we need to
      94             :    * tell the storagedir backend to allow far more files than this consensus
      95             :    * cache actually wants, so that it can hold files which, from this cache's
      96             :    * perspective, have become useless.
      97             :    */
      98             : #define VERY_LARGE_STORAGEDIR_LIMIT (1000*1000)
      99             :   storagedir_max_entries = VERY_LARGE_STORAGEDIR_LIMIT;
     100             : #else /* !defined(MUST_UNMAP_TO_UNLINK) */
     101             :   /* Otherwise, we can just tell the storagedir to use the same limits
     102             :    * as this cache. */
     103          27 :   storagedir_max_entries = max_entries;
     104             : #endif /* defined(MUST_UNMAP_TO_UNLINK) */
     105             : 
     106          27 :   cache->dir = storage_dir_new(directory, storagedir_max_entries);
     107          27 :   tor_free(directory);
     108          27 :   if (!cache->dir) {
     109           1 :     tor_free(cache);
     110           1 :     return NULL;
     111             :   }
     112             : 
     113          26 :   consensus_cache_rescan(cache);
     114          26 :   return cache;
     115             : }
     116             : 
     117             : /** Return true if it's okay to put more entries in this cache than
     118             :  * its official file limit.
     119             :  *
     120             :  * (We need this method on Windows, where we can't unlink files that are still
     121             :  * in use, and therefore might need to temporarily exceed the file limit until
     122             :  * the no-longer-wanted files are deletable.)
     123             :  */
     124             : int
     125           0 : consensus_cache_may_overallocate(consensus_cache_t *cache)
     126             : {
     127           0 :   (void) cache;
     128             : #ifdef MUST_UNMAP_TO_UNLINK
     129             :   return 1;
     130             : #else
     131           0 :   return 0;
     132             : #endif
     133             : }
     134             : 
     135             : // HACK: GCC on Appveyor hates that we may assert before returning. Work around
     136             : // the error.
     137             : #ifdef _WIN32
     138             : #ifndef COCCI
     139             : #pragma GCC diagnostic push
     140             : #pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn"
     141             : #endif
     142             : #endif /* defined(_WIN32) */
     143             : 
     144             : /**
     145             :  * Tell the sandbox (if any) configured by <b>cfg</b> to allow the
     146             :  * operations that <b>cache</b> will need.
     147             :  */
     148             : int
     149           0 : consensus_cache_register_with_sandbox(consensus_cache_t *cache,
     150             :                                       struct sandbox_cfg_elem_t **cfg)
     151             : {
     152             : #ifdef MUST_UNMAP_TO_UNLINK
     153             :   /* Our Linux sandbox doesn't support huge file lists like the one that would
     154             :    * be generated by using VERY_LARGE_STORAGEDIR_LIMIT above in
     155             :    * consensus_cache_open().  Since the Linux sandbox is the only one we have
     156             :    * right now, we just assert that we never reach this point when we've had
     157             :    * to use VERY_LARGE_STORAGEDIR_LIMIT.
     158             :    *
     159             :    * If at some point in the future we have a different sandbox mechanism that
     160             :    * can handle huge file lists, we can remove this assertion or make it
     161             :    * conditional.
     162             :    */
     163             :   tor_assert_nonfatal_unreached();
     164             : #endif /* defined(MUST_UNMAP_TO_UNLINK) */
     165           0 :   return storage_dir_register_with_sandbox(cache->dir, cfg);
     166             : }
     167             : 
     168             : #ifdef _WIN32
     169             : #ifndef COCCI
     170             : #pragma GCC diagnostic pop
     171             : #endif
     172             : #endif
     173             : 
     174             : /**
     175             :  * Helper: clear all entries from <b>cache</b> (but do not delete
     176             :  * any that aren't marked for removal
     177             :  */
     178             : static void
     179          22 : consensus_cache_clear(consensus_cache_t *cache)
     180             : {
     181          22 :   consensus_cache_delete_pending(cache, 0);
     182             : 
     183         216 :   SMARTLIST_FOREACH_BEGIN(cache->entries, consensus_cache_entry_t *, ent) {
     184         194 :     ent->in_cache = NULL;
     185         194 :     consensus_cache_entry_decref(ent);
     186         194 :   } SMARTLIST_FOREACH_END(ent);
     187          22 :   smartlist_free(cache->entries);
     188          22 :   cache->entries = NULL;
     189          22 : }
     190             : 
     191             : /**
     192             :  * Drop all storage held by <b>cache</b>.
     193             :  */
     194             : void
     195         253 : consensus_cache_free_(consensus_cache_t *cache)
     196             : {
     197         253 :   if (! cache)
     198             :     return;
     199             : 
     200          22 :   if (cache->entries) {
     201          22 :     consensus_cache_clear(cache);
     202             :   }
     203          22 :   storage_dir_free(cache->dir);
     204          22 :   tor_free(cache);
     205             : }
     206             : 
     207             : /**
     208             :  * Write <b>datalen</b> bytes of data at <b>data</b> into the <b>cache</b>,
     209             :  * labeling that data with <b>labels</b>.  On failure, return NULL. On
     210             :  * success, return a newly created consensus_cache_entry_t.
     211             :  *
     212             :  * The returned value will be owned by the cache, and you will have a
     213             :  * reference to it.  Call consensus_cache_entry_decref() when you are
     214             :  * done with it.
     215             :  *
     216             :  * The provided <b>labels</b> MUST have distinct keys: if they don't,
     217             :  * this API does not specify which values (if any) for the duplicate keys
     218             :  * will be considered.
     219             :  */
     220             : consensus_cache_entry_t *
     221         188 : consensus_cache_add(consensus_cache_t *cache,
     222             :                     const config_line_t *labels,
     223             :                     const uint8_t *data,
     224             :                     size_t datalen)
     225             : {
     226         188 :   char *fname = NULL;
     227         188 :   int r = storage_dir_save_labeled_to_file(cache->dir,
     228             :                                             labels, data, datalen, &fname);
     229         188 :   if (r < 0 || fname == NULL) {
     230             :     return NULL;
     231             :   }
     232         188 :   consensus_cache_entry_t *ent =
     233         188 :     tor_malloc_zero(sizeof(consensus_cache_entry_t));
     234         188 :   ent->magic = CCE_MAGIC;
     235         188 :   ent->fname = fname;
     236         188 :   ent->labels = config_lines_dup(labels);
     237         188 :   ent->in_cache = cache;
     238         188 :   ent->unused_since = TIME_MAX;
     239         188 :   smartlist_add(cache->entries, ent);
     240             :   /* Start the reference count at 2: the caller owns one copy, and the
     241             :    * cache owns another.
     242             :    */
     243         188 :   ent->refcnt = 2;
     244             : 
     245         188 :   return ent;
     246             : }
     247             : 
     248             : /**
     249             :  * Given a <b>cache</b>, return some entry for which <b>key</b>=<b>value</b>.
     250             :  * Return NULL if no such entry exists.
     251             :  *
     252             :  * Does not adjust reference counts.
     253             :  */
     254             : consensus_cache_entry_t *
     255          14 : consensus_cache_find_first(consensus_cache_t *cache,
     256             :                            const char *key,
     257             :                            const char *value)
     258             : {
     259          14 :   smartlist_t *tmp = smartlist_new();
     260          14 :   consensus_cache_find_all(tmp, cache, key, value);
     261          14 :   consensus_cache_entry_t *ent = NULL;
     262          14 :   if (smartlist_len(tmp))
     263          10 :     ent = smartlist_get(tmp, 0);
     264          14 :   smartlist_free(tmp);
     265          14 :   return ent;
     266             : }
     267             : 
     268             : /**
     269             :  * Given a <b>cache</b>, add every entry to <b>out</b> for which
     270             :  * <b>key</b>=<b>value</b>.  If <b>key</b> is NULL, add every entry.
     271             :  *
     272             :  * Do not add any entry that has been marked for removal.
     273             :  *
     274             :  * Does not adjust reference counts.
     275             :  */
     276             : void
     277         253 : consensus_cache_find_all(smartlist_t *out,
     278             :                          consensus_cache_t *cache,
     279             :                          const char *key,
     280             :                          const char *value)
     281             : {
     282        2777 :   SMARTLIST_FOREACH_BEGIN(cache->entries, consensus_cache_entry_t *, ent) {
     283        2524 :     if (ent->can_remove == 1) {
     284             :       /* We want to delete this; pretend it isn't there. */
     285          60 :       continue;
     286             :     }
     287        2464 :     if (! key) {
     288         242 :       smartlist_add(out, ent);
     289         242 :       continue;
     290             :     }
     291        2222 :     const char *found_val = consensus_cache_entry_get_value(ent, key);
     292        2222 :     if (found_val && !strcmp(value, found_val)) {
     293         989 :       smartlist_add(out, ent);
     294             :     }
     295        2524 :   } SMARTLIST_FOREACH_END(ent);
     296         253 : }
     297             : 
     298             : /**
     299             :  * Given a list of consensus_cache_entry_t, remove all those entries
     300             :  * that do not have <b>key</b>=<b>value</b> in their labels.
     301             :  *
     302             :  * Does not adjust reference counts.
     303             :  */
     304             : void
     305         260 : consensus_cache_filter_list(smartlist_t *lst,
     306             :                             const char *key,
     307             :                             const char *value)
     308             : {
     309         260 :   if (BUG(lst == NULL))
     310             :     return; // LCOV_EXCL_LINE
     311         260 :   if (key == NULL)
     312             :     return;
     313        1414 :   SMARTLIST_FOREACH_BEGIN(lst, consensus_cache_entry_t *, ent) {
     314        1154 :     const char *found_val = consensus_cache_entry_get_value(ent, key);
     315        1154 :     if (! found_val || strcmp(value, found_val)) {
     316         543 :       SMARTLIST_DEL_CURRENT(lst, ent);
     317             :     }
     318        1154 :   } SMARTLIST_FOREACH_END(ent);
     319             : }
     320             : 
     321             : /**
     322             :  * If <b>ent</b> has a label with the given <b>key</b>, return its
     323             :  * value.  Otherwise return NULL.
     324             :  *
     325             :  * The return value is only guaranteed to be valid for as long as you
     326             :  * hold a reference to <b>ent</b>.
     327             :  */
     328             : const char *
     329        5203 : consensus_cache_entry_get_value(const consensus_cache_entry_t *ent,
     330             :                                 const char *key)
     331             : {
     332        5203 :   const config_line_t *match = config_line_find(ent->labels, key);
     333        5203 :   if (match)
     334        5112 :     return match->value;
     335             :   else
     336             :     return NULL;
     337             : }
     338             : 
     339             : /**
     340             :  * Return a pointer to the labels in <b>ent</b>.
     341             :  *
     342             :  * This pointer is only guaranteed to be valid for as long as you
     343             :  * hold a reference to <b>ent</b>.
     344             :  */
     345             : const config_line_t *
     346           0 : consensus_cache_entry_get_labels(const consensus_cache_entry_t *ent)
     347             : {
     348           0 :   return ent->labels;
     349             : }
     350             : 
     351             : /**
     352             :  * Increase the reference count of <b>ent</b>.
     353             :  */
     354             : void
     355          70 : consensus_cache_entry_incref(consensus_cache_entry_t *ent)
     356             : {
     357          70 :   if (BUG(ent->magic != CCE_MAGIC))
     358             :     return; // LCOV_EXCL_LINE
     359          70 :   ++ent->refcnt;
     360          70 :   ent->unused_since = TIME_MAX;
     361             : }
     362             : 
     363             : /**
     364             :  * Release a reference held to <b>ent</b>.
     365             :  *
     366             :  * If it was the last reference, ent will be freed. Therefore, you must not
     367             :  * use <b>ent</b> after calling this function.
     368             :  */
     369             : void
     370         515 : consensus_cache_entry_decref(consensus_cache_entry_t *ent)
     371             : {
     372         515 :   if (! ent)
     373             :     return;
     374         485 :   if (BUG(ent->refcnt <= 0))
     375             :     return; // LCOV_EXCL_LINE
     376         485 :   if (BUG(ent->magic != CCE_MAGIC))
     377             :     return; // LCOV_EXCL_LINE
     378             : 
     379         485 :   --ent->refcnt;
     380             : 
     381         485 :   if (ent->refcnt == 1 && ent->in_cache) {
     382             :     /* Only the cache has a reference: we don't need to keep the file
     383             :      * mapped */
     384         236 :     if (ent->map) {
     385          69 :       if (ent->release_aggressively) {
     386          20 :         consensus_cache_entry_unmap(ent);
     387             :       } else {
     388          49 :         ent->unused_since = approx_time();
     389             :       }
     390             :     }
     391         236 :     return;
     392             :   }
     393             : 
     394         249 :   if (ent->refcnt > 0)
     395             :     return;
     396             : 
     397             :   /* Refcount is zero; we can free it. */
     398         229 :   if (ent->map) {
     399          37 :     consensus_cache_entry_unmap(ent);
     400             :   }
     401         229 :   tor_free(ent->fname);
     402         229 :   config_free_lines(ent->labels);
     403         229 :   consensus_cache_entry_handles_clear(ent);
     404         229 :   memwipe(ent, 0, sizeof(consensus_cache_entry_t));
     405         229 :   tor_free(ent);
     406             : }
     407             : 
     408             : /**
     409             :  * Mark <b>ent</b> for deletion from the cache.  Deletion will not occur
     410             :  * until the cache is the only place that holds a reference to <b>ent</b>.
     411             :  */
     412             : void
     413          39 : consensus_cache_entry_mark_for_removal(consensus_cache_entry_t *ent)
     414             : {
     415          39 :   ent->can_remove = 1;
     416          39 : }
     417             : 
     418             : /**
     419             :  * Mark <b>ent</b> as the kind of entry that we don't need to keep mmap'd for
     420             :  * any longer than we're actually using it.
     421             :  */
     422             : void
     423          37 : consensus_cache_entry_mark_for_aggressive_release(consensus_cache_entry_t *ent)
     424             : {
     425          37 :   ent->release_aggressively = 1;
     426          37 : }
     427             : 
     428             : /**
     429             :  * Try to read the body of <b>ent</b> into memory if it isn't already
     430             :  * loaded.  On success, set *<b>body_out</b> to the body, *<b>sz_out</b>
     431             :  * to its size, and return 0.  On failure return -1.
     432             :  *
     433             :  * The resulting body pointer will only be valid for as long as you
     434             :  * hold a reference to <b>ent</b>.
     435             :  */
     436             : int
     437         130 : consensus_cache_entry_get_body(const consensus_cache_entry_t *ent,
     438             :                                const uint8_t **body_out,
     439             :                                size_t *sz_out)
     440             : {
     441         130 :   if (BUG(ent->magic != CCE_MAGIC))
     442             :     return -1; // LCOV_EXCL_LINE
     443             : 
     444         130 :   if (! ent->map) {
     445          69 :     if (! ent->in_cache)
     446             :       return -1;
     447             : 
     448          69 :     consensus_cache_entry_map((consensus_cache_t *)ent->in_cache,
     449             :                               (consensus_cache_entry_t *)ent);
     450          69 :     if (! ent->map) {
     451             :       return -1;
     452             :     }
     453             :   }
     454             : 
     455         130 :   *body_out = ent->body;
     456         130 :   *sz_out = ent->bodylen;
     457         130 :   return 0;
     458             : }
     459             : 
     460             : /**
     461             :  * Unmap every mmap'd element of <b>cache</b> that has been unused
     462             :  * since <b>cutoff</b>.
     463             :  */
     464             : void
     465           2 : consensus_cache_unmap_lazy(consensus_cache_t *cache, time_t cutoff)
     466             : {
     467          40 :   SMARTLIST_FOREACH_BEGIN(cache->entries, consensus_cache_entry_t *, ent) {
     468          38 :     tor_assert_nonfatal(ent->in_cache == cache);
     469          38 :     if (ent->refcnt > 1 || BUG(ent->in_cache == NULL)) {
     470             :       /* Somebody is using this entry right now */
     471          20 :       continue;
     472             :     }
     473          18 :     if (ent->unused_since > cutoff) {
     474             :       /* Has been unused only for a little while */
     475          12 :       continue;
     476             :     }
     477           6 :     if (ent->map == NULL) {
     478             :       /* Not actually mapped. */
     479           0 :       continue;
     480             :     }
     481           6 :     consensus_cache_entry_unmap(ent);
     482          38 :   } SMARTLIST_FOREACH_END(ent);
     483           2 : }
     484             : 
     485             : /**
     486             :  * Return the number of currently unused filenames available in this cache.
     487             :  */
     488             : int
     489          40 : consensus_cache_get_n_filenames_available(consensus_cache_t *cache)
     490             : {
     491          40 :   tor_assert(cache);
     492          40 :   int max = cache->max_entries;
     493          40 :   int used = smartlist_len(storage_dir_list(cache->dir));
     494             : #ifdef MUST_UNMAP_TO_UNLINK
     495             :   if (used > max)
     496             :     return 0;
     497             : #else
     498          40 :   tor_assert_nonfatal(max >= used);
     499             : #endif /* defined(MUST_UNMAP_TO_UNLINK) */
     500          40 :   return max - used;
     501             : }
     502             : 
     503             : /**
     504             :  * Delete every element of <b>cache</b> has been marked with
     505             :  * consensus_cache_entry_mark_for_removal. If <b>force</b> is false,
     506             :  * retain those entries which are in use by something other than the cache.
     507             :  */
     508             : void
     509          41 : consensus_cache_delete_pending(consensus_cache_t *cache, int force)
     510             : {
     511         454 :   SMARTLIST_FOREACH_BEGIN(cache->entries, consensus_cache_entry_t *, ent) {
     512         413 :     tor_assert_nonfatal(ent->in_cache == cache);
     513         413 :     int force_ent = force;
     514             : #ifdef MUST_UNMAP_TO_UNLINK
     515             :     /* We cannot delete anything with an active mmap on win32, so no
     516             :      * force-deletion. */
     517             :     if (ent->map) {
     518             :       force_ent = 0;
     519             :     }
     520             : #endif /* defined(MUST_UNMAP_TO_UNLINK) */
     521         413 :     if (! force_ent) {
     522         413 :       if (ent->refcnt > 1 || BUG(ent->in_cache == NULL)) {
     523             :         /* Somebody is using this entry right now */
     524          19 :         continue;
     525             :       }
     526             :     }
     527         394 :     if (ent->can_remove == 0) {
     528             :       /* Don't want to delete this. */
     529         355 :       continue;
     530             :     }
     531          39 :     if (BUG(ent->refcnt <= 0)) {
     532             :       continue; // LCOV_EXCL_LINE
     533             :     }
     534             : 
     535          39 :     SMARTLIST_DEL_CURRENT(cache->entries, ent);
     536          39 :     ent->in_cache = NULL;
     537          39 :     char *fname = tor_strdup(ent->fname); /* save a copy */
     538          39 :     consensus_cache_entry_decref(ent);
     539          39 :     storage_dir_remove_file(cache->dir, fname);
     540          39 :     tor_free(fname);
     541         413 :   } SMARTLIST_FOREACH_END(ent);
     542          41 : }
     543             : 
     544             : /**
     545             :  * Internal helper: rescan <b>cache</b> and rebuild its list of entries.
     546             :  */
     547             : static void
     548          26 : consensus_cache_rescan(consensus_cache_t *cache)
     549             : {
     550          26 :   if (cache->entries) {
     551           0 :     consensus_cache_clear(cache);
     552             :   }
     553             : 
     554          26 :   cache->entries = smartlist_new();
     555          26 :   const smartlist_t *fnames = storage_dir_list(cache->dir);
     556          89 :   SMARTLIST_FOREACH_BEGIN(fnames, const char *, fname) {
     557          63 :     tor_mmap_t *map = NULL;
     558          63 :     config_line_t *labels = NULL;
     559          63 :     const uint8_t *body;
     560          63 :     size_t bodylen;
     561          63 :     map = storage_dir_map_labeled(cache->dir, fname,
     562             :                                   &labels, &body, &bodylen);
     563          63 :     if (! map) {
     564             :       /* The ERANGE error might come from tor_mmap_file() -- it means the file
     565             :        * was empty. EINVAL might come from ..map_labeled() -- it means the
     566             :        * file was misformatted. In both cases, we should just delete it.
     567             :        */
     568           0 :       if (errno == ERANGE || errno == EINVAL) {
     569           0 :         log_warn(LD_FS, "Found %s file %s in consensus cache; removing it.",
     570             :                  errno == ERANGE ? "empty" : "misformatted",
     571             :                  escaped(fname));
     572           0 :         storage_dir_remove_file(cache->dir, fname);
     573             :       } else {
     574             :         /* Can't load this; continue */
     575           0 :         log_warn(LD_FS, "Unable to map file %s from consensus cache: %s",
     576             :                  escaped(fname), strerror(errno));
     577             :       }
     578           0 :       continue;
     579             :     }
     580          63 :     consensus_cache_entry_t *ent =
     581          63 :       tor_malloc_zero(sizeof(consensus_cache_entry_t));
     582          63 :     ent->magic = CCE_MAGIC;
     583          63 :     ent->fname = tor_strdup(fname);
     584          63 :     ent->labels = labels;
     585          63 :     ent->refcnt = 1;
     586          63 :     ent->in_cache = cache;
     587          63 :     ent->unused_since = TIME_MAX;
     588          63 :     smartlist_add(cache->entries, ent);
     589          63 :     tor_munmap_file(map); /* don't actually need to keep this around */
     590          63 :   } SMARTLIST_FOREACH_END(fname);
     591          26 : }
     592             : 
     593             : /**
     594             :  * Make sure that <b>ent</b> is mapped into RAM.
     595             :  */
     596             : static void
     597          69 : consensus_cache_entry_map(consensus_cache_t *cache,
     598             :                           consensus_cache_entry_t *ent)
     599             : {
     600          69 :   if (ent->map)
     601             :     return;
     602             : 
     603          69 :   ent->map = storage_dir_map_labeled(cache->dir, ent->fname,
     604             :                                      NULL, &ent->body, &ent->bodylen);
     605          69 :   ent->unused_since = TIME_MAX;
     606             : }
     607             : 
     608             : /**
     609             :  * Unmap <b>ent</b> from RAM.
     610             :  *
     611             :  * Do not call this if something other than the cache is holding a reference
     612             :  * to <b>ent</b>
     613             :  */
     614             : static void
     615          63 : consensus_cache_entry_unmap(consensus_cache_entry_t *ent)
     616             : {
     617          63 :   ent->unused_since = TIME_MAX;
     618          63 :   if (!ent->map)
     619             :     return;
     620             : 
     621          63 :   tor_munmap_file(ent->map);
     622          63 :   ent->map = NULL;
     623          63 :   ent->body = NULL;
     624          63 :   ent->bodylen = 0;
     625          63 :   ent->unused_since = TIME_MAX;
     626             : }
     627             : 
     628        2235 : HANDLE_IMPL(consensus_cache_entry, consensus_cache_entry_t, )
     629             : 
     630             : #ifdef TOR_UNIT_TESTS
     631             : /**
     632             :  * Testing only: Return true iff <b>ent</b> is mapped into memory.
     633             :  *
     634             :  * (In normal operation, this information is not exposed.)
     635             :  */
     636             : int
     637          59 : consensus_cache_entry_is_mapped(consensus_cache_entry_t *ent)
     638             : {
     639          59 :   if (ent->map) {
     640          34 :     tor_assert(ent->body);
     641             :     return 1;
     642             :   } else {
     643          25 :     tor_assert(!ent->body);
     644             :     return 0;
     645             :   }
     646             : }
     647             : #endif /* defined(TOR_UNIT_TESTS) */

Generated by: LCOV version 1.14