diff --git a/easystorage.c b/easystorage.c index b99931b..706582e 100644 --- a/easystorage.c +++ b/easystorage.c @@ -9,7 +9,7 @@ #include #include -#define MAX_RETRIES 250 +#define MAX_RETRIES 1000 #define POLL_INTERVAL_US (100 * 1000) #define DEFAULT_CHUNK_SIZE (64 * 1024) @@ -45,10 +45,12 @@ static void resp_destroy(resp *r) { free(r); } -static void resp_wait(resp *r) { - for (int i = 0; i < MAX_RETRIES && r->ret == -1; i++) { +static bool resp_wait(resp *r) { + int i; + for (i = 0; i < MAX_RETRIES && r->ret == -1; i++) { usleep(POLL_INTERVAL_US); } + return i == MAX_RETRIES; } // Callback for simple (non-progress) async operations. @@ -119,13 +121,18 @@ static void on_progress(int ret, const char *msg, size_t len, void *userData) { // Returns RET_OK/RET_ERR. If dispatch_ret != RET_OK, returns RET_ERR immediately. // If **out is non-NULL, allocates a buffer and copies the content of // resp->msg onto it, which the caller must then free. +// Manages deallocation of resp such that it only gets deallocated after both +// this call returns AND the callback has run. Will leak memory if the call +// succeeds but the callback then fails to run. static int call_wait(int dispatch_ret, resp *r, char **out) { if (dispatch_ret != RET_OK) { resp_destroy(r); return RET_ERR; } - resp_wait(r); + if (resp_wait(r)) { + fprintf(stderr, "CRITICAL: Call timed out!"); + } pthread_mutex_lock(&mutex); int result = (r->ret == RET_OK) ? RET_OK : RET_ERR; @@ -294,6 +301,18 @@ int e_storage_download(STORAGE_NODE node, const char *cid, const char *filepath, return ret; } +int e_storage_delete(STORAGE_NODE node, const char *cid) { + if (!node || !cid) return RET_ERR; + + resp *r = resp_alloc(); + int ret = call_wait(storage_delete(node, cid, on_complete, r), r, NULL); + if (ret != RET_OK) { + return RET_ERR; + } + + return ret; +} + static int handler(void *user, const char *section, const char *name, const char *value) { node_config *cfg = (node_config *) user; #define MATCH(n) strcmp(section, "easystorage") == 0 && strcmp(name, n) == 0 diff --git a/easystorage.h b/easystorage.h index 36a8c22..3bf5ce0 100644 --- a/easystorage.h +++ b/easystorage.h @@ -4,6 +4,8 @@ #include #define STORAGE_NODE void * +#define RET_OK 0 +#define RET_ERR 1 typedef struct { int api_port; @@ -34,6 +36,9 @@ char *e_storage_upload(STORAGE_NODE node, const char *filepath, progress_callbac // Downloads content identified by cid to filepath. Returns 0 on success. int e_storage_download(STORAGE_NODE node, const char *cid, const char *filepath, progress_callback cb); +// Deletes a previously uploaded file from the node. +int e_storage_delete(STORAGE_NODE node, const char *cid); + // Config handling utilities. Note that for e_storage_read_config and e_storage_read_config, the // caller is responsible for freeing the config object and its members. int e_storage_read_config(char *filepath, node_config *config); diff --git a/examples/downloader.c b/examples/downloader.c index 5107154..6701b95 100644 --- a/examples/downloader.c +++ b/examples/downloader.c @@ -4,6 +4,11 @@ #include #include "easystorage.h" +void panic(const char *msg) { + fprintf(stderr, "Panic: %s\n", msg); + exit(1); +} + void progress(int total, int complete, int status) { printf("\r %d / %d bytes", complete, total); fflush(stdout); @@ -29,8 +34,8 @@ int main(int argc, char *argv[]) { }; STORAGE_NODE node = e_storage_new(cfg); - e_storage_start(node); - e_storage_download(node, cid, filepath, progress); + if (e_storage_start(node) != RET_OK) panic("Failed to start storage node"); + if (e_storage_download(node, cid, filepath, progress) != RET_OK) panic("Failed to download file"); e_storage_stop(node); e_storage_destroy(node); } diff --git a/examples/uploader.c b/examples/uploader.c index 8b8eb1c..a41be62 100644 --- a/examples/uploader.c +++ b/examples/uploader.c @@ -4,6 +4,11 @@ #include #include "easystorage.h" +void panic(const char *msg) { + fprintf(stderr, "Panic: %s\n", msg); + exit(1); +} + void progress(int total, int complete, int status) { printf("\r %d / %d bytes", complete, total); fflush(stdout); @@ -27,18 +32,25 @@ int main(int argc, char *argv[]) { char *filepath = argv[1]; STORAGE_NODE node = e_storage_new(cfg); - e_storage_start(node); + if (node == NULL) panic("Failed to create node"); + if (e_storage_start(node) != RET_OK) panic("Failed to start storage node"); char *cid = e_storage_upload(node, filepath, progress); + if (cid == NULL) panic("Failed to upload file to node"); char *spr = e_storage_spr(node); + if (spr == NULL) panic("Failed to obtain node's Signed Peer Record (SPR)"); printf("Run: downloader %s %s ./output-file\n", spr, cid); - free(cid); - free(spr); - printf("\nPress Enter to exit\n"); getchar(); + printf("Deleting file (this could take a while)..."); + fflush(stdout); + if (e_storage_delete(node, cid) != RET_OK) panic("Failed to delete file"); + printf("Done\n"); + + free(cid); + free(spr); e_storage_stop(node); e_storage_destroy(node); diff --git a/tests/mock_libstorage.c b/tests/mock_libstorage.c index ce1cec2..713b823 100644 --- a/tests/mock_libstorage.c +++ b/tests/mock_libstorage.c @@ -3,8 +3,11 @@ #include #include +#define FAKE_CID "zDvZRwzmAbCdEfGhIjKlMnOpQrStUvWxYz0123456789ABCD" + // A fake context to return from storage_new. static int fake_ctx_data = 42; +bool exists = false; void libstorageNimMain(void) { // no-op @@ -70,12 +73,29 @@ int storage_upload_file(void *ctx, const char *sessionId, StorageCallback callba // Fire a progress callback first, then final OK with CID if (callback) { callback(RET_PROGRESS, "chunk", 5, userData); - const char *cid = "zDvZRwzmAbCdEfGhIjKlMnOpQrStUvWxYz0123456789ABCD"; + const char *cid = FAKE_CID; callback(RET_OK, cid, strlen(cid), userData); + exists = true; } return RET_OK; } +int storage_delete(void *ctx, const char *cid, StorageCallback callback, void *userData) { + if (!ctx) + return RET_ERR; + + if (callback) { + if (strcmp(cid, FAKE_CID) == 0 && exists) { + callback(RET_OK, "", 0, userData); + exists = false; + } else { + callback(RET_ERR, "Failed", 6, userData); + } + } + + return RET_OK; +} + int storage_download_init(void *ctx, const char *cid, size_t chunkSize, bool local, StorageCallback callback, void *userData) { if (!ctx) diff --git a/tests/test_runner.c b/tests/test_runner.c index 3753f24..9993d40 100644 --- a/tests/test_runner.c +++ b/tests/test_runner.c @@ -122,6 +122,22 @@ static void test_download_null(void) { assert(ret == RET_ERR); } +static void test_should_delete_file(void) { + node_config cfg = default_config(); + STORAGE_NODE node = e_storage_new(cfg); + assert(node != NULL); + e_storage_start(node); + + char *cid = e_storage_upload(node, "/tmp/test.txt", NULL); + assert(cid != NULL); + assert(strlen(cid) > 0); + + assert(e_storage_delete(node, cid) == RET_OK); + // Non-existing files can't be deleted. + assert(e_storage_delete(node, cid) == RET_ERR); + free(cid); +} + static void test_get_should_get_node_spr(void) { node_config cfg = default_config(); STORAGE_NODE node = e_storage_new(cfg); @@ -193,6 +209,7 @@ int main(void) { RUN_TEST(test_upload_null); RUN_TEST(test_download); RUN_TEST(test_download_null); + RUN_TEST(test_should_delete_file); RUN_TEST(test_get_should_get_node_spr); RUN_TEST(test_full_lifecycle); RUN_TEST(test_should_read_configuration_file);