Files
ToastyFS/src/chunk_store.c
T
Claude b0cc529773 Fix GET request returning corrupted data for multi-chunk blobs
Four bugs caused data mismatches between PUT and GET operations:

1. chunk_store_exists() used file_open (O_CREAT) to check if a chunk
   file exists, which created empty files as a side effect. Servers
   would then read from these empty files and return uninitialized
   heap memory as chunk data. Fixed by using file_exists (access())
   instead.

2. Server's process_fetch_chunk checked `ret < 0` after
   chunk_store_read, missing the EOF case (ret == 0) from empty
   files. Changed to `ret <= 0`.

3. begin_transfers() == 0 was used as a completion check in both
   STEP_FETCH_CHUNK and STEP_STORE_CHUNK, but returning 0 only means
   "no new transfers started", not "all done". With concurrent chunk
   transfers, this caused premature completion, leaving in-flight
   transfers to hit UNREACHABLE. Fixed by using
   all_chunk_transfers_completed() instead.

4. choose_store_locations_for_chunk() returned [0, 1] for ALL chunks
   regardless of index, while GET fetches chunk i from servers
   (i+j) % num_servers. This made some chunks unreachable. Fixed by
   using (chunk_idx + j) % num_servers for store locations.

Also added SO_REUSEADDR to the server listen socket so restarting
the cluster doesn't fail with "Couldn't setup TCP listener", and
added a note on mock_access about the simulation mismatch.

https://claude.ai/code/session_01JniNxxDP4NbsDXNGmGNG7H
2026-02-21 22:31:33 +00:00

102 lines
2.2 KiB
C

#if defined(MAIN_SIMULATION) || defined(MAIN_TEST)
#define QUAKEY_ENABLE_MOCKS
#endif
#include <quakey.h>
#include <stdio.h>
#include "chunk_store.h"
#include "file_system.h"
// Build the full path for a chunk: "base_path/HEX_HASH"
// SHA256 hex = 64 chars. Returns string pointing into buf.
static string chunk_path(ChunkStore *cs, SHA256 hash, char *buf, int buf_size)
{
int base_len = strlen(cs->base_path);
if (base_len + 1 + 64 + 1 > buf_size)
return (string){ buf, 0 };
memcpy(buf, cs->base_path, base_len);
buf[base_len] = '/';
append_hex_as_str(buf + base_len + 1, hash);
buf[base_len + 1 + 64] = '\0';
return (string){ buf, base_len + 1 + 64 };
}
int chunk_store_init(ChunkStore *cs, const char *base_path)
{
int len = strlen(base_path);
if (len >= (int) sizeof(cs->base_path))
return -1;
memcpy(cs->base_path, base_path, len + 1);
string path = { cs->base_path, len };
create_dir(path); // Ignore error if already exists
return 0;
}
void chunk_store_free(ChunkStore *cs)
{
(void) cs;
}
int chunk_store_write(ChunkStore *cs, SHA256 hash, char *data, uint32_t size)
{
char buf[512];
string path = chunk_path(cs, hash, buf, sizeof(buf));
if (path.len == 0)
return -1;
Handle fd;
if (file_open(path, &fd) < 0)
return -1;
if (file_truncate(fd, 0) < 0) {
file_close(fd);
return -1;
}
int ret = file_write_exact(fd, data, size);
file_close(fd);
return ret;
}
int chunk_store_read(ChunkStore *cs, SHA256 hash, char *dst, uint32_t size)
{
char buf[512];
string path = chunk_path(cs, hash, buf, sizeof(buf));
if (path.len == 0)
return -1;
Handle fd;
if (file_open(path, &fd) < 0)
return -1;
int ret = file_read_exact(fd, dst, size);
file_close(fd);
return ret;
}
bool chunk_store_exists(ChunkStore *cs, SHA256 hash)
{
char buf[512];
string path = chunk_path(cs, hash, buf, sizeof(buf));
if (path.len == 0)
return false;
return file_exists(path);
}
int chunk_store_delete(ChunkStore *cs, SHA256 hash)
{
char buf[512];
string path = chunk_path(cs, hash, buf, sizeof(buf));
if (path.len == 0)
return -1;
return remove_file_or_dir(path);
}