]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Tuesday
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Tue, 16 Jul 2024 18:19:28 +0000 (12:19 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Wed, 17 Jul 2024 22:13:00 +0000 (16:13 -0600)
src/cache/local_cache.c
src/cache/local_cache.h
src/common.c
src/data_structure/path_builder.c
src/file.c
src/http/http.c
src/http/http.h
src/object/tal.c
test/Makefile.am
test/cache/local_cache_test.c

index ccc8e7fe6d9bfa3d7d6f6d95fe3813dc73c1422a..2aa7267146932f6be5c63b4dbd2ca299f9ff9c69 100644 (file)
@@ -30,6 +30,8 @@
 
 /* XXX force RRDP if one RPP fails to validate by rsync? */
 
+typedef int (*dl_cb)(struct cache_node *rpp);
+
 struct cached_file {
        char *url;
        char *path;
@@ -558,23 +560,41 @@ dl_rsync(struct cache_node *rpp)
        return 0;
 }
 
-/*
 static int
 dl_http(struct cache_node *node)
 {
+       char *path;
+       bool changed;
+       int error;
+
        if (!config_get_http_enabled()) {
                pr_val_debug("HTTP is disabled.");
                return 1;
        }
 
-       return http_download_cache_node(node);
+       error = cache_tmpfile(&path);
+       if (error)
+               return error;
+
+       error = http_download(node->url, path, node->mtim, &changed);
+       if (error) {
+               free(path);
+               return error;
+       }
+
+       node->flags |= CNF_CACHED | CNF_FRESH; // XXX on notification, preserve node but not file
+       if (changed) {
+               node->flags |= CNF_CHANGED;
+               node->mtim = time(NULL); // XXX catch -1
+       }
+       node->tmpdir = path;
+       return 0;
 }
-*/
 
 /* @uri is either a caRepository or a rpkiNotify */
 static int
-try_uri(char const *uri, int (*download)(struct cache_node *),
-    maps_dl_cb validate, void *arg)
+try_uri(char const *uri, struct cache_node *root,
+    dl_cb download, maps_dl_cb validate, void *arg)
 {
        struct cache_node *rpp;
        int error;
@@ -584,7 +604,7 @@ try_uri(char const *uri, int (*download)(struct cache_node *),
 
        pr_val_debug("Trying %s (%s)...", uri, download ? "online" : "offline");
 
-       rpp = cachent_provide(cache.rsync, uri);
+       rpp = cachent_provide(root, uri);
        if (!rpp)
                return pr_val_err("Malformed URL: %s", uri);
 
@@ -606,11 +626,51 @@ try_uri(char const *uri, int (*download)(struct cache_node *),
                return error;
        }
 
-       /* XXX commit the files (later, during cleanup) */
        pr_val_debug("RPP validated successfully.");
        return 0;
 }
 
+static int
+try_uris(struct strlist *uris, struct cache_node *root,
+    char const *prefix, dl_cb dl, maps_dl_cb cb, void *arg)
+{
+       char **str;
+       int error;
+
+       ARRAYLIST_FOREACH(uris, str)
+               if (str_starts_with(*str, prefix)) {
+                       error = try_uri(*str, root, dl, cb, arg);
+                       if (error <= 0)
+                               return error;
+               }
+
+       return 1;
+}
+
+int
+cache_download_uri(struct strlist *uris, maps_dl_cb cb, void *arg)
+{
+       char **_str, *str;
+       int error;
+
+       // XXX mutex
+       // XXX review result signs
+
+       /* Online attempts */
+       error = try_uris(uris, cache.https, "https://", dl_http, cb, arg);
+       if (error <= 0)
+               return error;
+       error = try_uris(uris, cache.rsync, "rsync://", dl_rsync, cb, arg);
+       if (error <= 0)
+               return error;
+
+       /* Offline attempts */
+       error = try_uris(uris, cache.https, "https://", NULL, cb, arg);
+       if (error <= 0)
+               return error;
+       return try_uris(uris, cache.rsync, "rsync://", NULL, cb, arg);
+}
+
 /**
  * XXX outdated comment
  *
@@ -630,29 +690,23 @@ cache_download_alt(struct sia_uris *uris, maps_dl_cb cb, void *arg)
        int error;
 
        // XXX Make sure somewhere validates rpkiManifest matches caRepository.
-
        /* XXX mutex */
-//     /* XXX if parent is downloaded, child is downloaded. */
-//     mft = cachent_provide(cache.rsync, uris->rpkiManifest);
-//
-//     if (mft->flags & CNF_FRESH)
-//             return cb(mft->rpp, arg); // XXX can rpp be NULL?
 
        /* Online attempts */
        // XXX review result signs
        // XXX normalize rpkiNotify & caRepository?
-       error = try_uri(uris->rpkiNotify, dl_rrdp, cb, arg);
+       error = try_uri(uris->rpkiNotify, cache.https, dl_rrdp, cb, arg);
        if (error <= 0)
                return error;
-       error = try_uri(uris->caRepository, dl_rsync, cb, arg);
+       error = try_uri(uris->caRepository, cache.rsync, dl_rsync, cb, arg);
        if (error <= 0)
                return error;
 
        /* Offline attempts */
-       error = try_uri(uris->rpkiNotify, NULL, cb, arg);
+       error = try_uri(uris->rpkiNotify, cache.https, NULL, cb, arg);
        if (error <= 0)
                return error;
-       return try_uri(uris->caRepository, NULL, cb, arg);
+       return try_uri(uris->caRepository, cache.rsync, NULL, cb, arg);
 }
 
 void
@@ -889,15 +943,15 @@ nftw_remove_abandoned(const char *path, const struct stat *st,
                }
 
        } else if (S_ISREG(st->st_mode)) {
-               if ((msm->flags & CNF_RSYNC) || !pm || (pm->flags & CNF_WITHDRAWN)) {
-                       clock_gettime(CLOCK_REALTIME, &now); // XXX
-                       PR_DEBUG_MSG("%ld > %ld", now.tv_sec - st->st_atim.tv_sec, cfg_cache_threshold());
-                       if (now.tv_sec - st->st_atim.tv_sec > cfg_cache_threshold()) {
-                               pr_op_debug("Too old; abandoned.");
-                               goto abandoned;
-                       }
-                       pr_op_debug("Still young; preserving.");
+
+//             if ((msm->flags & CNF_RSYNC) || !pm || (pm->flags & CNF_WITHDRAWN))
+               clock_gettime(CLOCK_REALTIME, &now); // XXX
+               PR_DEBUG_MSG("%ld > %ld", now.tv_sec - st->st_atim.tv_sec, cfg_cache_threshold());
+               if (now.tv_sec - st->st_atim.tv_sec > cfg_cache_threshold()) {
+                       pr_op_debug("Too old; abandoned.");
+                       goto abandoned;
                }
+               pr_op_debug("Still young; preserving.");
 
        } else {
                pr_op_debug("Unknown type; abandoned.");
@@ -981,12 +1035,10 @@ cleanup_cache(void)
        pr_op_debug("Cleaning up old abandoned and unknown cache files.");
        remove_abandoned();
 
-       pr_op_debug("Cleaning up leftover nodes.");
+       pr_op_debug("Cleaning up orphaned nodes.");
        remove_leftover_nodes();
        cachent_traverse(cache.rsync, remove_orphaned);
        cachent_traverse(cache.https, remove_orphaned);
-
-       /* XXX delete nodes for which no file exists? */
 }
 
 void
index 9a899a89ee9f46907dc479ad2b6652222fb99627..593ee14602e7d20c09204a990925a32c45eaa649 100644 (file)
@@ -2,6 +2,7 @@
 #define SRC_CACHE_LOCAL_CACHE_H_
 
 #include "cache/cachent.h"
+#include "types/str.h"
 
 void cache_setup(void);                /* Init this module */
 void cache_teardown(void);     /* Destroy this module */
@@ -31,6 +32,7 @@ void sias_cleanup(struct sia_uris *);
  * XXX rename
  */
 typedef int (*maps_dl_cb)(struct cache_node *rpp, void *arg);
+int cache_download_uri(struct strlist *, maps_dl_cb, void *);
 int cache_download_alt(struct sia_uris *, maps_dl_cb, void *);
 
 void cache_print(void); /* Dump cache in stdout. Recursive; tests only */
index d5f9ec25b992caefdbb201bbeb0b2a8f1566e2ea..6aee1b822b377ccc8ae269eab629a0ab97363d64 100644 (file)
@@ -238,31 +238,16 @@ 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)
+stat_dir(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;
+       if (stat(path, &meta) != 0)
+               return errno;
+       if (!S_ISDIR(meta.st_mode))
+               return ENOTDIR;
+       return 0;
 }
 
 static int
