]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Tuesday
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 10 Jul 2024 00:06:15 +0000 (18:06 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 10 Jul 2024 00:06:15 +0000 (18:06 -0600)
21 files changed:
src/cache/cachent.c
src/cache/cachent.h
src/cache/local_cache.c
src/cache/local_cache.h
src/common.c
src/common.h
src/data_structure/path_builder.c
src/data_structure/path_builder.h
src/file.c
src/file.h
src/http/http.c
src/rrdp.c
src/types/str.c [new file with mode: 0644]
src/types/str.h
test/Makefile.am
test/cache/cachent_test.c
test/cache/common.c [new file with mode: 0644]
test/cache/common.h [new file with mode: 0644]
test/cache/local_cache_test.c
test/file_test.c [deleted file]
test/mock.c

index 272bb1b41c7f46abff6f125709843789a411660d..161ca83efbe40e422d293913c7c27841a88e348e 100644 (file)
@@ -5,6 +5,19 @@
 #include "data_structure/common.h"
 #include "data_structure/path_builder.h"
 
+/* @schema must contain a colon suffix, otherwise lookups won't work */
+struct cache_node *
+cachent_create_root(char const *schema)
+{
+       struct cache_node *root;
+
+       root = pzalloc(sizeof(struct cache_node));
+       root->url = pstrdup(schema);
+       root->name = root->url;
+
+       return root;
+}
+
 /* Preorder. @cb returns whether the children should be traversed. */
 int
 cachent_traverse(struct cache_node *root,
@@ -66,38 +79,6 @@ end: pb_cleanup(&pb);
        return error;
 }
 
-struct tokenizer {
-       char const *str;
-       size_t len;
-};
-
-static bool
-is_delimiter(char chara)
-{
-       return chara == '/' || chara == '\0';
-}
-
-static void
-token_init(struct tokenizer *tkn, char const *str)
-{
-       tkn->str = str;
-       tkn->len = 0;
-}
-
-/* Like strtok_r(), but doesn't corrupt the string. */
-static bool
-token_next(struct tokenizer *tkn)
-{
-       tkn->str += tkn->len;
-       while (tkn->str[0] == '/')
-               tkn->str++;
-       if (tkn->str[0] == '\0')
-               return false;
-       for (tkn->len = 1; !is_delimiter(tkn->str[tkn->len]); tkn->len++)
-               ;
-       return true;
-}
-
 static char *
 path_rewind(char const *root, char *cursor)
 {
@@ -148,34 +129,30 @@ fail:     free(normal);
        return NULL;
 }
 
+/* Get or create parent's child. */
 static struct cache_node *
 provide(struct cache_node *parent, char const *url,
     char const *name, size_t namelen)
 {
        struct cache_node *child;
 
-       if (parent != NULL) {
-               HASH_FIND(hh, parent->children, name, namelen, child);
-               if (child != NULL)
-                       return child;
-       }
+       HASH_FIND(hh, parent->children, name, namelen, child);
+       if (child != NULL)
+               return child;
 
        child = pzalloc(sizeof(struct cache_node));
        child->url = pstrndup(url, name - url + namelen);
        child->name = child->url + (name - url);
        child->parent = parent;
-       if (parent != NULL)
-               HASH_ADD_KEYPTR(hh, parent->children, child->name,
-                   namelen, child);
-
+       HASH_ADD_KEYPTR(hh, parent->children, child->name, namelen, child);
        return child;
 }
 
 /*
- * Find and return. If not found, create and return.
+ * Get or create ancestor's descendant.
  *
  * Suppose @url is "rsync://a.b.c/d/e/f.cer": @ancestor has to be either
- * NULL, "rsync", "rsync://a.b.c", "rsync://a.b.c/d", "rsync://a.b.c/d/e" or
+ * "rsync:", "rsync://a.b.c", "rsync://a.b.c/d", "rsync://a.b.c/d/e" or
  * "rsync://a.b.c/d/e/f.cer".
  *
  * Returns NULL if @ancestor doesn't match @url.
@@ -190,20 +167,18 @@ struct cache_node *
 cachent_provide(struct cache_node *ancestor, char const *url)
 {
        char *normal;
-       array_index i = 0;
+       array_index i;
        struct tokenizer tkn;
 
        normal = normalize(url);
        if (!normal)
                return NULL;
 
-       if (ancestor != NULL) {
-               for (; ancestor->url[i] != 0; i++)
-                       if (ancestor->url[i] != normal[i])
-                               goto fail;
-               if (!is_delimiter(normal[i]))
+       for (i = 0; ancestor->url[i] != 0; i++)
+               if (ancestor->url[i] != normal[i])
                        goto fail;
-       }
+       if (normal[i] != '/' && normal[i] != '\0')
+               goto fail;
 
        token_init(&tkn, normal + i);
        while (token_next(&tkn))
@@ -257,7 +232,7 @@ cachent_delete(struct cache_node *node)
        } while (node != NULL);
 }
 
-void
+static void
 print_node(struct cache_node *node, unsigned int tabs)
 {
        unsigned int i;
@@ -268,9 +243,9 @@ print_node(struct cache_node *node, unsigned int tabs)
 
        printf("%s ", node->name);
        printf("%s", (node->flags & CNF_RSYNC) ? "RSYNC " : "");
-       printf("%s", (node->flags & CNF_DOWNLOADED) ? "DL " : "");
+       printf("%s", (node->flags & CNF_FRESH) ? "Fresh " : "");
        printf("%s", (node->flags & CNF_TOUCHED) ? "Touched " : "");
-       printf("%s", (node->flags & CNF_VALIDATED) ? "Valid " : "");
+       printf("%s", (node->flags & CNF_VALID) ? "Valid " : "");
        printf("%s\n", (node->flags & CNF_WITHDRAWN) ? "Withdrawn " : "");
 
        HASH_ITER(hh, node->children, child, tmp)
index 2676a57f506429f00c941c0ee38c4e07d3309202..c6841c1e37a67f6fc8c841350c093c1e6ec49c1f 100644 (file)
@@ -12,8 +12,8 @@
 #define CNF_RSYNC              (1 << 0)
 /* Do we have a copy in the cache? */
 #define CNF_CACHED             (1 << 1)
-/* Was it downloaded during the current cycle? XXX Probably rename to "FRESH" */
-#define CNF_DOWNLOADED         (1 << 2)
+/* Was it downloaded during the current cycle? */
+#define CNF_FRESH              (1 << 2)
 /* Did it change between the previous cycle and the current one? */
 #define CNF_CHANGED            (1 << 3)
 /* Was it read during the current cycle? */
@@ -23,7 +23,7 @@
  * (It's technically possible for two different repositories to map to the same
  * cache node. One of them is likely going to fail validation.)
  */
-#define CNF_VALIDATED          (1 << 5)
+#define CNF_VALID              (1 << 5)
 /* Is the node an RRDP Update Notification? */
 #define CNF_NOTIFICATION       (1 << 6)
 /* Withdrawn by RRDP? */
@@ -37,7 +37,7 @@ struct cache_node {
        /* Last successful download time, or zero */
        time_t mtim;
        /*
-        * If flags & CNF_DOWNLOADED, path to the temporal directory where we
+        * If flags & CNF_FRESH, path to the temporal directory where we
         * downloaded the latest refresh.
         * (See --compare-dest at rsync(1). RRDP is basically the same.)
         * Otherwise undefined.
@@ -57,6 +57,8 @@ struct cache_node {
        UT_hash_handle hh; /* Hash table hook */
 };
 
+struct cache_node *cachent_create_root(char const *);
+
 int cachent_traverse(struct cache_node *,
     bool (*cb)(struct cache_node *, char const *));
 
index faef9be47e5727a4c9601a12ee19b179e2ea0ffe..b63dee4f5ab2f650b932c95b6917e5e1871ffbc8 100644 (file)
@@ -39,8 +39,8 @@ struct cached_rpp {
 };
 
 static struct rpki_cache {
-       struct cache_node *https;
        struct cache_node *rsync;
+       struct cache_node *https;
 //     time_t startup_ts; /* When we started the last validation */
 } cache;
 
@@ -112,45 +112,45 @@ fail:
 static void
 init_cache_metafile(void)
 {
-       char *filename;
-       json_t *root;
-       json_error_t jerror;
-       char const *file_version;
-       int error;
-
-       filename = get_cache_filename(CACHE_METAFILE, true);
-       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);
+//     char *filename;
+//     json_t *root;
+//     json_error_t jerror;
+//     char const *file_version;
+//     int error;
+//
+//     filename = get_cache_filename(CACHE_METAFILE, true);
+//     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);
 }
 
 static void
@@ -180,8 +180,8 @@ init_tmp_dir(void)
 
        dirname = get_cache_filename(TMPDIR, true);
 
-       error = mkdir_p(dirname, true);
-       if (error)
+       error = mkdir(dirname, true);
+       if (error != EEXIST)
                pr_crit("Cannot create %s: %s", dirname, strerror(error));
 
        free(dirname);
@@ -333,60 +333,58 @@ json2node(json_t *json)
 }
 
 static void
-load_tal_json(struct rpki_cache *cache)
+load_tal_json(void)
 {
-       char *filename;
-       json_t *root;
-       json_error_t jerror;
-       size_t n;
-       struct cache_node *node;
-
-       /*
-        * Note: Loading TAL_METAFILE is one of few things Fort can fail at
-        * without killing itself. It's just a cache of a cache.
-        */
-
-       filename = get_tal_json_filename();
-       if (filename == NULL)
-               return;
-
-       pr_op_debug("Loading %s.", filename);
-
-       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 end;
-       }
-       if (json_typeof(root) != JSON_ARRAY) {
-               pr_op_err("The root tag of %s is not an array.", filename);
-               goto end;
-       }
+       cache.rsync = cachent_create_root("rsync:");
+       cache.https = cachent_create_root("https:");
 
-       for (n = 0; n < json_array_size(root); n++) {
-               node = json2node(json_array_get(root, n));
-               if (node != NULL)
-                       add_node(cache, node);
-       }
-
-end:   json_decref(root);
-       free(filename);
+//     char *filename;
+//     json_t *root;
+//     json_error_t jerror;
+//     size_t n;
+//     struct cache_node *node;
+//
+//     /*
+//      * Note: Loading TAL_METAFILE is one of few things Fort can fail at
+//      * without killing itself. It's just a cache of a cache.
+//      */
+//
+//     filename = get_tal_json_filename();
+//     if (filename == NULL)
+//             return;
+//
+//     pr_op_debug("Loading %s.", filename);
+//
+//     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 end;
+//     }
+//     if (json_typeof(root) != JSON_ARRAY) {
+//             pr_op_err("The root tag of %s is not an array.", filename);
+//             goto end;
+//     }
+//
+//     for (n = 0; n < json_array_size(root); n++) {
+//             node = json2node(json_array_get(root, n));
+//             if (node != NULL)
+//                     add_node(cache, node);
+//     }
+//
+//end: json_decref(root);
+//     free(filename);
 }
 
-struct rpki_cache *
-cache_create(void)
+void
+cache_prepare(void)
 {
-       struct rpki_cache *cache;
-       cache = pzalloc(sizeof(struct rpki_cache));
-       cache->startup_ts = time(NULL);
-       if (cache->startup_ts == (time_t) -1)
-               pr_crit("time(NULL) returned (time_t) -1.");
-       load_tal_json(cache);
-       return cache;
+       memset(&cache, 0, sizeof(cache));
+       load_tal_json();
 }
 
 static json_t *
@@ -461,24 +459,24 @@ build_tal_json(struct rpki_cache *cache)
 }
 
 static void
-write_tal_json(struct rpki_cache *cache)
+write_tal_json(void)
 {
-       char *filename;
-       struct json_t *json;
-
-       json = build_tal_json(cache);
-       if (json == NULL)
-               return;
-
-       filename = get_tal_json_filename();
-       if (filename == NULL)
-               goto end;
-
-       if (json_dump_file(json, filename, JSON_INDENT(2)))
-               pr_op_err("Unable to write %s; unknown cause.", filename);
-
-end:   json_decref(json);
-       free(filename);
+//     char *filename;
+//     struct json_t *json;
+//
+//     json = build_tal_json(cache);
+//     if (json == NULL)
+//             return;
+//
+//     filename = get_tal_json_filename();
+//     if (filename == NULL)
+//             goto end;
+//
+//     if (json_dump_file(json, filename, JSON_INDENT(2)))
+//             pr_op_err("Unable to write %s; unknown cause.", filename);
+//
+//end: json_decref(json);
+//     free(filename);
 }
 
 /*
@@ -486,14 +484,14 @@ end:      json_decref(json);
  * Always consumes @path.
  */
 static struct cache_node *
