From 0f131937a8b07f3b44450e5118b3b036d25b35a7 Mon Sep 17 00:00:00 2001 From: gmega Date: Fri, 30 Jan 2026 17:33:19 -0300 Subject: [PATCH] feat: add e_storage_spr, fix memory management for resp --- README.md | 2 +- easystorage.c | 80 ++++++++++++++++++++++++++++++----------- easystorage.h | 3 ++ tests/mock_libstorage.c | 40 ++++++++++++++++----- tests/test_runner.c | 14 ++++++++ 5 files changed, 110 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index b0f18d5..23e91e3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# easystorage +# libeasystorage A simplified, higher level C wrapper around [libstorage](https://github.com/status-im/logos-storage-nim) built to showcase it. Allows filesharing in the Logos Storage network. Comes with an example console application to interact with it. diff --git a/easystorage.c b/easystorage.c index edab05e..e81b0de 100644 --- a/easystorage.c +++ b/easystorage.c @@ -1,6 +1,7 @@ #include "easystorage.h" #include "libstorage.h" +#include #include #include #include @@ -17,8 +18,11 @@ typedef struct { size_t len; progress_callback pcb; int bytes_done; + bool unreferenced; } resp; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + static resp *resp_alloc(void) { resp *r = calloc(1, sizeof(resp)); r->ret = -1; @@ -27,7 +31,7 @@ static resp *resp_alloc(void) { static void resp_destroy(resp *r) { if (!r) return; - free(r->msg); + if (r->msg) free(r->msg); free(r); } @@ -42,9 +46,12 @@ static void on_complete(int ret, const char *msg, size_t len, void *userData) { resp *r = userData; if (!r) return; - free(r->msg); - r->msg = NULL; - r->len = 0; + pthread_mutex_lock(&mutex); + if (r->unreferenced) { + resp_destroy(r); + pthread_mutex_unlock(&mutex); + return; + } if (msg && len > 0) { r->msg = malloc(len + 1); @@ -56,6 +63,8 @@ static void on_complete(int ret, const char *msg, size_t len, void *userData) { } r->ret = ret; + r->unreferenced = true; + pthread_mutex_unlock(&mutex); } // Callback for operations that report progress before completing. @@ -63,19 +72,23 @@ static void on_progress(int ret, const char *msg, size_t len, void *userData) { resp *r = userData; if (!r) return; + pthread_mutex_lock(&mutex); + + if (r->unreferenced) { + resp_destroy(r); + pthread_mutex_unlock(&mutex); + return; + } + if (ret == RET_PROGRESS) { r->bytes_done += (int) len; if (r->pcb) { r->pcb(0, r->bytes_done, ret); } + pthread_mutex_unlock(&mutex); return; // don't set r->ret yet — still in progress } - // Final callback (RET_OK or RET_ERR) - free(r->msg); - r->msg = NULL; - r->len = 0; - if (msg && len > 0) { r->msg = malloc(len + 1); if (r->msg) { @@ -86,11 +99,15 @@ static void on_progress(int ret, const char *msg, size_t len, void *userData) { } r->ret = ret; + r->unreferenced = true; + + pthread_mutex_unlock(&mutex); } // Dispatches an async call, waits for completion, extracts the result. // Returns RET_OK/RET_ERR. If dispatch_ret != RET_OK, returns RET_ERR immediately. -// If out is non-NULL and the response has a message, *out receives a strdup'd copy. +// If **out is non-NULL, allocates a buffer and copies the content of +// resp->msg onto it, which the caller must then free. static int call_wait(int dispatch_ret, resp *r, char **out) { if (dispatch_ret != RET_OK) { resp_destroy(r); @@ -99,13 +116,19 @@ static int call_wait(int dispatch_ret, resp *r, char **out) { resp_wait(r); + pthread_mutex_lock(&mutex); int result = (r->ret == RET_OK) ? RET_OK : RET_ERR; if (out) { *out = r->msg ? strdup(r->msg) : NULL; } - resp_destroy(r); + if (r->unreferenced) { + resp_destroy(r); + } else { + r->unreferenced = true; + } + pthread_mutex_unlock(&mutex); return result; } @@ -160,19 +183,22 @@ STORAGE_NODE e_storage_new(node_config config) { } int e_storage_start(STORAGE_NODE node) { - if (!node) return RET_ERR; + if (!node) + return RET_ERR; resp *r = resp_alloc(); return call_wait(storage_start(node, (StorageCallback) on_complete, r), r, NULL); } int e_storage_stop(STORAGE_NODE node) { - if (!node) return RET_ERR; + if (!node) + return RET_ERR; resp *r = resp_alloc(); return call_wait(storage_stop(node, (StorageCallback) on_complete, r), r, NULL); } int e_storage_destroy(STORAGE_NODE node) { - if (!node) return RET_ERR; + if (!node) + return RET_ERR; // Close first (tolerate failure) resp *r = resp_alloc(); @@ -183,8 +209,21 @@ int e_storage_destroy(STORAGE_NODE node) { return call_wait(storage_destroy(node, (StorageCallback) on_complete, r), r, NULL); } +char *e_storage_spr(STORAGE_NODE node) { + if (!node) + return NULL; + resp *r = resp_alloc(); + char *spr = NULL; + int ret = call_wait(storage_spr(node, (StorageCallback) on_complete, r), r, &spr); + if (ret != RET_OK) { + return NULL; + } + return spr; +} + char *e_storage_upload(STORAGE_NODE node, const char *filepath, progress_callback cb) { - if (!node || !filepath) return NULL; + if (!node || !filepath) + return NULL; // Init upload session resp *r = resp_alloc(); @@ -216,14 +255,15 @@ char *e_storage_upload(STORAGE_NODE node, const char *filepath, progress_callbac } int e_storage_download(STORAGE_NODE node, const char *cid, const char *filepath, progress_callback cb) { - if (!node || !cid || !filepath) return RET_ERR; + if (!node || !cid || !filepath) + return RET_ERR; // Init download resp *r = resp_alloc(); - int ret = - call_wait(storage_download_init(node, cid, DEFAULT_CHUNK_SIZE, false, (StorageCallback) on_complete, r), r, - NULL); - if (ret != RET_OK) return RET_ERR; + int ret = call_wait(storage_download_init(node, cid, DEFAULT_CHUNK_SIZE, false, (StorageCallback) on_complete, r), + r, NULL); + if (ret != RET_OK) + return RET_ERR; // Stream to file with progress r = resp_alloc(); diff --git a/easystorage.h b/easystorage.h index d84a840..3e12889 100644 --- a/easystorage.h +++ b/easystorage.h @@ -20,6 +20,9 @@ int e_storage_start(STORAGE_NODE node); int e_storage_stop(STORAGE_NODE node); int e_storage_destroy(STORAGE_NODE node); +// Retrieves the node's SPR (caller must free), or NULL on failure. +char *e_storage_spr(STORAGE_NODE node); + // Uploads a file. Returns CID string on success (caller must free), or NULL on failure. char *e_storage_upload(STORAGE_NODE node, const char *filepath, progress_callback cb); diff --git a/tests/mock_libstorage.c b/tests/mock_libstorage.c index 941e473..ce1cec2 100644 --- a/tests/mock_libstorage.c +++ b/tests/mock_libstorage.c @@ -18,7 +18,8 @@ void *storage_new(const char *configJson, StorageCallback callback, void *userDa } int storage_start(void *ctx, StorageCallback callback, void *userData) { - if (!ctx) return RET_ERR; + if (!ctx) + return RET_ERR; if (callback) { callback(RET_OK, "started", 7, userData); } @@ -26,7 +27,8 @@ int storage_start(void *ctx, StorageCallback callback, void *userData) { } int storage_stop(void *ctx, StorageCallback callback, void *userData) { - if (!ctx) return RET_ERR; + if (!ctx) + return RET_ERR; if (callback) { callback(RET_OK, "stopped", 7, userData); } @@ -34,7 +36,8 @@ int storage_stop(void *ctx, StorageCallback callback, void *userData) { } int storage_close(void *ctx, StorageCallback callback, void *userData) { - if (!ctx) return RET_ERR; + if (!ctx) + return RET_ERR; if (callback) { callback(RET_OK, "closed", 6, userData); } @@ -42,7 +45,8 @@ int storage_close(void *ctx, StorageCallback callback, void *userData) { } int storage_destroy(void *ctx, StorageCallback callback, void *userData) { - if (!ctx) return RET_ERR; + if (!ctx) + return RET_ERR; if (callback) { callback(RET_OK, "destroyed", 9, userData); } @@ -50,7 +54,8 @@ int storage_destroy(void *ctx, StorageCallback callback, void *userData) { } int storage_upload_init(void *ctx, const char *filepath, size_t chunkSize, StorageCallback callback, void *userData) { - if (!ctx) return RET_ERR; + if (!ctx) + return RET_ERR; // Return a fake session ID const char *session_id = "mock-session-123"; if (callback) { @@ -60,7 +65,8 @@ int storage_upload_init(void *ctx, const char *filepath, size_t chunkSize, Stora } int storage_upload_file(void *ctx, const char *sessionId, StorageCallback callback, void *userData) { - if (!ctx) return RET_ERR; + if (!ctx) + return RET_ERR; // Fire a progress callback first, then final OK with CID if (callback) { callback(RET_PROGRESS, "chunk", 5, userData); @@ -72,7 +78,8 @@ int storage_upload_file(void *ctx, const char *sessionId, StorageCallback callba int storage_download_init(void *ctx, const char *cid, size_t chunkSize, bool local, StorageCallback callback, void *userData) { - if (!ctx) return RET_ERR; + if (!ctx) + return RET_ERR; if (callback) { callback(RET_OK, "init", 4, userData); } @@ -81,10 +88,27 @@ int storage_download_init(void *ctx, const char *cid, size_t chunkSize, bool loc int storage_download_stream(void *ctx, const char *cid, size_t chunkSize, bool local, const char *filepath, StorageCallback callback, void *userData) { - if (!ctx) return RET_ERR; + if (!ctx) + return RET_ERR; if (callback) { callback(RET_PROGRESS, "data", 4, userData); callback(RET_OK, "done", 4, userData); } return RET_OK; } + +int storage_spr(void *ctx, StorageCallback callback, void *userData) { + const char *resp = "spr:" + "CiUIAhIhAjWYLRhJho1LoZbaxILgJVTrHptSiejsvLKAqlumo4c4EgIDARpJCicAJQgCEiECNZgtGEmGjUuhltrEguAlVOs" + "em1KJ6Oy8soCqW6ajhzgQsdDzywYaCwoJBLtZShSRAh-" + "aGgsKCQS7WUoUkQIfmipHMEUCIQChjWYvEJE4bE55vtwyAuFvHoGqvpRouXasY7CaIg6nCQIgeCxTWoFmSCNEgMYinM7HuG" + "oCKfoRkcC1pAycNnk-z3A"; + if (!ctx) + return RET_ERR; + + if (callback) { + callback(RET_OK, resp, strlen(resp), userData); + } + + return RET_OK; +} diff --git a/tests/test_runner.c b/tests/test_runner.c index 3750a93..d3677e3 100644 --- a/tests/test_runner.c +++ b/tests/test_runner.c @@ -114,6 +114,19 @@ static void test_download_null(void) { assert(ret == RET_ERR); } +static void test_get_should_get_node_spr(void) { + node_config cfg = default_config(); + STORAGE_NODE node = e_storage_new(cfg); + assert(node != NULL); + assert(e_storage_start(node) == RET_OK); + + const char *sprprefix = "spr:CiUIAhIhA"; + const char *spr = e_storage_spr(node); + assert(spr != NULL); + assert(strlen(spr) > 0); + assert(strncmp(sprprefix, spr, strlen(sprprefix)) == 0); +} + static void test_full_lifecycle(void) { node_config cfg = default_config(); cfg.bootstrap_node = "spr:node1"; @@ -148,6 +161,7 @@ int main(void) { RUN_TEST(test_upload_null); RUN_TEST(test_download); RUN_TEST(test_download_null); + RUN_TEST(test_get_should_get_node_spr); RUN_TEST(test_full_lifecycle); printf("\n%d/%d tests passed.\n", tests_passed, tests_run);