@@ -270,13 +255,17 @@ ensure_dir(char const *path, mode_t mode)
 {
        int error;
 
-       if (mkdir(path, mode) != 0) {
+       /*
+        * Perplexingly, mkdir() returns EEXIST instead of ENOTDIR when the
+        * path actually refers to a file.
+        * So it looks like this stat() is unavoidable.
+        */
+       if (stat_dir(path) == ENOTDIR && remove(path))
+               return errno;
+
+       if (mkdir(path, mode)) {
                error = errno;
-               if (error != EEXIST) {
-                       pr_op_err_st("Error while making directory '%s': %s",
-                           path, strerror(error));
-                       return error;
-               }
+               return (error == EEXIST) ? 0 : error;
        }
 
        return 0;
@@ -299,27 +288,33 @@ mkdir_p(char const *_path, bool include_basename, mode_t mode)
                *last_slash = '\0';
        }
 
-       result = dir_exists(path); /* short circuit */
-       if (result > 0) {
-               result = 0;
-               goto end;
-       } else if (result < 0) {
+       result = stat_dir(path); /* short circuit */
+       if (result == 0)
+               goto end; /* Already exists */
+       if (result != ENOENT && result != ENOTDIR) {
+               pr_op_err_st("Error during stat(%s): %s",
+                   path, strerror(result));
                goto end;
        }
 
+       if (result == ENOTDIR)
+               pr_op_err_st("stack tracing...");
+
        for (i = 1; path[i] != '\0'; i++) {
                if (path[i] == '/') {
                        path[i] = '\0';
                        result = ensure_dir(path, mode);
                        path[i] = '/';
-                       if (result != 0)
-                               goto end; /* error msg already printed */
+                       if (result != 0) {
+                               pr_op_err_st("Error during mkdir(%s): %s",
+                                   path, strerror(result));
+                               goto end;
+                       }
                }
        }
        result = ensure_dir(path, mode);
 
-end:
-       free(path);
+end:   free(path);
        return result;
 }
 
index 68b99ba77fe3bf62f63a1568b43c799d4359ca73..e444a0b39ea14ff4bb3f8fa8f8109415a2747769 100644 (file)
@@ -224,6 +224,12 @@ join_paths(char const *path1, char const *path2)
        char *result;
        int written;
 
+       // XXX needed?
+       if (path1[0] == 0)
+               return pstrdup(path2);
+       if (path2[0] == 0)
+               return pstrdup(path1);
+
        n = strlen(path1) + strlen(path2) + 2;
        result = pmalloc(n);
 
index bd27d4feb52b336ed5f04329feaf6d01354814cf..909268c53c2d06b54e676a0d2a72dbc938a49b87 100644 (file)
@@ -176,7 +176,7 @@ end:        free(dst);
  * 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 must exist.
  *
  * @src: cache/tmp/123
  * @dst: cache/rsync/a.b.c/d/e
@@ -184,6 +184,12 @@ end:       free(dst);
 int
 file_merge_into(char const *src, char const *dst)
 {
+       int error;
+
+       error = mkdir_p(dst, false, 0777);
+       if (error)
+               return error;
+
        src_offset = strlen(src);
        merge_dst = dst;
        /* TODO (performance) optimize that 32 */
index 3e67596e73808e432033e6f52148567ea8ff8480..714cdd6fb94365ecc69a34e83785f90f6511fa9d 100644 (file)
@@ -432,40 +432,3 @@ http_download_direct(char const *src, char const *dst)
        pr_val_info("HTTP GET: %s -> %s", src, dst);
        return http_fetch(src, dst, 0, NULL);
 }
-
-int
-http_download_tmp(char const *url, char const **path,
-    curl_off_t ims, bool *changed)
-{
-       int error;
-
-       error = cache_tmpfile(path);
-       if (error)
-               return error;
-
-       error = http_download(url, *path, ims, changed);
-       if (error)
-               free(*path);
-
-       return error;
-}
-
-int
-http_download_cache_node(struct cache_node *node)
-{
-       char *path;
-       bool changed;
-       int error;
-
-       error = http_download_tmp(node->url, &path, node->mtim, &changed);
-       if (error)
-               return error;
-
-       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
-       }
-       node->tmpdir = path;
-       return 0;
-}
index 55b63f872b2d408e0b2531c82da397e487d7b794..981aa1ba2aaaf8fbf1b7a1ca53868d6be68eb770 100644 (file)
@@ -8,7 +8,5 @@ void http_cleanup(void);
 
 int http_download(char const *, char const *, curl_off_t, bool *);
 int http_download_direct(char const *, char const *);