-find_msm(char *path, struct cache_node **msm)
+find_msm(struct cache_node *root, char *path, struct cache_node **msm)
 {
        struct cache_node *node, *child;
        char *nm, *sp; /* name, saveptr */
        size_t keylen;
 
        *msm = NULL;
-       node = cache.root;
+       node = root;
        nm = strtok_r(path + RPKI_SCHEMA_LEN, "/", &sp); // XXX
 
        for (; nm; nm = strtok_r(NULL, "/", &sp)) {
@@ -533,25 +531,17 @@ dl_rsync(char const *uri, struct cache_node *node)
        if (error)
                return error;
 
-       /*
-        * XXX the slow (-p) version is unlikely to be necessary.
-        * Maybe this function should also short-circuit by parent.
-        */
-       error = mkdir_p(path, true);
-       if (error)
-               goto cancel;
-
        // XXX looks like the third argument is redundant now.
        error = rsync_download(module->url, path, true);
        if (error)
                goto cancel;
 
-       module->flags |= CNF_DOWNLOADED;
+       module->flags |= CNF_FRESH;
        module->mtim = time(NULL); // XXX catch -1
        module->tmpdir = path;
 
        while (node != NULL) {
-               node->flags |= CNF_DOWNLOADED;
+               node->flags |= CNF_FRESH;
                node->mtim = module->mtim;
                node = node->parent;
        }
@@ -585,7 +575,7 @@ dl_rrdp(char const *notif_url, struct cache_node *mft)
        // XXX maybe pr_crit() on !mft->parent?
        return rrdp_update(cachent_provide(cache.https, notif_url), mft->parent);
 
-//     node->flags |= CNF_DOWNLOADED;
+//     node->flags |= CNF_FRESH;
 //     node->mtim = time(NULL); // XXX catch -1
 //     node->tmpdir = path;
 }
@@ -659,7 +649,7 @@ cache_download_alt(struct sia_uris *uris, maps_dl_cb cb, void *arg)
        /* XXX if parent is downloaded, child is downloaded. */
        mft = cachent_provide(cache.rsync, uris->rpkiManifest);
 
-       if (mft->flags & CNF_DOWNLOADED)
+       if (mft->flags & CNF_FRESH)
                return cb(mft, arg);
 
        for (online = 1; online >= 0; online--) {
@@ -678,6 +668,13 @@ cache_download_alt(struct sia_uris *uris, maps_dl_cb cb, void *arg)
        return error;
 }
 
+void
+cache_print(void)
+{
+       cachent_print(cache.rsync);
+       cachent_print(cache.https);
+}
+
 /*
  * XXX this needs to be hit only by files now
  * XXX result is redundant
@@ -688,9 +685,8 @@ commit_rpp_delta(struct cache_node *node, char const *path)
        if (node->tmpdir == NULL)
                return true; /* Not updated */
 
-       if (node->flags & CNF_VALIDATED)
-               /* XXX nftw() no longer needed; rename() is enough */
-               file_merge_into(node->tmpdir, path);
+       if (node->flags & CNF_VALID)
+               rename(node->tmpdir, path); // XXX
        else
                /* XXX same; just do remove(). */
                /* XXX and rename "tmpdir" into "tmp". */
@@ -845,16 +841,18 @@ commit_rpp_delta(struct cache_node *node, char const *path)
 //     return false;
 //}
 
+static struct cache_node *nftw_root;
+
 static int
-__remove_abandoned(const char *path, const struct stat *st, int typeflag,
-    struct FTW *ftw)
+nftw_remove_abandoned(const char *path, const struct stat *st,
+    int typeflag, struct FTW *ftw)
 {
        struct cache_node *pm; /* Perfect Match */
        struct cache_node *msm; /* Most Specific Match */
        struct timespec now;
 
        /* XXX node->parent has to be set */
-       pm = find_msm(pstrdup(path), &msm);
+       pm = find_msm(nftw_root, pstrdup(path), &msm);
        if (!pm && !(msm->flags & CNF_RSYNC))
                goto unknown; /* The traversal is depth-first */
 
@@ -897,16 +895,26 @@ unknown:
 static void
 remove_abandoned(void)
 {
-       char *root = join_paths(config_get_local_repository(), "rsync");
-       nftw(root, __remove_abandoned, 32, FTW_DEPTH | FTW_PHYS); // XXX
-       free(root);
+       char *rootpath;
+
+       rootpath = join_paths(config_get_local_repository(), "rsync");
+
+       nftw_root = cache.rsync;
+       nftw(rootpath, nftw_remove_abandoned, 32, FTW_DEPTH | FTW_PHYS); // XXX
+
+       strcpy(rootpath + strlen(rootpath) - 5, "https");
+
+       nftw_root = cache.https;
+       nftw(rootpath, nftw_remove_abandoned, 32, FTW_DEPTH | FTW_PHYS); // XXX
+
+       free(rootpath);
 }
 
 /*
  * Deletes unknown and old untraversed cached files, writes metadata into XML.
  */
 static void
-cache_cleanup(void)
+cleanup_cache(void)
 {
 //     struct cache_node *node, *tmp;
 //     time_t last_week;
@@ -925,13 +933,27 @@ cache_cleanup(void)
        /* XXX delete nodes for which no file exists? */
 }
 
-//void
-//cache_destroy(void)
-//{
-//     cache_cleanup();
-//     write_tal_json(cache);
-//
-//     HASH_ITER(hh, cache->ht, node, tmp)
-//             delete_node(cache, node);
-//     free(cache);
-//}
+void
+cache_commit(void)
+{
+       cleanup_cache();
+       write_tal_json();
+       cachent_delete(cache.rsync);
+       cachent_delete(cache.https);
+}
+
+void
+sias_init(struct sia_uris *sias)
+{
+       strlist_init(&sias->caRepository);
+       strlist_init(&sias->rpkiNotify);
+       sias->rpkiManifest = NULL;
+}
+
+void
+sias_cleanup(struct sia_uris *sias)
+{
+       strlist_cleanup(&sias->caRepository);
+       strlist_cleanup(&sias->rpkiNotify);
+       free(sias->rpkiManifest);
+}
index 3c6a66b770d5ccc2702089a437fb0421618908d8..02b83aa9fb524a004557b574c1913cf2fd465b04 100644 (file)
@@ -1,21 +1,17 @@
 #ifndef SRC_CACHE_LOCAL_CACHE_H_
 #define SRC_CACHE_LOCAL_CACHE_H_
 
-#include "types/map.h"
-#include "types/str.h"
 #include "cache/cachent.h"
+#include "types/str.h"
 
-struct rpki_cache;
-struct cache_node;
-
-void cache_setup(void);
-void cache_teardown(void);
+void cache_setup(void);                /* Init this module */
+void cache_teardown(void);     /* Destroy this module */
 
-int cache_tmpfile(char **);
+int cache_tmpfile(char **);    /* Return new unique path in <cache>/tmp/ */
 
-struct rpki_cache *cache_create(void);
-/* Will destroy the cache object, but not the cache directory itself, obv. */
-void cache_destroy(void);
+void cache_prepare(void);      /* Prepare cache for new validation cycle */
+void cache_commit(void);       /* Finish successful validation cycle */
+/* XXX Huh. Looks like this could use a cache_rollback() */
 
 struct sia_uris {
        struct strlist caRepository; /* rsync RPPs */
@@ -23,6 +19,9 @@ struct sia_uris {
        char *rpkiManifest;
 };
 
+void sias_init(struct sia_uris *);
+void sias_cleanup(struct sia_uris *);
+
 /*
  * The callback should return
  *
@@ -35,7 +34,6 @@ struct sia_uris {
 typedef int (*maps_dl_cb)(struct cache_node *, void *);
 int cache_download_alt(struct sia_uris *, maps_dl_cb, void *);
 
-/* Prints the cache in standard output. */
-void cache_print(struct rpki_cache *);
+void cache_print(void); /* Dump cache in stdout. Recursive; tests only */
 
 #endif /* SRC_CACHE_LOCAL_CACHE_H_ */
index 74e7383131181537b78e6a763f07a8620efe5793..daaaa7af98a53b5712b3a4758267a4b6c7adcd3e 100644 (file)
@@ -238,90 +238,6 @@ valid_file_or_dir(char const *location, bool check_file)
        return result;
 }
 
-/*
- * > 0: exists
- * = 0: !exists
- * < 0: error
- */
-static int
-dir_exists(char const *path)
-{
-       struct stat meta;
-       int error;
-
-       if (stat(path, &meta) != 0) {
-               error = errno;
-               if (error == ENOENT)
-                       return 0;
-               pr_op_err_st("stat() failed: %s", strerror(error));
-               return -error;
-       }
-
-       if (!S_ISDIR(meta.st_mode)) {
-               return pr_op_err_st("Path '%s' exists and is not a directory.",
-                   path);
-       }
-
-       return 1;
-}
-
-static int
-create_dir(char const *path)
-{
-       int error;
-
-       if (mkdir(path, 0777) != 0) {
-               error = errno;
-               if (error != EEXIST) {
-                       pr_op_err_st("Error while making directory '%s': %s",
-                           path, strerror(error));
-                       return error;
-               }
-       }
-
-       return 0;
-}
-
-/* mkdir -p $_path */
-int
-mkdir_p(char const *_path, bool include_basename)
-{
-       char *path, *last_slash;
-       int i, result = 0;
-
-       path = pstrdup(_path); /* Remove const */
-
-       if (!include_basename) {
-               last_slash = strrchr(path, '/');
-               if (last_slash == NULL)
-                       goto end;
-               *last_slash = '\0';
-       }
-
-       result = dir_exists(path); /* short circuit */
-       if (result > 0) {
-               result = 0;
-               goto end;
-       } else if (result < 0) {
-               goto end;
-       }
-
-       for (i = 1; path[i] != '\0'; i++) {
-               if (path[i] == '/') {
-                       path[i] = '\0';
-                       result = create_dir(path);
-                       path[i] = '/';
-                       if (result != 0)
-                               goto end; /* error msg already printed */
-               }
-       }
-       result = create_dir(path);
-
-end:
-       free(path);
-       return result;
-}
-
 /*
  * Delete @path.
  * If path's parent is now empty, delete parent as well.
index 01c3a788731faff2b2622c92e591630c7b2ca30f..6a3989e94da866ad357bd9a47b43ba359fbefd2f 100644 (file)
@@ -48,9 +48,8 @@ void rwlock_unlock(pthread_rwlock_t *);
 typedef int (*foreach_file_cb)(char const *, void *);
 int foreach_file(char const *, char const *, bool, foreach_file_cb, void *);
 
+// XXX
 bool valid_file_or_dir(char const *, bool);
-
-int mkdir_p(char const *, bool);
 int delete_dir_recursive_bottom_up(char const *);
 
 int get_current_time(time_t *);
index c1a226bc230840a21d3cbe04c4ca17966c2e2d09..68b99ba77fe3bf62f63a1568b43c799d4359ca73 100644 (file)
 #endif
 #define MAX_CAPACITY 4096u
 
+static bool
+is_delimiter(char chara)
+{
+       return chara == '/' || chara == '\0';
+}
+
+void
+token_init(struct tokenizer *tkn, char const *str)
+{
+       tkn->str = str;
+       tkn->len = 0;
+}
+
+/* Like strtok_r(), but doesn't corrupt the string. */
+bool
+token_next(struct tokenizer *tkn)
+{
+       tkn->str += tkn->len;
+       while (tkn->str[0] == '/')
+               tkn->str++;
+       if (tkn->str[0] == '\0')
+               return false;
+       for (tkn->len = 1; !is_delimiter(tkn->str[tkn->len]); tkn->len++)
+               ;
+       return true;
+}
+
 /* @reserve needs to be < INITIAL_CAPACITY. */
 void
 __pb_init(struct path_builder *pb, size_t reserve)
@@ -188,3 +215,21 @@ pb_cleanup(struct path_builder *pb)
 {
        free(pb->string);
 }
+
+/* Cannot return NULL. */
+char *
+join_paths(char const *path1, char const *path2)
+{
+       size_t n;
+       char *result;
+       int written;
+
+       n = strlen(path1) + strlen(path2) + 2;
+       result = pmalloc(n);
+
+       written = snprintf(result, n, "%s/%s", path1, path2);
+       if (written != n - 1)
+               pr_crit("join_paths: %zu %d %s %s", n, written, path1, path2);
+
+       return result;
+}
index 1ddc4bee195ade88b9ff41f893be9cf3bfe9e877..fb5986441a5d6b16d4bfc9cc3d3e6cca94b56f80 100644 (file)
@@ -1,9 +1,17 @@
 #ifndef SRC_DATA_STRUCTURE_PATH_BUILDER_H_
 #define SRC_DATA_STRUCTURE_PATH_BUILDER_H_
 
+#include <stdbool.h>
 #include <netdb.h>
 
-#include "types/map.h"
+// XXX rename
+struct tokenizer {
+       char const *str;
+       size_t len;
+};
+
+void token_init(struct tokenizer *, char const *);
+bool token_next(struct tokenizer *tkn);
 
 struct path_builder {
        char *string;
@@ -30,4 +38,6 @@ void pb_reverse(struct path_builder *);
 
 void pb_cleanup(struct path_builder *);
 
+char *join_paths(char const *, char const *);
+
 #endif /* SRC_DATA_STRUCTURE_PATH_BUILDER_H_ */
index 838dae30175e43dc38d82d7b697891217f73cc37..81a5057201c9137836c220ce74832752fc0d31a1 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "alloc.h"
 #include "log.h"
+#include "data_structure/path_builder.h"
 #include "data_structure/uthash.h"
 
 int
@@ -135,53 +136,6 @@ file_exists(char const *path)
        return (stat(path, &meta) == 0) ? 0 : errno;
 }
 
-/* strlen("cache/tmp/123"), ie. 13 */
-static size_t src_offset;
-/* cache/rsync/a.b.c/d/e */
-static char const *merge_dst;
-
-/* Moves cache/tmp/123/z into cache/rsync/a.b.c/d/e/z. */
-static int
-merge_into(const char *src, const struct stat *st, int typeflag,
-    struct FTW *ftw)
-{
-       char *dst;
-       struct timespec times[2];
-
-       dst = join_paths(merge_dst, &src[src_offset]);
-
-       if (S_ISDIR(st->st_mode)) {
-               mkdir(dst, st->st_mode); /* XXX catch error */
-
-               times[0] = st->st_atim;
-               times[1] = st->st_mtim;
-               utimensat(AT_FDCWD, dst, times, AT_SYMLINK_NOFOLLOW); /* XXX catch error */
-       } else {
-               rename(src, dst); /* XXX catch error */
-       }
-
-       free(dst);
-       return 0;
-}
-
-/*
- * Move all the files contained in @src to @dst, overwriting when necessary,
- * not touching files that exist in @dst but not in @src.
- *
- * Both directories have to already exist.
- *
- * @src: cache/tmp/123
- * @dst: cache/rsync/a.b.c/d/e
- */
-int
-file_merge_into(char const *src, char const *dst)
-{
-       src_offset = strlen(src);
-       merge_dst = dst;
-       /* TODO (performance) optimize that 32 */
-       return nftw(src, merge_into, 32, FTW_PHYS);
-}
-
 /*
  * Like remove(), but don't care if the file is already deleted.
  */
@@ -216,20 +170,87 @@ file_rm_rf(char const *path)
        return nftw(path, rm, 32, FTW_DEPTH | FTW_PHYS);
 }
 
-/* Cannot return NULL. */
-char *
-join_paths(char const *path1, char const *path2)
+/*
+ * > 0: exists
+ * = 0: !exists
+ * < 0: error
+ */
+static int
+dir_exists(char const *path)
 {
-       size_t n;
-       char *result;
-       int written;
+       struct stat meta;
+       int error;
 
-       n = strlen(path1) + strlen(path2) + 2;
-       result = pmalloc(n);
+       if (stat(path, &meta) != 0) {
+               error = errno;
+               if (error == ENOENT)
+                       return 0;
+               pr_op_err_st("stat() failed: %s", strerror(error));
+               return -error;
+       }
 
-       written = snprintf(result, n, "%s/%s", path1, path2);
-       if (written != n - 1)
-               pr_crit("join_paths: %zu %d %s %s", n, written, path1, path2);
+       if (!S_ISDIR(meta.st_mode)) {
+               return pr_op_err_st("Path '%s' exists and is not a directory.",
+                   path);
+       }
+
+       return 1;
+}
 
+static int
+ensure_dir(char const *path)
+{
+       int error;
+
+       if (mkdir(path, 0777) != 0) {
+               error = errno;
+               if (error != EEXIST) {
+                       pr_op_err_st("Error while making directory '%s': %s",
+                           path, strerror(error));
+                       return error;
+               }
+       }
+
+       return 0;
+}
+
+/* mkdir -p $_path */
+/* XXX Maybe also short-circuit by parent? */
+int
+mkdir_p(char const *_path, bool include_basename)
+{
+       char *path, *last_slash;
+       int i, result = 0;
+
+       path = pstrdup(_path); /* Remove const */
+
+       if (!include_basename) {
+               last_slash = strrchr(path, '/');
+               if (last_slash == NULL)
+                       goto end;
+               *last_slash = '\0';
+       }
+
+       result = dir_exists(path); /* short circuit */
+       if (result > 0) {
+               result = 0;
+               goto end;
+       } else if (result < 0) {
+               goto end;
+       }
+
+       for (i = 1; path[i] != '\0'; i++) {
+               if (path[i] == '/') {
+                       path[i] = '\0';
+                       result = ensure_dir(path);
+                       path[i] = '/';
+                       if (result != 0)
+                               goto end; /* error msg already printed */
+               }
+       }
+       result = ensure_dir(path);
+
+end:
+       free(path);
        return result;
 }
index 8f6f959b69eda8a898c60af3230d2c7e63c5e9bd..0d1fd8c7e5d6f0f46296c14c5ef20f4837f05c23 100644 (file)
@@ -36,7 +36,7 @@ int file_merge_into(char const *, char const *);
 int file_rm_f(char const *);
 int file_rm_rf(char const *);
 
-char *join_paths(char const *, char const *);
+int mkdir_p(char const *, bool);
 
 /*
  * Remember that this API is awkward:
index 1314c91fa1a8be72a306fba15d5db7676ffe87ec..86b0eb58914caf59508452fe4d7008f0222ca5af 100644 (file)
@@ -461,7 +461,7 @@ http_download_cache_node(struct cache_node *node)
        if (error)
                return error;
 
-       node->flags |= CNF_DOWNLOADED; // XXX on notification, preserve node but not file
+       node->flags |= CNF_FRESH; // XXX on notification, preserve node but not file
        if (changed) {
                node->flags |= CNF_CHANGED;
                node->mtim = time(NULL); // XXX catch -1
index c0f87151c4caa69b80a89dddcf0b6053807c8236..d616d76d3ed9f488dcfac66624bba9b2f3853546 100644 (file)
@@ -1110,7 +1110,7 @@ rrdp_update(struct cache_node *notif, struct cache_node *rpp)
 
        if (!(notif->flags & CNF_CHANGED)) {
                pr_val_debug("The Notification has not changed.");
-               rpp->flags |= CNF_DOWNLOADED; /* Success */
+               rpp->flags |= CNF_FRESH; /* Success */
                goto end;
        }
 
