## Root Cause
The most likely failure is in the C test harness, not `storage_space`
itself.
The callback writes `r->ret` before it finishes writing `r->msg`, and
the polling thread treats `ret != -1` as “response is complete”. That
can make `is_resp_ok` return with `res == NULL`, after which
`check_space` calls `strstr(res, ...)` and segfaults.
The segfault is very likely a race in `tests/cbindings/storage.c`.
In `callback`:
```c
r->ret = ret;
...
r->msg = malloc(len + 1);
memcpy(r->msg, msg, len);
```
But `wait_resp` treats `r->ret != -1` as “the response is ready”:
```c
while (get_ret(r) == -1 && retries < MAX_RETRIES)
```
So this can happen:
1. Storage worker thread enters `callback`.
2. Callback sets `r->ret = RET_OK`.
3. Main C test thread sees `ret != -1` and exits `wait_resp`.
4. `is_resp_ok` observes `r->msg == NULL`, so `res` remains `NULL`.
5. `check_space` calls:
```c
strstr(res, "totalBlocks")
```
6. `strstr(NULL, ...)` segfaults.
It can also be worse: `is_resp_ok` may call `free_resp(r)` while the
callback is still writing into `r`, causing use-after-free.
## Why It Appears At `check_space`
This is probably timing. Earlier responses happened to finish copying
before the polling thread observed `ret`; `check_space` just hit the
race.
The new metrics PR did not reach `check_get_metrics`; the crash happens
before it. But since the PR now makes `make testLibstorage` run the C
test consistently, it exposes this existing test harness race.
## Minimal Fix
1. In `callback`, set `r->ret = ret` last, after `msg`, `chunk`, and
`len` are fully populated.
2. Add defensive `res == NULL` checks in tests like `check_space`,
similar to `check_get_metrics`.
## Longer-Term Fix
Ideally, replace polling shared fields with a mutex/condition variable
or atomics, because the current cross-thread access is technically a C
data race.
```
For point 3: the current fix improves ordering, but it still relies on unsynchronized reads/writes between threads. In C, one thread writing `r->ret`, `r->msg`, and `r->len` while another thread reads them without atomics or a lock is undefined behavior. Practically, it may work most of the time, but the compiler/CPU are not required to make those writes visible in the order we expect.
A proper fix would make `Resp` thread-safe. The cleanest approach is usually:
```c
typedef struct
{
pthread_mutex_t mutex;
pthread_cond_t cond;
bool done;
int ret;
char *msg;
char *chunk;
size_t len;
} Resp;
```
Then:
- `callback` locks the mutex.
- It writes `msg`, `chunk`, `len`, and `ret`.
- It sets `done = true`.
- It signals the condition variable.
- It unlocks.
And:
- `wait_resp` locks the mutex.
- It waits on the condition variable until `done == true` or timeout.
- It unlocks.
This removes the sleep-based polling and gives the test a real happens-before relationship between callback writes and test-thread reads.
Logos Storage Filesharing Client
The Logos Storage project aims to create a filesharing client that allows sharing data privately in p2p networks.
WARNING: This project is under active development and is considered pre-alpha.
Build and Run
To build the project, clone it and run:
make update && make
# Tip: use -j{ncpu} to for parallel execution, eg:
# make -j12 update && make -j12
The executable will be placed under the build directory under the project root.
Run the client with:
build/storage
Configuration
It is possible to configure a Logos Storage node in several ways:
- CLI options
- Environment variables
- Configuration file
The order of priority is the same as above: CLI options --> Environment variables --> Configuration file.
Please check build/storage --help for more information.
API
The client exposes a REST API that can be used to interact with the clients. Overview of the API can be found on api.codex.storage.
Bindings
Logos Storage provides a C API that can be wrapped by other languages. The C API bindings are located in the library folder.
Currently, only Go bindings are provided in this repo. However, Rust bindings for Logos Storage can be found at https://github.com/nipsysdev/storage-rust-bindings.
Build the C library
make libstorage
This produces the shared library under build/.
Run the Go example
See https://github.com/logos-storage/logos-storage-go-bindings-example.
Static vs Dynamic build
By default, Logos Storage builds a dynamic library (libstorage.so/libstorage.dylib/libstroage.dll), which you can load at runtime.
If you prefer a static library (libstorage.a), set the STATIC flag:
# Build dynamic (default)
make libstorage
# Build static
make STATIC=1 libstorage
Limitation
Callbacks must be fast and non-blocking; otherwise, the working thread will hang and prevent other requests from being processed.
Contributing and development
Feel free to dive in, contributions are welcomed! Open an issue or submit PRs.
Linting and formatting
logos-storage-nim uses nph for formatting our code and it is required to adhere to its styling.
If you are setting up fresh setup, in order to get nph run make build-nph.
In order to format files run make nph/<file/folder you want to format>.
If you want you can install Git pre-commit hook using make install-nph-commit, which will format modified files prior committing them.
If you are using VSCode and the NimLang extension you can enable "Format On Save" (eq. the nim.formatOnSave property) that will format the files using nph.