tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

test_conscache.c (11243B)


      1 /* Copyright (c) 2017-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 #include "core/or/or.h"
      5 #include "app/config/config.h"
      6 #include "feature/dircache/conscache.h"
      7 #include "lib/encoding/confline.h"
      8 #include "test/test.h"
      9 
     10 #ifdef HAVE_UTIME_H
     11 #include <utime.h>
     12 #endif
     13 
     14 static void
     15 test_conscache_open_failure(void *arg)
     16 {
     17  (void) arg;
     18  /* Try opening a directory that doesn't exist and which we shouldn't be
     19   * able to create. */
     20  consensus_cache_t *cache = consensus_cache_open("a/b/c/d/e/f/g", 128);
     21  tt_ptr_op(cache, OP_EQ, NULL);
     22 
     23 done:
     24  ;
     25 }
     26 
     27 static void
     28 test_conscache_simple_usage(void *arg)
     29 {
     30  (void)arg;
     31  consensus_cache_entry_t *ent = NULL, *ent2 = NULL;
     32 
     33  /* Make a temporary datadir for these tests */
     34  char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
     35  tor_free(get_options_mutable()->CacheDirectory);
     36  get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
     37  check_private_dir(ddir_fname, CPD_CREATE, NULL);
     38  consensus_cache_t *cache = consensus_cache_open("cons", 128);
     39 
     40  tt_assert(cache);
     41 
     42  /* Create object; make sure it exists. */
     43  config_line_t *labels = NULL;
     44  config_line_append(&labels, "Hello", "world");
     45  config_line_append(&labels, "Adios", "planetas");
     46  ent = consensus_cache_add(cache,
     47                            labels, (const uint8_t *)"A\0B\0C", 5);
     48  config_free_lines(labels);
     49  labels = NULL;
     50  tt_assert(ent);
     51 
     52  /* Make a second object */
     53  config_line_append(&labels, "Hello", "mundo");
     54  config_line_append(&labels, "Adios", "planets");
     55  ent2 = consensus_cache_add(cache,
     56                             labels, (const uint8_t *)"xyzzy", 5);
     57  config_free_lines(labels);
     58  labels = NULL;
     59  tt_assert(ent2);
     60  tt_assert(! consensus_cache_entry_is_mapped(ent2));
     61  consensus_cache_entry_decref(ent2);
     62  ent2 = NULL;
     63 
     64  /* Check get_value */
     65  tt_ptr_op(NULL, OP_EQ, consensus_cache_entry_get_value(ent, "hebbo"));
     66  tt_str_op("world", OP_EQ, consensus_cache_entry_get_value(ent, "Hello"));
     67 
     68  /* Check find_first */
     69  ent2 = consensus_cache_find_first(cache, "Hello", "world!");
     70  tt_ptr_op(ent2, OP_EQ, NULL);
     71  ent2 = consensus_cache_find_first(cache, "Hello", "world");
     72  tt_ptr_op(ent2, OP_EQ, ent);
     73  ent2 = consensus_cache_find_first(cache, "Hello", "mundo");
     74  tt_ptr_op(ent2, OP_NE, ent);
     75 
     76  tt_assert(! consensus_cache_entry_is_mapped(ent));
     77 
     78  /* Check get_body */
     79  const uint8_t *bp = NULL;
     80  size_t sz = 0;
     81  int r = consensus_cache_entry_get_body(ent, &bp, &sz);
     82  tt_int_op(r, OP_EQ, 0);
     83  tt_u64_op(sz, OP_EQ, 5);
     84  tt_mem_op(bp, OP_EQ, "A\0B\0C", 5);
     85  tt_assert(consensus_cache_entry_is_mapped(ent));
     86 
     87  /* Free and re-create the cache, to rescan the directory. */
     88  consensus_cache_free(cache);
     89  consensus_cache_entry_decref(ent);
     90  cache = consensus_cache_open("cons", 128);
     91 
     92  /* Make sure the entry is still there */
     93  ent = consensus_cache_find_first(cache, "Hello", "mundo");
     94  tt_assert(ent);
     95  ent2 = consensus_cache_find_first(cache, "Adios", "planets");
     96  tt_ptr_op(ent, OP_EQ, ent2);
     97  consensus_cache_entry_incref(ent);
     98  tt_assert(! consensus_cache_entry_is_mapped(ent));
     99  r = consensus_cache_entry_get_body(ent, &bp, &sz);
    100  tt_int_op(r, OP_EQ, 0);
    101  tt_u64_op(sz, OP_EQ, 5);
    102  tt_mem_op(bp, OP_EQ, "xyzzy", 5);
    103  tt_assert(consensus_cache_entry_is_mapped(ent));
    104 
    105  /* There should be two entries total. */
    106  smartlist_t *entries = smartlist_new();
    107  consensus_cache_find_all(entries, cache, NULL, NULL);
    108  int n = smartlist_len(entries);
    109  smartlist_free(entries);
    110  tt_int_op(n, OP_EQ, 2);
    111 
    112 done:
    113  consensus_cache_entry_decref(ent);
    114  tor_free(ddir_fname);
    115  consensus_cache_free(cache);
    116 }
    117 
    118 static void
    119 test_conscache_cleanup(void *arg)
    120 {
    121  (void)arg;
    122  const int N = 20;
    123  consensus_cache_entry_t **ents =
    124    tor_calloc(N, sizeof(consensus_cache_entry_t*));
    125 
    126  /* Make a temporary datadir for these tests */
    127  char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
    128  tor_free(get_options_mutable()->CacheDirectory);
    129  get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
    130  check_private_dir(ddir_fname, CPD_CREATE, NULL);
    131  consensus_cache_t *cache = consensus_cache_open("cons", 128);
    132 
    133  tt_assert(cache);
    134 
    135  /* Create a bunch of entries. */
    136  int i;
    137  for (i = 0; i < N; ++i) {
    138    config_line_t *labels = NULL;
    139    char num[8];
    140    tor_snprintf(num, sizeof(num), "%d", i);
    141    config_line_append(&labels, "test-id", "cleanup");
    142    config_line_append(&labels, "index", num);
    143    size_t bodylen = i * 3;
    144    uint8_t *body = tor_malloc(bodylen);
    145    memset(body, i, bodylen);
    146    ents[i] = consensus_cache_add(cache, labels, body, bodylen);
    147    tor_free(body);
    148    config_free_lines(labels);
    149    tt_assert(ents[i]);
    150    /* We're still holding a reference to each entry at this point. */
    151  }
    152 
    153  /* Page all of the entries into RAM */
    154  for (i = 0; i < N; ++i) {
    155    const uint8_t *bp;
    156    size_t sz;
    157    tt_assert(! consensus_cache_entry_is_mapped(ents[i]));
    158    consensus_cache_entry_get_body(ents[i], &bp, &sz);
    159    tt_assert(consensus_cache_entry_is_mapped(ents[i]));
    160  }
    161 
    162  /* Mark some of the entries as deletable. */
    163  for (i = 7; i < N; i += 7) {
    164    consensus_cache_entry_mark_for_removal(ents[i]);
    165    tt_assert(consensus_cache_entry_is_mapped(ents[i]));
    166  }
    167 
    168  /* Mark some of the entries as aggressively unpaged. */
    169  for (i = 3; i < N; i += 3) {
    170    consensus_cache_entry_mark_for_aggressive_release(ents[i]);
    171    tt_assert(consensus_cache_entry_is_mapped(ents[i]));
    172  }
    173 
    174  /* Incref some of the entries again */
    175  for (i = 0; i < N; i += 2) {
    176    consensus_cache_entry_incref(ents[i]);
    177  }
    178 
    179  /* Now we're going to decref everything. We do so at a specific time.  I'm
    180   * picking the moment when I was writing this test, at 2017-04-05 12:16:48
    181   * UTC. */
    182  const time_t example_time = 1491394608;
    183  update_approx_time(example_time);
    184  for (i = 0; i < N; ++i) {
    185    consensus_cache_entry_decref(ents[i]);
    186    if (i % 2) {
    187      ents[i] = NULL; /* We're no longer holding any reference here. */
    188    }
    189  }
    190 
    191  /* At this point, the aggressively-released items with refcount 1 should
    192   * be unmapped. Nothing should be deleted. */
    193  consensus_cache_entry_t *e_tmp;
    194  e_tmp = consensus_cache_find_first(cache, "index", "3");
    195  tt_assert(e_tmp);
    196  tt_assert(! consensus_cache_entry_is_mapped(e_tmp));
    197  e_tmp = consensus_cache_find_first(cache, "index", "5");
    198  tt_assert(e_tmp);
    199  tt_assert(consensus_cache_entry_is_mapped(e_tmp));
    200  e_tmp = consensus_cache_find_first(cache, "index", "6");
    201  tt_assert(e_tmp);
    202  tt_assert(consensus_cache_entry_is_mapped(e_tmp));
    203  e_tmp = consensus_cache_find_first(cache, "index", "7");
    204  tt_ptr_op(e_tmp, OP_EQ, NULL); // not found because pending deletion.
    205 
    206  /* Delete the pending-deletion items. */
    207  consensus_cache_delete_pending(cache, 0);
    208  {
    209    smartlist_t *entries = smartlist_new();
    210    consensus_cache_find_all(entries, cache, NULL, NULL);
    211    int n = smartlist_len(entries);
    212    smartlist_free(entries);
    213    tt_int_op(n, OP_EQ, 20 - 2); /* 1 entry was deleted; 1 is not-found. */
    214  }
    215  e_tmp = consensus_cache_find_first(cache, "index", "7"); // refcnt == 1...
    216  tt_ptr_op(e_tmp, OP_EQ, NULL); // so deleted.
    217  e_tmp = consensus_cache_find_first(cache, "index", "14"); // refcnt == 2
    218  tt_ptr_op(e_tmp, OP_EQ, NULL); // not deleted; but not found.
    219 
    220  /* Now do lazy unmapping. */
    221  // should do nothing.
    222  consensus_cache_unmap_lazy(cache, example_time - 10);
    223  e_tmp = consensus_cache_find_first(cache, "index", "11");
    224  tt_assert(e_tmp);
    225  tt_assert(consensus_cache_entry_is_mapped(e_tmp));
    226  // should actually unmap
    227  consensus_cache_unmap_lazy(cache, example_time + 10);
    228  e_tmp = consensus_cache_find_first(cache, "index", "11");
    229  tt_assert(e_tmp);
    230  tt_assert(! consensus_cache_entry_is_mapped(e_tmp));
    231  // This one will still be mapped, since it has a reference.
    232  e_tmp = consensus_cache_find_first(cache, "index", "16");
    233  tt_assert(e_tmp);
    234  tt_assert(consensus_cache_entry_is_mapped(e_tmp));
    235 
    236  for (i = 0; i < N; ++i) {
    237    consensus_cache_entry_decref(ents[i]);
    238    ents[i] = NULL;
    239  }
    240 
    241  /* Free and re-create the cache, to rescan the directory. Make sure the
    242   * deleted thing is still deleted, along with the other deleted thing. */
    243  consensus_cache_free(cache);
    244  cache = consensus_cache_open("cons", 128);
    245  {
    246    smartlist_t *entries = smartlist_new();
    247    consensus_cache_find_all(entries, cache, NULL, NULL);
    248    int n = smartlist_len(entries);
    249    smartlist_free(entries);
    250    tt_int_op(n, OP_EQ, 18);
    251  }
    252 
    253 done:
    254  for (i = 0; i < N; ++i) {
    255    consensus_cache_entry_decref(ents[i]);
    256  }
    257  tor_free(ents);
    258  tor_free(ddir_fname);
    259  consensus_cache_free(cache);
    260 }
    261 
    262 static void
    263 test_conscache_filter(void *arg)
    264 {
    265  (void)arg;
    266  const int N = 30;
    267  smartlist_t *lst = NULL;
    268 
    269  /* Make a temporary datadir for these tests */
    270  char *ddir_fname = tor_strdup(get_fname_rnd("datadir_cache"));
    271  tor_free(get_options_mutable()->CacheDirectory);
    272  get_options_mutable()->CacheDirectory = tor_strdup(ddir_fname);
    273  check_private_dir(ddir_fname, CPD_CREATE, NULL);
    274  consensus_cache_t *cache = consensus_cache_open("cons", 128);
    275 
    276  tt_assert(cache);
    277 
    278  /* Create a bunch of entries with different labels */
    279  int i;
    280  for (i = 0; i < N; ++i) {
    281    config_line_t *labels = NULL;
    282    char num[8];
    283    tor_snprintf(num, sizeof(num), "%d", i);
    284    config_line_append(&labels, "test-id", "filter");
    285    config_line_append(&labels, "index", num);
    286    tor_snprintf(num, sizeof(num), "%d", i % 3);
    287    config_line_append(&labels, "mod3", num);
    288    tor_snprintf(num, sizeof(num), "%d", i % 5);
    289    config_line_append(&labels, "mod5", num);
    290 
    291    size_t bodylen = i * 3;
    292    uint8_t *body = tor_malloc(bodylen);
    293    memset(body, i, bodylen);
    294    consensus_cache_entry_t *ent =
    295      consensus_cache_add(cache, labels, body, bodylen);
    296    tor_free(body);
    297    config_free_lines(labels);
    298    tt_assert(ent);
    299    consensus_cache_entry_decref(ent);
    300  }
    301 
    302  lst = smartlist_new();
    303  /* Find nothing. */
    304  consensus_cache_find_all(lst, cache, "mod5", "5");
    305  tt_int_op(smartlist_len(lst), OP_EQ, 0);
    306  /* Find everything. */
    307  consensus_cache_find_all(lst, cache, "test-id", "filter");
    308  tt_int_op(smartlist_len(lst), OP_EQ, N);
    309 
    310  /* Now filter to find the entries that have i%3 == 1 */
    311  consensus_cache_filter_list(lst, "mod3", "1");
    312  tt_int_op(smartlist_len(lst), OP_EQ, 10);
    313  /* Now filter to find the entries that also have i%5 == 3 */
    314  consensus_cache_filter_list(lst, "mod5", "3");
    315  tt_int_op(smartlist_len(lst), OP_EQ, 2);
    316  /* So now we have those entries for which i%15 == 13. */
    317 
    318  consensus_cache_entry_t *ent1 = smartlist_get(lst, 0);
    319  consensus_cache_entry_t *ent2 = smartlist_get(lst, 1);
    320  const char *idx1 = consensus_cache_entry_get_value(ent1, "index");
    321  const char *idx2 = consensus_cache_entry_get_value(ent2, "index");
    322  tt_assert( (!strcmp(idx1, "28") && !strcmp(idx2, "13")) ||
    323             (!strcmp(idx1, "13") && !strcmp(idx2, "28")) );
    324 
    325 done:
    326  tor_free(ddir_fname);
    327  consensus_cache_free(cache);
    328  smartlist_free(lst);
    329 }
    330 
    331 #define ENT(name)                                               \
    332  { #name, test_conscache_ ## name, TT_FORK, NULL, NULL }
    333 
    334 struct testcase_t conscache_tests[] = {
    335  ENT(open_failure),
    336  ENT(simple_usage),
    337  ENT(cleanup),
    338  ENT(filter),
    339  END_OF_TESTCASES
    340 };