diff --git a/src/types/str.c b/src/types/str.c
new file mode 100644 (file)
index 0000000..155425d
--- /dev/null
@@ -0,0 +1,37 @@
+#include "types/str.h"
+
+void
+strlist_init(struct strlist *list)
+{
+       list->array = NULL;
+       list->len = 0;
+       list->capacity = 0;
+}
+
+void
+strlist_add(struct strlist *list, char *str)
+{
+       if (list->array == NULL) {
+               list->capacity = 8;
+               list->array = pmalloc(list->capacity * sizeof(char *));
+       }
+
+       list->len++;
+       while (list->len >= list->capacity) {
+               list->capacity *= 2;
+               list->array = prealloc(list->array,
+                   list->capacity * sizeof(char *));
+       }
+
+       list->array[list->len - 1] = str;
+}
+
+/* Call strlist_init() again if you want to reuse the list. */
+void
+strlist_cleanup(struct strlist *list)
+{
+       array_index i;
+       for (i = 0; i < list->len; i++)
+               free(list->array[i]);
+       free(list->array);
+}
index 4ca8572a3c5cf5a9823f19b20cd9448373def3ff..b49c3e8a2bd257577cb0a0d04b14453a7f1e1dba 100644 (file)
@@ -7,6 +7,6 @@ DEFINE_ARRAY_LIST_STRUCT(strlist, char *);
 
 void strlist_init(struct strlist *);
 void strlist_cleanup(struct strlist *);
-void strlist_add(struct strlist *list, char *elem);
+void strlist_add(struct strlist *, char *);
 
 #endif /* SRC_TYPES_STR_H_ */
index ee26a1dba6c145a6e1681993b31b14b641552e88..79c88a47461c946ea06f79d3977dde0a0ef9630a 100644 (file)
@@ -22,16 +22,12 @@ AM_CFLAGS += -I../src -DUNIT_TESTING ${CHECK_CFLAGS} ${XML2_CFLAGS} ${JANSSON_CF
 # target.
 MY_LDADD = ${CHECK_LIBS} ${JANSSON_LIBS}
 
-#check_PROGRAMS = file.test
-#check_PROGRAMS  = cache.test
-check_PROGRAMS = cachent.test
+check_PROGRAMS  = cachent.test
+check_PROGRAMS += cache.test
 TESTS = ${check_PROGRAMS}
 
-#file_test_SOURCES = file_test.c
-#file_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS}
-
-#cache_test_SOURCES = cache/local_cache_test.c
-#cache_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS}
+cache_test_SOURCES = cache/local_cache_test.c
+cache_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS}
 
 cachent_test_SOURCES = cache/cachent_test.c
 cachent_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS}
index a4c6b571aaa83a39de1d64050fd4a70a1b0b1ae8..bec2105441c3b10e8be41692ab2e624f019a6554 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "alloc.c"
 #include "cache/cachent.c"
+#include "cache/common.c"
 #include "data_structure/path_builder.c"
 #include "mock.c"
 
@@ -14,28 +15,6 @@ __delete_node_cb(struct cache_node const *node)
        strcpy(deleted[dn++], node->name);
 }
 