-int http_download_tmp(char const *, char const **, curl_off_t, bool *);
-int http_download_cache_node(struct cache_node *);
 
 #endif /* SRC_HTTP_HTTP_H_ */
index 35ab767b192453eb443320eebf3cf3ba769e66dd..2002c372a5024f31977a5a5684539b9dc1db6377 100644 (file)
@@ -267,7 +267,7 @@ do_file_validation(void *arg)
                goto end;
 
        args.db = db_table_create();
-       thread->error = cache_download_alt(&args.tal.urls, MAP_HTTP,
+       thread->error = cache_download_urls(&args.tal.urls,
            __handle_tal_map, &args);
        if (thread->error) {
                pr_op_err("None of the URIs of the TAL '%s' yielded a successful traversal.",
index a2805cbb5f62e78f064a346b552fbcd6a762368d..28ada82fbc72eb4107bac559178439be9c198ee8 100644 (file)
@@ -12,7 +12,7 @@ if USE_TESTS
 # <mumble>_CFLAGS is not defined.
 # Otherwise it must be included manually:
 #      mumble_mumble_CFLAGS = ${AM_CFLAGS} flag1 flag2 flag3 ...
-AM_CFLAGS =  -pedantic -Wall
+AM_CFLAGS =  -pedantic -Wall -Wno-unused
 #AM_CFLAGS += -Wno-unused
 AM_CFLAGS += -std=c99 -D_DEFAULT_SOURCE=1 -D_XOPEN_SOURCE=700 -D_BSD_SOURCE=1
 AM_CFLAGS += -I../src -DUNIT_TESTING ${CHECK_CFLAGS} ${XML2_CFLAGS} ${JANSSON_CFLAGS}
index 303fe5dca8087a80ff42d5bd2402627d2792c907..b224f53294aa0e8f2a59b90296c3a9e60c9069dd 100644 (file)
@@ -50,7 +50,7 @@ rsync_download(char const *src, char const *dst, bool is_directory)
 }
 
 int
-http_download(char const *url, char const *path, curl_off_t ims, bool *changed)
+http_download(char const *src, char const *dst, curl_off_t ims, bool *changed)
 {
        char cmd[61];
 
@@ -61,9 +61,9 @@ http_download(char const *url, char const *path, curl_off_t ims, bool *changed)
                return dl_error;
        }
 
-       ck_assert_int_eq(0, mkdir_p(path, false, 0777));
+       ck_assert_int_eq(0, mkdir_p(dst, false, 0777));
 
-       ck_assert(snprintf(cmd, sizeof(cmd), "touch %s", path) < sizeof(cmd));
+       ck_assert(snprintf(cmd, sizeof(cmd), "touch %s", dst) < sizeof(cmd));
        ck_assert_int_eq(0, system(cmd));
 
        *changed = true;
@@ -79,7 +79,7 @@ MOCK(cfg_cache_threshold, time_t, 60 * 60 * 24 * 7, void)
 /* Helpers */
 
 static void
-setup_test(bool dl_type)
+setup_test(void)
 {
        dl_error = 0;
        ck_assert_int_eq(0, system("rm -rf tmp/"));
@@ -100,7 +100,7 @@ static void
 run_dl_rsync(char const *caRepository, int expected_error,
     unsigned int expected_calls)
 {
-       static struct sia_uris sias;
+       struct sia_uris sias;
 
        sias.caRepository = pstrdup(caRepository);
        sias.rpkiNotify = NULL;
@@ -117,6 +117,25 @@ run_dl_rsync(char const *caRepository, int expected_error,
        sias_cleanup(&sias);
 }
 
+static void
+run_dl_https(char const *url, int expected_error, unsigned int expected_calls)
+{
+       struct strlist uris;
+
+       strlist_init(&uris);
+       strlist_add(&uris, pstrdup(url));
+
+       rsync_counter = 0;
+       https_counter = 0;
+       printf("---- Downloading... ----\n");
+       ck_assert_int_eq(expected_error, cache_download_uri(&uris, okay, NULL));
+       printf("---- Downloaded. ----\n");
+       ck_assert_uint_eq(0, rsync_counter);
+       ck_assert_uint_eq(expected_calls, https_counter);
+
+       strlist_cleanup(&uris);
+}
+
 static int
 print_file(const char *fpath, const struct stat *sb, int typeflag,
     struct FTW *ftwbuf)
@@ -162,38 +181,6 @@ ck_path(struct cache_node *node, char const *_)
        return true;
 }
 
-//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
 ck_assert_cachent_eq(struct cache_node *expected, struct cache_node *actual)
 {
@@ -260,6 +247,12 @@ ck_cache_rsync(struct cache_node *rsync)
        ck_cache(rsync, unode("https", NULL));
 }
 
+static void
+ck_cache_https(struct cache_node *https)
+{
+       ck_cache(unode("rsync", NULL), https);
+}
+
 static time_t epoch;
 
 static bool
@@ -291,10 +284,15 @@ nftw_unfreshen(const char *fpath, const struct stat *sb, int typeflag,
 static void
 new_iteration(bool outdate)
 {
+       pr_op_debug("--- Unfreshening... ---");
        epoch = outdate ? get_days_ago(30) : get_days_ago(1);
        cachent_traverse(cache.rsync, unfreshen);
+       cachent_traverse(cache.https, unfreshen);
        ck_assert_int_eq(0, nftw("tmp/rsync", nftw_unfreshen, 32, FTW_PHYS));
        ck_assert_int_eq(0, nftw("tmp/https", nftw_unfreshen, 32, FTW_PHYS));
+
+       pr_op_debug("---- Tree now stale. ----");
+       cache_print();
 }
 
 //static void
@@ -325,7 +323,7 @@ static const int FAILED = CNF_FRESH;
 
 START_TEST(test_cache_download_rsync)
 {
-       setup_test(false);
+       setup_test();
 
        run_dl_rsync("rsync://a.b.c/d", 0, 1);
        ck_cache_rsync(
@@ -388,7 +386,7 @@ END_TEST
 
 START_TEST(test_cache_download_rsync_error)
 {
-       setup_test(false);
+       setup_test();
 
        dl_error = 0;
        run_dl_rsync("rsync://a.b.c/d", 0, 1);
@@ -423,7 +421,7 @@ END_TEST
 
 START_TEST(test_cache_cleanup_rsync)
 {
-       setup_test(true);
+       setup_test();
 
        /*
         * First iteration: Tree is created. No prunes, because nothing's
@@ -551,182 +549,219 @@ START_TEST(test_cache_cleanup_rsync)
 }
 END_TEST
 
-//START_TEST(test_cache_cleanup_rsync_error)
-//{
-//     setup_test();
-//
-//     /* Set up */
-//     dl_error = false;
-//     run_dl_rsync("rsync://a.b.c/d", 0, 1);
-//     dl_error = true;
-//     run_dl_rsync("rsync://a.b.c/e", -EINVAL, 1);
-//     ck_cache(
-//             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 */
-//     do_cleanup();
-//     ck_cache(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_dl_rsync("rsync://a.b.c/d", -EINVAL, 1);
-//     ck_cache(NODE("rsync://a.b.c/d/", -EINVAL, 1, true), NULL);
-//
-//     /* Error is old; gets deleted */
-//     new_iteration(true);
-//     do_cleanup();
-//     ck_cache(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);
-//     ck_cache(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);
-//     ck_cache(
-//         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);
-//     ck_cache(
-//         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);
-//     ck_cache(
-//         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);
-//     ck_cache(
-//         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);
-//     do_cleanup();
-//     ck_cache(
-//             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);
-//     do_cleanup();
-//     ck_cache(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);
-//     do_cleanup();
-//     ck_cache(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);
-//     do_cleanup();
-//     ck_cache(
-//             NODE("https://a.b.c/e/f/g", 0, 1, 1), NULL);
-//
+START_TEST(test_cache_cleanup_rsync_error)
+{
+       setup_test();
+
+       /* Set up */
+       dl_error = 0;
+       run_dl_rsync("rsync://a.b.c/d", 0, 1);
+       dl_error = -EINVAL;
+       run_dl_rsync("rsync://a.b.c/e", -EINVAL, 1);
+       ck_cache_rsync(
+               unode("rsync",
+                       unode("rsync/a.b.c",
+                               uftnode("rsync/a.b.c/d", FULL, "tmp/tmp/0", NULL),
+                               ufnode("rsync/a.b.c/e", FAILED, NULL), NULL), NULL));
+
+       /* Node gets deleted because cached file doesn't exist */
+       run_cleanup();
+       ck_cache_rsync(
+               unode("rsync",
+                       unode("rsync/a.b.c",
+                               ufnode("rsync/a.b.c/d", FULL, NULL), NULL), 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 = -EINVAL;
+       run_dl_rsync("rsync://a.b.c/d", -EINVAL, 1);
+       ck_cache_rsync(
+               unode("rsync",
+                       unode("rsync/a.b.c",
+                               ufnode("rsync/a.b.c/d", FULL, NULL), NULL), NULL));
+
+       /* Error is old; gets deleted */
+       new_iteration(true);
+       run_cleanup();
+       ck_cache_rsync(unode("rsync", NULL));
+
+       cleanup_test();
+}
+END_TEST
+
+static const int HDOWNLOADED = CNF_CACHED | CNF_FRESH | CNF_CHANGED;
+static const int HVALIDATED = CNF_CACHED | CNF_VALID;
+static const int HFULL = HDOWNLOADED | HVALIDATED;
+static const int HFAILED = CNF_FRESH;
+
+START_TEST(test_cache_download_https)
+{
+       setup_test();
+
+       /* Download *file* e. */
+       run_dl_https("https://a.b.c/d/e", 0, 1);
+       ck_cache_https(
+               unode("https",
+                       unode("https/a.b.c",
+                               unode("https/a.b.c/d",
+                                       uftnode("https/a.b.c/d/e", HFULL, "tmp/tmp/0", NULL), NULL), NULL), NULL));
+
+       /* Download something else 1 */
+       run_dl_https("https://a.b.c/e", 0, 1);
+       ck_cache_https(
+               unode("https",
+                       unode("https/a.b.c",
+                               unode("https/a.b.c/d",
+                                       uftnode("https/a.b.c/d/e", HFULL, "tmp/tmp/0", NULL), NULL),
+                               uftnode("https/a.b.c/e", HFULL, "tmp/tmp/1", NULL), NULL), NULL));
+
+       /* Download something else 2 */
+       run_dl_https("https://x.y.z/e", 0, 1);
+       ck_cache_https(
+               unode("https",
+                       unode("https/a.b.c",
+                               unode("https/a.b.c/d",
+                                       uftnode("https/a.b.c/d/e", HFULL, "tmp/tmp/0", NULL), NULL),
+                               uftnode("https/a.b.c/e", HFULL, "tmp/tmp/1", NULL), NULL),
+                       unode("https/x.y.z",
+                               uftnode("https/x.y.z/e", HFULL, "tmp/tmp/2", NULL), NULL), NULL));
+
+       cleanup_test();
+}
+END_TEST
+
+START_TEST(test_cache_download_https_error)
+{
+       setup_test();
+
+       dl_error = 0;
+       run_dl_https("https://a.b.c/d", 0, 1);
+       dl_error = -EINVAL;
+       run_dl_https("https://a.b.c/e", -EINVAL, 1);
+       ck_cache_https(
+               unode("https",
+                       unode("https/a.b.c",
+                               uftnode("https/a.b.c/d", HFULL, "tmp/tmp/0", NULL),
+                               uftnode("https/a.b.c/e", HFAILED, NULL, NULL), NULL), NULL));
+
+       /* Regardless of error, not reattempted because same iteration */
+       dl_error = -EINVAL;
+       run_dl_https("https://a.b.c/d", 0, 0);
+       dl_error = 0;
+       run_dl_https("https://a.b.c/e", -EINVAL, 0);
+       ck_cache_https(
+               unode("https",
+                       unode("https/a.b.c",
+                               uftnode("https/a.b.c/d", HFULL, "tmp/tmp/0", NULL),
+                               uftnode("https/a.b.c/e", HFAILED, NULL, NULL), NULL), NULL));
+
+       cleanup_test();
+}
+END_TEST
+
+// XXX not testing alts so far
+
+START_TEST(test_cache_cleanup_https)
+{
+       setup_test();
+
+       /* First iteration; make a tree and clean it */
+       new_iteration(true);
+       run_dl_https("https://a.b.c/d", 0, 1);
+       run_dl_https("https://a.b.c/e", 0, 1);
+       run_cleanup();
+       ck_cache_https(
+               unode("https",
+                       unode("https/a.b.c",
+                               ufnode("https/a.b.c/d", HFULL, NULL),
+                               ufnode("https/a.b.c/e", HFULL, NULL), NULL), NULL));
+
+       /* Remove one branch */
+       new_iteration(true);
+       run_dl_https("https://a.b.c/d", 0, 1);
+       run_cleanup();
+       ck_cache_https(
+               unode("https",
+                       unode("https/a.b.c",
+                               ufnode("https/a.b.c/d", HFULL, NULL), NULL), NULL));
+
+       /* Change the one branch */
+       new_iteration(true);
+       run_dl_https("https://a.b.c/e", 0, 1);
+       run_cleanup();
+       ck_cache_https(
+               unode("https",
+                       unode("https/a.b.c",
+                               ufnode("https/a.b.c/e", HFULL, NULL), NULL), NULL));
+
+       /* Add a child to the same branch, do not update the old one */
+       new_iteration(true);
+       PR_DEBUG;
+       run_dl_https("https://a.b.c/e/f/g", 0, 1);
+       run_cleanup();
+       ck_cache_https(
+               unode("https",
+                       unode("https/a.b.c",
+                               unode("https/a.b.c/e",
+                                       unode("https/a.b.c/e/f",
+                                               ufnode("https/a.b.c/e/f/g", HFULL, NULL), NULL), NULL), NULL), 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);
-//     do_cleanup();
+//     run_dl_https("https://a.b.c/e/f", 0, 1);
+//     run_cleanup();
 //     ck_cache(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);
-//     do_cleanup();
+//     run_dl_https("https://a.b.c/e", 0, 1);
+//     run_cleanup();
 //     ck_cache(NODE("https://a.b.c/e", 0, 1, 1), NULL);
 //
 //     /* Empty the tree */
 //     new_iteration(true);
-//     do_cleanup();
+//     run_cleanup();
 //     ck_cache(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);
+//     run_dl_https("https://a.b.c/e", 0, 1);
+//     run_dl_https("https://a.b.c/f/g/h", 0, 1);
 //     ck_cache(
 //         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"));
-//     do_cleanup();
+//     run_cleanup();
 //     ck_cache(NODE("https://a.b.c/e", 0, 1, 1), NULL);
-//
-//     cleanup_test();
-//}
-//END_TEST
-//
+
+       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);
+//     run_dl_https("https://a.b.c/d", 0, 1);
 //     dl_error = true;
-//     run_cache_download("https://a.b.c/e", -EINVAL, 0, 1);
+//     run_dl_https("https://a.b.c/e", -EINVAL, 1);
 //     ck_cache(
 //         NODE("https://a.b.c/d", 0, 1, 1),
 //         NODE("https://a.b.c/e", -EINVAL, 0, 0),
 //         NULL);
 //
 //     /* Deleted because file ENOENT. */
-//     do_cleanup();
+//     run_cleanup();
 //     ck_cache(
 //         NODE("https://a.b.c/d", 0, 1, 1),
 //         NULL);
@@ -734,17 +769,17 @@ END_TEST
 //     /* Fail d */
 //     new_iteration(false);
 //     dl_error = true;
-//     run_cache_download("https://a.b.c/d", -EINVAL, 0, 1);
+//     run_dl_https("https://a.b.c/d", -EINVAL, 1);
 //     ck_cache(NODE("https://a.b.c/d", -EINVAL, 1, 1), NULL);
 //
 //     /* Not deleted, because not old */
 //     new_iteration(false);
-//     do_cleanup();
+//     run_cleanup();
 //     ck_cache(NODE("https://a.b.c/d", -EINVAL, 1, 1), NULL);
 //
 //     /* Become old */
 //     new_iteration(true);
-//     do_cleanup();
+//     run_cleanup();
 //     ck_cache(NULL);
 //
 //     cleanup_test();
@@ -970,15 +1005,15 @@ static Suite *thread_pool_suite(void)
        TCase *rsync, *https, *dot, *meta, *recover;
 
        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_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);
 
        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);
 //     tcase_add_test(https, test_cache_cleanup_https_error);
 
        dot = tcase_create("dot");