feat: add delete; update examples

This commit is contained in:
gmega 2026-02-03 16:29:29 -03:00
parent 3baf2d65d3
commit 7513c2945b
No known key found for this signature in database
GPG Key ID: 6290D34EAD824B18
6 changed files with 89 additions and 11 deletions

View File

@ -9,7 +9,7 @@
#include <string.h>
#include <unistd.h>
#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

View File

@ -4,6 +4,8 @@
#include <stdio.h>
#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);

View File

@ -4,6 +4,11 @@
#include <stdlib.h>
#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);
}

View File

@ -4,6 +4,11 @@
#include <stdlib.h>
#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);

View File

@ -3,8 +3,11 @@
#include <stdlib.h>
#include <string.h>
#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)

View File

@ -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);