-static struct cache_node *
-node(char const *name, int flags, ...)
-{
-       struct cache_node *result;
-       struct cache_node *child;
-       va_list args;
-
-       result = pzalloc(sizeof(struct cache_node));
-       result->name = pstrdup(name);
-       result->flags = flags;
-
-       va_start(args, flags);
-       while ((child = va_arg(args, struct cache_node *)) != NULL) {
-               HASH_ADD_KEYPTR(hh, result->children, child->name,
-                   strlen(child->name), child);
-               child->parent = result;
-       }
-       va_end(args);
-
-       return result;
-}
-
 START_TEST(test_delete)
 {
        struct cache_node *root, *a, *b;
@@ -274,8 +253,14 @@ START_TEST(test_provide)
 {
        struct cache_node *rsync, *abc, *d, *e, *f, *g, *h, *ee;
 
-       /* Create tree from nothing */
-       e = cachent_provide(NULL, "rsync://a.b.c/d/e");
+       rsync = cachent_create_root("rsync:");
+       ck_assert_ptr_ne(NULL, rsync);
+       ck_assert_ptr_eq(NULL, rsync->parent);
+       ck_assert_str_eq("rsync:", rsync->url);
+       ck_assert_str_eq("rsync:", rsync->name);
+
+       /* Create branch chain from root */
+       e = cachent_provide(rsync, "rsync://a.b.c/d/e");
        ck_assert_ptr_ne(NULL, e);
        ck_assert_str_eq("rsync://a.b.c/d/e", e->url);
        ck_assert_str_eq("e", e->name);
@@ -290,11 +275,7 @@ START_TEST(test_provide)
        ck_assert_str_eq("rsync://a.b.c", abc->url);
        ck_assert_str_eq("a.b.c", abc->name);
 
-       rsync = abc->parent;
-       ck_assert_ptr_ne(NULL, rsync);
-       ck_assert_ptr_eq(NULL, rsync->parent);
-       ck_assert_str_eq("rsync:", rsync->url);
-       ck_assert_str_eq("rsync:", rsync->name);
+       ck_assert_ptr_eq(rsync, abc->parent);
 
        /* Find leaf from root */
        ck_assert_ptr_eq(e, cachent_provide(rsync, "rsync://a.b.c/d/e"));
diff --git a/test/cache/common.c b/test/cache/common.c
new file mode 100644 (file)
index 0000000..fac9b2c
--- /dev/null
@@ -0,0 +1,30 @@
+#include "cache/common.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include "data_structure/uthash.h"
+
+struct cache_node *
+node(char const *url, int flags, ...)
+{
+       struct cache_node *result;
+       struct cache_node *child;
+       char const *slash;
+       va_list args;
+
+       result = pzalloc(sizeof(struct cache_node));
+       result->url = pstrdup(url);
+       slash = strrchr(url, '/');
+       result->name = slash ? (slash + 1) : result->url;
+       result->flags = flags;
+
+       va_start(args, flags);
+       while ((child = va_arg(args, struct cache_node *)) != NULL) {
+               HASH_ADD_KEYPTR(hh, result->children, child->name,
+                   strlen(child->name), child);
+               child->parent = result;
+       }
+       va_end(args);
+
+       return result;
+}
diff --git a/test/cache/common.h b/test/cache/common.h
new file mode 100644 (file)
index 0000000..8708d33
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef TEST_CACHE_COMMON_H_
+#define TEST_CACHE_COMMON_H_
+
+#include "cache/cachent.h"
+
+struct cache_node *node(char const *, int , ...);
+
+#endif /* TEST_CACHE_COMMON_H_ */
index 2b0fc0fcc4fd2ab5cd08a381c4e98a4b3ee93123..82885f63dc1ca1aa1c46b659d2caf51cfe836a0b 100644 (file)
@@ -5,13 +5,16 @@
 
 #include <check.h>
 //#include <stdarg.h>
-//#include <sys/queue.h>
+#include <sys/queue.h>
 //
 #include "alloc.c"
-//#include "common.c"
 //#include "json_util.c"
 #include "mock.c"
+#include "cache/cachent.c"
+#include "cache/common.c"
 #include "cache/local_cache.c"
+#include "data_structure/path_builder.c"
+#include "types/str.c"
 //#include "types/map.c"
 
 /* Mocks */
@@ -63,6 +66,12 @@ file_rm_f(char const *file)
 
 MOCK_ABORT_INT(file_get_mtim, char const *file, time_t *ims)
 
+static void
+__delete_node_cb(struct cache_node const *node)
+{
+       /* Nothing */
+}
+
 static int
 pretend_download(char const *local)
 {
@@ -88,17 +97,17 @@ rsync_download(char const *src, char const *dst, bool is_directory)
 }
 
 int
-http_download(struct cache_mapping *map, curl_off_t ims, bool *changed)
+http_download(char const *url, char const *path, curl_off_t ims, bool *changed)
 {
        int error;
        https_counter++;
-       error = pretend_download(map_get_path(map));
+       error = pretend_download(path);
        if (changed != NULL)
                *changed = error ? false : true;
        return error;
 }
 
-MOCK_ABORT_INT(rrdp_update, struct cache_mapping *map)
+MOCK_ABORT_INT(rrdp_update, struct cache_node *notif, struct cache_node *rpp)
 __MOCK_ABORT(rrdp_notif2json, json_t *, NULL, struct cachefile_notification *notif)
 MOCK_VOID(rrdp_notif_free, struct cachefile_notification *notif)
 MOCK_ABORT_INT(rrdp_json2notif, json_t *json, struct cachefile_notification **result)
@@ -108,65 +117,38 @@ MOCK_ABORT_INT(rrdp_json2notif, json_t *json, struct cachefile_notification **re
 static void
 setup_test(void)
 {
-       ck_assert_int_eq(0, system("rm -rf tmp/"));
-
        dl_error = false;
-       cache = cache_create();
-       ck_assert_ptr_ne(NULL, cache);
        SLIST_INIT(&downloaded);
+
+       ck_assert_int_eq(0, system("rm -rf tmp/"));
+       cache_prepare();
+}
+
+static int
+okay(struct cache_node *node, void *arg)
+{
+       return 0;
 }
 
 static void
-run_cache_download(char const *url, int expected_error,
-    unsigned int rsync_calls, unsigned int https_calls)
+run_dl_rsync(char const *caRepository, char const *rpkiManifest,
+    int expected_error, unsigned int expected_calls)
 {
-       struct cache_mapping *map;
-       enum map_type type;
+       static struct sia_uris sias;
 
-       if (str_starts_with(url, "https://"))
-               type = MAP_HTTP;
-       else if (str_starts_with(url, "rsync://"))
-               type = MAP_RSYNC;
-       else
-               ck_abort_msg("Bad protocol: %s", url);
+       sias_init(&sias);
+       strlist_add(&sias.caRepository, pstrdup(caRepository));
+       sias.rpkiManifest = pstrdup(rpkiManifest);
 
        rsync_counter = 0;
        https_counter = 0;
+       ck_assert_int_eq(expected_error, cache_download_alt(&sias, okay, NULL));
+       ck_assert_uint_eq(expected_calls, rsync_counter);
+       ck_assert_uint_eq(0, https_counter);
 
-       ck_assert_int_eq(0, map_create(&map, type, url));
-       ck_assert_int_eq(expected_error, cache_download(cache, map, NULL, NULL));
-       ck_assert_uint_eq(rsync_calls, rsync_counter);
-       ck_assert_uint_eq(https_calls, https_counter);
-
-       map_refput(map);
-}
-
-static struct cache_node *
-node(char const *url, time_t attempt, int err, bool succeeded, time_t success,
-    bool is_notif)
-{
-       enum map_type type;
-       struct cache_node *result;
-
-       if (str_starts_with(url, "https://"))
-               type = is_notif ? MAP_NOTIF : MAP_HTTP;
-       else if (str_starts_with(url, "rsync://"))
-               type = MAP_RSYNC;
-       else
-               ck_abort_msg("Bad protocol: %s", url);
-
-       result = pzalloc(sizeof(struct cache_node));
-       ck_assert_int_eq(0, map_create(&result->map, type, url));
-       result->attempt.ts = attempt;
-       result->attempt.result = err;
-       result->success.happened = succeeded;
-       result->success.ts = success;
-
-       return result;
+       sias_cleanup(&sias);
 }
 
-#define NODE(url, err, succeeded, has_file) node(url, has_file, err, succeeded, 0, 0)
-
 static void
 reset_visiteds(void)
 {
@@ -181,18 +163,35 @@ find_downloaded_path(struct cache_node *node)
        struct downloaded_path *path;
 
        SLIST_FOREACH(path, &downloaded, hook)
-               if (strcmp(map_get_path(node->map), path->path) == 0) {
+               if (strcmp(node->tmpdir, path->path) == 0) {
                        if (path->visited)
-                               return NULL;
-                       else {
-                               path->visited = true;
-                               return path;
-                       }
+                               ck_abort_msg("Looked up twice: %s", path->path);
+                       path->visited = true;
+                       return path;
                }
 
        return NULL;
 }
 
+static bool
+check_path(struct cache_node *node, char const *_)
+{
+       struct downloaded_path *path;
+
+       path = find_downloaded_path(node);
+       if (node->flags & CNF_CACHED) {
+               if (path == NULL)
+                       ck_abort_msg("Cached file is missing: %s",
+                           node->tmpdir);
+       } else {
+               if (path != NULL)
+                       ck_abort_msg("Cached file should not exist: %s",
+                           node->tmpdir);
+       }
+
+       return true;
+}
+
 static void
 fail_if_nonvisited(void)
 {
@@ -202,50 +201,77 @@ fail_if_nonvisited(void)
                        ck_abort_msg("Unexpected cache file: %s", path->path);
 }
 
+//static struct cache_node *
+//cachent_find(struct cache_node *root, char const *url)
+//{
+//     struct cache_node *node, *child;
+//     struct tokenizer tkn;
+//
+//     node = root;
+//     token_init(&tkn, url);
+//     if (!token_next(&tkn))
+//             ck_abort_msg("Path too short: %s", url);
+//     if (strncmp(root->name, tkn.str, tkn.len) != 0) {
+//             ck_abort_msg("Root doesn't match: %s != %.*s",
+//                 root->name, (int)tkn.len, tkn.str);
+//     }
+//
+//     while (token_next(&tkn)) {
+//             if (tkn.len == 1 && tkn.str[0] == '.')
+//                     continue;
+//             if (tkn.len == 2 && tkn.str[0] == '.' && tkn.str[1] == '.')
+//                     node = node->parent;
+//
+//             HASH_FIND(hh, node->children, tkn.str, tkn.len, child);
+//             if (child == NULL)
+//                     ck_abort_msg("Child not found: %s > %.*s",
+//                         node->name, (int)tkn.len, tkn.str);
+//
+//             node = child;
+//     }
+//
+//     return node;
+//}
+
 static void
-validate_node(struct cache_node *expected, struct cache_node *actual)
+ck_assert_cachent_eq(struct cache_node *expected, struct cache_node *actual)
 {
-       if (expected == NULL) {
-               ck_assert_ptr_eq(NULL, actual);
-               return;
+       struct cache_node *echild, *achild, *tmp;
+
+       ck_assert_str_eq(expected->url, actual->url);
+       ck_assert_str_eq(expected->name, actual->name);
+       ck_assert_int_eq(expected->flags, actual->flags);
+
+       HASH_ITER(hh, expected->children, echild, tmp) {
+               HASH_FIND(hh, actual->children, echild->name,
+                   strlen(echild->name), achild);
+               if (achild == NULL)
+                       ck_abort_msg("Expected not found: %s", echild->url);
+               ck_assert_cachent_eq(echild, achild);
        }
 
-       ck_assert_str_eq(map_get_url(expected->map), map_get_url(actual->map));
-       /* ck_assert_int_eq(expected->attempt.ts, actual->attempt.ts); */
-       ck_assert_int_eq(expected->attempt.result, actual->attempt.result);
-       ck_assert_int_eq(expected->success.happened, actual->success.happened);
-       /* ck_assert_int_eq(expected->success.ts, actual->success.ts); */
+       HASH_ITER(hh, actual->children, achild, tmp) {
+               HASH_FIND(hh, expected->children, achild->name,
+                   strlen(achild->name), echild);
+               if (echild == NULL)
+                       ck_abort_msg("Actual not found: %s", achild->url);
+       }
 }
 
 static void
-validate_cache(int trash, ...)
+validate_cache(struct cache_node *rsync, struct cache_node *https)
 {
-       struct cache_node *expected = NULL;
-       struct cache_node *e, *a, *tmp;
        struct downloaded_path *path;
-       char const *key;
-       va_list args;
 
        printf("------------------------------\n");
-       printf("Expected nodes:\n");
 
-       va_start(args, trash);
-       while ((e = va_arg(args, struct cache_node *)) != NULL) {
-               printf("- %s %s error:%u success:%u\n",
-                   map_get_url(e->map), map_get_path(e->map),
-                   e->attempt.result, e->success.happened);
-
-               key = map_get_url(e->map);
-               HASH_ADD_KEYPTR(hh, expected, key, strlen(key), e);
-       }
-       va_end(args);
+       printf("Expected nodes:\n");
+       cachent_print(rsync);
+       cachent_print(https);
        printf("\n");
 
        printf("Actual nodes:\n");
-       HASH_ITER(hh, cache->ht, a, tmp)
-               printf("- %s %s attempt:%u success:%u\n",
-                   map_get_url(a->map), map_get_path(a->map),
-                   a->attempt.result, a->success.happened);
+       cache_print();
        printf("\n");
 
        printf("Files in cache:\n");
@@ -255,61 +281,33 @@ validate_cache(int trash, ...)
 
        /* Compare expected and cache */
        reset_visiteds();
-
-       HASH_ITER(hh, expected, e, tmp) {
-               path = find_downloaded_path(e);
-               if (e->attempt.ts) { /* "if should have cache file" */
-                       if (path == NULL)
-                               ck_abort_msg("Cached file is missing: %s",
-                                   map_get_path(e->map));
-                       path->visited = true;
-               } else {
-                       if (path != NULL) {
-                               ck_abort_msg("Cached file should not exist: %s",
-                                   path->path);
-                       }
-               }
-       }
-
+       cachent_traverse(rsync, check_path);
+       cachent_traverse(https, check_path);
        fail_if_nonvisited();
 
        /* Compare expected and actual */
-       HASH_ITER(hh, cache->ht, a, tmp) {
-               key = map_get_url(a->map);
-               HASH_FIND_STR(expected, key, e);
-               if (e == NULL)
-                       ck_abort_msg("Unexpected actual: %s", key);
-
-               validate_node(e, a);
-
-               HASH_DEL(expected, e);
-               map_refput(e->map);
-               free(e);
-       }
-
-       if (HASH_COUNT(expected) != 0)
-               ck_abort_msg("Actual node is mising: %s",
-                   map_get_url(expected->map));
-}
-
-static void
-new_iteration(bool outdate)
-{
-       struct cache_node *node, *tmp;
-       time_t epoch;
-
-       epoch = outdate ? get_days_ago(30) : get_days_ago(1);
-       HASH_ITER(hh, cache->ht, node, tmp)
-               node->attempt.ts = epoch;
+       ck_assert_cachent_eq(rsync, cache.rsync);
+       ck_assert_cachent_eq(https, cache.https);
 }
 
-static void
-cache_reset(struct rpki_cache *cache)
-{
-       struct cache_node *node, *tmp;
-       HASH_ITER(hh, cache->ht, node, tmp)
-               delete_node(cache, node);
-}
+//static void
+//new_iteration(bool outdate)
+//{
+//     struct cache_node *node, *tmp;
+//     time_t epoch;
+//
+//     epoch = outdate ? get_days_ago(30) : get_days_ago(1);
+//     HASH_ITER(hh, cache->ht, node, tmp)
+//             node->attempt.ts = epoch;
+//}
+//
+//static void
+//cache_reset(struct rpki_cache *cache)
+//{
+//     struct cache_node *node, *tmp;
+//     HASH_ITER(hh, cache->ht, node, tmp)
+//             delete_node(cache, node);
+//}
 
 static void
 cleanup_test(void)
@@ -317,7 +315,7 @@ cleanup_test(void)
        struct downloaded_path *path;
 
        dl_error = false;
-       cache_destroy(cache);
+       cache_commit();
 
        while (!SLIST_EMPTY(&downloaded)) {
                path = SLIST_FIRST(&downloaded);
@@ -331,581 +329,588 @@ cleanup_test(void)
 
 START_TEST(test_cache_download_rsync)
 {
-       setup_test();
-
-       run_cache_download("rsync://a.b.c/d", 0, 1, 0);
-       validate_cache(0, NODE("rsync://a.b.c/d/", 0, 1, true), NULL);
-
-       /* Redownload same file, nothing should happen */
-       run_cache_download("rsync://a.b.c/d", 0, 0, 0);
-       validate_cache(0, NODE("rsync://a.b.c/d/", 0, 1, true), NULL);
-
-       /*
-        * rsyncs are recursive, which means if we've been recently asked to
-        * download d, we needn't bother redownloading d/e.
-        */
-       run_cache_download("rsync://a.b.c/d/e", 0, 0, 0);
-       validate_cache(0, NODE("rsync://a.b.c/d/", 0, 1, true), NULL);
-
-       /*
-        * rsyncs get truncated, because it results in much faster
-        * synchronization in practice.
-        * This is not defined in any RFCs; it's an effective standard,
-        * and there would be consequences for violating it.
-        */
-       run_cache_download("rsync://x.y.z/m/n/o", 0, 1, 0);
-       validate_cache(0,
-           NODE("rsync://a.b.c/d/", 0, 1, true),
-           NODE("rsync://x.y.z/m/", 0, 1, true),
-           NULL);
-
-       /* Sibling */
-       run_cache_download("rsync://a.b.c/e/f", 0, 1, 0);
-       validate_cache(0,
-           NODE("rsync://a.b.c/d/", 0, 1, true),
-           NODE("rsync://a.b.c/e/", 0, 1, true),
-           NODE("rsync://x.y.z/m/", 0, 1, true),
-           NULL);
-
-       cleanup_test();
-}
-END_TEST
-
-START_TEST(test_cache_download_rsync_error)
-{
-       setup_test();
-
-       dl_error = false;
-       run_cache_download("rsync://a.b.c/d", 0, 1, 0);
-       dl_error = true;
-       run_cache_download("rsync://a.b.c/e", -EINVAL, 1, 0);
-       validate_cache(0,
-           NODE("rsync://a.b.c/d/", 0, 1, true),
-           NODE("rsync://a.b.c/e/", -EINVAL, 0, false),
-           NULL);
-
-       /* Regardless of error, not reattempted because same iteration */
-       dl_error = true;
-       run_cache_download("rsync://a.b.c/e", -EINVAL, 0, 0);
-       validate_cache(0,
-           NODE("rsync://a.b.c/d/", 0, 1, true),
-           NODE("rsync://a.b.c/e/", -EINVAL, 0, false),
-           NULL);
-
-       dl_error = false;
-       run_cache_download("rsync://a.b.c/e", -EINVAL, 0, 0);
-       validate_cache(0,
-           NODE("rsync://a.b.c/d/", 0, 1, true),
-           NODE("rsync://a.b.c/e/", -EINVAL, 0, false),
-           NULL);
-
-       cleanup_test();
-}
-END_TEST
-
-START_TEST(test_cache_cleanup_rsync)
-{
-       setup_test();
-
-       /*
-        * First iteration: Tree is created. No prunes, because nothing's
-        * outdated.
-        */
-       new_iteration(true);
-       run_cache_download("rsync://a.b.c/d", 0, 1, 0);
-       run_cache_download("rsync://a.b.c/e", 0, 1, 0);
-       cache_cleanup(cache);
-       validate_cache(0,
-           NODE("rsync://a.b.c/d/", 0, 1, true),
-           NODE("rsync://a.b.c/e/", 0, 1, true),
-           NULL);
-
-       /* One iteration with no changes, for paranoia */
-       new_iteration(true);
-       run_cache_download("rsync://a.b.c/d", 0, 1, 0);
-       run_cache_download("rsync://a.b.c/e", 0, 1, 0);
-       cache_cleanup(cache);
-       validate_cache(0,
-               NODE("rsync://a.b.c/d/", 0, 1, true),
-               NODE("rsync://a.b.c/e/", 0, 1, true),
-               NULL);
-
-       /* Add one sibling */
-       new_iteration(true);
-       run_cache_download("rsync://a.b.c/d", 0, 1, 0);
-       run_cache_download("rsync://a.b.c/e", 0, 1, 0);
-       run_cache_download("rsync://a.b.c/f", 0, 1, 0);
-       cache_cleanup(cache);
-       validate_cache(0,
-               NODE("rsync://a.b.c/d/", 0, 1, true),
-               NODE("rsync://a.b.c/e/", 0, 1, true),
-               NODE("rsync://a.b.c/f/", 0, 1, true),
-               NULL);
-
-       /* Nodes don't get updated, but they're still too young. */
-       new_iteration(false);
-       cache_cleanup(cache);
-       validate_cache(0,
-               NODE("rsync://a.b.c/d/", 0, 1, true),
-               NODE("rsync://a.b.c/e/", 0, 1, true),
-               NODE("rsync://a.b.c/f/", 0, 1, true),
-               NULL);
-
-       /* Remove some branches */
-       new_iteration(true);
-       run_cache_download("rsync://a.b.c/d", 0, 1, 0);
-       cache_cleanup(cache);
-       validate_cache(0, NODE("rsync://a.b.c/d/", 0, 1, true), NULL);
-
-       /* Remove old branch and add sibling at the same time */
-       new_iteration(true);
-       run_cache_download("rsync://a.b.c/e", 0, 1, 0);
-       cache_cleanup(cache);
-       validate_cache(0, NODE("rsync://a.b.c/e/", 0, 1, true), NULL);
-
-       /* Try child */
-       new_iteration(true);
-       run_cache_download("rsync://a.b.c/e/f/g", 0, 1, 0);
-       cache_cleanup(cache);
-       validate_cache(0, NODE("rsync://a.b.c/e/", 0, 1, true), NULL);
-
-       /* Parent again */
-       new_iteration(true);
-       run_cache_download("rsync://a.b.c/e", 0, 1, 0);
-       cache_cleanup(cache);
-       validate_cache(0, NODE("rsync://a.b.c/e/", 0, 1, true), NULL);
-
-       /* Empty the tree */
-       new_iteration(true);
-       cache_cleanup(cache);
-       validate_cache(0, NULL);
-
-       /* Node exists, but file doesn't */
-       new_iteration(true);
-       run_cache_download("rsync://a.b.c/e", 0, 1, 0);
-       run_cache_download("rsync://a.b.c/f", 0, 1, 0);
-       validate_cache(0,
-               NODE("rsync://a.b.c/e/", 0, 1, true),
-               NODE("rsync://a.b.c/f/", 0, 1, true),
-               NULL);
-       ck_assert_int_eq(0, file_rm_rf("tmp/rsync/a.b.c/f"));
-       cache_cleanup(cache);
-       validate_cache(0, NODE("rsync://a.b.c/e/", 0, 1, true), NULL);
-
-       cleanup_test();
-}
-END_TEST
-
-START_TEST(test_cache_cleanup_rsync_error)
-{
-       setup_test();
-
-       /* Set up */
-       dl_error = false;
-       run_cache_download("rsync://a.b.c/d", 0, 1, 0);
-       dl_error = true;
-       run_cache_download("rsync://a.b.c/e", -EINVAL, 1, 0);
-       validate_cache(0,
-               NODE("rsync://a.b.c/d/", 0, 1, true),
-               NODE("rsync://a.b.c/e/", -EINVAL, 0, false),
-               NULL);
-
-       /* Node gets deleted because cached file doesn't exist */
-       cache_cleanup(cache);
-       validate_cache(0, NODE("rsync://a.b.c/d/", 0, 1, true), NULL);
-
-       /*
-        * Node and file do not get deleted, because the failure is still not
-        * that old.
-        * Deletion does not depend on success or failure.
-        */
-       new_iteration(false);
-       dl_error = true;
-       run_cache_download("rsync://a.b.c/d", -EINVAL, 1, 0);
-       validate_cache(0, NODE("rsync://a.b.c/d/", -EINVAL, 1, true), NULL);
-
-       /* Error is old; gets deleted */
-       new_iteration(true);
-       cache_cleanup(cache);
-       validate_cache(0, NULL);
-
-       cleanup_test();
-}
-END_TEST
-
-START_TEST(test_cache_download_https)
-{
-       setup_test();
-
-       /* Download *file* e. */
-       run_cache_download("https://a.b.c/d/e", 0, 0, 1);
-       validate_cache(0, NODE("https://a.b.c/d/e", 0, 1, 1), NULL);
-
-       /* Download something else 1 */
-       run_cache_download("https://a.b.c/e", 0, 0, 1);
-       validate_cache(0,
-           NODE("https://a.b.c/d/e", 0, 1, 1),
-           NODE("https://a.b.c/e", 0, 1, 1),
-           NULL);
-
-       /* Download something else 2 */
-       run_cache_download("https://x.y.z/e", 0, 0, 1);
-       validate_cache(0,
-           NODE("https://a.b.c/d/e", 0, 1, 1),
-           NODE("https://a.b.c/e", 0, 1, 1),
-           NODE("https://x.y.z/e", 0, 1, 1),
-           NULL);
-
-       cleanup_test();
-}
-END_TEST
-
-START_TEST(test_cache_download_https_error)
-{
-       setup_test();
-
-       dl_error = false;
-       run_cache_download("https://a.b.c/d", 0, 0, 1);
-       dl_error = true;
-       run_cache_download("https://a.b.c/e", -EINVAL, 0, 1);
-       validate_cache(0,
-           NODE("https://a.b.c/d", 0, 1, 1),
-           NODE("https://a.b.c/e", -EINVAL, 0, 0),
-           NULL);
-
-       /* Regardless of error, not reattempted because same iteration */
-       dl_error = true;
-       run_cache_download("https://a.b.c/d", 0, 0, 0);
-       dl_error = false;
-       run_cache_download("https://a.b.c/e", -EINVAL, 0, 0);
-       validate_cache(0,
-           NODE("https://a.b.c/d", 0, 1, 1),
-           NODE("https://a.b.c/e", -EINVAL, 0, 0),
-           NULL);
-
-       cleanup_test();
-}
-END_TEST
+       int flags = CNF_CACHED | CNF_FRESH | CNF_VALID;
 
-START_TEST(test_cache_cleanup_https)
-{
        setup_test();
 
-       /* First iteration; make a tree and clean it */
-       new_iteration(true);
-       run_cache_download("https://a.b.c/d", 0, 0, 1);
-       run_cache_download("https://a.b.c/e", 0, 0, 1);
-       cache_cleanup(cache);
-       validate_cache(0,
-               NODE("https://a.b.c/d", 0, 1, 1),
-               NODE("https://a.b.c/e", 0, 1, 1),
-               NULL);
-
-       /* Remove one branch */
-       new_iteration(true);
-       run_cache_download("https://a.b.c/d", 0, 0, 1);
-       cache_cleanup(cache);
-       validate_cache(0, NODE("https://a.b.c/d", 0, 1, 1), NULL);
-
-       /* Change the one branch */
-       new_iteration(true);
-       run_cache_download("https://a.b.c/e", 0, 0, 1);
-       cache_cleanup(cache);
-       validate_cache(0, NODE("https://a.b.c/e", 0, 1, 1), NULL);
-
-       /* Add a child to the same branch, do not update the old one */
-       new_iteration(true);
-       run_cache_download("https://a.b.c/e/f/g", 0, 0, 1);
-       cache_cleanup(cache);
-       validate_cache(0,
-               NODE("https://a.b.c/e/f/g", 0, 1, 1), NULL);
-
-       /*
-        * Download parent, do not update child.
-        * Children need to die, because parent is now a file.
-        */
-       new_iteration(true);
-       run_cache_download("https://a.b.c/e/f", 0, 0, 1);
-       cache_cleanup(cache);
-       validate_cache(0, NODE("https://a.b.c/e/f", 0, 1, 1), NULL);
-
-       /* Do it again. */
-       new_iteration(true);
-       run_cache_download("https://a.b.c/e", 0, 0, 1);
-       cache_cleanup(cache);
-       validate_cache(0, NODE("https://a.b.c/e", 0, 1, 1), NULL);
-
-       /* Empty the tree */
-       new_iteration(true);
-       cache_cleanup(cache);
-       validate_cache(0, NULL);
-
-       /* Node exists, but file doesn't */
-       new_iteration(true);
-       run_cache_download("https://a.b.c/e", 0, 0, 1);
-       run_cache_download("https://a.b.c/f/g/h", 0, 0, 1);
-       validate_cache(0,
-           NODE("https://a.b.c/e", 0, 1, 1),
-           NODE("https://a.b.c/f/g/h", 0, 1, 1),
-           NULL);
-       ck_assert_int_eq(0, file_rm_rf("tmp/https/a.b.c/f/g/h"));
-       cache_cleanup(cache);
-       validate_cache(0, NODE("https://a.b.c/e", 0, 1, 1), NULL);
+       run_dl_rsync("rsync://a.b.c/d", "rsync://a.b.c/d/mft", 0, 1);
+       validate_cache(
+               node("rsync:", flags,
+                       node("rsync://a.b.c", flags,
+                               node("rsync://a.b.c/d", flags, NULL)),
+                       NULL),
+               node("https:", 0, NULL));
+
+//     /* Redownload same file, nothing should happen */
+//     run_dl_rsync("rsync://a.b.c/d", "rsync://a.b.c/d/mft", 0, 0);
+//     validate_cache(0, NODE("rsync://a.b.c/d/", 0, 1, true), NULL);
+//
+//     /*
+//      * rsyncs are recursive, which means if we've been recently asked to
+//      * download d, we needn't bother redownloading d/e.
+//      */
+//     run_dl_rsync("rsync://a.b.c/d/e", "rsync://a.b.c/d/e/mft", 0, 0);
+//     validate_cache(0, NODE("rsync://a.b.c/d/", 0, 1, true), NULL);
+//
+//     /*
+//      * rsyncs get truncated, because it results in much faster
+//      * synchronization in practice.
+//      * This is not defined in any RFCs; it's an effective standard,
+//      * and there would be consequences for violating it.
+//      */
+//     run_dl_rsync("rsync://x.y.z/m/n/o", "rsync://x.y.z/m/n/o/mft", 0, 1);
+//     validate_cache(0,
+//         NODE("rsync://a.b.c/d/", 0, 1, true),
+//         NODE("rsync://x.y.z/m/", 0, 1, true),
+//         NULL);
+//
+//     /* Sibling */
+//     run_dl_rsync("rsync://a.b.c/e/f", "rsync://a.b.c/e/f/mft", 0, 1);
+//     validate_cache(0,
+//         NODE("rsync://a.b.c/d/", 0, 1, true),
+//         NODE("rsync://a.b.c/e/", 0, 1, true),
+//         NODE("rsync://x.y.z/m/", 0, 1, true),
+//         NULL);
 
        cleanup_test();
 }
 END_TEST
 
-START_TEST(test_cache_cleanup_https_error)
-{
-       setup_test();
-
-       /* Set up */
-       dl_error = false;
-       run_cache_download("https://a.b.c/d", 0, 0, 1);
-       dl_error = true;
-       run_cache_download("https://a.b.c/e", -EINVAL, 0, 1);
-       validate_cache(0,
-           NODE("https://a.b.c/d", 0, 1, 1),
-           NODE("https://a.b.c/e", -EINVAL, 0, 0),
-           NULL);
-
-       /* Deleted because file ENOENT. */
-       cache_cleanup(cache);
-       validate_cache(0,
-           NODE("https://a.b.c/d", 0, 1, 1),
-           NULL);
-
-       /* Fail d */
-       new_iteration(false);
-       dl_error = true;
-       run_cache_download("https://a.b.c/d", -EINVAL, 0, 1);
-       validate_cache(0, NODE("https://a.b.c/d", -EINVAL, 1, 1), NULL);
-
-       /* Not deleted, because not old */
-       new_iteration(false);
-       cache_cleanup(cache);
-       validate_cache(0, NODE("https://a.b.c/d", -EINVAL, 1, 1), NULL);
-
-       /* Become old */
-       new_iteration(true);
-       cache_cleanup(cache);
-       validate_cache(0, NULL);
-
-       cleanup_test();
-}
-END_TEST
+//START_TEST(test_cache_download_rsync_error)
+//{
+//     setup_test();
+//
+//     dl_error = false;
+//     run_cache_download("rsync://a.b.c/d", 0, 1, 0);
+//     dl_error = true;
+//     run_cache_download("rsync://a.b.c/e", -EINVAL, 1, 0);
+//     validate_cache(0,
+//         NODE("rsync://a.b.c/d/", 0, 1, true),
+//         NODE("rsync://a.b.c/e/", -EINVAL, 0, false),
+//         NULL);
+//
+//     /* Regardless of error, not reattempted because same iteration */
+//     dl_error = true;
+//     run_cache_download("rsync://a.b.c/e", -EINVAL, 0, 0);
+//     validate_cache(0,
+//         NODE("rsync://a.b.c/d/", 0, 1, true),
+//         NODE("rsync://a.b.c/e/", -EINVAL, 0, false),
+//         NULL);
+//
+//     dl_error = false;
+//     run_cache_download("rsync://a.b.c/e", -EINVAL, 0, 0);
+//     validate_cache(0,
+//         NODE("rsync://a.b.c/d/", 0, 1, true),
+//         NODE("rsync://a.b.c/e/", -EINVAL, 0, false),
+//         NULL);
+//
+//     cleanup_test();
+//}
+//END_TEST
+//
+//START_TEST(test_cache_cleanup_rsync)
+//{
+//     setup_test();
+//
+//     /*
+//      * First iteration: Tree is created. No prunes, because nothing's
+//      * outdated.
+//      */
+//     new_iteration(true);
+//     run_cache_download("rsync://a.b.c/d", 0, 1, 0);
+//     run_cache_download("rsync://a.b.c/e", 0, 1, 0);
+//     cache_cleanup(cache);
+//     validate_cache(0,
+//         NODE("rsync://a.b.c/d/", 0, 1, true),
+//         NODE("rsync://a.b.c/e/", 0, 1, true),
+//         NULL);
+//
+//     /* One iteration with no changes, for paranoia */
+//     new_iteration(true);
+//     run_cache_download("rsync://a.b.c/d", 0, 1, 0);
+//     run_cache_download("rsync://a.b.c/e", 0, 1, 0);
+//     cache_cleanup(cache);
+//     validate_cache(0,
+//             NODE("rsync://a.b.c/d/", 0, 1, true),
+//             NODE("rsync://a.b.c/e/", 0, 1, true),
+//             NULL);
+//
+//     /* Add one sibling */
+//     new_iteration(true);
+//     run_cache_download("rsync://a.b.c/d", 0, 1, 0);
+//     run_cache_download("rsync://a.b.c/e", 0, 1, 0);
+//     run_cache_download("rsync://a.b.c/f", 0, 1, 0);
+//     cache_cleanup(cache);
+//     validate_cache(0,
+//             NODE("rsync://a.b.c/d/", 0, 1, true),
+//             NODE("rsync://a.b.c/e/", 0, 1, true),
+//             NODE("rsync://a.b.c/f/", 0, 1, true),
+//             NULL);
+//
+//     /* Nodes don't get updated, but they're still too young. */
+//     new_iteration(false);
+//     cache_cleanup(cache);
+//     validate_cache(0,
+//             NODE("rsync://a.b.c/d/", 0, 1, true),
+//             NODE("rsync://a.b.c/e/", 0, 1, true),
+//             NODE("rsync://a.b.c/f/", 0, 1, true),
+//             NULL);
+//
+//     /* Remove some branches */
+//     new_iteration(true);
+//     run_cache_download("rsync://a.b.c/d", 0, 1, 0);
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("rsync://a.b.c/d/", 0, 1, true), NULL);
+//
+//     /* Remove old branch and add sibling at the same time */
+//     new_iteration(true);
+//     run_cache_download("rsync://a.b.c/e", 0, 1, 0);
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("rsync://a.b.c/e/", 0, 1, true), NULL);
+//
+//     /* Try child */
+//     new_iteration(true);
+//     run_cache_download("rsync://a.b.c/e/f/g", 0, 1, 0);
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("rsync://a.b.c/e/", 0, 1, true), NULL);
+//
+//     /* Parent again */
+//     new_iteration(true);
+//     run_cache_download("rsync://a.b.c/e", 0, 1, 0);
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("rsync://a.b.c/e/", 0, 1, true), NULL);
+//
+//     /* Empty the tree */
+//     new_iteration(true);
+//     cache_cleanup(cache);
+//     validate_cache(0, NULL);
+//
+//     /* Node exists, but file doesn't */
+//     new_iteration(true);
+//     run_cache_download("rsync://a.b.c/e", 0, 1, 0);
+//     run_cache_download("rsync://a.b.c/f", 0, 1, 0);
+//     validate_cache(0,
+//             NODE("rsync://a.b.c/e/", 0, 1, true),
+//             NODE("rsync://a.b.c/f/", 0, 1, true),
+//             NULL);
+//     ck_assert_int_eq(0, file_rm_rf("tmp/rsync/a.b.c/f"));
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("rsync://a.b.c/e/", 0, 1, true), NULL);
+//
+//     cleanup_test();
+//}
+//END_TEST
+//
+//START_TEST(test_cache_cleanup_rsync_error)
+//{
+//     setup_test();
+//
+//     /* Set up */
+//     dl_error = false;
+//     run_cache_download("rsync://a.b.c/d", 0, 1, 0);
+//     dl_error = true;
+//     run_cache_download("rsync://a.b.c/e", -EINVAL, 1, 0);
+//     validate_cache(0,
+//             NODE("rsync://a.b.c/d/", 0, 1, true),
+//             NODE("rsync://a.b.c/e/", -EINVAL, 0, false),
+//             NULL);
+//
+//     /* Node gets deleted because cached file doesn't exist */
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("rsync://a.b.c/d/", 0, 1, true), NULL);
+//
+//     /*
+//      * Node and file do not get deleted, because the failure is still not
+//      * that old.
+//      * Deletion does not depend on success or failure.
+//      */
+//     new_iteration(false);
+//     dl_error = true;
+//     run_cache_download("rsync://a.b.c/d", -EINVAL, 1, 0);
+//     validate_cache(0, NODE("rsync://a.b.c/d/", -EINVAL, 1, true), NULL);
+//
+//     /* Error is old; gets deleted */
+//     new_iteration(true);
+//     cache_cleanup(cache);
+//     validate_cache(0, NULL);
+//
+//     cleanup_test();
+//}
+//END_TEST
+//
+//START_TEST(test_cache_download_https)
+//{
+//     setup_test();
+//
+//     /* Download *file* e. */
+//     run_cache_download("https://a.b.c/d/e", 0, 0, 1);
+//     validate_cache(0, NODE("https://a.b.c/d/e", 0, 1, 1), NULL);
+//
+//     /* Download something else 1 */
+//     run_cache_download("https://a.b.c/e", 0, 0, 1);
+//     validate_cache(0,
+//         NODE("https://a.b.c/d/e", 0, 1, 1),
+//         NODE("https://a.b.c/e", 0, 1, 1),
+//         NULL);
+//
+//     /* Download something else 2 */
+//     run_cache_download("https://x.y.z/e", 0, 0, 1);
+//     validate_cache(0,
+//         NODE("https://a.b.c/d/e", 0, 1, 1),
+//         NODE("https://a.b.c/e", 0, 1, 1),
+//         NODE("https://x.y.z/e", 0, 1, 1),
+//         NULL);
+//
+//     cleanup_test();
+//}
+//END_TEST
+//
+//START_TEST(test_cache_download_https_error)
+//{
+//     setup_test();
+//
+//     dl_error = false;
+//     run_cache_download("https://a.b.c/d", 0, 0, 1);
+//     dl_error = true;
+//     run_cache_download("https://a.b.c/e", -EINVAL, 0, 1);
+//     validate_cache(0,
+//         NODE("https://a.b.c/d", 0, 1, 1),
+//         NODE("https://a.b.c/e", -EINVAL, 0, 0),
+//         NULL);
+//
+//     /* Regardless of error, not reattempted because same iteration */
+//     dl_error = true;
+//     run_cache_download("https://a.b.c/d", 0, 0, 0);
+//     dl_error = false;
+//     run_cache_download("https://a.b.c/e", -EINVAL, 0, 0);
+//     validate_cache(0,
+//         NODE("https://a.b.c/d", 0, 1, 1),
+//         NODE("https://a.b.c/e", -EINVAL, 0, 0),
+//         NULL);
+//
+//     cleanup_test();
+//}
+//END_TEST
+//
+//START_TEST(test_cache_cleanup_https)
+//{
+//     setup_test();
+//
+//     /* First iteration; make a tree and clean it */
+//     new_iteration(true);
+//     run_cache_download("https://a.b.c/d", 0, 0, 1);
+//     run_cache_download("https://a.b.c/e", 0, 0, 1);
+//     cache_cleanup(cache);
+//     validate_cache(0,
+//             NODE("https://a.b.c/d", 0, 1, 1),
+//             NODE("https://a.b.c/e", 0, 1, 1),
+//             NULL);
+//
+//     /* Remove one branch */
+//     new_iteration(true);
+//     run_cache_download("https://a.b.c/d", 0, 0, 1);
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("https://a.b.c/d", 0, 1, 1), NULL);
+//
+//     /* Change the one branch */
+//     new_iteration(true);
+//     run_cache_download("https://a.b.c/e", 0, 0, 1);
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("https://a.b.c/e", 0, 1, 1), NULL);
+//
+//     /* Add a child to the same branch, do not update the old one */
+//     new_iteration(true);
+//     run_cache_download("https://a.b.c/e/f/g", 0, 0, 1);
+//     cache_cleanup(cache);
+//     validate_cache(0,
+//             NODE("https://a.b.c/e/f/g", 0, 1, 1), NULL);
+//
+//     /*
+//      * Download parent, do not update child.
+//      * Children need to die, because parent is now a file.
+//      */
+//     new_iteration(true);
+//     run_cache_download("https://a.b.c/e/f", 0, 0, 1);
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("https://a.b.c/e/f", 0, 1, 1), NULL);
+//
+//     /* Do it again. */
+//     new_iteration(true);
+//     run_cache_download("https://a.b.c/e", 0, 0, 1);
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("https://a.b.c/e", 0, 1, 1), NULL);
+//
+//     /* Empty the tree */
+//     new_iteration(true);
+//     cache_cleanup(cache);
+//     validate_cache(0, NULL);
+//
+//     /* Node exists, but file doesn't */
+//     new_iteration(true);
+//     run_cache_download("https://a.b.c/e", 0, 0, 1);
+//     run_cache_download("https://a.b.c/f/g/h", 0, 0, 1);
+//     validate_cache(0,
+//         NODE("https://a.b.c/e", 0, 1, 1),
+//         NODE("https://a.b.c/f/g/h", 0, 1, 1),
+//         NULL);
+//     ck_assert_int_eq(0, file_rm_rf("tmp/https/a.b.c/f/g/h"));
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("https://a.b.c/e", 0, 1, 1), NULL);
+//
+//     cleanup_test();
+//}
+//END_TEST
+//
+//START_TEST(test_cache_cleanup_https_error)
+//{
+//     setup_test();
+//
+//     /* Set up */
+//     dl_error = false;
+//     run_cache_download("https://a.b.c/d", 0, 0, 1);
+//     dl_error = true;
+//     run_cache_download("https://a.b.c/e", -EINVAL, 0, 1);
+//     validate_cache(0,
+//         NODE("https://a.b.c/d", 0, 1, 1),
+//         NODE("https://a.b.c/e", -EINVAL, 0, 0),
+//         NULL);
+//
+//     /* Deleted because file ENOENT. */
+//     cache_cleanup(cache);
+//     validate_cache(0,
+//         NODE("https://a.b.c/d", 0, 1, 1),
+//         NULL);
+//
+//     /* Fail d */
+//     new_iteration(false);
+//     dl_error = true;
+//     run_cache_download("https://a.b.c/d", -EINVAL, 0, 1);
+//     validate_cache(0, NODE("https://a.b.c/d", -EINVAL, 1, 1), NULL);
+//
+//     /* Not deleted, because not old */
+//     new_iteration(false);
+//     cache_cleanup(cache);
+//     validate_cache(0, NODE("https://a.b.c/d", -EINVAL, 1, 1), NULL);
+//
+//     /* Become old */
+//     new_iteration(true);
+//     cache_cleanup(cache);
+//     validate_cache(0, NULL);
+//
+//     cleanup_test();
+//}
+//END_TEST
 
 START_TEST(test_dots)
 {
-       setup_test();
-
-       run_cache_download("https://a.b.c/d", 0, 0, 1);
-       validate_cache(0, NODE("https://a.b.c/d", 0, 1, 1), NULL);
-
-       run_cache_download("https://a.b.c/d/.", 0, 0, 0);
-       validate_cache(0, NODE("https://a.b.c/d", 0, 1, 1), NULL);
-
-       run_cache_download("https://a.b.c/d/e/..", 0, 0, 0);
-       validate_cache(0, NODE("https://a.b.c/d", 0, 1, 1), NULL);
-
-       run_cache_download("https://a.b.c/./d/../e", 0, 0, 1);
-       validate_cache(0,
-           NODE("https://a.b.c/d", 0, 1, 1),
-           NODE("https://a.b.c/./d/../e", 0, 1, 1),
-           NULL);
-
-       cleanup_test();
-}
-END_TEST
-
-START_TEST(test_tal_json)
-{
-       json_t *json;
-       char *str;
-
-       setup_test();
-
-       ck_assert_int_eq(0, system("rm -rf tmp/"));
-       ck_assert_int_eq(0, system("mkdir -p tmp"));
-
-       add_node(cache, NODE("rsync://a.b.c/d", 0, 1, 0));
-       add_node(cache, NODE("rsync://a.b.c/e", 1, 0, 0));
-       add_node(cache, NODE("rsync://x.y.z/e", 0, 1, 0));
-       add_node(cache, NODE("https://a/b", 1, 1, 0));
-       add_node(cache, node("https://a/c", 0, 0, 1, 0, 1));
-
-       json = build_tal_json(cache);
-       ck_assert_int_eq(0, json_dump_file(json, "tmp/" TAL_METAFILE, JSON_COMPACT));
-
-       str = json_dumps(json, /* JSON_INDENT(4) */ JSON_COMPACT);
-       json_decref(json);
-
-       ck_assert_str_eq(
-           "[{\"type\":\"RPP\",\"url\":\"rsync://a.b.c/d\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
-           "{\"type\":\"RPP\",\"url\":\"rsync://a.b.c/e\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":1},"
-           "{\"type\":\"RPP\",\"url\":\"rsync://x.y.z/e\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
-           "{\"type\":\"TA (HTTP)\",\"url\":\"https://a/b\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":1,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
-           "{\"type\":\"RRDP Notification\",\"url\":\"https://a/c\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"}]",
-           str);
-       free(str);
-
-       cache_reset(cache);
-
-       load_tal_json(cache);
-       ck_assert_ptr_ne(NULL, cache->ht);
-
-       validate_cache(0,
-           NODE("rsync://a.b.c/d", 0, 1, 0),
-           NODE("rsync://a.b.c/e", 1, 0, 0),
-           NODE("rsync://x.y.z/e", 0, 1, 0),
-           NODE("https://a/b", 1, 1, 0),
-           NODE("https://a/c", 0, 1, 0),
-           NULL);
-
-       cleanup_test();
+//     setup_test();
+//
+//     run_cache_download("https://a.b.c/d", 0, 0, 1);
+//     validate_cache(0, NODE("https://a.b.c/d", 0, 1, 1), NULL);
+//
+//     run_cache_download("https://a.b.c/d/.", 0, 0, 0);
+//     validate_cache(0, NODE("https://a.b.c/d", 0, 1, 1), NULL);
+//
+//     run_cache_download("https://a.b.c/d/e/..", 0, 0, 0);
+//     validate_cache(0, NODE("https://a.b.c/d", 0, 1, 1), NULL);
+//
+//     run_cache_download("https://a.b.c/./d/../e", 0, 0, 1);
+//     validate_cache(0,
+//         NODE("https://a.b.c/d", 0, 1, 1),
+//         NODE("https://a.b.c/./d/../e", 0, 1, 1),
+//         NULL);
+//
+//     cleanup_test();
 }
 END_TEST
 
