Major refactoring

This commit is contained in:
2026-02-25 10:44:43 +01:00
parent bac3477991
commit ef1d65ad2b
45 changed files with 5404 additions and 2120 deletions
+30 -38
View File
@@ -4,6 +4,7 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -26,8 +27,6 @@
#include <pthread.h>
#endif
#include <stdbool.h>
typedef struct {} Quakey;
// Function pointers to a simulated program's code
@@ -35,11 +34,6 @@ typedef int (*QuakeyInitFunc)(void *state, int argc, char **argv, void **ctxs, s
typedef int (*QuakeyTickFunc)(void *state, void **ctxs, struct pollfd *pdata, int pcap, int *pnum, int *timeout);
typedef int (*QuakeyFreeFunc)(void *state);
typedef enum {
QUAKEY_LINUX,
QUAKEY_WINDOWS,
} QuakeyPlatform;
typedef struct {
// Label associated to the process for debugging
@@ -61,9 +55,6 @@ typedef struct {
// Disk size for the process
int disk_size;
// Platform used by this program
QuakeyPlatform platform;
} QuakeySpawn;
typedef unsigned long long QuakeyUInt64;
@@ -84,9 +75,6 @@ void *quakey_node_state(QuakeyNode node);
// Schedule and executes one program until it would block, then returns
int quakey_schedule_one(Quakey *quakey);
// Get the current simulated time in nanoseconds
QuakeyUInt64 quakey_current_time(Quakey *quakey);
// Generate a random u64
QuakeyUInt64 quakey_random(void);
@@ -98,30 +86,40 @@ typedef struct {
void quakey_signal(char *name);
int quakey_get_signal(Quakey *quakey, QuakeySignal *signal);
// Limit the number of hosts that can be crashed at the same time.
// Set to 0 for no limit (default).
void quakey_set_max_crashes(Quakey *quakey, int max_crashes);
void quakey_network_partitioning(Quakey *quakey, bool enable);
// Access spawned host information
//
// TODO: The following are to be refactored as the host index is not stable
int quakey_num_hosts(Quakey *quakey);
void *quakey_host_state(Quakey *quakey, int idx); // Returns NULL if host is dead
int quakey_host_is_dead(Quakey *quakey, int idx);
const char *quakey_host_name(Quakey *quakey, int idx);
// Enter/leave a host's context so that mock_xxx functions (file I/O,
// etc.) operate on that host's resources. Use from code that runs
// outside of the normal init/tick/free callbacks (e.g. invariant
// checkers).
// Fault injection control
void quakey_set_max_crashes(Quakey *quakey, int max_crashes);
void quakey_network_partitioning(Quakey *quakey, bool enabled);
// Simulation time
QuakeyUInt64 quakey_current_time(Quakey *quakey);
// Host context (for accessing mock filesystem from outside scheduled context)
void quakey_enter_host(QuakeyNode node);
void quakey_leave_host(void);
int *mock_errno_ptr(void);
// Network mocks (POSIX-style, available on all platforms)
int mock_socket(int domain, int type, int protocol);
int mock_bind(int fd, void *addr, unsigned long addr_len);
int mock_connect(int fd, void *addr, unsigned long addr_len);
int mock_getsockopt(int fd, int level, int optname, void *optval, unsigned int *optlen);
int mock_listen(int fd, int backlog);
int mock_accept(int fd, void *addr, unsigned int *addr_len);
int mock_pipe(int *fds);
int mock_recv(int fd, char *dst, int len, int flags);
int mock_send(int fd, char *src, int len, int flags);
#ifdef _WIN32
// Windows-specific socket functions (use SOCKET type)
int mock_closesocket(SOCKET fd);
int mock_ioctlsocket(SOCKET fd, long cmd, unsigned long *argp);
BOOL mock_QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
BOOL mock_QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
HANDLE mock_CreateFileW(WCHAR *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
@@ -139,23 +137,18 @@ HANDLE mock_FindFirstFileA(char *lpFileName, WIN32_FIND_DATAA *lpFindFileData);
BOOL mock_FindNextFileA(HANDLE hFindFile, WIN32_FIND_DATAA *lpFindFileData);
BOOL mock_FindClose(HANDLE hFindFile);
int mock__mkdir(char *path);
int mock_open(char *path, int flags, int mode);
int mock_close(int fd);
int mock_read(int fd, char *dst, int len);
int mock_write(int fd, char *src, int len);
int mock_remove(char *path);
int mock_rename(char *oldpath, char *newpath);
int mock_mkdir(char *path, int mode);
#else
int mock_socket(int domain, int type, int protocol);
int mock_closesocket(int fd);
int mock_ioctlsocket(int fd, long cmd, unsigned long *argp);
int mock_bind(int fd, void *addr, unsigned long addr_len);
int mock_connect(int fd, void *addr, unsigned long addr_len);
int mock_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen);
int mock_listen(int fd, int backlog);
int mock_accept(int fd, void *addr, socklen_t *addr_len);
int mock_pipe(int *fds);
int mock_recv(int fd, char *dst, int len, int flags);
int mock_send(int fd, char *src, int len, int flags);
int mock_clock_gettime(clockid_t clockid, struct timespec *tp);
int mock_open(char *path, int flags, int mode);
int mock_fcntl(int fd, int cmd, int flags);
int mock_close(int fd);
int mock_access(const char *path, int mode);
int mock_ftruncate(int fd, size_t new_size);
int mock_fstat(int fd, struct stat *buf);
int mock_read(int fd, char *dst, int len);
@@ -204,7 +197,6 @@ void mock_free(void *ptr);
#define open mock_open
#define fcntl mock_fcntl
#define close mock_close
#define access mock_access
#define ftruncate mock_ftruncate
#define CreateFileW mock_CreateFileW
#define CloseHandle mock_CloseHandle
+111 -199
View File
@@ -5,6 +5,7 @@
#include <quakey.h>
#include <stdint.h>
#include <assert.h>
#include <fcntl.h>
/////////////////////////////////////////////////////////////////
// Utilities
@@ -244,9 +245,6 @@ struct Host {
char *name;
// Platform used by this host
QuakeyPlatform platform;
// State of the ephimeral port allocation
u16 next_ephemeral_port;
@@ -402,11 +400,6 @@ struct Sim {
// reached, a new random target is generated.
HostPairList partition;
HostPairList target_partition;
// Maximum number of hosts that can be dead at the same time.
int max_crashes;
bool network_partitioning;
};
static void time_event_wakeup(Sim *sim, Nanos time, Host *host);
@@ -745,6 +738,11 @@ static b32 socket_queue_empty(SocketQueue *queue)
return queue->used == 0;
}
static b32 socket_queue_used(SocketQueue *queue)
{
return queue->used;
}
/////////////////////////////////////////////////////////////////
// Accept Queue Code
@@ -940,7 +938,6 @@ static void host_init(Host *host, Sim *sim, QuakeySpawn config, char *arg)
{
host->sim = sim;
host->name = config.name;
host->platform = config.platform;
host->next_ephemeral_port = FIRST_EPHEMERAL_PORT;
// Save spawn config for restart after crash
@@ -1062,11 +1059,8 @@ static void host_free(Host *host)
}
// Remove all events associated with a host (WAKEUP events and events
// whose source or destination descriptors belong to this host).
// If skip_restart is true, RESTART events are kept (used during
// restart failure to avoid removing the event currently being
// processed by process_events_at_current_time).
static void remove_events_for_host_ex(Sim *sim, Host *host, bool skip_restart)
// whose source or destination descriptors belong to this host)
static void remove_events_for_host(Sim *sim, Host *host)
{
int i = 0;
while (i < sim->num_events) {
@@ -1075,7 +1069,7 @@ static void remove_events_for_host_ex(Sim *sim, Host *host, bool skip_restart)
if (event->type == EVENT_TYPE_WAKEUP && event->host == host)
remove = true;
if (!skip_restart && event->type == EVENT_TYPE_RESTART && event->host == host)
if (event->type == EVENT_TYPE_RESTART && event->host == host)
remove = true;
if (event->src_desc && event->src_desc->host == host)
remove = true;
@@ -1091,11 +1085,6 @@ static void remove_events_for_host_ex(Sim *sim, Host *host, bool skip_restart)
}
}
static void remove_events_for_host(Sim *sim, Host *host)
{
remove_events_for_host_ex(sim, host, false);
}
static int count_dead(Sim *sim)
{
int n = 0;
@@ -1123,11 +1112,6 @@ static void host_crash(Host *host)
// Remove all pending events for this host
remove_events_for_host(sim, host);
// Let the application clean up (e.g. log the crash)
host___ = host;
host->free_func(host->state);
host___ = NULL;
// Free application state
free(host->state);
host->state = NULL;
@@ -1138,6 +1122,7 @@ static void host_crash(Host *host)
host->poll_count = 0;
host->poll_timeout = -1;
printf("Quakey: Host crash (%d/%d dead)\n", count_dead(sim), sim->num_hosts);
}
// Restart a crashed host: re-initialize from saved config,
@@ -1208,22 +1193,10 @@ static void host_restart(Host *host)
desc_free(sim, &host->desc[i], true);
}
host->num_desc = 0;
// Skip removing RESTART events: this function is called from
// time_event_process (EVENT_TYPE_RESTART), so the current
// restart event is still in the array being iterated by
// process_events_at_current_time. Removing it here would cause
// the outer loop's swap-removal to discard an unrelated event.
remove_events_for_host_ex(sim, host, true);
remove_events_for_host(sim, host);
free(host->state);
host->state = NULL;
host->dead = true;
// Schedule another restart attempt. Init can fail due to
// transient I/O errors from fault injection; permanently
// killing the host would be unrealistic since a real
// process supervisor would keep retrying.
Nanos restart_delay = 1000000000ULL + (sim_random(sim) % 9000000000ULL);
time_event_restart(sim, sim->current_time + restart_delay, host);
return;
}
@@ -1233,17 +1206,9 @@ static void host_restart(Host *host)
} else if (host->poll_timeout == 0)
host->timedout = true;
printf("Quakey: Host restart (%d/%d dead)\n", count_dead(sim), sim->num_hosts);
}
static b32 host_is_linux(Host *host)
{
return host->platform == QUAKEY_LINUX;
}
static b32 host_is_windows(Host *host)
{
return host->platform == QUAKEY_WINDOWS;
}
static Nanos host_time(Host *host)
{
@@ -1402,10 +1367,7 @@ static void host_update(Host *host)
);
host___ = NULL;
if (ret < 0) {
host_crash(host);
Nanos restart_delay = 1000000000ULL + (sim_random(host->sim) % 9000000000ULL);
time_event_restart(host->sim, host->sim->current_time + restart_delay, host);
return;
TODO;
}
if (host->poll_timeout > 0) {
@@ -1985,7 +1947,7 @@ static int host_read(Host *host, int desc_idx, char *dst, int len)
desc->type == DESC_PIPE) {
num = recv_inner(desc, dst, len);
} else if (desc->type == DESC_FILE) {
#if defined(FAULT_INJECTION) && !defined(DISABLE_SPURIOUS_IO_ERRORS)
#ifdef FAULT_INJECTION
uint64_t roll = sim_random(host->sim) % 1000;
if (roll == 0) return HOST_ERROR_IO;
#endif
@@ -2029,7 +1991,7 @@ static int host_write(Host *host, int desc_idx, char *src, int len)
desc->type == DESC_PIPE) {
num = send_inner(desc, src, len);
} else if (desc->type == DESC_FILE) {
#if defined(FAULT_INJECTION) && !defined(DISABLE_SPURIOUS_IO_ERRORS)
#ifdef FAULT_INJECTION
uint64_t roll = sim_random(host->sim) % 1000;
if (roll == 0) return HOST_ERROR_IO;
if (roll == 1) return HOST_ERROR_NOSPC;
@@ -2205,7 +2167,7 @@ static int host_fsync(Host *host, int desc_idx)
if (desc->type != DESC_FILE)
return HOST_ERROR_BADIDX;
#if defined(FAULT_INJECTION) && !defined(DISABLE_SPURIOUS_IO_ERRORS)
#ifdef FAULT_INJECTION
uint64_t roll = sim_random(host->sim) % 100;
if (roll == 0)
return HOST_ERROR_IO;
@@ -2430,7 +2392,7 @@ static bool hosts_are_partitioned(Sim *sim, Host *a, Host *b);
static Nanos pick_partition_delay(Sim *sim, bool longer)
{
Nanos min_delay = 1000000000; // 1s
Nanos min_delay = 10000000; // 10ms
Nanos max_delay = 10000000000; // 10s
if (longer) {
min_delay = 10000000000;
@@ -2447,13 +2409,10 @@ static b32 time_event_process(TimeEvent *event, Sim *sim)
{
// Try to move one step toward the target partition.
// If current already matches target, pick a new target.
if (!break_or_repair_link(sim)) {
if (sim->network_partitioning) {
if (change_target_partition(sim) < 0) {
TODO;
}
if (!break_or_repair_link(sim))
if (change_target_partition(sim) < 0) {
TODO;
}
}
// Reschedule for the next partition step
event->time = sim->current_time + pick_partition_delay(sim, true);
@@ -2473,8 +2432,8 @@ static b32 time_event_process(TimeEvent *event, Sim *sim)
Host *peer_host = sim->hosts[idx];
if (hosts_are_partitioned(sim, src_desc->host, peer_host)) {
//SIM_TRACE(sim, "PARTITION: blocked connection %s -> %s",
// src_desc->host->name, peer_host->name);
SIM_TRACE(sim, "PARTITION: blocked connection %s -> %s",
src_desc->host->name, peer_host->name);
src_desc->connect_status = CONNECT_STATUS_NOHOST;
break;
}
@@ -2791,22 +2750,11 @@ static void sim_init(Sim *sim, uint64_t seed)
sim->events = NULL;
sim->num_signals = 0;
sim->head_signal = 0;
sim->max_crashes = 0;
sim->network_partitioning = true;
host_pair_list_init(&sim->partition);
host_pair_list_init(&sim->target_partition);
time_event_partition(sim, pick_partition_delay(sim, false));
}
static void sim_network_partitioning(Sim *sim, bool enable)
{
sim->network_partitioning = enable;
if (!enable) {
host_pair_list_free(&sim->target_partition);
host_pair_list_init(&sim->target_partition);
}
}
static void sim_free(Sim *sim)
{
host_pair_list_free(&sim->target_partition);
@@ -2890,8 +2838,8 @@ static void process_events_at_current_time(Sim *sim)
}
}
//if (sim->num_events > 10000)
// SIM_TRACE(sim, "WARNING: event queue large: %d events", sim->num_events);
if (sim->num_events > 10000)
SIM_TRACE(sim, "WARNING: event queue large: %d events", sim->num_events);
}
static int find_first_ready_host(Sim *sim)
@@ -2954,14 +2902,14 @@ static b32 sim_update(Sim *sim)
if (sim->num_hosts > 1) {
uint64_t rng = sim_random(sim);
if ((rng % 10000) == 0) {
// Pick a random live host to crash
int live_count = 0;
for (int i = 0; i < sim->num_hosts; i++)
if (!sim->hosts[i]->dead)
live_count++;
int dead_count = sim->num_hosts - live_count;
if (dead_count < sim->max_crashes) {
// Only crash if at least 2 hosts are alive (keep a majority)
if (live_count >= 2) {
// Pick one of the live hosts at random
int target = sim_random(sim) % live_count;
int j = 0;
@@ -3053,18 +3001,6 @@ void quakey_free(Quakey *quakey)
}
}
void quakey_set_max_crashes(Quakey *quakey, int max_crashes)
{
Sim *sim = (Sim*) quakey;
sim->max_crashes = max_crashes;
}
void quakey_network_partitioning(Quakey *quakey, bool enable)
{
Sim *sim = (Sim*) quakey;
sim_network_partitioning(sim, enable);
}
QuakeyNode quakey_spawn(Quakey *quakey, QuakeySpawn config, char *arg)
{
return (QuakeyNode) sim_spawn((Sim*) quakey, config, arg);
@@ -3076,28 +3012,11 @@ void *quakey_node_state(QuakeyNode node)
return host->state;
}
void quakey_enter_host(QuakeyNode node)
{
Host *host = (void*) node;
host___ = host;
}
void quakey_leave_host(void)
{
host___ = NULL;
}
int quakey_schedule_one(Quakey *quakey)
{
return sim_update((Sim*) quakey);
}
QuakeyUInt64 quakey_current_time(Quakey *quakey)
{
Sim *sim = (Sim*) quakey;
return sim->current_time;
}
QuakeyUInt64 quakey_random(void)
{
Host *host = host___;
@@ -3157,6 +3076,39 @@ const char *quakey_host_name(Quakey *quakey, int idx)
return sim->hosts[idx]->name;
}
void quakey_set_max_crashes(Quakey *quakey, int max_crashes)
{
// TODO: Implement crash limiting. For now this is a no-op
// that allows the simulation to run without the feature.
(void) quakey;
(void) max_crashes;
}
void quakey_network_partitioning(Quakey *quakey, bool enabled)
{
// TODO: Implement network partitioning toggle. For now this is
// a no-op that allows the simulation to run without the feature.
(void) quakey;
(void) enabled;
}
QuakeyUInt64 quakey_current_time(Quakey *quakey)
{
Sim *sim = (Sim*) quakey;
return (QuakeyUInt64) sim->current_time;
}
void quakey_enter_host(QuakeyNode node)
{
Host *host = (void*) node;
host___ = host;
}
void quakey_leave_host(void)
{
host___ = NULL;
}
/////////////////////////////////////////////////////////////////
// Mock System Calls
@@ -3169,7 +3121,7 @@ int *mock_errno_ptr(void)
return host_errno_ptr(host);
}
#ifndef _WIN32
// Platform-independent mock functions (used by lib/ on all platforms)
int mock_socket(int domain, int type, int protocol)
{
@@ -3221,8 +3173,6 @@ int mock_close(int fd)
if (host == NULL)
abort_("Call to mock_close() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_close() not from Linux\n");
int desc_idx = fd;
int ret = host_close(host, desc_idx, false);
@@ -3241,17 +3191,6 @@ int mock_close(int fd)
return 0;
}
// NOTE: mock_access must be implemented for simulation to work correctly.
// chunk_store_exists() uses file_exists() which calls access(). Without a
// working mock_access, the simulation cannot check chunk existence and
// must fall back to file_open (O_CREAT), which creates empty files as a
// side effect and leads to data corruption (servers return uninitialized
// data for chunks they don't actually have).
int mock_access(const char *path, int mode)
{
assert(0); // TODO
}
static int convert_addr(void *addr, size_t addr_len,
Addr *converted_addr, uint16_t *converted_port)
{
@@ -3443,8 +3382,6 @@ int mock_open(char *path, int flags, int mode)
if (host == NULL)
abort_("Call to mock_open() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_open() not from Linux\n");
int converted_flags = convert_linux_open_flags_to_mockfs(flags);
@@ -3474,8 +3411,6 @@ int mock_read(int fd, char *dst, int len)
if (host == NULL)
abort_("Call to mock_read() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_read() not from Linux\n");
int ret = host_read(host, fd, dst, len);
if (ret < 0) {
@@ -3506,8 +3441,6 @@ int mock_write(int fd, char *src, int len)
if (host == NULL)
abort_("Call to mock_write() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_write() not from Linux\n");
int ret = host_write(host, fd, src, len);
if (ret < 0) {
@@ -3603,7 +3536,7 @@ int mock_send(int fd, char *src, int len, int flags)
return ret;
}
int mock_accept(int fd, void *addr, socklen_t *addr_len)
int mock_accept(int fd, void *addr, unsigned int *addr_len)
{
Host *host = host___;
if (host == NULL)
@@ -3661,7 +3594,7 @@ int mock_accept(int fd, void *addr, socklen_t *addr_len)
return new_fd;
}
int mock_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen)
int mock_getsockopt(int fd, int level, int optname, void *optval, unsigned int *optlen)
{
if (level != SOL_SOCKET)
abort_("Call to mock_getsockopt() with level other than SOL_SOCKET\n");
@@ -3716,8 +3649,6 @@ int mock_remove(char *path)
if (host == NULL)
abort_("Call to mock_remove() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_remove() not from Linux\n");
int ret = host_remove(host, path);
if (ret < 0) {
@@ -3744,8 +3675,6 @@ int mock_rename(char *oldpath, char *newpath)
if (host == NULL)
abort_("Call to mock_rename() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_rename() not from Linux\n");
int ret = host_rename(host, oldpath, newpath);
if (ret < 0) {
@@ -3772,14 +3701,14 @@ int mock_rename(char *oldpath, char *newpath)
return 0;
}
#ifndef _WIN32
int mock_clock_gettime(clockid_t clockid, struct timespec *tp)
{
Host *host = host___;
if (host == NULL)
abort_("Call to mock_clock_gettime() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_clock_gettime() not from Linux\n");
if (tp == NULL) {
*host_errno_ptr(host) = EINVAL;
@@ -3817,8 +3746,6 @@ int mock_fsync(int fd)
if (host == NULL)
abort_("Call to mock_fsync() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_fsync() not from Linux\n");
int ret = host_fsync(host, fd);
if (ret < 0) {
@@ -3838,8 +3765,6 @@ int mock_ftruncate(int fd, size_t new_size)
if (host == NULL)
abort_("Call to mock_ftruncate() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_ftruncate() not from Linux\n");
int ret = host_ftruncate(host, fd, (int) new_size);
if (ret < 0) {
@@ -3861,8 +3786,6 @@ off_t mock_lseek(int fd, off_t offset, int whence)
if (host == NULL)
abort_("Call to mock_lseek() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_lseek() not from Linux\n");
// Convert POSIX whence to HOST whence
int host_whence;
@@ -3899,8 +3822,6 @@ int mock_fstat(int fd, struct stat *buf)
if (host == NULL)
abort_("Call to mock_fstat() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_fstat() not from Linux\n");
if (buf == NULL) {
*host_errno_ptr(host) = EINVAL;
@@ -3942,8 +3863,6 @@ char *mock_realpath(char *path, char *dst)
if (host == NULL)
abort_("Call to mock_realpath() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_realpath() not from Linux\n");
if (path == NULL) {
*host_errno_ptr(host) = EINVAL;
@@ -4057,8 +3976,6 @@ int mock_mkdir(char *path, mode_t mode)
if (host == NULL)
abort_("Call to mock_mkdir() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_mkdir() not from Linux\n");
// LittleFS doesn't use mode, but we accept it for API compatibility
(void) mode;
@@ -4088,8 +4005,6 @@ int mock_fcntl(int fd, int cmd, int flags)
if (host == NULL)
abort_("Call to mock_fcntl() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_fcntl() not from Linux\n");
switch (cmd) {
@@ -4142,8 +4057,6 @@ DIR *mock_opendir(char *name)
if (host == NULL)
abort_("Call to mock_opendir() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_opendir() not from Linux\n");
int ret = host_open_dir(host, name);
if (ret < 0) {
@@ -4180,8 +4093,6 @@ struct dirent* mock_readdir(DIR *dirp)
if (host == NULL)
abort_("Call to mock_readdir() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_readdir() not from Linux\n");
DIR_ *dirp_ = (DIR_*) dirp;
@@ -4230,8 +4141,6 @@ int mock_closedir(DIR *dirp)
if (host == NULL)
abort_("Call to mock_closedir() with no node scheduled\n");
if (!host_is_linux(host))
abort_("Call to mock_closedir() not from Linux\n");
DIR_ *dirp_ = (DIR_*) dirp;
@@ -4266,8 +4175,6 @@ int mock_GetLastError(void)
if (host == NULL)
abort_("Call to mock_GetLastError() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_GetLastError() not from Windows\n");
// Note that technically on windows errno and GetLastError
// are different things. Here we use errno_ to store the
@@ -4287,8 +4194,6 @@ void mock_SetLastError(int err)
if (host == NULL)
abort_("Call to mock_SetLastError() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_SetLastError() not from Windows\n");
*host_errno_ptr(host) = err;
}
@@ -4304,8 +4209,6 @@ int mock_closesocket(SOCKET fd)
if (host == NULL)
abort_("Call to mock_closesocket() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_closesocket() not from Windows\n");
int desc_idx = fd;
int ret = host_close(host, desc_idx, true); // expect_socket = true
@@ -4326,7 +4229,19 @@ int mock_closesocket(SOCKET fd)
int mock_ioctlsocket(SOCKET fd, long cmd, unsigned long *argp)
{
TODO;
Host *host = host___;
if (host == NULL)
abort_("Call to mock_ioctlsocket() with no node scheduled\n");
if (cmd == FIONBIO) {
int host_flags = (*argp != 0) ? HOST_FLAG_NONBLOCK : 0;
int ret = host_setdescflags(host, (int) fd, host_flags);
if (ret < 0)
return SOCKET_ERROR;
return 0;
}
return SOCKET_ERROR;
}
// Helper function to convert wide string to narrow string (ASCII subset)
@@ -4353,11 +4268,11 @@ static int convert_windows_flags_to_lfs(DWORD dwDesiredAccess,
// Convert access mode
if ((dwDesiredAccess & GENERIC_READ) && (dwDesiredAccess & GENERIC_WRITE))
lfs_flags = LFS_O_RDWR;
lfs_flags = MOCKFS_O_RDWR;
else if (dwDesiredAccess & GENERIC_WRITE)
lfs_flags = LFS_O_WRONLY;
lfs_flags = MOCKFS_O_WRONLY;
else
lfs_flags = LFS_O_RDONLY;
lfs_flags = MOCKFS_O_RDONLY;
*truncate = false;
@@ -4365,11 +4280,11 @@ static int convert_windows_flags_to_lfs(DWORD dwDesiredAccess,
switch (dwCreationDisposition) {
case CREATE_NEW:
// Creates a new file, fails if file exists
lfs_flags |= LFS_O_CREAT | LFS_O_EXCL;
lfs_flags |= MOCKFS_O_CREAT | MOCKFS_O_EXCL;
break;
case CREATE_ALWAYS:
// Creates a new file, always (truncates if exists)
lfs_flags |= LFS_O_CREAT | LFS_O_TRUNC;
lfs_flags |= MOCKFS_O_CREAT | MOCKFS_O_TRUNC;
*truncate = true;
break;
case OPEN_EXISTING:
@@ -4378,11 +4293,11 @@ static int convert_windows_flags_to_lfs(DWORD dwDesiredAccess,
break;
case OPEN_ALWAYS:
// Opens file if it exists, creates if it doesn't
lfs_flags |= LFS_O_CREAT;
lfs_flags |= MOCKFS_O_CREAT;
break;
case TRUNCATE_EXISTING:
// Opens and truncates, fails if file doesn't exist
lfs_flags |= LFS_O_TRUNC;
lfs_flags |= MOCKFS_O_TRUNC;
*truncate = true;
break;
default:
@@ -4402,8 +4317,6 @@ HANDLE mock_CreateFileW(WCHAR *lpFileName,
if (host == NULL)
abort_("Call to mock_CreateFileW() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_CreateFileW() not from Windows\n");
// lpSecurityAttributes and hTemplateFile are typically NULL
(void) lpSecurityAttributes;
@@ -4464,8 +4377,6 @@ BOOL mock_CloseHandle(HANDLE handle)
if (host == NULL)
abort_("Call to mock_CloseHandle() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_CloseHandle() not from Windows\n");
if (handle == INVALID_HANDLE_VALUE || handle == NULL) {
*host_errno_ptr(host) = ERROR_INVALID_HANDLE;
@@ -4513,8 +4424,6 @@ BOOL mock_ReadFile(HANDLE handle, char *dst, DWORD len, DWORD *num, OVERLAPPED *
if (host == NULL)
abort_("Call to mock_ReadFile() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_ReadFile() not from Windows\n");
// We don't support overlapped (async) I/O
if (ov != NULL)
@@ -4562,8 +4471,6 @@ BOOL mock_WriteFile(HANDLE handle, char *src, DWORD len, DWORD *num, OVERLAPPED
if (host == NULL)
abort_("Call to mock_WriteFile() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_WriteFile() not from Windows\n");
// We don't support overlapped (async) I/O
if (ov != NULL)
@@ -4607,8 +4514,6 @@ DWORD mock_SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceTo
if (host == NULL)
abort_("Call to mock_SetFilePointer() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_SetFilePointer() not from Windows\n");
if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) {
*host_errno_ptr(host) = ERROR_INVALID_HANDLE;
@@ -4675,8 +4580,6 @@ BOOL mock_GetFileSizeEx(HANDLE handle, LARGE_INTEGER *buf)
if (host == NULL)
abort_("Call to mock_GetFileSizeEx() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_GetFileSizeEx() not from Windows\n");
if (handle == INVALID_HANDLE_VALUE || handle == NULL) {
*host_errno_ptr(host) = ERROR_INVALID_HANDLE;
@@ -4716,8 +4619,6 @@ BOOL mock_QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount)
if (host == NULL)
abort_("Call to mock_QueryPerformanceCounter() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_QueryPerformanceCounter() not from Windows\n");
if (lpPerformanceCount == NULL)
return 0; // FALSE
@@ -4736,8 +4637,6 @@ BOOL mock_QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency)
if (host == NULL)
abort_("Call to mock_QueryPerformanceFrequency() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_QueryPerformanceFrequency() not from Windows\n");
if (lpFrequency == NULL)
return 0; // FALSE
@@ -4755,8 +4654,6 @@ char *mock__fullpath(char *path, char *dst, int cap)
if (host == NULL)
abort_("Call to mock__fullpath() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock__fullpath() not from Windows\n");
if (path == NULL) {
*host_errno_ptr(host) = EINVAL;
@@ -4864,8 +4761,6 @@ int mock__mkdir(char *path)
if (host == NULL)
abort_("Call to mock__mkdir() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock__mkdir() not from Windows\n");
int ret = host_mkdir(host, path);
if (ret < 0) {
@@ -4916,8 +4811,6 @@ HANDLE mock_FindFirstFileA(char *lpFileName, WIN32_FIND_DATAA *lpFindFileData)
if (host == NULL)
abort_("Call to mock_FindFirstFileA() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_FindFirstFileA() not from Windows\n");
if (lpFileName == NULL || lpFindFileData == NULL) {
*host_errno_ptr(host) = ERROR_INVALID_PARAMETER;
@@ -5016,8 +4909,6 @@ BOOL mock_FindNextFileA(HANDLE hFindFile, WIN32_FIND_DATAA *lpFindFileData)
if (host == NULL)
abort_("Call to mock_FindNextFileA() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_FindNextFileA() not from Windows\n");
if (hFindFile == INVALID_HANDLE_VALUE || hFindFile == NULL || lpFindFileData == NULL) {
*host_errno_ptr(host) = ERROR_INVALID_HANDLE;
@@ -5060,8 +4951,6 @@ BOOL mock_FindClose(HANDLE hFindFile)
if (host == NULL)
abort_("Call to mock_FindClose() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_FindClose() not from Windows\n");
if (hFindFile == INVALID_HANDLE_VALUE || hFindFile == NULL) {
*host_errno_ptr(host) = ERROR_INVALID_HANDLE;
@@ -5094,8 +4983,6 @@ BOOL mock_MoveFileExW(WCHAR *lpExistingFileName, WCHAR *lpNewFileName, DWORD dwF
if (host == NULL)
abort_("Call to mock_MoveFileExW() with no node scheduled\n");
if (!host_is_windows(host))
abort_("Call to mock_MoveFileExW() not from Windows\n");
// Validate parameters
if (lpExistingFileName == NULL) {
@@ -5133,7 +5020,7 @@ BOOL mock_MoveFileExW(WCHAR *lpExistingFileName, WCHAR *lpNewFileName, DWORD dwF
// We need to check this before calling host_rename
if (!(dwFlags & MOVEFILE_REPLACE_EXISTING)) {
// Try to check if destination exists by attempting to open it
int check = host_open_file(host, newpath, LFS_O_RDONLY);
int check = host_open_file(host, newpath, MOCKFS_O_RDONLY);
if (check >= 0) {
// File exists, close it and return error
host_close(host, check, false);
@@ -5168,6 +5055,31 @@ BOOL mock_MoveFileExW(WCHAR *lpExistingFileName, WCHAR *lpNewFileName, DWORD dwF
return 1; // TRUE
}
int mock_mkdir(char *path, int mode)
{
(void) mode;
Host *host = host___;
if (host == NULL)
abort_("Call to mock_mkdir() with no node scheduled\n");
int ret = host_mkdir(host, path);
if (ret < 0) {
switch (ret) {
case HOST_ERROR_EXIST:
*host_errno_ptr(host) = EEXIST;
return -1;
case HOST_ERROR_NOENT:
*host_errno_ptr(host) = ENOENT;
return -1;
default:
*host_errno_ptr(host) = EIO;
return -1;
}
}
return 0;
}
#endif
void *mock_malloc(size_t size)