/* Drives the download state machine without a network: feeds blocks straight * from the original file (as a perfect seed would) and checks the reassembled, * hash-verified output on disk is byte-identical. Also checks a corrupt block * is rejected at piece completion. */ #include "naut/piece.h" #include "naut/metainfo.h" #include "naut/storage.h" #include "naut/worker.h" #include "test.h" #include #include #include #include #include #ifndef NAUT_FIXTURES #define NAUT_FIXTURES "tests/fixtures" #endif static uint8_t *slurp(const char *path, size_t *len) { FILE *f = fopen(path, "rb"); if (!f) { fprintf(stderr, " open %s failed\n", path); return NULL; } fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET); uint8_t *b = malloc(n); if (fread(b, 1, n, f) != (size_t)n) { fclose(f); free(b); return NULL; } fclose(f); *len = (size_t)n; return b; } int main(void) { /* load torrent + its original payload */ size_t tlen, olen; uint8_t *tor = slurp(NAUT_FIXTURES "/single_v1.torrent", &tlen); uint8_t *orig = slurp(NAUT_FIXTURES "/data/single.bin", &olen); CHECK(tor && orig); if (!tor || !orig) return 1; naut_metainfo mi; CHECK(naut_metainfo_parse(tor, tlen, &mi) == NAUT_OK); CHECK_EQ(mi.total_length, (int64_t)olen); char tmpl[] = "/tmp/naut_dl_XXXXXX"; char *root = mkdtemp(tmpl); naut_err err; naut_storage *st = naut_storage_open(mi.files, mi.num_files, root, &err); CHECK(st && err == NAUT_OK); naut_download *d = naut_download_create(&mi, st); CHECK(d != NULL); /* feed every requested block straight from the original */ uint32_t idx, begin, len; int completed = 0; while (naut_download_next_request(d, &idx, &begin, &len)) { uint64_t global = (uint64_t)idx * (uint64_t)mi.piece_length + begin; bool done = false; CHECK(naut_download_on_block(d, idx, begin, orig + global, len, &done) == NAUT_OK); if (done) completed++; } CHECK(naut_download_complete(d)); CHECK_EQ(completed, (int)naut_download_num_pieces(d)); CHECK_EQ((long long)naut_download_bytes_done(d), (long long)olen); naut_download_destroy(d); /* on-disk result must be byte-identical to the original */ naut_storage_sync(st); naut_storage_close(st); char outpath[512]; snprintf(outpath, sizeof outpath, "%s/%s", root, mi.files[0].path); size_t glen; uint8_t *got = slurp(outpath, &glen); CHECK(got && glen == olen && memcmp(got, orig, olen) == 0); free(got); /* The same path with SHA-1 verification offloaded to bounded workers. */ { char t2[] = "/tmp/naut_async_XXXXXX"; char *r2 = mkdtemp(t2); naut_storage *st2 = naut_storage_open(mi.files, mi.num_files, r2, &err); naut_download *d2 = naut_download_create(&mi, st2); naut_worker_pool *workers = naut_worker_pool_create(2, 64, -1); CHECK(st2 && d2 && workers); naut_download_set_worker_pool(d2, workers); while (naut_download_next_request(d2, &idx, &begin, &len)) { uint64_t global = (uint64_t)idx * (uint64_t)mi.piece_length + begin; bool done = false; CHECK(naut_download_on_block( d2, idx, begin, orig + global, len, &done) == NAUT_OK); CHECK(!done); } uint32_t async_completed = 0; while (!naut_download_complete(d2)) { struct pollfd pfd = { .fd = naut_worker_eventfd(workers), .events = POLLIN, }; CHECK(poll(&pfd, 1, 5000) > 0); uint64_t count; (void)read(pfd.fd, &count, sizeof count); uint32_t batch = 0; CHECK(naut_download_poll(d2, &batch) == NAUT_OK); async_completed += batch; } CHECK_EQ(async_completed, (int)mi.num_pieces); naut_worker_pool_destroy(workers); naut_download_destroy(d2); naut_storage_close(st2); char cmd[256]; snprintf(cmd, sizeof cmd, "rm -rf '%s'", r2); if (system(cmd)) {} } /* corrupt-block rejection: a bad first block fails SHA-1 at completion */ { char t2[] = "/tmp/naut_dl2_XXXXXX"; char *r2 = mkdtemp(t2); naut_storage *st2 = naut_storage_open(mi.files, mi.num_files, r2, &err); naut_download *d2 = naut_download_create(&mi, st2); CHECK(naut_download_next_request(d2, &idx, &begin, &len)); /* piece 0, block 0 */ uint8_t bad[NAUT_BLOCK]; memcpy(bad, orig, len); bad[0] ^= 0xff; /* flip a bit */ bool done = false; CHECK(naut_download_on_block(d2, idx, begin, bad, len, &done) == NAUT_ERR_PROTO); CHECK(!done); naut_download_destroy(d2); naut_storage_close(st2); char cmd[256]; snprintf(cmd, sizeof cmd, "rm -rf '%s'", r2); if (system(cmd)) {} } char cmd[256]; snprintf(cmd, sizeof cmd, "rm -rf '%s'", root); if (system(cmd)) {} naut_metainfo_free(&mi); free(tor); free(orig); TEST_MAIN_END(); }