-static void
-prepare_map_list(struct map_list *maps, ...)
-{
-       char const *str;
-       enum map_type type;
-       struct cache_mapping *map;
-       va_list args;
-
-       maps_init(maps);
-
-       va_start(args, maps);
-       while ((str = va_arg(args, char const *)) != NULL) {
-               if (str_starts_with(str, "https://"))
-                       type = MAP_HTTP;
-               else if (str_starts_with(str, "rsync://"))
-                       type = MAP_RSYNC;
-               else
-                       ck_abort_msg("Bad protocol: %s", str);
-               ck_assert_int_eq(0, map_create(&map, type, str));
-               maps_add(maps, map);
-       }
-       va_end(args);
-}
-
-#define PREPARE_MAP_LIST(maps, ...) prepare_map_list(maps, ##__VA_ARGS__, NULL)
-
-START_TEST(test_recover)
-{
-       struct map_list maps;
-
-       setup_test();
-
-       /* Query on empty database */
-       PREPARE_MAP_LIST(&maps, "rsync://a.b.c/d", "https://a.b.c/d");
-       ck_assert_ptr_eq(NULL, cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       /* Only first URI is cached */
-       cache_reset(cache);
-       run_cache_download("rsync://a/b/c", 0, 1, 0);
-
-       PREPARE_MAP_LIST(&maps, "rsync://a/b/c", "https://d/e", "https://f");
-       ck_assert_ptr_eq(maps.array[0], cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       /* Only second URI is cached */
-       cache_reset(cache);
-       run_cache_download("https://d/e", 0, 0, 1);
-
-       PREPARE_MAP_LIST(&maps, "rsync://a/b/c", "https://d/e", "https://f");
-       ck_assert_ptr_eq(maps.array[1], cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       /* Only third URI is cached */
-       cache_reset(cache);
-       run_cache_download("https://f", 0, 0, 1);
-
-       PREPARE_MAP_LIST(&maps, "rsync://a/b/c", "https://d/e", "https://f");
-       ck_assert_ptr_eq(maps.array[2], cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       /* None was cached */
-       cache_reset(cache);
-       run_cache_download("rsync://d/e", 0, 1, 0);
-
-       PREPARE_MAP_LIST(&maps, "rsync://a/b/c", "https://d/e", "https://f");
-       ck_assert_ptr_eq(NULL, cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       /*
-        * At present, cache_recover() can only be called after all of a
-        * download's URLs yielded failure.
-        * However, node.error can still be zero. This happens when the download
-        * was successful, but the RRDP code wasn't able to expand the snapshot
-        * or deltas.
-        */
-       cache_reset(cache);
-
-       add_node(cache, node("rsync://a/1", 100, 0, 1, 100, 0));
-       add_node(cache, node("rsync://a/2", 100, 1, 1, 100, 0));
-       add_node(cache, node("rsync://a/3", 200, 0, 1, 100, 0));
-       add_node(cache, node("rsync://a/4", 200, 1, 1, 100, 0));
-       add_node(cache, node("rsync://a/5", 100, 0, 1, 200, 0));
-       add_node(cache, node("rsync://a/6", 100, 1, 1, 200, 0));
-       add_node(cache, node("rsync://b/1", 100, 0, 0, 100, 0));
-       add_node(cache, node("rsync://b/2", 100, 1, 0, 100, 0));
-       add_node(cache, node("rsync://b/3", 200, 0, 0, 100, 0));
-       add_node(cache, node("rsync://b/4", 200, 1, 0, 100, 0));
-       add_node(cache, node("rsync://b/5", 100, 0, 0, 200, 0));
-       add_node(cache, node("rsync://b/6", 100, 1, 0, 200, 0));
-
-       /* Multiple successful caches: Prioritize the most recent one */
-       PREPARE_MAP_LIST(&maps, "rsync://a/1", "rsync://a/3", "rsync://a/5");
-       ck_assert_ptr_eq(maps.array[2], cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       PREPARE_MAP_LIST(&maps, "rsync://a/5", "rsync://a/1", "rsync://a/3");
-       ck_assert_ptr_eq(maps.array[0], cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       /* No successful caches: No viable candidates */
-       PREPARE_MAP_LIST(&maps, "rsync://b/2", "rsync://b/4", "rsync://b/6");
-       ck_assert_ptr_eq(NULL, cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       /* Status: CNF_SUCCESS is better than 0. */
-       PREPARE_MAP_LIST(&maps, "rsync://b/1", "rsync://a/1");
-       ck_assert_ptr_eq(maps.array[1], cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       /*
-        * If CNF_SUCCESS && error, Fort will probably run into a problem
-        * reading the cached directory, because it's either outdated or
-        * recently corrupted.
-        * But it should still TRY to read it, as there's a chance the
-        * outdatedness is not that severe.
-        */
-       PREPARE_MAP_LIST(&maps, "rsync://a/2", "rsync://b/2");
-       ck_assert_ptr_eq(maps.array[0], cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       /* Parents of downloaded nodes */
-       PREPARE_MAP_LIST(&maps, "rsync://a", "rsync://b");
-       ck_assert_ptr_eq(NULL, cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       /* Try them all at the same time */
-       PREPARE_MAP_LIST(&maps,
-           "rsync://a", "rsync://a/1", "rsync://a/2", "rsync://a/3",
-           "rsync://a/4", "rsync://a/5", "rsync://a/6",
-           "rsync://b", "rsync://b/1", "rsync://b/2", "rsync://b/3",
-           "rsync://b/4", "rsync://b/5", "rsync://b/6",
-           "rsync://e/1");
-       ck_assert_ptr_eq(maps.array[5], cache_recover(cache, &maps));
-       maps_cleanup(&maps);
-
-       cleanup_test();
-}
-END_TEST
+//START_TEST(test_tal_json)
+//{
+//     json_t *json;
+//     char *str;
+//
+//     setup_test();
+//
+//     ck_assert_int_eq(0, system("rm -rf tmp/"));
+//     ck_assert_int_eq(0, system("mkdir -p tmp"));
+//
+//     add_node(cache, NODE("rsync://a.b.c/d", 0, 1, 0));
+//     add_node(cache, NODE("rsync://a.b.c/e", 1, 0, 0));
+//     add_node(cache, NODE("rsync://x.y.z/e", 0, 1, 0));
+//     add_node(cache, NODE("https://a/b", 1, 1, 0));
+//     add_node(cache, node("https://a/c", 0, 0, 1, 0, 1));
+//
+//     json = build_tal_json(cache);
+//     ck_assert_int_eq(0, json_dump_file(json, "tmp/" TAL_METAFILE, JSON_COMPACT));
+//
+//     str = json_dumps(json, /* JSON_INDENT(4) */ JSON_COMPACT);
+//     json_decref(json);
+//
+//     ck_assert_str_eq(
+//         "[{\"type\":\"RPP\",\"url\":\"rsync://a.b.c/d\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
+//         "{\"type\":\"RPP\",\"url\":\"rsync://a.b.c/e\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":1},"
+//         "{\"type\":\"RPP\",\"url\":\"rsync://x.y.z/e\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
+//         "{\"type\":\"TA (HTTP)\",\"url\":\"https://a/b\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":1,\"success-timestamp\":\"1970-01-01T00:00:00Z\"},"
+//         "{\"type\":\"RRDP Notification\",\"url\":\"https://a/c\",\"attempt-timestamp\":\"1970-01-01T00:00:00Z\",\"attempt-result\":0,\"success-timestamp\":\"1970-01-01T00:00:00Z\"}]",
+//         str);
+//     free(str);
+//
+//     cache_reset(cache);
+//
+//     load_tal_json(cache);
+//     ck_assert_ptr_ne(NULL, cache->ht);
+//
+//     validate_cache(0,
+//         NODE("rsync://a.b.c/d", 0, 1, 0),
+//         NODE("rsync://a.b.c/e", 1, 0, 0),
+//         NODE("rsync://x.y.z/e", 0, 1, 0),
+//         NODE("https://a/b", 1, 1, 0),
+//         NODE("https://a/c", 0, 1, 0),
+//         NULL);
+//
+//     cleanup_test();
+//}
+//END_TEST
+//
+//static void
+//prepare_map_list(struct map_list *maps, ...)
+//{
+//     char const *str;
+//     enum map_type type;
+//     struct cache_mapping *map;
+//     va_list args;
+//
+//     maps_init(maps);
+//
+//     va_start(args, maps);
+//     while ((str = va_arg(args, char const *)) != NULL) {
+//             if (str_starts_with(str, "https://"))
+//                     type = MAP_HTTP;
+//             else if (str_starts_with(str, "rsync://"))
+//                     type = MAP_RSYNC;
+//             else
+//                     ck_abort_msg("Bad protocol: %s", str);
+//             ck_assert_int_eq(0, map_create(&map, type, str));
+//             maps_add(maps, map);
+//     }
+//     va_end(args);
+//}
+//
+//#define PREPARE_MAP_LIST(maps, ...) prepare_map_list(maps, ##__VA_ARGS__, NULL)
+//
+//START_TEST(test_recover)
+//{
+//     struct map_list maps;
+//
+//     setup_test();
+//
+//     /* Query on empty database */
+//     PREPARE_MAP_LIST(&maps, "rsync://a.b.c/d", "https://a.b.c/d");
+//     ck_assert_ptr_eq(NULL, cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     /* Only first URI is cached */
+//     cache_reset(cache);
+//     run_cache_download("rsync://a/b/c", 0, 1, 0);
+//
+//     PREPARE_MAP_LIST(&maps, "rsync://a/b/c", "https://d/e", "https://f");
+//     ck_assert_ptr_eq(maps.array[0], cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     /* Only second URI is cached */
+//     cache_reset(cache);
+//     run_cache_download("https://d/e", 0, 0, 1);
+//
+//     PREPARE_MAP_LIST(&maps, "rsync://a/b/c", "https://d/e", "https://f");
+//     ck_assert_ptr_eq(maps.array[1], cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     /* Only third URI is cached */
+//     cache_reset(cache);
+//     run_cache_download("https://f", 0, 0, 1);
+//
+//     PREPARE_MAP_LIST(&maps, "rsync://a/b/c", "https://d/e", "https://f");
+//     ck_assert_ptr_eq(maps.array[2], cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     /* None was cached */
+//     cache_reset(cache);
+//     run_cache_download("rsync://d/e", 0, 1, 0);
+//
+//     PREPARE_MAP_LIST(&maps, "rsync://a/b/c", "https://d/e", "https://f");
+//     ck_assert_ptr_eq(NULL, cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     /*
+//      * At present, cache_recover() can only be called after all of a
+//      * download's URLs yielded failure.
+//      * However, node.error can still be zero. This happens when the download
+//      * was successful, but the RRDP code wasn't able to expand the snapshot
+//      * or deltas.
+//      */
+//     cache_reset(cache);
+//
+//     add_node(cache, node("rsync://a/1", 100, 0, 1, 100, 0));
+//     add_node(cache, node("rsync://a/2", 100, 1, 1, 100, 0));
+//     add_node(cache, node("rsync://a/3", 200, 0, 1, 100, 0));
+//     add_node(cache, node("rsync://a/4", 200, 1, 1, 100, 0));
+//     add_node(cache, node("rsync://a/5", 100, 0, 1, 200, 0));
+//     add_node(cache, node("rsync://a/6", 100, 1, 1, 200, 0));
+//     add_node(cache, node("rsync://b/1", 100, 0, 0, 100, 0));
+//     add_node(cache, node("rsync://b/2", 100, 1, 0, 100, 0));
+//     add_node(cache, node("rsync://b/3", 200, 0, 0, 100, 0));
+//     add_node(cache, node("rsync://b/4", 200, 1, 0, 100, 0));
+//     add_node(cache, node("rsync://b/5", 100, 0, 0, 200, 0));
+//     add_node(cache, node("rsync://b/6", 100, 1, 0, 200, 0));
+//
+//     /* Multiple successful caches: Prioritize the most recent one */
+//     PREPARE_MAP_LIST(&maps, "rsync://a/1", "rsync://a/3", "rsync://a/5");
+//     ck_assert_ptr_eq(maps.array[2], cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     PREPARE_MAP_LIST(&maps, "rsync://a/5", "rsync://a/1", "rsync://a/3");
+//     ck_assert_ptr_eq(maps.array[0], cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     /* No successful caches: No viable candidates */
+//     PREPARE_MAP_LIST(&maps, "rsync://b/2", "rsync://b/4", "rsync://b/6");
+//     ck_assert_ptr_eq(NULL, cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     /* Status: CNF_SUCCESS is better than 0. */
+//     PREPARE_MAP_LIST(&maps, "rsync://b/1", "rsync://a/1");
+//     ck_assert_ptr_eq(maps.array[1], cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     /*
+//      * If CNF_SUCCESS && error, Fort will probably run into a problem
+//      * reading the cached directory, because it's either outdated or
+//      * recently corrupted.
+//      * But it should still TRY to read it, as there's a chance the
+//      * outdatedness is not that severe.
+//      */
+//     PREPARE_MAP_LIST(&maps, "rsync://a/2", "rsync://b/2");
+//     ck_assert_ptr_eq(maps.array[0], cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     /* Parents of downloaded nodes */
+//     PREPARE_MAP_LIST(&maps, "rsync://a", "rsync://b");
+//     ck_assert_ptr_eq(NULL, cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     /* Try them all at the same time */
+//     PREPARE_MAP_LIST(&maps,
+//         "rsync://a", "rsync://a/1", "rsync://a/2", "rsync://a/3",
+//         "rsync://a/4", "rsync://a/5", "rsync://a/6",
+//         "rsync://b", "rsync://b/1", "rsync://b/2", "rsync://b/3",
+//         "rsync://b/4", "rsync://b/5", "rsync://b/6",
+//         "rsync://e/1");
+//     ck_assert_ptr_eq(maps.array[5], cache_recover(cache, &maps));
+//     maps_cleanup(&maps);
+//
+//     cleanup_test();
+//}
+//END_TEST
 
 /* Boilerplate */
 
@@ -916,24 +921,24 @@ static Suite *thread_pool_suite(void)
 
        rsync = tcase_create("rsync");
        tcase_add_test(rsync, test_cache_download_rsync);
-       tcase_add_test(rsync, test_cache_download_rsync_error);
-       tcase_add_test(rsync, test_cache_cleanup_rsync);
-       tcase_add_test(rsync, test_cache_cleanup_rsync_error);
+//     tcase_add_test(rsync, test_cache_download_rsync_error);
+//     tcase_add_test(rsync, test_cache_cleanup_rsync);
+//     tcase_add_test(rsync, test_cache_cleanup_rsync_error);
 
        https = tcase_create("https");
-       tcase_add_test(https, test_cache_download_https);
-       tcase_add_test(https, test_cache_download_https_error);
-       tcase_add_test(https, test_cache_cleanup_https);
-       tcase_add_test(https, test_cache_cleanup_https_error);
+//     tcase_add_test(https, test_cache_download_https);
+//     tcase_add_test(https, test_cache_download_https_error);
+//     tcase_add_test(https, test_cache_cleanup_https);
+//     tcase_add_test(https, test_cache_cleanup_https_error);
 
        dot = tcase_create("dot");
        tcase_add_test(dot, test_dots);
 
        meta = tcase_create(TAL_METAFILE);
-       tcase_add_test(meta, test_tal_json);
+//     tcase_add_test(meta, test_tal_json);
 
        recover = tcase_create("recover");
-       tcase_add_test(recover, test_recover);
+//     tcase_add_test(recover, test_recover);
 
        suite = suite_create("local-cache");
        suite_add_tcase(suite, rsync);
diff --git a/test/file_test.c b/test/file_test.c
deleted file mode 100644 (file)
index 31cb1ef..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-#include <check.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "alloc.c"
-#include "file.c"
-#include "mock.c"
-
-static void
-create_file(char const *prefix, char const *suffix)
-{
-       char *full_path;
-       FILE *file;
-
-       full_path = join_paths(prefix, suffix);
-       ck_assert_ptr_ne(NULL, full_path);
-
-       file = fopen(full_path, "w");
-       ck_assert_ptr_ne(NULL, file);
-       fclose(file);
-
-       free(full_path);
-}
-
-static void
-create_dir(char const *prefix, char const *suffix)
-{
-       char *full_path;
-
-       full_path = join_paths(prefix, suffix);
-       ck_assert_ptr_ne(NULL, full_path);
-
-       ck_assert_int_eq(0, mkdir(full_path, S_IRWXU));
-
-       free(full_path);
-}
-
-static void
-__file_merge_into(char const *root, char const *src, char const *dst)
-{
-       char *full_src;
-       char *full_dst;
-
-       full_src = join_paths(root, src);
-       ck_assert_ptr_ne(NULL, full_src);
-       full_dst = join_paths(root, dst);
-       ck_assert_ptr_ne(NULL, full_dst);
-
-       ck_assert_int_eq(0, file_merge_into(full_src, full_dst));
-
-       free(full_src);
-       free(full_dst);
-}
-
-static bool
-is_dots(char const *str)
-{
-       return (strcmp(".", str) == 0) || (strcmp("..", str) == 0);
-}
-
-static void
-check_empty_dir(char const *prefix, char const *suffix)
-{
-       char *full_path;
-       DIR *dir;
-       struct dirent *child;
-
-       full_path = join_paths(prefix, suffix);
-       ck_assert_ptr_ne(NULL, full_path);
-
-       dir = opendir(full_path);
-       ck_assert_ptr_ne(NULL, dir);
-       child = readdir(dir);
-       ck_assert(is_dots(child->d_name));
-       child = readdir(dir);
-       ck_assert(is_dots(child->d_name));
-       errno = 0;
-       ck_assert_ptr_eq(NULL, readdir(dir));
-       ck_assert_int_eq(0, errno);
-       closedir(dir);
-
-       free(full_path);
-}
-
-static void
-check_file(char const *prefix, char const *suffix)
-{
-       char *full_path;
-       struct stat st;
-
-       full_path = join_paths(prefix, suffix);
-       ck_assert_ptr_ne(NULL, full_path);
-
-       ck_assert_int_eq(0, stat(full_path, &st));
-       ck_assert_int_ne(0, S_ISREG(st.st_mode));
-
-       free(full_path);
-}
-
-START_TEST(test_merge_empty)
-{
-       char *root;
-
-       root = mkdtemp(pstrdup("/tmp/fort_test_XXXXXX"));
-       ck_assert_ptr_ne(NULL, root);
-
-       create_dir(root, "src");
-       create_dir(root, "dst");
-
-       __file_merge_into(root, "src", "dst");
-
-       check_empty_dir(root, "dst");
-
-       file_rm_rf(root);
-       free(root);
-}
-END_TEST
-
-START_TEST(test_merge_simple)
-{
-       char *root;
-
-       root = mkdtemp(pstrdup("/tmp/fort_test_XXXXXX"));
-       ck_assert_ptr_ne(NULL, root);
-
-       create_dir(root, "src");
-       create_file(root, "src/a");
-       create_dir(root, "dst");
-
-       __file_merge_into(root, "src", "dst");
-
-       check_file(root, "dst/a");
-
-       file_rm_rf(root);
-       free(root);
-}
-END_TEST
-
-START_TEST(test_merge_no_override)
-{
-       char *root;
-
-       root = mkdtemp(pstrdup("/tmp/fort_test_XXXXXX"));
-       ck_assert_ptr_ne(NULL, root);
-
-       create_dir(root, "src");
-       create_dir(root, "dst");
-       create_file(root, "dst/a");
-
-       __file_merge_into(root, "src", "dst");
-
-       check_file(root, "dst/a");
-
-       file_rm_rf(root);
-       free(root);
-}
-END_TEST
-
-START_TEST(test_merge_override)
-{
-       char *root;
-
-       root = mkdtemp(pstrdup("/tmp/fort_test_XXXXXX"));
-       ck_assert_ptr_ne(NULL, root);
-
-       create_dir(root, "src");
-       create_file(root, "src/a");
-       create_dir(root, "dst");
-       create_file(root, "dst/a");
-
-       __file_merge_into(root, "src", "dst");
-
-       check_file(root, "dst/a");
-
-       file_rm_rf(root);
-       free(root);
-}
-END_TEST
-
-START_TEST(test_merge_dirs)
-{
-       char *root;
-
-       root = mkdtemp(pstrdup("/tmp/fort_test_XXXXXX"));
-       ck_assert_ptr_ne(NULL, root);
-
-       create_dir(root, "src");
-       create_file(root, "src/a");
-       create_dir(root, "src/c");
-       create_file(root, "src/c/m");
-       create_file(root, "src/c/n");
-       create_file(root, "src/e");
-
-       create_dir(root, "dst");
-       create_file(root, "dst/b");
-       create_dir(root, "dst/d");
-       create_file(root, "dst/d/o");
-       create_file(root, "dst/d/p");
-
-       __file_merge_into(root, "src", "dst");
-
-       check_file(root, "dst/a");
-       check_file(root, "dst/b");
-       check_file(root, "dst/c/m");
-       check_file(root, "dst/c/n");
-       check_file(root, "dst/d/o");
-       check_file(root, "dst/d/p");
-       check_file(root, "dst/e");
-
-       file_rm_rf(root);
-       free(root);
-}
-END_TEST
-
-static Suite *xml_load_suite(void)
-{
-       Suite *suite;
-       TCase *todo;
-
-       todo = tcase_create("misc");
-       tcase_add_test(todo, test_merge_empty);
-       tcase_add_test(todo, test_merge_simple);
-       tcase_add_test(todo, test_merge_no_override);
-       tcase_add_test(todo, test_merge_override);
-       tcase_add_test(todo, test_merge_dirs);
-
-       suite = suite_create("file");
-       suite_add_tcase(suite, todo);
-
-       return suite;
-}
-
-int main(void)
-{
-       Suite *suite;
-       SRunner *runner;
-       int tests_failed;
-
-       suite = xml_load_suite();
-
-       runner = srunner_create(suite);
-       srunner_run_all(runner, CK_NORMAL);
-       tests_failed = srunner_ntests_failed(runner);
-       srunner_free(runner);
-
-       return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-}
index b0bb560df20c64bb9909575d04372c28603af268..5717f797f55ae2112e6c78d601bc78f565c5ac63 100644 (file)
@@ -113,6 +113,7 @@ v6addr2str2(struct in6_addr const *addr)
 MOCK_NULL(config_get_slurm, char const *, void)
 MOCK(config_get_tal, char const *, "tal/", void)
 MOCK(config_get_local_repository, char const *, "tmp", void)
+MOCK(cfg_cache_threshold, time_t, 0, void)
 MOCK(config_get_mode, enum mode, STANDALONE, void)
 MOCK_TRUE(config_get_rsync_enabled, void)
 MOCK_UINT(config_get_rsync_priority, 50, void)