From 161c2af306d1e2ea87cfe540f1b69770ff84bd8d Mon Sep 17 00:00:00 2001 From: Alberto Leiva Popper Date: Tue, 5 Dec 2023 18:16:21 -0300 Subject: [PATCH] Empty cache whenever Fort's version changes Because people might have missed the release notes, and also because I expect to keep improving the cache code, possibly in ways that are not backwards compatible. Case in point: I already had to tweak the TAL metadata file in the previous commit. I will probably downgrade this to something less aggressive later. --- src/cache/local_cache.c | 121 +++++++++++++++++++++++++++++++--- src/cache/local_cache.h | 3 + src/file.c | 1 + src/object/tal.c | 4 ++ test/cache/local_cache_test.c | 12 ++-- test/tal_test.c | 1 + 6 files changed, 127 insertions(+), 15 deletions(-) diff --git a/src/cache/local_cache.c b/src/cache/local_cache.c index 6cdb6e6b..ae1e9d1d 100644 --- a/src/cache/local_cache.c +++ b/src/cache/local_cache.c @@ -6,6 +6,7 @@ #include "alloc.h" #include "common.h" #include "config.h" +#include "configure_ac.h" #include "file.h" #include "json_util.h" #include "log.h" @@ -40,17 +41,117 @@ struct rpki_cache { time_t startup_ts; /* When we started the last validation */ }; +#define CACHE_METAFILE "cache.json" +#define TAGNAME_VERSION "fort-version" + +#define TAL_METAFILE "tal.json" #define TAGNAME_URL "url" #define TAGNAME_ATTEMPT_TS "attempt-timestamp" #define TAGNAME_ATTEMPT_ERR "attempt-result" #define TAGNAME_SUCCESS_TS "success-timestamp" #define TAGNAME_IS_NOTIF "is-rrdp-notification" +static char * +get_cache_metafile_name(void) +{ + struct path_builder pb; + int error; + + error = pb_init_cache(&pb, NULL, CACHE_METAFILE); + if (error) { + pr_op_err("Cannot create path to " CACHE_METAFILE ": %s", + strerror(error)); + return NULL; + } + + return pb.string; +} + +void +cache_setup(void) +{ + char *filename; + json_t *root; + json_error_t jerror; + char const *file_version; + int error; + + filename = get_cache_metafile_name(); + if (filename == NULL) + return; + + root = json_load_file(filename, 0, &jerror); + + if (root == NULL) { + if (json_error_code(&jerror) == json_error_cannot_open_file) + pr_op_debug("%s does not exist.", filename); + else + pr_op_err("Json parsing failure at %s (%d:%d): %s", + filename, jerror.line, jerror.column, jerror.text); + goto invalid_cache; + } + if (json_typeof(root) != JSON_OBJECT) { + pr_op_err("The root tag of %s is not an object.", filename); + goto invalid_cache; + } + + error = json_get_str(root, TAGNAME_VERSION, &file_version); + if (error) { + if (error > 0) + pr_op_err("%s is missing the " TAGNAME_VERSION " tag.", + filename); + goto invalid_cache; + } + + if (strcmp(file_version, PACKAGE_VERSION) == 0) + goto end; + +invalid_cache: + pr_op_info("The cache appears to have been built by a different version of Fort. I'm going to clear it, just to be safe."); + file_rm_rf(config_get_local_repository()); + +end: json_decref(root); + free(filename); +} + +void +cache_teardown(void) +{ + static char const * const CONTENT = "{ \"" TAGNAME_VERSION "\": \"" + PACKAGE_VERSION "\" }\n"; + + char *filename; + FILE *file = NULL; + int error; + + filename = get_cache_metafile_name(); + if (filename == NULL) + return; + + file = fopen(filename, "w"); + if (file == NULL) + goto fail; + + if (fprintf(file, CONTENT) < 0) + goto fail; + + free(filename); + fclose(file); + return; + +fail: + error = errno; + pr_op_err("Cannot write %s: %s", filename, strerror(error)); + free(filename); + if (file != NULL) + fclose(file); +} + static char * get_json_filename(struct rpki_cache *cache) { struct path_builder pb; - return pb_init_cache(&pb, cache->tal, "metadata.json") + return pb_init_cache(&pb, cache->tal, TAL_METAFILE) ? NULL : pb.string; } @@ -134,7 +235,7 @@ add_node(struct rpki_cache *cache, struct cache_node *node) } static void -load_metadata_json(struct rpki_cache *cache) +load_tal_json(struct rpki_cache *cache) { char *filename; json_t *root; @@ -143,7 +244,7 @@ load_metadata_json(struct rpki_cache *cache) struct cache_node *node; /* - * Note: Loading metadata.json is one of few things Fort can fail at + * Note: Loading TAL_METAFILE is one of few things Fort can fail at * without killing itself. It's just a cache of a cache. */ @@ -151,6 +252,8 @@ load_metadata_json(struct rpki_cache *cache) if (filename == NULL) return; + pr_op_debug("Loading %s.", filename); + root = json_load_file(filename, 0, &jerror); if (root == NULL) { @@ -185,7 +288,7 @@ cache_create(char const *tal) cache->startup_ts = time(NULL); if (cache->startup_ts == (time_t) -1) pr_crit("time(NULL) returned (time_t) -1."); - load_metadata_json(cache); + load_tal_json(cache); return cache; } @@ -221,7 +324,7 @@ cancel: } static json_t * -build_metadata_json(struct rpki_cache *cache) +build_tal_json(struct rpki_cache *cache) { struct cache_node *node, *tmp; json_t *root, *child; @@ -245,12 +348,12 @@ build_metadata_json(struct rpki_cache *cache) } static void -write_metadata_json(struct rpki_cache *cache) +write_tal_json(struct rpki_cache *cache) { char *filename; struct json_t *json; - json = build_metadata_json(cache); + json = build_tal_json(cache); if (json == NULL) return; @@ -736,7 +839,7 @@ delete_unknown_files(struct rpki_cache *cache) struct path_builder pb; int error; - error = pb_init_cache(&pb, cache->tal, "metadata.json"); + error = pb_init_cache(&pb, cache->tal, TAL_METAFILE); if (error) { pr_op_err("Cannot delete unknown files from %s's cache: %s", cache->tal, strerror(error)); @@ -799,7 +902,7 @@ cache_destroy(struct rpki_cache *cache) struct cache_node *node, *tmp; cache_cleanup(cache); - write_metadata_json(cache); + write_tal_json(cache); HASH_ITER(hh, cache->ht, node, tmp) delete_node(cache, node); diff --git a/src/cache/local_cache.h b/src/cache/local_cache.h index 3b677fdb..d01932a5 100644 --- a/src/cache/local_cache.h +++ b/src/cache/local_cache.h @@ -5,6 +5,9 @@ struct rpki_cache; +void cache_setup(void); +void cache_teardown(void); + struct rpki_cache *cache_create(char const *); /* Will destroy the cache object, but not the cache directory itself, obv. */ void cache_destroy(struct rpki_cache *); diff --git a/src/file.c b/src/file.c index 2cbc2e66..40a2da82 100644 --- a/src/file.c +++ b/src/file.c @@ -151,6 +151,7 @@ file_valid(char const *file_name) static int rm(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { + pr_op_debug("Deleting %s.", fpath); return (remove(fpath) != 0) ? errno : 0; } diff --git a/src/object/tal.c b/src/object/tal.c index 2d0fe8d2..0f5ad1e9 100644 --- a/src/object/tal.c +++ b/src/object/tal.c @@ -507,6 +507,8 @@ perform_standalone_validation(void) struct db_table *db = NULL; int error, tmperr; + cache_setup(); + error = init_tmpdir(); if (error) { pr_val_err("Cannot initialize the cache's temporal directory: %s", @@ -551,6 +553,8 @@ perform_standalone_validation(void) thread_destroy(thread); } + cache_teardown(); + /* If one thread has errors, we can't keep the resulting table. */ if (error) { db_table_destroy(db); diff --git a/test/cache/local_cache_test.c b/test/cache/local_cache_test.c index 0491c7d4..3cd53ae7 100644 --- a/test/cache/local_cache_test.c +++ b/test/cache/local_cache_test.c @@ -712,7 +712,7 @@ START_TEST(test_dots) } END_TEST -START_TEST(test_metadata_json) +START_TEST(test_tal_json) { json_t *json; char *str; @@ -728,8 +728,8 @@ START_TEST(test_metadata_json) add_node(cache, NODE("https://a/b", 1, 1, 0)); add_node(cache, node("https://a/c", 0, 0, 1, 0, 1)); - json = build_metadata_json(cache); - ck_assert_int_eq(0, json_dump_file(json, "tmp/" TAL_FILE "/metadata.json", JSON_COMPACT)); + json = build_tal_json(cache); + ck_assert_int_eq(0, json_dump_file(json, "tmp/" TAL_FILE "/" TAL_METAFILE, JSON_COMPACT)); str = json_dumps(json, /* JSON_INDENT(4) */ JSON_COMPACT); json_decref(json); @@ -745,7 +745,7 @@ START_TEST(test_metadata_json) cache_reset(cache); - load_metadata_json(cache); + load_tal_json(cache); ck_assert_ptr_ne(NULL, cache->ht); validate_cache(0, @@ -923,8 +923,8 @@ static Suite *thread_pool_suite(void) dot = tcase_create("dot"); tcase_add_test(dot, test_dots); - meta = tcase_create("metadata.json"); - tcase_add_test(meta, test_metadata_json); + meta = tcase_create(TAL_METAFILE); + tcase_add_test(meta, test_tal_json); recover = tcase_create("recover"); tcase_add_test(recover, test_recover); diff --git a/test/tal_test.c b/test/tal_test.c index b461ac42..930bed7b 100644 --- a/test/tal_test.c +++ b/test/tal_test.c @@ -13,6 +13,7 @@ /* Mocks */ +MOCK_ABORT_VOID(cache_setup, void) MOCK(cache_create, struct rpki_cache *, NULL, char const *tal) MOCK_VOID(cache_destroy, struct rpki_cache *cache) MOCK_ABORT_INT(cache_download, struct rpki_cache *cache, struct rpki_uri *uri, -- 2.47.2