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
102 lines
2.2 KiB
C
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);
|
|
}
|