From: Alberto Leiva Popper Date: Thu, 11 Jul 2024 16:27:52 +0000 (-0600) Subject: Wednesday X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c05c086dbdf7dccc7ce1ac3a73708955f32e4181;p=thirdparty%2FFORT-validator.git Wednesday Oh dear. I misunderstood RFC 9589. I'm going to have to restore RRDP caging. Aaaaaaaaaahhhhhhhhhhhhhhh. Think I'm going to have to out-of-scope 9589 for this release. Unstable, squash later. --- diff --git a/src/cache/cachent.c b/src/cache/cachent.c index 161ca83e..36a0acfe 100644 --- a/src/cache/cachent.c +++ b/src/cache/cachent.c @@ -4,6 +4,7 @@ #include "config.h" #include "data_structure/common.h" #include "data_structure/path_builder.h" +#include "types/url.h" /* @schema must contain a colon suffix, otherwise lookups won't work */ struct cache_node * @@ -79,56 +80,6 @@ end: pb_cleanup(&pb); return error; } -static char * -path_rewind(char const *root, char *cursor) -{ - for (cursor -= 2; root <= cursor; cursor--) - if (*cursor == '/') - return cursor + 1; - return NULL; -} - -/* Collapses '//' (after the schema), '.' and '..'. */ -static char * -normalize(char const *url) -{ - char *normal, *dst, *root; - struct tokenizer tkn; - - if (strncmp(url, "rsync://", RPKI_SCHEMA_LEN) && - strncmp(url, "https://", RPKI_SCHEMA_LEN)) - return NULL; - - normal = pstrdup(url); - dst = normal + RPKI_SCHEMA_LEN; - root = dst - 1; - token_init(&tkn, url + RPKI_SCHEMA_LEN); - - while (token_next(&tkn)) { - if (tkn.len == 1 && tkn.str[0] == '.') - continue; - if (tkn.len == 2 && tkn.str[0] == '.' && tkn.str[1] == '.') { - dst = path_rewind(root, dst); - if (!dst) - goto fail; - continue; - } - strncpy(dst, tkn.str, tkn.len); - dst[tkn.len] = '/'; - dst += tkn.len + 1; - } - - /* Reject URL if there's nothing after the schema. Maybe unnecessary. */ - if (dst == normal + RPKI_SCHEMA_LEN) - goto fail; - - dst[-1] = '\0'; - return normal; - -fail: free(normal); - return NULL; -} - /* Get or create parent's child. */ static struct cache_node * provide(struct cache_node *parent, char const *url, @@ -143,6 +94,8 @@ provide(struct cache_node *parent, char const *url, child = pzalloc(sizeof(struct cache_node)); child->url = pstrndup(url, name - url + namelen); child->name = child->url + (name - url); + if (parent->flags & RSYNC_INHERIT) + child->flags = RSYNC_INHERIT; child->parent = parent; HASH_ADD_KEYPTR(hh, parent->children, child->name, namelen, child); return child; @@ -170,7 +123,7 @@ cachent_provide(struct cache_node *ancestor, char const *url) array_index i; struct tokenizer tkn; - normal = normalize(url); + normal = url_normalize(url); if (!normal) return NULL; @@ -241,13 +194,18 @@ print_node(struct cache_node *node, unsigned int tabs) for (i = 0; i < tabs; i++) printf("\t"); - printf("%s ", node->name); - printf("%s", (node->flags & CNF_RSYNC) ? "RSYNC " : ""); - printf("%s", (node->flags & CNF_FRESH) ? "Fresh " : ""); - printf("%s", (node->flags & CNF_TOUCHED) ? "Touched " : ""); - printf("%s", (node->flags & CNF_VALID) ? "Valid " : ""); - printf("%s\n", (node->flags & CNF_WITHDRAWN) ? "Withdrawn " : ""); - + printf("%s -- ", node->name); + printf("%s", (node->flags & CNF_RSYNC) ? "rsync " : ""); + printf("%s", (node->flags & CNF_CACHED) ? "cached " : ""); + printf("%s", (node->flags & CNF_FRESH) ? "fresh " : ""); + printf("%s", (node->flags & CNF_CHANGED) ? "changed " : ""); + printf("%s", (node->flags & CNF_TOUCHED) ? "touched " : ""); + printf("%s", (node->flags & CNF_VALID) ? "valid " : ""); + printf("%s", (node->flags & CNF_NOTIFICATION) ? "notification " : ""); + printf("%s", (node->flags & CNF_WITHDRAWN) ? "withdrawn " : ""); + printf(" -- %s", node->tmpdir); + + printf("\n"); HASH_ITER(hh, node->children, child, tmp) print_node(child, tabs + 1); } diff --git a/src/cache/cachent.h b/src/cache/cachent.h index c6841c1e..5f9107b7 100644 --- a/src/cache/cachent.h +++ b/src/cache/cachent.h @@ -3,18 +3,26 @@ /* CACHE ENTity, CACHE elemENT, CACHE componENT */ +#include #include "data_structure/uthash.h" -#define RPKI_SCHEMA_LEN 8 /* strlen("rsync://"), strlen("https://") */ - /* XXX rename "touched" and "validated" into "preserve"? */ #define CNF_RSYNC (1 << 0) -/* Do we have a copy in the cache? */ +/* + * Do we have a (full) copy in the cache? + * If disabled, we don't know (because an ancestor was recursively rsync'd). + */ #define CNF_CACHED (1 << 1) -/* Was it downloaded during the current cycle? */ +/* + * Was it (allegedly) downloaded during the current cycle? + * "Allegedly" because we might have rsync'd an ancestor. + */ #define CNF_FRESH (1 << 2) -/* Did it change between the previous cycle and the current one? */ +/* + * Did it change between the previous cycle and the current one? + * (This is HTTP and RRDP only; rsync doesn't tell us.) + */ #define CNF_CHANGED (1 << 3) /* Was it read during the current cycle? */ #define CNF_TOUCHED (1 << 4) @@ -29,6 +37,12 @@ /* Withdrawn by RRDP? */ #define CNF_WITHDRAWN (1 << 7) +/* + * Flags for children of downloaded rsync nodes that should be cleaned later. + * (FRESH prevents redownload.) + */ +#define RSYNC_INHERIT (CNF_RSYNC | CNF_FRESH) + // XXX rename to cache_entity or cachent struct cache_node { char const *name; /* Points to the last component of @url */ @@ -46,6 +60,8 @@ struct cache_node { */ char *tmpdir; + struct cache_node *rpp; // XXX delete? + /* Only if flags & CNF_NOTIFICATION. */ // struct cachefile_notification notif; diff --git a/src/cache/local_cache.c b/src/cache/local_cache.c index b63dee4f..52cba691 100644 --- a/src/cache/local_cache.c +++ b/src/cache/local_cache.c @@ -25,6 +25,7 @@ #include "http/http.h" #include "rsync/rsync.h" #include "types/str.h" +#include "types/url.h" /* XXX force RRDP if one RPP fails to validate by rsync? */ @@ -510,10 +511,48 @@ find_msm(struct cache_node *root, char *path, struct cache_node **msm) return node; } +/* + * The "rsync module" is the component immediately after the domain. + * + * get_rsync_module(rsync://a.b.c/d/e/f/potato.mft) = d + */ +static struct cache_node * +get_rsync_module(struct cache_node *node) +{ + struct cache_node *gp; /* Grandparent */ + + if (!node || !node->parent || !node->parent->parent) + return NULL; + + for (gp = node->parent->parent; gp->parent != NULL; gp = gp->parent) + node = node->parent; + return node; +} + +static struct cache_node * +get_rsync_rpp(char const *caRepository, struct cache_node *rpkiManifest) +{ + struct cache_node *node; + char *normal; + + normal = url_normalize(caRepository); + if (normal == NULL) + return NULL; + + for (node = rpkiManifest; node != NULL; node = node->parent) + if (strcmp(node->url, normal) == 0) { + free(normal); + return node; + } + + free(normal); + return NULL; +} + static int -dl_rsync(char const *uri, struct cache_node *node) +dl_rsync(char const *caRepository, struct cache_node *mft) { - struct cache_node *module; + struct cache_node *module, *node; char *path; int error; @@ -522,10 +561,14 @@ dl_rsync(char const *uri, struct cache_node *node) return 1; } - /* XXX this is probably wrong; get the real module. */ - module = node; - while (module->parent != NULL) - module = module->parent; + module = get_rsync_module(mft); + if (module == NULL) { + return -EINVAL; // XXX + } + mft->rpp = get_rsync_rpp(caRepository, mft); + if (!mft->rpp) + return pr_val_err("Manifest '%s' does not seem to be inside its publication point '%s'.", + mft->url, caRepository); error = cache_tmpfile(&path); if (error) @@ -533,23 +576,26 @@ dl_rsync(char const *uri, struct cache_node *node) // XXX looks like the third argument is redundant now. error = rsync_download(module->url, path, true); - if (error) - goto cancel; + if (error) { + free(path); + return error; + } - module->flags |= CNF_FRESH; + module->flags |= CNF_RSYNC | CNF_CACHED | CNF_FRESH; module->mtim = time(NULL); // XXX catch -1 module->tmpdir = path; - while (node != NULL) { - node->flags |= CNF_FRESH; + for (node = mft; node != module; node = node->parent) { + node->flags |= RSYNC_INHERIT; node->mtim = module->mtim; - node = node->parent; + if (mft) { + node->rpp = mft->rpp; + if (node == mft->rpp) + mft = NULL; + } } return 0; - -cancel: free(path); - return error; } static int @@ -598,6 +644,8 @@ download(char const *uri, enum map_type type, struct cache_node *mft) /* * XXX review result sign + * + * uri is a rpkiNotify or a caRepository. */ static int try_uri(struct cache_node *mft, char const *uri, enum map_type type, @@ -615,7 +663,7 @@ try_uri(struct cache_node *mft, char const *uri, enum map_type type, } } - error = cb(mft, arg); + error = cb(mft->rpp, arg); if (error) { pr_val_debug("RPP validation failed."); return error; @@ -642,7 +690,6 @@ cache_download_alt(struct sia_uris *uris, maps_dl_cb cb, void *arg) { struct cache_node *mft; int online; - char **uri; int error = 0; /* XXX mutex */ @@ -650,19 +697,19 @@ cache_download_alt(struct sia_uris *uris, maps_dl_cb cb, void *arg) mft = cachent_provide(cache.rsync, uris->rpkiManifest); if (mft->flags & CNF_FRESH) - return cb(mft, arg); + return cb(mft->rpp, arg); // XXX can rpp be NULL? for (online = 1; online >= 0; online--) { - ARRAYLIST_FOREACH(&uris->rpkiNotify, uri) { - error = try_uri(mft, *uri, MAP_NOTIF, online, cb, arg); - if (error <= 0) - return error; - } - ARRAYLIST_FOREACH(&uris->caRepository, uri) { - error = try_uri(mft, *uri, MAP_RSYNC, online, cb, arg); + if (uris->rpkiNotify) { + error = try_uri(mft, uris->rpkiNotify, MAP_NOTIF, + online, cb, arg); if (error <= 0) return error; } + error = try_uri(mft, uris->caRepository, MAP_RSYNC, + online, cb, arg); + if (error <= 0) + return error; } return error; @@ -945,15 +992,13 @@ cache_commit(void) void sias_init(struct sia_uris *sias) { - strlist_init(&sias->caRepository); - strlist_init(&sias->rpkiNotify); - sias->rpkiManifest = NULL; + memset(sias, 0, sizeof(*sias)); } void sias_cleanup(struct sia_uris *sias) { - strlist_cleanup(&sias->caRepository); - strlist_cleanup(&sias->rpkiNotify); + free(sias->caRepository); + free(sias->rpkiNotify); free(sias->rpkiManifest); } diff --git a/src/cache/local_cache.h b/src/cache/local_cache.h index 02b83aa9..9a899a89 100644 --- a/src/cache/local_cache.h +++ b/src/cache/local_cache.h @@ -2,7 +2,6 @@ #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 */ @@ -14,8 +13,8 @@ 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 */ - struct strlist rpkiNotify; /* RRDP Notifications */ + char *caRepository; /* RPP cage */ + char *rpkiNotify; /* RRDP Notification */ char *rpkiManifest; }; @@ -31,7 +30,7 @@ void sias_cleanup(struct sia_uris *); * * XXX rename */ -typedef int (*maps_dl_cb)(struct cache_node *, void *); +typedef int (*maps_dl_cb)(struct cache_node *rpp, void *arg); int cache_download_alt(struct sia_uris *, maps_dl_cb, void *); void cache_print(void); /* Dump cache in stdout. Recursive; tests only */ diff --git a/src/object/certificate.c b/src/object/certificate.c index e44d5038..293bad12 100644 --- a/src/object/certificate.c +++ b/src/object/certificate.c @@ -96,22 +96,6 @@ static const struct ad_metadata RPKI_MANIFEST = { .required = true, }; -static void -sia_uris_init(struct sia_uris *uris) -{ - strlist_init(&uris->caRepository); - strlist_init(&uris->rpkiNotify); - uris->rpkiManifest = NULL; -} - -static void -sia_uris_cleanup(struct sia_uris *uris) -{ - strlist_cleanup(&uris->caRepository); - strlist_cleanup(&uris->rpkiNotify); - free(uris->rpkiManifest); -} - static void debug_serial_number(BIGNUM *number) { @@ -1193,23 +1177,45 @@ static void handle_rpkiManifest(char *uri, void *arg) { struct sia_uris *uris = arg; - uris->rpkiManifest = uri; + + pr_val_debug("rpkiManifest: %s", uri); + + if (uris->rpkiManifest != NULL) { + pr_val_warn("Ignoring additional rpkiManifest: %s", uri); + free(uri); + } else { + uris->rpkiManifest = uri; + } } static void handle_caRepository(char *uri, void *arg) { struct sia_uris *uris = arg; + pr_val_debug("caRepository: %s", uri); - strlist_add(&uris->caRepository, uri); + + if (uris->caRepository != NULL) { + pr_val_warn("Ignoring additional caRepository: %s", uri); + free(uri); + } else { + uris->caRepository = uri; + } } static void handle_rpkiNotify(char *uri, void *arg) { struct sia_uris *uris = arg; + pr_val_debug("rpkiNotify: %s", uri); - strlist_add(&uris->rpkiNotify, uri); + + if (uris->rpkiNotify != NULL) { + pr_val_warn("Ignoring additional rpkiNotify: %s", uri); + free(uri); + } else { + uris->rpkiNotify = uri; + } } static void @@ -1217,6 +1223,7 @@ handle_signedObject(char *uri, void *arg) { struct certificate_refs *refs = arg; pr_val_debug("signedObject: %s", uri); + // XXX Maybe it's time to review this API. refs->signedObject = uri; } @@ -1799,15 +1806,6 @@ certificate_validate_aia(char const *caIssuers, X509 *cert) return 0; } -static int -download_rpp(struct sia_uris *uris) -{ - if (uris->caRepository.len == 0 && uris->rpkiNotify.len == 0) - return pr_val_err("SIA lacks both caRepository and rpkiNotify."); - - return cache_download_alt(uris, MAP_NOTIF, NULL, NULL); -} - /** Boilerplate code for CA certificate validation and recursive traversal. */ int certificate_traverse(struct rpp *rpp_parent, struct cache_mapping *cert_map) @@ -1883,7 +1881,7 @@ certificate_traverse(struct rpp *rpp_parent, struct cache_mapping *cert_map) if (error) goto revert_uris; - error = download_rpp(&sia_uris); + error = cache_download_alt(&sia_uris, MAP_NOTIF, NULL, NULL); if (error) goto revert_uris; @@ -1904,7 +1902,7 @@ certificate_traverse(struct rpp *rpp_parent, struct cache_mapping *cert_map) rpp_refput(pp); revert_uris: - sia_uris_cleanup(&sia_uris); + sias_cleanup(&sia_uris); revert_cert: if (cert != NULL) X509_free(cert); diff --git a/src/types/str.h b/src/types/str.h index b49c3e8a..0de5bca8 100644 --- a/src/types/str.h +++ b/src/types/str.h @@ -3,6 +3,7 @@ #include "data_structure/array_list.h" +/* XXX delete? */ DEFINE_ARRAY_LIST_STRUCT(strlist, char *); void strlist_init(struct strlist *); diff --git a/src/types/url.c b/src/types/url.c new file mode 100644 index 00000000..9b0162e9 --- /dev/null +++ b/src/types/url.c @@ -0,0 +1,54 @@ +#include "types/url.h" + +#include "alloc.h" +#include "data_structure/path_builder.h" + +static char * +path_rewind(char const *root, char *cursor) +{ + for (cursor -= 2; root <= cursor; cursor--) + if (*cursor == '/') + return cursor + 1; + return NULL; +} + +/* Collapses '//' (after the schema), '.' and '..'. */ +char * +url_normalize(char const *url) +{ + char *normal, *dst, *root; + struct tokenizer tkn; + + if (strncmp(url, "rsync://", RPKI_SCHEMA_LEN) && + strncmp(url, "https://", RPKI_SCHEMA_LEN)) + return NULL; + + normal = pstrdup(url); + dst = normal + RPKI_SCHEMA_LEN; + root = dst - 1; + token_init(&tkn, url + RPKI_SCHEMA_LEN); + + while (token_next(&tkn)) { + if (tkn.len == 1 && tkn.str[0] == '.') + continue; + if (tkn.len == 2 && tkn.str[0] == '.' && tkn.str[1] == '.') { + dst = path_rewind(root, dst); + if (!dst) + goto fail; + continue; + } + strncpy(dst, tkn.str, tkn.len); + dst[tkn.len] = '/'; + dst += tkn.len + 1; + } + + /* Reject URL if there's nothing after the schema. Maybe unnecessary. */ + if (dst == normal + RPKI_SCHEMA_LEN) + goto fail; + + dst[-1] = '\0'; + return normal; + +fail: free(normal); + return NULL; +} diff --git a/src/types/url.h b/src/types/url.h new file mode 100644 index 00000000..45607186 --- /dev/null +++ b/src/types/url.h @@ -0,0 +1,8 @@ +#ifndef SRC_TYPES_URL_H_ +#define SRC_TYPES_URL_H_ + +#define RPKI_SCHEMA_LEN 8 /* strlen("rsync://"), strlen("https://") */ + +char *url_normalize(char const *); + +#endif /* SRC_TYPES_URL_H_ */ diff --git a/test/Makefile.am b/test/Makefile.am index 79c88a47..a0ff28aa 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -22,10 +22,14 @@ AM_CFLAGS += -I../src -DUNIT_TESTING ${CHECK_CFLAGS} ${XML2_CFLAGS} ${JANSSON_CF # target. MY_LDADD = ${CHECK_LIBS} ${JANSSON_LIBS} -check_PROGRAMS = cachent.test +check_PROGRAMS = url.test +#check_PROGRAMS += cachent.test check_PROGRAMS += cache.test TESTS = ${check_PROGRAMS} +url_test_SOURCES = types/url_test.c +url_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS} + cache_test_SOURCES = cache/local_cache_test.c cache_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS} diff --git a/test/cache/cachent_test.c b/test/cache/cachent_test.c index bec21054..17df8b42 100644 --- a/test/cache/cachent_test.c +++ b/test/cache/cachent_test.c @@ -5,6 +5,7 @@ #include "cache/common.c" #include "data_structure/path_builder.c" #include "mock.c" +#include "types/url.c" static char deleted[16][5]; static unsigned int dn; @@ -222,33 +223,6 @@ START_TEST(test_traverse) } END_TEST -#define TEST_NORMALIZE(dirty, clean) \ - normal = normalize(dirty); \ - ck_assert_str_eq(clean, normal); \ - free(normal) - -START_TEST(test_normalize) -{ - char *normal; - - TEST_NORMALIZE("rsync://a.b.c", "rsync://a.b.c"); - TEST_NORMALIZE("rsync://a.b.c/", "rsync://a.b.c"); - TEST_NORMALIZE("rsync://a.b.c//////", "rsync://a.b.c"); - TEST_NORMALIZE("rsync://a.b.c/d/e", "rsync://a.b.c/d/e"); - TEST_NORMALIZE("rsync://a.b.c/d/e/.", "rsync://a.b.c/d/e"); - TEST_NORMALIZE("rsync://a.b.c/d/e/.", "rsync://a.b.c/d/e"); - TEST_NORMALIZE("rsync://a.b.c/d/./e/.", "rsync://a.b.c/d/e"); - TEST_NORMALIZE("rsync://a.b.c/d/../d/../d/e/", "rsync://a.b.c/d/e"); - TEST_NORMALIZE("rsync://a.b.c/../x/y/z", "rsync://x/y/z"); - TEST_NORMALIZE("rsync://x//y/z/../../../m/./n/o", "rsync://m/n/o"); - ck_assert_ptr_eq(NULL, normalize("rsync://")); - ck_assert_ptr_eq(NULL, normalize("rsync://..")); - ck_assert_ptr_eq(NULL, normalize("rsync://a.b.c/..")); - ck_assert_ptr_eq(NULL, normalize("rsync://a.b.c/d/e/../../..")); - ck_assert_ptr_eq(NULL, normalize("abcde://a.b.c/d")); -} -END_TEST - START_TEST(test_provide) { struct cache_node *rsync, *abc, *d, *e, *f, *g, *h, *ee; @@ -342,7 +316,6 @@ static Suite *thread_pool_suite(void) tcase_add_test(traverses, test_traverse); provide = tcase_create("provide"); - tcase_add_test(provide, test_normalize); tcase_add_test(provide, test_provide); suite = suite_create("cachent"); diff --git a/test/cache/common.c b/test/cache/common.c index fac9b2cd..ebb67dc6 100644 --- a/test/cache/common.c +++ b/test/cache/common.c @@ -5,7 +5,7 @@ #include "data_structure/uthash.h" struct cache_node * -node(char const *url, int flags, ...) +node(char const *url, int flags, char const *tmpdir, ...) { struct cache_node *result; struct cache_node *child; @@ -17,8 +17,9 @@ node(char const *url, int flags, ...) slash = strrchr(url, '/'); result->name = slash ? (slash + 1) : result->url; result->flags = flags; + result->tmpdir = tmpdir ? pstrdup(tmpdir) : NULL; - va_start(args, flags); + va_start(args, tmpdir); while ((child = va_arg(args, struct cache_node *)) != NULL) { HASH_ADD_KEYPTR(hh, result->children, child->name, strlen(child->name), child); diff --git a/test/cache/common.h b/test/cache/common.h index 8708d338..d8b2ec3c 100644 --- a/test/cache/common.h +++ b/test/cache/common.h @@ -3,6 +3,6 @@ #include "cache/cachent.h" -struct cache_node *node(char const *, int , ...); +struct cache_node *node(char const *, int , char const *, ...); #endif /* TEST_CACHE_COMMON_H_ */ diff --git a/test/cache/local_cache_test.c b/test/cache/local_cache_test.c index 82885f63..5b99e27a 100644 --- a/test/cache/local_cache_test.c +++ b/test/cache/local_cache_test.c @@ -6,7 +6,7 @@ #include //#include #include -// + #include "alloc.c" //#include "json_util.c" #include "mock.c" @@ -15,7 +15,7 @@ #include "cache/local_cache.c" #include "data_structure/path_builder.c" #include "types/str.c" -//#include "types/map.c" +#include "types/url.c" /* Mocks */ @@ -127,6 +127,8 @@ setup_test(void) static int okay(struct cache_node *node, void *arg) { + // XXX ensure the rsync and RRDP codes do this + node->flags |= CNF_VALID; return 0; } @@ -136,8 +138,8 @@ run_dl_rsync(char const *caRepository, char const *rpkiManifest, { static struct sia_uris sias; - sias_init(&sias); - strlist_add(&sias.caRepository, pstrdup(caRepository)); + sias.caRepository = pstrdup(caRepository); + sias.rpkiNotify = NULL; sias.rpkiManifest = pstrdup(rpkiManifest); rsync_counter = 0; @@ -162,13 +164,17 @@ find_downloaded_path(struct cache_node *node) { struct downloaded_path *path; - SLIST_FOREACH(path, &downloaded, hook) + if (!node->tmpdir) + return NULL; + + SLIST_FOREACH(path, &downloaded, hook) { if (strcmp(node->tmpdir, path->path) == 0) { if (path->visited) ck_abort_msg("Looked up twice: %s", path->path); path->visited = true; return path; } + } return NULL; } @@ -179,14 +185,14 @@ check_path(struct cache_node *node, char const *_) struct downloaded_path *path; path = find_downloaded_path(node); - if (node->flags & CNF_CACHED) { + if (node->tmpdir) { 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); + path->path); } return true; @@ -259,7 +265,7 @@ ck_assert_cachent_eq(struct cache_node *expected, struct cache_node *actual) } static void -validate_cache(struct cache_node *rsync, struct cache_node *https) +ck_cache(struct cache_node *rsync, struct cache_node *https) { struct downloaded_path *path; @@ -288,6 +294,9 @@ validate_cache(struct cache_node *rsync, struct cache_node *https) /* Compare expected and actual */ ck_assert_cachent_eq(rsync, cache.rsync); ck_assert_cachent_eq(https, cache.https); + + cachent_delete(rsync); + cachent_delete(https); } //static void @@ -329,44 +338,83 @@ cleanup_test(void) START_TEST(test_cache_download_rsync) { - int flags = CNF_CACHED | CNF_FRESH | CNF_VALID; + static const int SUCCESS = CNF_RSYNC | CNF_CACHED | CNF_FRESH | CNF_VALID; setup_test(); 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)), + ck_cache( + node("rsync:", 0, NULL, + node("rsync://a.b.c", 0, NULL, + node("rsync://a.b.c/d", SUCCESS, "tmp/tmp/0", + node("rsync://a.b.c/d/mft", RSYNC_INHERIT, NULL, NULL), + NULL), + NULL), + NULL), + node("https:", 0, NULL, NULL)); + + /* Redownload same file, nothing should happen */ + run_dl_rsync("rsync://a.b.c/d", "rsync://a.b.c/d/mft", 0, 0); + ck_cache( + node("rsync:", 0, NULL, + node("rsync://a.b.c", 0, NULL, + node("rsync://a.b.c/d", SUCCESS, "tmp/tmp/0", + node("rsync://a.b.c/d/mft", RSYNC_INHERIT, NULL, NULL), + NULL), + NULL), + NULL), + node("https:", 0, NULL, 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); + ck_cache( + node("rsync:", 0, NULL, + node("rsync://a.b.c", 0, NULL, + node("rsync://a.b.c/d", SUCCESS, "tmp/tmp/0", + node("rsync://a.b.c/d/e", RSYNC_INHERIT, NULL, + node("rsync://a.b.c/d/e/mft", RSYNC_INHERIT, NULL, NULL), + NULL), + node("rsync://a.b.c/d/mft", RSYNC_INHERIT, NULL, NULL), + NULL), + NULL), + NULL), + node("https:", 0, NULL, 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); + ck_cache( + node("rsync:", 0, NULL, + node("rsync://a.b.c", 0, NULL, + node("rsync://a.b.c/d", SUCCESS, "tmp/tmp/0", + node("rsync://a.b.c/d/e", RSYNC_INHERIT, NULL, + node("rsync://a.b.c/d/e/mft", RSYNC_INHERIT, NULL, NULL), + NULL), + node("rsync://a.b.c/d/mft", RSYNC_INHERIT, NULL, NULL), + NULL), + NULL), + node("rsync://x.y.z", 0, NULL, + node("rsync://x.y.z/m", SUCCESS, "tmp/tmp/1", + node("rsync://x.y.z/m/n", RSYNC_INHERIT, NULL, + node("rsync://x.y.z/m/n/o", RSYNC_INHERIT, NULL, + node("rsync://x.y.z/m/n/o/mft", RSYNC_INHERIT, NULL, NULL), + NULL), + NULL), + NULL), + NULL), NULL), - node("https:", 0, NULL)); + node("https:", 0, NULL, 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, +// ck_cache( // 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), @@ -384,7 +432,7 @@ END_TEST // 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, +// ck_cache( // NODE("rsync://a.b.c/d/", 0, 1, true), // NODE("rsync://a.b.c/e/", -EINVAL, 0, false), // NULL); @@ -392,14 +440,14 @@ END_TEST // /* 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, +// ck_cache( // 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, +// ck_cache( // NODE("rsync://a.b.c/d/", 0, 1, true), // NODE("rsync://a.b.c/e/", -EINVAL, 0, false), // NULL); @@ -420,7 +468,7 @@ END_TEST // 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, +// ck_cache( // NODE("rsync://a.b.c/d/", 0, 1, true), // NODE("rsync://a.b.c/e/", 0, 1, true), // NULL); @@ -430,7 +478,7 @@ END_TEST // 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, +// ck_cache( // NODE("rsync://a.b.c/d/", 0, 1, true), // NODE("rsync://a.b.c/e/", 0, 1, true), // NULL); @@ -441,7 +489,7 @@ END_TEST // 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, +// ck_cache( // 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), @@ -450,7 +498,7 @@ END_TEST // /* Nodes don't get updated, but they're still too young. */ // new_iteration(false); // cache_cleanup(cache); -// validate_cache(0, +// ck_cache( // 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), @@ -460,42 +508,42 @@ END_TEST // 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); +// ck_cache(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); +// ck_cache(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); +// ck_cache(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); +// ck_cache(NODE("rsync://a.b.c/e/", 0, 1, true), NULL); // // /* Empty the tree */ // new_iteration(true); // cache_cleanup(cache); -// validate_cache(0, NULL); +// ck_cache(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, +// ck_cache( // 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); +// ck_cache(NODE("rsync://a.b.c/e/", 0, 1, true), NULL); // // cleanup_test(); //} @@ -510,14 +558,14 @@ END_TEST // 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, +// 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 */ // cache_cleanup(cache); -// validate_cache(0, NODE("rsync://a.b.c/d/", 0, 1, true), NULL); +// 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 @@ -527,12 +575,12 @@ END_TEST // 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); +// ck_cache(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); +// ck_cache(NULL); // // cleanup_test(); //} @@ -544,18 +592,18 @@ END_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); +// 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); -// validate_cache(0, +// 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); -// validate_cache(0, +// 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), @@ -573,7 +621,7 @@ END_TEST // 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, +// ck_cache( // NODE("https://a.b.c/d", 0, 1, 1), // NODE("https://a.b.c/e", -EINVAL, 0, 0), // NULL); @@ -583,7 +631,7 @@ END_TEST // 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, +// ck_cache( // NODE("https://a.b.c/d", 0, 1, 1), // NODE("https://a.b.c/e", -EINVAL, 0, 0), // NULL); @@ -601,7 +649,7 @@ END_TEST // 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, +// ck_cache( // NODE("https://a.b.c/d", 0, 1, 1), // NODE("https://a.b.c/e", 0, 1, 1), // NULL); @@ -610,19 +658,19 @@ END_TEST // 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); +// 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); // cache_cleanup(cache); -// validate_cache(0, NODE("https://a.b.c/e", 0, 1, 1), NULL); +// 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); // cache_cleanup(cache); -// validate_cache(0, +// ck_cache( // NODE("https://a.b.c/e/f/g", 0, 1, 1), NULL); // // /* @@ -632,30 +680,30 @@ END_TEST // 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); +// 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); // cache_cleanup(cache); -// validate_cache(0, NODE("https://a.b.c/e", 0, 1, 1), NULL); +// ck_cache(NODE("https://a.b.c/e", 0, 1, 1), NULL); // // /* Empty the tree */ // new_iteration(true); // cache_cleanup(cache); -// validate_cache(0, NULL); +// 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); -// validate_cache(0, +// 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")); // cache_cleanup(cache); -// validate_cache(0, NODE("https://a.b.c/e", 0, 1, 1), NULL); +// ck_cache(NODE("https://a.b.c/e", 0, 1, 1), NULL); // // cleanup_test(); //} @@ -670,14 +718,14 @@ END_TEST // 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, +// 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. */ // cache_cleanup(cache); -// validate_cache(0, +// ck_cache( // NODE("https://a.b.c/d", 0, 1, 1), // NULL); // @@ -685,17 +733,17 @@ END_TEST // 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); +// ck_cache(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); +// ck_cache(NODE("https://a.b.c/d", -EINVAL, 1, 1), NULL); // // /* Become old */ // new_iteration(true); // cache_cleanup(cache); -// validate_cache(0, NULL); +// ck_cache(NULL); // // cleanup_test(); //} @@ -706,16 +754,16 @@ 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); +// ck_cache(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); +// ck_cache(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); +// ck_cache(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, +// ck_cache( // NODE("https://a.b.c/d", 0, 1, 1), // NODE("https://a.b.c/./d/../e", 0, 1, 1), // NULL); @@ -760,7 +808,7 @@ END_TEST // load_tal_json(cache); // ck_assert_ptr_ne(NULL, cache->ht); // -// validate_cache(0, +// ck_cache( // 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), diff --git a/test/types/url_test.c b/test/types/url_test.c new file mode 100644 index 00000000..0497cb87 --- /dev/null +++ b/test/types/url_test.c @@ -0,0 +1,64 @@ +#include +#include + +#include "alloc.c" +#include "mock.c" +#include "data_structure/path_builder.c" +#include "types/url.c" + +#define TEST_NORMALIZE(dirty, clean) \ + normal = url_normalize(dirty); \ + ck_assert_str_eq(clean, normal); \ + free(normal) + +START_TEST(test_normalize) +{ + char *normal; + + TEST_NORMALIZE("rsync://a.b.c", "rsync://a.b.c"); + TEST_NORMALIZE("rsync://a.b.c/", "rsync://a.b.c"); + TEST_NORMALIZE("rsync://a.b.c//////", "rsync://a.b.c"); + TEST_NORMALIZE("rsync://a.b.c/d/e", "rsync://a.b.c/d/e"); + TEST_NORMALIZE("rsync://a.b.c/d/e/.", "rsync://a.b.c/d/e"); + TEST_NORMALIZE("rsync://a.b.c/d/e/.", "rsync://a.b.c/d/e"); + TEST_NORMALIZE("rsync://a.b.c/d/./e/.", "rsync://a.b.c/d/e"); + TEST_NORMALIZE("rsync://a.b.c/d/../d/../d/e/", "rsync://a.b.c/d/e"); + TEST_NORMALIZE("rsync://a.b.c/../x/y/z", "rsync://x/y/z"); + TEST_NORMALIZE("rsync://x//y/z/../../../m/./n/o", "rsync://m/n/o"); + ck_assert_ptr_eq(NULL, url_normalize("rsync://")); + ck_assert_ptr_eq(NULL, url_normalize("rsync://..")); + ck_assert_ptr_eq(NULL, url_normalize("rsync://a.b.c/..")); + ck_assert_ptr_eq(NULL, url_normalize("rsync://a.b.c/d/e/../../..")); + ck_assert_ptr_eq(NULL, url_normalize("abcde://a.b.c/d")); +} +END_TEST + +static Suite *thread_pool_suite(void) +{ + Suite *suite; + TCase *normalize; + + normalize = tcase_create("normalize"); + tcase_add_test(normalize, test_normalize); + + suite = suite_create("url"); + suite_add_tcase(suite, normalize); + + return suite; +} + +int main(void) +{ + Suite *suite; + SRunner *runner; + int tests_failed; + + suite = thread_pool_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; +}