add ntfs.sys standalone driver target, change default usb vhd layout formatting to ntfs, implement Fat32CreateDir for directory creation support in the fat32 file system driver, add a dedicated HAL serial output endpoint for COM1 writes in hal_serial.c and subscribe serial to HAL text streams, introduce waitable kernel timers with KeTimerCreate, KeTimerSet, and KeTimerCancel alongside spinlock-protected global timer list initialization and scheduler tick tracking, implement SyscallWriteFileAsync system call for asynchronous non-blocking file writes, refactor NetSocketConnect and NetSocketSendTo to use generic SYS_SOCKET_ADDRESS_K structures instead of being hardcoded to IPv4, update gethostbyname and DNS name resolution to support IPv6 parsing via ResolveNameFamily, update tcpserve.exe with -4 and -6 command-line flags to accept and display IPv6 addresses, append size and version headers to all kernel telemetry and statistics structures including SYS_PMM_STATS, SYS_CPU_STATS, and SYS_SCHEDULER_STATS, update winmm timeGetTime to look up high-resolution hardware frequencies via SysGetTickFrequency, integrate 14 native smoke test applications.

This commit is contained in:
maksw committed 2026-06-26 15:18:48 +02:00
1 parent 2c4a68a75f
commit ed3d065abd
156 files changed
+10509 -2643

No files matched your search

+44 -13
View File
@@ -55,12 +55,21 @@ static void* doom_open_wrapper(const char* path, const char* mode)
uint32_t handle = 0;
uint32_t result;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), &handle };
uint32_t access = SYS_FILE_ACCESS_QUERY;
uint32_t disposition = SYS_FILE_OPEN_EXISTING;
if (mode[0] == 'w') {
result = SysCreateFile(&req);
access |= SYS_FILE_ACCESS_WRITE;
disposition = SYS_FILE_CREATE_ALWAYS;
} else {
result = SysOpenFile(&req);
access |= SYS_FILE_ACCESS_READ;
}
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(path),
access,
disposition,
SYS_FILE_FLAG_NONE,
&handle);
result = SysOpenFile(&req);
if (result != 0) return 0;
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)malloc(sizeof(DOOM_FILE_HANDLE));
@@ -69,9 +78,17 @@ static void* doom_open_wrapper(const char* path, const char* mode)
return 0;
}
uint64_t size = 0;
result = SysGetFileSize(handle, &size);
if (result != 0) {
SysCloseHandle(handle);
free(f);
return 0;
}
f->Handle = handle;
f->Pos = 0;
f->Size = SysGetFileSize(handle);
f->Size = size;
return f;
}
@@ -155,9 +172,16 @@ static int doom_eof_wrapper(void* handle)
// ---------------------------------------------------------------------------
static void doom_gettime_wrapper(int* sec, int* usec)
{
uint64_t t = SysGetTick();
*sec = (int)(t / 1000000); // 1,000,000 us = 1 second
*usec = (int)(t % 1000000); // Remainder in microseconds
uint64_t frequency = 0;
if (SysGetTickFrequency(&frequency) != STATUS_SUCCESS || !frequency) {
*sec = 0;
*usec = 0;
return;
}
uint64_t totalUs = SysTicksToMicroseconds(SysGetTick(), frequency);
*sec = (int)(totalUs / 1000000);
*usec = (int)(totalUs % 1000000);
}
// ---------------------------------------------------------------------------
@@ -169,7 +193,7 @@ static void doom_exit_wrapper(int code)
}
// ---------------------------------------------------------------------------
// doom_getenv_fn
// doom_getenv_fn
// ---------------------------------------------------------------------------
static char* doom_getenv_wrapper(const char* name)
{
@@ -204,7 +228,7 @@ static const KeyMap g_keymap[] = {
{ INPUT_KEY_LEFT, DOOM_KEY_LEFT_ARROW },
{ INPUT_KEY_RIGHT, DOOM_KEY_RIGHT_ARROW },
// Action keys
// Action keys
{ INPUT_KEY_LEFT_CTRL, DOOM_KEY_CTRL },
{ INPUT_KEY_SPACE, DOOM_KEY_SPACE },
{ INPUT_KEY_LEFT_SHIFT, DOOM_KEY_SHIFT },
@@ -250,10 +274,17 @@ static const KeyMap g_keymap[] = {
static bool g_prev_key[KEYMAP_COUNT];
// Returns whether one NTLite input key is currently held.
static bool ntlite_key_down(INPUT_KEY key)
{
bool down = false;
return SysIsKeyDown(key, &down) == STATUS_SUCCESS && down;
}
static void poll_input(void)
{
for (int i = 0; i < (int)KEYMAP_COUNT; i++) {
bool cur_down = (SysIsKeyDown(g_keymap[i].inputKey) != 0);
bool cur_down = ntlite_key_down(g_keymap[i].inputKey);
bool prev_down = g_prev_key[i];
if (cur_down && !prev_down) doom_key_down(g_keymap[i].key);
@@ -280,7 +311,7 @@ static void init_surface(void)
{
uint32_t s = SysCreateSurface(DISP_W, DISP_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &g_surface_handle);
if (s != SYS_STATUS_SUCCESS || !g_surface_handle) {
if (s != STATUS_SUCCESS || !g_surface_handle) {
doom_print_wrapper("doom: SysCreateSurface failed\n");
SysExitProcess(1);
}
@@ -288,7 +319,7 @@ static void init_surface(void)
void* base = 0;
uint32_t pitchBytes = 0;
s = SysMapSurface(g_surface_handle, &base, &pitchBytes);
if (s != SYS_STATUS_SUCCESS || !base || pitchBytes < DISP_W * sizeof(uint32_t)) {
if (s != STATUS_SUCCESS || !base || pitchBytes < DISP_W * sizeof(uint32_t)) {
doom_print_wrapper("doom: SysMapSurface failed\n");
SysExitProcess(1);
}
@@ -328,7 +359,7 @@ static void render_frame(const uint8_t* rgba)
DISPLAY_RECT rect = { 0, 0, DISP_W, DISP_H };
uint32_t code = SysPresentSurface(g_surface_handle, &rect, 1);
if (code != SYS_STATUS_SUCCESS) {
if (code != STATUS_SUCCESS) {
SysExitProcess(code);
}
}
+13 -6
View File
@@ -369,12 +369,14 @@ static void move_down(void) {
// Updates file.
static void save_file(void) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST pathReq = { SysSvFromCstr(gFilename), &h };
uint32_t st = SysCreateFile(&pathReq);
if (st != 0) st = SysOpenFile(&pathReq);
SYS_OPEN_FILE_REQUEST pathReq = SysMakeOpenFileRequest(SysSvFromCstr(gFilename),
SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_CREATE_ALWAYS,
SYS_FILE_FLAG_NONE,
&h);
uint32_t st = SysOpenFile(&pathReq);
if (st != 0) { msg_set("save failed: cannot open file"); return; }
SysTruncateFile(h, 0);
SysSeekFile(h, 0);
for (int i = 0; i < gLineCount; i++) {
@@ -411,7 +413,11 @@ static void load_file(void) {
reset_buffer();
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST pathReq = { SysSvFromCstr(gFilename), &h };
SYS_OPEN_FILE_REQUEST pathReq = SysMakeOpenFileRequest(SysSvFromCstr(gFilename),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
if (SysOpenFile(&pathReq) != 0) {
msg_set("new file");
return;
@@ -501,7 +507,8 @@ static void editor_loop(void) {
while (true) {
render();
INPUT_KEY key = SysReadKey();
INPUT_KEY key = INPUT_KEY_NONE;
if (SysReadKey(&key) != STATUS_SUCCESS) continue;
uint32_t mods = SysGetKeyModifiers();
bool ctrl = (mods & INPUT_MOD_CTRL) != 0;
+62 -34
View File
@@ -69,6 +69,38 @@ static void format_ipv4(char* dst, size_t cap, const uint8_t ip[4]) {
append_u8(dst, cap, &off, ip[3]);
}
static void append_hex16(char* dst, size_t cap, size_t* off, uint16_t value) {
static const char hex[] = "0123456789abcdef";
int started = 0;
for (int shift = 12; shift >= 0; shift -= 4) {
unsigned nibble = (value >> shift) & 0xfu;
if (nibble || started || shift == 0) {
append_char(dst, cap, off, hex[nibble]);
started = 1;
}
}
}
// Implements the format ipv6 operation.
static void format_ipv6(char* dst, size_t cap, const uint8_t ip[16]) {
size_t off = 0;
if (!dst || cap == 0) return;
dst[0] = 0;
for (int i = 0; i < 8; ++i) {
uint16_t word = ((uint16_t)ip[i * 2] << 8) | ip[i * 2 + 1];
append_hex16(dst, cap, &off, word);
if (i != 7) append_char(dst, cap, &off, ':');
}
}
// Implements the format socket address operation.
static void format_socket_address(char* dst, size_t cap, const SYS_SOCKET_ADDRESS* address) {
if (!address || address->Family == SYS_AF_INET)
format_ipv4(dst, cap, address ? address->Address : (const uint8_t*)"\0\0\0\0");
else
format_ipv6(dst, cap, address->Address);
}
// Parses u8.
static bool parse_u8(const char** p, uint8_t* out) {
const char* s = *p;
@@ -121,36 +153,39 @@ static void usage(void) {
}
// Implements the resolve host operation.
static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
if (parse_ipv4(host, ip))
return SYS_STATUS_SUCCESS;
static uint32_t resolve_host(const char* host, SYS_SOCKET_ADDRESS* address) {
uint8_t ip[4];
if (parse_ipv4(host, ip)) {
memset(address, 0, sizeof(*address));
address->Size = sizeof(*address);
address->Family = SYS_AF_INET;
memcpy(address->Address, ip, 4);
return STATUS_SUCCESS;
}
SYS_SOCKET_ADDRESS_IPV4 resolved;
size_t resolvedSize = sizeof(resolved);
size_t resolvedSize = sizeof(*address);
SYS_RESOLVE_HOST_REQUEST req;
memset(&resolved, 0, sizeof(resolved));
memset(address, 0, sizeof(*address));
memset(&req, 0, sizeof(req));
req.Name = SysSvFromCstr(host);
req.Family = SYS_AF_INET;
req.AddressOut = &resolved;
req.Family = SYS_AF_INET6;
req.AddressOut = address;
req.AddressSizeInOut = &resolvedSize;
uint32_t st = SysResolveHost(&req);
if (st != SYS_STATUS_SUCCESS)
return st;
if (resolved.Family != SYS_AF_INET)
return SYS_STATUS_NOT_FOUND;
if (st == STATUS_SUCCESS)
return STATUS_SUCCESS;
ip[0] = resolved.Address[0];
ip[1] = resolved.Address[1];
ip[2] = resolved.Address[2];
ip[3] = resolved.Address[3];
return SYS_STATUS_SUCCESS;
resolvedSize = sizeof(*address);
memset(address, 0, sizeof(*address));
req.Family = SYS_AF_INET;
st = SysResolveHost(&req);
return st;
}
// Runs the requested operation.
int main(void) {
uint8_t ip[4] = {10, 0, 2, 2};
SYS_SOCKET_ADDRESS addr;
char host[256];
char path[128];
strcpy(host, "10.0.2.2");
@@ -188,15 +223,15 @@ int main(void) {
}
}
uint32_t st = resolve_host(host, ip);
if (st != SYS_STATUS_SUCCESS) {
uint32_t st = resolve_host(host, &addr);
if (st != STATUS_SUCCESS) {
print("httpget: resolve failed "); print_u64(st); print(" for "); print(host); print("\n");
usage();
SysExitProcess(1);
}
char ipText[16];
format_ipv4(ipText, sizeof(ipText), ip);
char ipText[64];
format_socket_address(ipText, sizeof(ipText), &addr);
print("httpget: GET http://");
print(host);
@@ -206,23 +241,16 @@ int main(void) {
print(")\n");
uint32_t sock = 0;
st = SysCreateSocket(SYS_AF_INET, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &sock);
if (st != SYS_STATUS_SUCCESS) {
st = SysCreateSocket(addr.Family, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &sock);
if (st != STATUS_SUCCESS) {
print("httpget: SysCreateSocket failed "); print_u64(st); print("\n");
SysExitProcess(1);
}
SYS_SOCKET_ADDRESS_IPV4 addr;
memset(&addr, 0, sizeof(addr));
addr.Family = SYS_AF_INET;
addr.Port = 80;
addr.Address[0] = ip[0];
addr.Address[1] = ip[1];
addr.Address[2] = ip[2];
addr.Address[3] = ip[3];
st = SysConnectSocket(sock, &addr, sizeof(addr));
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("httpget: SysConnectSocket failed "); print_u64(st); print("\n");
SysCloseHandle(sock);
SysExitProcess(1);
@@ -241,7 +269,7 @@ int main(void) {
while (totalSent < off) {
size_t sent = 0;
st = SysSendSocket(sock, request + totalSent, off - totalSent, 0, &sent);
if (st != SYS_STATUS_SUCCESS || sent == 0) {
if (st != STATUS_SUCCESS || sent == 0) {
print("httpget: SysSendSocket failed "); print_u64(st); print(" sent="); print_u64(sent); print("\n");
SysCloseHandle(sock);
SysExitProcess(1);
@@ -254,7 +282,7 @@ int main(void) {
for (;;) {
size_t got = 0;
st = SysRecvSocket(sock, buf, sizeof(buf) - 1, 0, &got);
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("\nhttpget: SysRecvSocket failed "); print_u64(st); print(" after "); print_u64(total); print(" bytes\n");
SysCloseHandle(sock);
SysExitProcess(1);
+2 -2
View File
@@ -30,8 +30,8 @@ int main(void) {
char metricsBuf[128];
while (true) {
if (SysGetConsoleInfo(&console) == SYS_STATUS_SUCCESS &&
SysGetMemoryStatus(&mem) == SYS_STATUS_SUCCESS) {
if (SysGetConsoleInfo(&console) == STATUS_SUCCESS &&
SysGetMemoryStatus(&mem) == STATUS_SUCCESS) {
// If the system isn't reporting columns correctly, default to a safe standard width
int columns = (console.Columns > 0) ? (int)console.Columns : 80;
+23 -14
View File
@@ -133,9 +133,9 @@ static bool parse_u32(const char* s, uint32_t* out) {
// Implements the resolve host operation.
static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
if (parse_ipv4(host, ip))
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
SYS_SOCKET_ADDRESS_IPV4 resolved;
SYS_SOCKET_ADDRESS resolved;
size_t resolvedSize = sizeof(resolved);
SYS_RESOLVE_HOST_REQUEST req;
memset(&resolved, 0, sizeof(resolved));
@@ -146,16 +146,16 @@ static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
req.AddressSizeInOut = &resolvedSize;
uint32_t st = SysResolveHost(&req);
if (st != SYS_STATUS_SUCCESS)
if (st != STATUS_SUCCESS)
return st;
if (resolved.Family != SYS_AF_INET)
return SYS_STATUS_NOT_FOUND;
return STATUS_NOT_FOUND;
ip[0] = resolved.Address[0];
ip[1] = resolved.Address[1];
ip[2] = resolved.Address[2];
ip[3] = resolved.Address[3];
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
// Implements the icmp checksum operation.
@@ -258,7 +258,7 @@ int main(void) {
uint8_t ip[4];
uint32_t st = resolve_host(host, ip);
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("ping: resolve failed "); print_u64(st); print(" for "); print(host); print("\n");
SysExitProcess(1);
}
@@ -268,7 +268,7 @@ int main(void) {
uint32_t sock = 0;
st = SysCreateSocket(SYS_AF_INET, SYS_SOCK_RAW, SYS_IPPROTO_ICMP, &sock);
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("ping: SysCreateSocket(raw icmp) failed "); print_u64(st); print("\n");
SysExitProcess(1);
}
@@ -281,8 +281,17 @@ int main(void) {
opt.ValueSize = sizeof(timeout);
(void)SysSetSocketOption(sock, &opt);
SYS_SOCKET_ADDRESS_IPV4 remote;
uint64_t tickFrequency = 0;
st = SysGetTickFrequency(&tickFrequency);
if (st != STATUS_SUCCESS || !tickFrequency) {
print("ping: SysGetTickFrequency failed "); print_u64(st); print("\n");
SysCloseHandle(sock);
SysExitProcess(1);
}
SYS_SOCKET_ADDRESS remote;
memset(&remote, 0, sizeof(remote));
remote.Size = sizeof(remote);
remote.Family = SYS_AF_INET;
remote.Port = 0;
remote.Address[0] = ip[0];
@@ -314,7 +323,7 @@ int main(void) {
uint64_t start = SysGetTick();
st = SysSendToSocket(sock, &sendReq);
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("ping: send failed seq="); print_u64(seq); print(" status="); print_u64(st); print("\n");
if (i + 1 < count) SysSleep(1000);
continue;
@@ -325,7 +334,7 @@ int main(void) {
uint8_t lastType = 0, lastCode = 0;
for (;;) {
uint8_t recvBuf[512];
SYS_SOCKET_ADDRESS_IPV4 from;
SYS_SOCKET_ADDRESS from;
size_t fromSize = sizeof(from);
size_t got = 0;
SYS_SOCKET_RECV_FROM_REQUEST recvReq;
@@ -337,9 +346,9 @@ int main(void) {
recvReq.ReceivedOut = &got;
st = SysRecvFromSocket(sock, &recvReq);
if (st == SYS_STATUS_TIMEOUT)
if (st == STATUS_TIMEOUT)
break;
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("ping: recv failed status="); print_u64(st); print("\n");
break;
}
@@ -347,7 +356,7 @@ int main(void) {
size_t icmpLen = 0;
if (parse_echo_reply(recvBuf, got, ident, seq, &icmpLen, &lastType, &lastCode)) {
uint64_t end = SysGetTick();
uint64_t deltaUs = end >= start ? end - start : 0;
uint64_t deltaUs = end >= start ? SysTicksToMicroseconds(end - start, tickFrequency) : 0;
receivedPackets++;
sumUs += deltaUs;
if (receivedPackets == 1 || deltaUs < minUs) minUs = deltaUs;
@@ -361,7 +370,7 @@ int main(void) {
}
uint64_t now = SysGetTick();
if (now - start >= ((uint64_t)PING_TIMEOUT_MS * US_PER_MS))
if (SysTicksToMilliseconds(now - start, tickFrequency) >= PING_TIMEOUT_MS)
break;
}
+50 -8
View File
@@ -49,40 +49,82 @@ static void print_ipv4(const uint8_t ip[4]) {
print_u64(ip[2]); print("."); print_u64(ip[3]);
}
// Prints ipv6.
static void print_ipv6(const uint8_t ip[16]) {
static const char hex[] = "0123456789abcdef";
for (int i = 0; i < 8; ++i) {
uint16_t word = ((uint16_t)ip[i * 2] << 8) | ip[i * 2 + 1];
int started = 0;
for (int shift = 12; shift >= 0; shift -= 4) {
unsigned nibble = (word >> shift) & 0xfu;
if (nibble || started || shift == 0) {
char c = hex[nibble];
print_char(c);
started = 1;
}
}
if (i != 7)
print(":");
}
}
// Runs the requested operation.
int main(void) {
char familyText[16];
char name[256];
uint32_t family = 0;
const char* cmd = SysGetPeb()->CommandLine.Data;
cmd = next_token(cmd, name, sizeof(name)); // program path
cmd = next_token(cmd, name, sizeof(name));
(void)cmd;
familyText[0] = 0;
name[0] = 0;
if (cmd)
(void)next_token(cmd, name, sizeof(name));
if (cmd) {
const char* rest = next_token(cmd, familyText, sizeof(familyText));
if (strcmp(familyText, "-4") == 0) {
family = SYS_AF_INET;
if (rest)
(void)next_token(rest, name, sizeof(name));
} else if (strcmp(familyText, "-6") == 0) {
family = SYS_AF_INET6;
if (rest)
(void)next_token(rest, name, sizeof(name));
} else {
strcpy(name, familyText);
}
}
if (!name[0]) {
print("usage: resolve.exe hostname\n");
print("usage: resolve.exe [-4|-6] hostname\n");
SysExitProcess(1);
}
SYS_SOCKET_ADDRESS_IPV4 addr;
SYS_SOCKET_ADDRESS addr;
size_t addrSize = sizeof(addr);
SYS_RESOLVE_HOST_REQUEST req;
memset(&addr, 0, sizeof(addr));
memset(&req, 0, sizeof(req));
req.Name = SysSvFromCstr(name);
req.Family = SYS_AF_INET;
req.Family = family ? family : SYS_AF_INET6;
req.AddressOut = &addr;
req.AddressSizeInOut = &addrSize;
uint32_t st = SysResolveHost(&req);
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS && family == 0) {
req.Family = SYS_AF_INET;
addrSize = sizeof(addr);
st = SysResolveHost(&req);
}
if (st != STATUS_SUCCESS) {
print("resolve: SysResolveHost failed "); print_u64(st); print("\n");
SysExitProcess(1);
}
print(name);
print(" -> ");
print_ipv4(addr.Address);
if (addr.Family == SYS_AF_INET6)
print_ipv6(addr.Address);
else
print_ipv4(addr.Address);
print("\n");
SysExitProcess(0);
return 0;
+111 -50
View File
@@ -18,14 +18,22 @@ static void print(const char* s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCs
// Opens file.
static uint32_t open_file(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(path),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
handleOut);
return SysOpenFile(&req);
}
// Creates file.
static uint32_t create_file(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysCreateFile(&req);
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(path),
SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_CREATE_ALWAYS,
SYS_FILE_FLAG_NONE,
handleOut);
return SysOpenFile(&req);
}
// Opens dir.
@@ -513,7 +521,13 @@ static bool readLine(char* buf, int len) {
while (true) {
INPUT_KEY_RECORD input;
if (SysReadInputEvent(&input) != 0) { SysYield(); continue; }
SYS_INPUT_EVENT_REQUEST inputReq;
inputReq.Size = sizeof(inputReq);
inputReq.QueueHandle = 0;
inputReq.Flags = SYS_INPUT_EVENT_FLAG_NONE;
inputReq.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
inputReq.RecordOut = &input;
if (SysReadInputEvent(&inputReq) != STATUS_SUCCESS) { SysYield(); continue; }
INPUT_KEY key = input.Key;
if (key == INPUT_KEY_NONE) { SysYield(); continue; }
@@ -609,7 +623,6 @@ static void printSizeKiB(uint64_t bytes) {
print(" KiB");
}
// Prints pages Ki B.
static void printPagesKiB(uint64_t pages) {
printUInt64(pages * 4u);
@@ -630,7 +643,8 @@ static void printCpuFlags(uint32_t flags) {
static void cmd_stats(void) {
SYS_SCHEDULER_STATS sched;
SYS_PMM_STATS pmm;
SYS_CPU_STATS cpu;
SYS_CPU_STATS* cpu = 0;
void* cpuBuffer = 0;
SYS_PFN_STATS pfn;
SYS_VAD_STATS vad;
SYS_SWAP_STATS swap;
@@ -657,11 +671,32 @@ static void cmd_stats(void) {
return;
}
status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, &cpu, sizeof(cpu), NULL);
size_t cpuSize = 0;
status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, 0, 0, &cpuSize);
if (status != 0 || cpuSize < sizeof(SYS_CPU_STATS)) {
print("stats: cpu size query failed: ");
printUInt(status);
print("\n");
return;
}
size_t cpuPages = (cpuSize + 4095u) / 4096u;
if (cpuPages == 0) cpuPages = 1;
status = SysAllocVm(0, cpuPages, SYS_VM_READWRITE, SYS_VM_FLAG_NONE, &cpuBuffer);
if (status != 0 || !cpuBuffer) {
print("stats: cpu buffer allocation failed: ");
printUInt(status);
print("\n");
return;
}
cpu = (SYS_CPU_STATS*)cpuBuffer;
status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, cpu, cpuPages * 4096u, NULL);
if (status != 0) {
print("stats: cpu query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
@@ -670,6 +705,7 @@ static void cmd_stats(void) {
print("stats: pfn query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
@@ -678,6 +714,7 @@ static void cmd_stats(void) {
print("stats: vad query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
@@ -686,6 +723,7 @@ static void cmd_stats(void) {
print("stats: swap query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
@@ -694,6 +732,7 @@ static void cmd_stats(void) {
print("stats: file cache query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
@@ -702,6 +741,7 @@ static void cmd_stats(void) {
print("stats: filesystem query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
@@ -710,6 +750,7 @@ static void cmd_stats(void) {
print("stats: disk query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
@@ -718,6 +759,7 @@ static void cmd_stats(void) {
print("stats: pte query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
@@ -726,6 +768,7 @@ static void cmd_stats(void) {
print("stats: surface query failed: ");
printUInt(status);
print("\n");
SysFreeVm(cpuBuffer);
return;
}
@@ -743,11 +786,10 @@ static void cmd_stats(void) {
print(" SMT primary pokes: "); printUInt64(sched.SmtPrimaryPokes); print("\n");
print("CPUs:\n");
uint32_t n = cpu.CpuCount;
if (n > SYS_KERNEL_STATS_MAX_CPUS)
n = SYS_KERNEL_STATS_MAX_CPUS;
const SYS_CPU_STAT* cpuRecords = SYS_CPU_STATS_CONST_RECORDS(cpu);
uint32_t n = cpu->RecordCount;
for (uint32_t i = 0; i < n; i++) {
SYS_CPU_STAT* c = &cpu.Cpu[i];
const SYS_CPU_STAT* c = &cpuRecords[i];
print(" cpu"); printUInt(c->CpuIndex);
print(" lapic="); printUInt(c->LapicId);
print(" current=");
@@ -929,14 +971,8 @@ static void cmd_stats(void) {
print(" invalidation: count="); printUInt64(filesystem.PathCacheInvalidations);
print(" generation="); printUInt64(filesystem.PathCacheGeneration); print("\n");
print(" directory search: searches="); printUInt64(filesystem.DirectorySearches);
print(" clusters="); printUInt64(filesystem.DirectoryClustersRead);
print(" blocks="); printUInt64(filesystem.DirectoryBlocksRead);
print(" entries="); printUInt64(filesystem.DirectoryEntriesScanned); print("\n");
print(" FAT chain cache: lookups="); printUInt64(filesystem.FatChainCacheLookups);
print(" hits="); printUInt64(filesystem.FatChainCacheHits);
print(" misses="); printUInt64(filesystem.FatChainCacheMisses);
print(" stores="); printUInt64(filesystem.FatChainCacheStores); print("\n");
print(" FAT chain work: invalidations="); printUInt64(filesystem.FatChainCacheInvalidations);
print(" fat-steps="); printUInt64(filesystem.FatChainWalkSteps); print("\n");
print(" data read spans: runs="); printUInt64(filesystem.DataReadRuns);
print(" sectors="); printUInt64(filesystem.DataReadSectors);
print(" merges="); printUInt64(filesystem.DataReadRunMerges);
@@ -988,18 +1024,18 @@ static void cmd_stats(void) {
print(" query: total="); printUInt64(pte.QueryCalls);
print(" failed="); printUInt64(pte.QueryFailures);
print(" translate="); printUInt64(pte.TranslateCalls); print("\n");
print(" generic: "); printUInt64(pte.QueryByPurpose[0]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[0]); print("\n");
print(" user-probe: "); printUInt64(pte.QueryByPurpose[1]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[1]); print("\n");
print(" heap: "); printUInt64(pte.QueryByPurpose[2]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[2]); print("\n");
print(" vad: "); printUInt64(pte.QueryByPurpose[3]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[3]); print("\n");
print(" debug: "); printUInt64(pte.QueryByPurpose[4]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[4]); print("\n");
print(" self-test: "); printUInt64(pte.QueryByPurpose[5]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[5]); print("\n");
print(" generic: "); printUInt64(pte.QueryByPurpose[SYS_PTE_QUERY_GENERIC]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_GENERIC]); print("\n");
print(" user-probe: "); printUInt64(pte.QueryByPurpose[SYS_PTE_QUERY_USER_PROBE]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_USER_PROBE]); print("\n");
print(" heap: "); printUInt64(pte.QueryByPurpose[SYS_PTE_QUERY_HEAP]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_HEAP]); print("\n");
print(" vad: "); printUInt64(pte.QueryByPurpose[SYS_PTE_QUERY_VAD]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_VAD]); print("\n");
print(" debug: "); printUInt64(pte.QueryByPurpose[SYS_PTE_QUERY_DEBUG]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_DEBUG]); print("\n");
print(" self-test: "); printUInt64(pte.QueryByPurpose[SYS_PTE_QUERY_SELF_TEST]);
print(" failed="); printUInt64(pte.QueryFailuresByPurpose[SYS_PTE_QUERY_SELF_TEST]); print("\n");
print(" tlb: local="); printUInt64(pte.LocalFlushes);
print(" global="); printUInt64(pte.GlobalFlushes);
print(" new-map-skip="); printUInt64(pte.NewMappingNoFlushes);
@@ -1009,9 +1045,9 @@ static void cmd_stats(void) {
print(" skipped="); printUInt64(pte.ShootdownSkippedNoTargets);
print(" acks="); printUInt64(pte.ShootdownAcks);
print(" timeouts="); printUInt64(pte.ShootdownTimeouts); print("\n");
SysFreeVm(cpuBuffer);
}
// Implements the cmd time operation.
static void cmd_time(void) {
SYS_SYSTEM_TIME t;
@@ -1197,16 +1233,17 @@ static void cmd_ps(void) {
printUInt((uint32_t)count);
print("):\n");
print(" PID State Thr Hnd Mod UserKiB ImageKiB Flags Name\n");
print(" Flags: S=system I=idle *=current T=terminating X=exited.\n");
print(" Flags: S=system I=idle P=protected *=current T=terminating X=exited.\n");
size_t displayCount = count < MAX_PROCS ? count : MAX_PROCS;
for (size_t i = 0; i < displayCount; i++) {
SYS_PROCESS_INFO* p = &procs[i];
char flags[6];
char flags[7];
size_t flagLen = 0;
if (p->Flags & SYS_PROCESS_FLAG_SYSTEM) flags[flagLen++] = 'S';
if (p->Flags & SYS_PROCESS_FLAG_IDLE) flags[flagLen++] = 'I';
if (p->Flags & SYS_PROCESS_FLAG_TERMINATION_PROTECTED) flags[flagLen++] = 'P';
if (p->Flags & SYS_PROCESS_FLAG_CURRENT) flags[flagLen++] = '*';
if (p->Flags & SYS_PROCESS_FLAG_TERMINATING) flags[flagLen++] = 'T';
if (p->Flags & SYS_PROCESS_FLAG_EXITED) flags[flagLen++] = 'X';
@@ -1310,9 +1347,20 @@ static void cmd_threads(const char* args) {
static void cmd_kill(const char* args) {
uint32_t pid = 0;
if (!parseUInt(args, &pid) || pid == 0) { print("kill: usage: kill <pid>\n"); return; }
uint32_t status = SysKillProcess(pid, (int)STATUS_PROCESS_KILLED);
if (status != 0) {
if (!parseUInt(args, &pid)) { print("kill: usage: kill <pid>\n"); return; }
uint32_t hProc = 0;
uint32_t status = SysOpenProcess(pid, &hProc);
if (status == 0) {
status = SysKillProcess(hProc, (int32_t)STATUS_PROCESS_KILLED);
SysCloseHandle(hProc);
}
if (status == STATUS_ACCESS_DENIED) {
print("kill: process ");
printUInt(pid);
print(" is termination-protected.\n");
} else if (status != 0) {
print("shell: failed to kill process ");
printUInt(pid);
print(", error code: ");
@@ -1347,8 +1395,11 @@ static void cmd_dir(const char* path) {
uint32_t hDir;
const char* target = (path && *path != '\0') ? path : ".";
if (open_dir(target, &hDir) != 0) {
print("dir: failed to open directory\n");
uint32_t st = open_dir(target, &hDir);
if (st != 0) {
print("dir: failed to open directory, status=0x");
printHex32(st);
print("\n");
return;
}
@@ -1384,7 +1435,6 @@ static void cmd_type(const char* filename) {
SysCloseHandle(hFile); print("\n");
}
// Implements the extract Program Path operation.
static bool extractProgramPath(const char* cmdline, char* out, size_t outSize) {
if (!cmdline || !out || outSize == 0) return false;
@@ -1410,15 +1460,21 @@ static bool extractProgramPath(const char* cmdline, char* out, size_t outSize) {
static bool shellConsumeQueuedCtrlC(void) {
for (int i = 0; i < 4; i++) {
INPUT_KEY_RECORD input;
if (SysPeekInputEvent(&input) != 0) return false;
SYS_INPUT_EVENT_REQUEST inputReq;
inputReq.Size = sizeof(inputReq);
inputReq.QueueHandle = 0;
inputReq.Flags = SYS_INPUT_EVENT_FLAG_NONE;
inputReq.TimeoutMilliseconds = 0;
inputReq.RecordOut = &input;
if (SysPeekInputEvent(&inputReq) != STATUS_SUCCESS) return false;
if ((input.Modifiers & INPUT_MOD_CTRL) && input.Key == INPUT_KEY_C) {
(void)SysReadInputEvent(&input);
(void)SysReadInputEvent(&inputReq);
return true;
}
if (input.Key == INPUT_KEY_LEFT_CTRL || input.Key == INPUT_KEY_RIGHT_CTRL) {
(void)SysReadInputEvent(&input);
(void)SysReadInputEvent(&inputReq);
continue;
}
@@ -1428,10 +1484,14 @@ static bool shellConsumeQueuedCtrlC(void) {
return false;
}
// Returns whether one input key is currently held.
static bool shellIsKeyDown(INPUT_KEY key) {
bool down = false;
return SysIsKeyDown(key, &down) == STATUS_SUCCESS && down;
}
// Waits for a foreground process while watching Ctrl-C without stealing ordinary stdin.
static uint32_t waitForegroundProcess(uint32_t hProc) {
uint32_t pid = 0;
bool havePid = SysGetProcessPid(hProc, &pid) == 0 && pid != 0;
bool sentBreak = false;
uint32_t inputQueue = 0;
@@ -1451,7 +1511,7 @@ static uint32_t waitForegroundProcess(uint32_t hProc) {
for (;;) {
index = 0;
uint32_t status = SysWaitMany(&waitReq);
if (status != SYS_STATUS_SUCCESS) {
if (status != STATUS_SUCCESS) {
SysCloseHandle(inputQueue);
return status;
}
@@ -1462,10 +1522,10 @@ static uint32_t waitForegroundProcess(uint32_t hProc) {
}
uint32_t modifiers = SysGetKeyModifiers();
if (((modifiers & INPUT_MOD_CTRL) && SysIsKeyDown(INPUT_KEY_C)) || shellConsumeQueuedCtrlC()) {
if (((modifiers & INPUT_MOD_CTRL) && shellIsKeyDown(INPUT_KEY_C)) || shellConsumeQueuedCtrlC()) {
if (!sentBreak) {
print("^C\n");
if (havePid) (void)SysKillProcess(pid, (int)STATUS_PROCESS_KILLED);
(void)SysKillProcess(hProc, (int32_t)STATUS_PROCESS_KILLED);
sentBreak = true;
}
}
@@ -1564,13 +1624,13 @@ static int runCommand(const char* cmd) {
} else {
// Foreground mode: Wait for completion and flush standard keyboard stream.
uint32_t waitStatus = waitForegroundProcess(hProc);
if (waitStatus != SYS_STATUS_SUCCESS) {
if (waitStatus != STATUS_SUCCESS) {
print("shell: wait failed: ");
printUInt(waitStatus);
print("\n");
}
print("Process exited with code: ");
int exitCode;
int32_t exitCode;
SysGetProcessExitCode(hProc, &exitCode);
printInt(exitCode);
const char* exitName = processExitStatusName((uint32_t)exitCode);
@@ -1583,7 +1643,8 @@ static int runCommand(const char* cmd) {
}
print("\n");
SysCloseHandle(hProc);
while (SysPollKeyboard()) (void)SysReadKey();
INPUT_KEY drainedKey;
while (SysPollKeyboard(&drainedKey) == STATUS_SUCCESS) (void)SysReadKey(&drainedKey);
}
} else {
print("shell: launch failed\n");
-1
View File
@@ -60,7 +60,6 @@ static void readLine(char* buf, int len) {
buf[pos] = '\0';
}
// Implements the split Command Line operation.
static int splitCommandLine(char* line, char** argv, int maxArgv) {
int argc = 0;
+62 -24
View File
@@ -35,6 +35,41 @@ static void print_ipv4(const uint8_t ip[4]) {
print_u64(ip[2]); print("."); print_u64(ip[3]);
}
// Prints ipv6.
static void print_ipv6(const uint8_t ip[16]) {
static const char hex[] = "0123456789abcdef";
for (int i = 0; i < 8; ++i) {
uint16_t word = ((uint16_t)ip[i * 2] << 8) | ip[i * 2 + 1];
int started = 0;
for (int shift = 12; shift >= 0; shift -= 4) {
unsigned nibble = (word >> shift) & 0xfu;
if (nibble || started || shift == 0) {
char c = hex[nibble];
print_char(c);
started = 1;
}
}
if (i != 7) print(":");
}
}
// Prints socket address.
static void print_socket_address(const SYS_SOCKET_ADDRESS* address) {
if (!address) return;
if (address->Family == SYS_AF_INET) {
print_ipv4(address->Address);
print(":");
print_u64(address->Port);
} else if (address->Family == SYS_AF_INET6) {
print("[");
print_ipv6(address->Address);
print("]:");
print_u64(address->Port);
} else {
print("unknown");
}
}
// Implements the next token operation.
static const char* next_token(const char* s, char* out, size_t outSize) {
if (!s || !out || outSize == 0) return NULL;
@@ -66,19 +101,28 @@ static bool parse_port(const char* s, uint16_t* out) {
// Implements the usage operation.
static void usage(void) {
print("usage: tcpserve.exe [port]\n");
print("usage: tcpserve.exe [-4|-6] [port]\n");
print("default: tcpserve.exe 8080\n");
}
// Runs the requested operation.
int main(void) {
uint16_t port = 8080;
uint32_t family = SYS_AF_INET;
const char* cmd = SysGetPeb()->CommandLine.Data;
char tok[64];
cmd = next_token(cmd, tok, sizeof(tok)); // program path
if (cmd) {
(void)next_token(cmd, tok, sizeof(tok));
const char* rest = next_token(cmd, tok, sizeof(tok));
if (strcmp(tok, "-4") == 0 || strcmp(tok, "-6") == 0) {
family = strcmp(tok, "-6") == 0 ? SYS_AF_INET6 : SYS_AF_INET;
cmd = rest;
if (cmd)
(void)next_token(cmd, tok, sizeof(tok));
else
tok[0] = 0;
}
if (tok[0] && !parse_port(tok, &port)) {
usage();
SysExitProcess(1);
@@ -86,61 +130,55 @@ int main(void) {
}
uint32_t listener = 0;
uint32_t st = SysCreateSocket(SYS_AF_INET, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &listener);
if (st != SYS_STATUS_SUCCESS) {
uint32_t st = SysCreateSocket(family, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &listener);
if (st != STATUS_SUCCESS) {
print("tcpserve: SysCreateSocket failed "); print_u64(st); print("\n");
SysExitProcess(1);
}
SYS_SOCKET_ADDRESS_IPV4 bindAddr;
SYS_SOCKET_ADDRESS bindAddr;
memset(&bindAddr, 0, sizeof(bindAddr));
bindAddr.Family = SYS_AF_INET;
bindAddr.Size = sizeof(bindAddr);
bindAddr.Family = family;
bindAddr.Port = port;
bindAddr.Address[0] = 0;
bindAddr.Address[1] = 0;
bindAddr.Address[2] = 0;
bindAddr.Address[3] = 0;
st = SysBindSocket(listener, &bindAddr, sizeof(bindAddr));
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("tcpserve: SysBindSocket failed "); print_u64(st); print("\n");
SysCloseHandle(listener);
SysExitProcess(1);
}
st = SysListenSocket(listener, 4);
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("tcpserve: SysListenSocket failed "); print_u64(st); print("\n");
SysCloseHandle(listener);
SysExitProcess(1);
}
print("tcpserve: listening on 0.0.0.0:"); print_u64(port); print("\n");
print("tcpserve: listening on ");
if (family == SYS_AF_INET6) print("[::]:"); else print("0.0.0.0:");
print_u64(port); print("\n");
print("tcpserve: accepting one connection...\n");
SYS_SOCKET_ADDRESS_IPV4 remote;
SYS_SOCKET_ADDRESS remote;
size_t remoteSize = sizeof(remote);
uint32_t client = 0;
st = SysAcceptSocket(listener, &remote, &remoteSize, &client);
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("tcpserve: SysAcceptSocket failed "); print_u64(st); print("\n");
SysCloseHandle(listener);
SysExitProcess(1);
}
print("tcpserve: accepted from ");
if (remote.Family == SYS_AF_INET) {
print_ipv4(remote.Address);
print(":"); print_u64(remote.Port);
} else {
print("unknown");
}
print_socket_address(&remote);
print("\n");
char request[1025];
size_t got = 0;
st = SysRecvSocket(client, request, sizeof(request) - 1, 0, &got);
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("tcpserve: SysRecvSocket failed "); print_u64(st); print("\n");
SysCloseHandle(client);
SysCloseHandle(listener);
@@ -179,9 +217,9 @@ int main(void) {
size_t sent = 0;
st = SysSendSocket(client, response, off, 0, &sent);
if (st == SYS_STATUS_SUCCESS)
if (st == STATUS_SUCCESS)
st = SysSendSocket(client, body, sizeof(body) - 1, 0, &sent);
if (st != SYS_STATUS_SUCCESS)
if (st != STATUS_SUCCESS)
print("tcpserve: SysSendSocket failed "), print_u64(st), print("\n");
SysShutdownSocket(client, SYS_SHUTDOWN_BOTH);
+98 -30
View File
@@ -35,6 +35,41 @@ static void print_ipv4(const uint8_t ip[4]) {
print_u64(ip[2]); print("."); print_u64(ip[3]);
}
// Prints ipv6.
static void print_ipv6(const uint8_t ip[16]) {
static const char hex[] = "0123456789abcdef";
for (int i = 0; i < 8; ++i) {
uint16_t word = ((uint16_t)ip[i * 2] << 8) | ip[i * 2 + 1];
int started = 0;
for (int shift = 12; shift >= 0; shift -= 4) {
unsigned nibble = (word >> shift) & 0xfu;
if (nibble || started || shift == 0) {
char c = hex[nibble];
print_char(c);
started = 1;
}
}
if (i != 7) print(":");
}
}
// Prints socket address.
static void print_socket_address(const SYS_SOCKET_ADDRESS* address) {
if (!address) return;
if (address->Family == SYS_AF_INET) {
print_ipv4(address->Address);
print(":");
print_u64(address->Port);
} else if (address->Family == SYS_AF_INET6) {
print("[");
print_ipv6(address->Address);
print("]:");
print_u64(address->Port);
} else {
print("unknown");
}
}
// Implements the next token operation.
static const char* next_token(const char* s, char* out, size_t outSize) {
if (!s || !out || outSize == 0) return NULL;
@@ -97,21 +132,54 @@ static bool parse_port(const char* s, uint16_t* out) {
// Implements the usage operation.
static void usage(void) {
print("usage:\n");
print(" udp.exe send <ipv4> <port> [message]\n");
print(" udp.exe listen <port>\n");
print(" udp.exe send <host|address> <port> [message]\n");
print(" udp.exe listen [-4|-6] <port>\n");
print("examples:\n");
print(" udp.exe send 10.0.2.2 9999 hello\n");
print(" udp.exe listen 9999\n");
print(" udp.exe send ::1 9999 hello\n");
print(" udp.exe listen -6 9999\n");
}
// Resolves an IPv4 or IPv6 destination.
static uint32_t resolve_address(const char* text, SYS_SOCKET_ADDRESS* out) {
uint8_t ip[4];
if (parse_ipv4(text, ip)) {
memset(out, 0, sizeof(*out));
out->Size = sizeof(*out);
out->Family = SYS_AF_INET;
memcpy(out->Address, ip, 4);
return STATUS_SUCCESS;
}
SYS_RESOLVE_HOST_REQUEST req;
size_t addressSize = sizeof(*out);
memset(out, 0, sizeof(*out));
memset(&req, 0, sizeof(req));
req.Name = SysSvFromCstr(text);
req.Family = SYS_AF_INET6;
req.AddressOut = out;
req.AddressSizeInOut = &addressSize;
uint32_t status = SysResolveHost(&req);
if (status == STATUS_SUCCESS)
return STATUS_SUCCESS;
addressSize = sizeof(*out);
memset(out, 0, sizeof(*out));
req.Family = SYS_AF_INET;
return SysResolveHost(&req);
}
// Runs send.
static int run_send(const char* cmd) {
char tok[256];
uint8_t ip[4];
SYS_SOCKET_ADDRESS remote;
uint16_t port = 0;
cmd = next_token(cmd, tok, sizeof(tok));
if (!tok[0] || !parse_ipv4(tok, ip)) { usage(); return 1; }
if (!tok[0]) { usage(); return 1; }
uint32_t status = resolve_address(tok, &remote);
if (status != STATUS_SUCCESS) { print("udp: resolve failed "); print_u64(status); print("\n"); return 1; }
cmd = next_token(cmd, tok, sizeof(tok));
if (!tok[0] || !parse_port(tok, &port)) { usage(); return 1; }
@@ -128,20 +196,13 @@ static int run_send(const char* cmd) {
}
uint32_t sock = 0;
uint32_t st = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock);
if (st != SYS_STATUS_SUCCESS) {
uint32_t st = SysCreateSocket(remote.Family, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock);
if (st != STATUS_SUCCESS) {
print("udp: SysCreateSocket failed "); print_u64(st); print("\n");
return 1;
}
SYS_SOCKET_ADDRESS_IPV4 remote;
memset(&remote, 0, sizeof(remote));
remote.Family = SYS_AF_INET;
remote.Port = port;
remote.Address[0] = ip[0];
remote.Address[1] = ip[1];
remote.Address[2] = ip[2];
remote.Address[3] = ip[3];
size_t sent = 0;
SYS_SOCKET_SEND_TO_REQUEST req;
@@ -154,12 +215,12 @@ static int run_send(const char* cmd) {
st = SysSendToSocket(sock, &req);
SysCloseHandle(sock);
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("udp: SysSendToSocket failed "); print_u64(st); print("\n");
return 1;
}
print("udp: sent "); print_u64(sent); print(" bytes to "); print_ipv4(ip); print(":"); print_u64(port); print("\n");
print("udp: sent "); print_u64(sent); print(" bytes to "); print_socket_address(&remote); print("\n");
return 0;
}
@@ -167,33 +228,44 @@ static int run_send(const char* cmd) {
static int run_listen(const char* cmd) {
char tok[64];
uint16_t port = 0;
uint32_t family = SYS_AF_INET;
(void)next_token(cmd, tok, sizeof(tok));
const char* rest = next_token(cmd, tok, sizeof(tok));
if (strcmp(tok, "-4") == 0 || strcmp(tok, "-6") == 0) {
family = strcmp(tok, "-6") == 0 ? SYS_AF_INET6 : SYS_AF_INET;
if (rest)
(void)next_token(rest, tok, sizeof(tok));
else
tok[0] = 0;
}
if (!tok[0] || !parse_port(tok, &port)) { usage(); return 1; }
uint32_t sock = 0;
uint32_t st = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock);
if (st != SYS_STATUS_SUCCESS) {
uint32_t st = SysCreateSocket(family, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock);
if (st != STATUS_SUCCESS) {
print("udp: SysCreateSocket failed "); print_u64(st); print("\n");
return 1;
}
SYS_SOCKET_ADDRESS_IPV4 bindAddr;
SYS_SOCKET_ADDRESS bindAddr;
memset(&bindAddr, 0, sizeof(bindAddr));
bindAddr.Family = SYS_AF_INET;
bindAddr.Size = sizeof(bindAddr);
bindAddr.Family = family;
bindAddr.Port = port;
st = SysBindSocket(sock, &bindAddr, sizeof(bindAddr));
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("udp: SysBindSocket failed "); print_u64(st); print("\n");
SysCloseHandle(sock);
return 1;
}
print("udp: listening on 0.0.0.0:"); print_u64(port); print(" for one datagram\n");
print("udp: listening on ");
if (family == SYS_AF_INET6) print("[::]:"); else print("0.0.0.0:");
print_u64(port); print(" for one datagram\n");
char buf[1025];
SYS_SOCKET_ADDRESS_IPV4 remote;
SYS_SOCKET_ADDRESS remote;
size_t remoteSize = sizeof(remote);
size_t got = 0;
SYS_SOCKET_RECV_FROM_REQUEST req;
@@ -206,18 +278,14 @@ static int run_listen(const char* cmd) {
st = SysRecvFromSocket(sock, &req);
SysCloseHandle(sock);
if (st != SYS_STATUS_SUCCESS) {
if (st != STATUS_SUCCESS) {
print("udp: SysRecvFromSocket failed "); print_u64(st); print("\n");
return 1;
}
buf[got] = 0;
print("udp: received "); print_u64(got); print(" bytes from ");
if (remote.Family == SYS_AF_INET) {
print_ipv4(remote.Address); print(":"); print_u64(remote.Port);
} else {
print("unknown");
}
print_socket_address(&remote);
print("\n");
print(buf);
if (got && buf[got - 1] != '\n') print("\n");
+17 -8
View File
@@ -130,7 +130,11 @@ static const char* path_to_cstr(SYS_STRING_VIEW path, char* out, size_t outSize)
}
static uint32_t open_file_view(SYS_STRING_VIEW path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { path, handleOut };
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(path,
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
handleOut);
return SysOpenFile(&req);
}
@@ -146,7 +150,7 @@ int main(void) {
uint32_t file = 0;
uint32_t status = open_file_view(pathView, &file);
if (status != SYS_STATUS_SUCCESS) {
if (status != STATUS_SUCCESS) {
char pathBuf[260];
print("video: failed to open input file: ");
print(path_to_cstr(pathView, pathBuf, sizeof(pathBuf)));
@@ -154,7 +158,12 @@ int main(void) {
fail_status("SysOpenFile", status);
}
uint64_t fileSize = SysGetFileSize(file);
uint64_t fileSize = 0;
status = SysGetFileSize(file, &fileSize);
if (status != STATUS_SUCCESS) {
SysCloseHandle(file);
fail_status("SysGetFileSize", status);
}
if (fileSize == 0) {
SysCloseHandle(file);
print("video: input file is empty\n");
@@ -163,7 +172,7 @@ int main(void) {
uint32_t mapping = 0;
status = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, fileSize, &mapping);
if (status != SYS_STATUS_SUCCESS || !mapping) {
if (status != STATUS_SUCCESS || !mapping) {
SysCloseHandle(file);
fail_status("SysCreateFileMapping", status);
}
@@ -171,7 +180,7 @@ int main(void) {
void* buf = 0;
status = SysMapFile(mapping, 0, (size_t)fileSize,
SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &buf);
if (status != SYS_STATUS_SUCCESS || !buf) {
if (status != STATUS_SUCCESS || !buf) {
SysCloseHandle(mapping);
SysCloseHandle(file);
fail_status("SysMapFile", status);
@@ -207,7 +216,7 @@ int main(void) {
uint32_t surface = 0;
status = SysCreateSurface(width, height, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (status != SYS_STATUS_SUCCESS || !surface) {
if (status != STATUS_SUCCESS || !surface) {
BinkClose(b);
SysFreeVm(buf);
fail_status("SysCreateSurface", status);
@@ -216,7 +225,7 @@ int main(void) {
void* surfaceBase = 0;
uint32_t pitchBytes = 0;
status = SysMapSurface(surface, &surfaceBase, &pitchBytes);
if (status != SYS_STATUS_SUCCESS || !surfaceBase || pitchBytes < width * sizeof(uint32_t)) {
if (status != STATUS_SUCCESS || !surfaceBase || pitchBytes < width * sizeof(uint32_t)) {
SysCloseHandle(surface);
BinkClose(b);
SysFreeVm(buf);
@@ -244,7 +253,7 @@ int main(void) {
BinkCopyToBuffer(b, surfaceBase, pitchBytes, height, 0, 0, BINKSURFACE32);
status = SysPresentSurface(surface, &fullFrame, 1);
if (status != SYS_STATUS_SUCCESS) {
if (status != STATUS_SUCCESS) {
SysCloseHandle(surface);
BinkClose(b);
SysFreeVm(buf);
+57 -2
View File
@@ -89,6 +89,43 @@ typedef int32_t pid_t;
typedef int32_t off_t;
typedef uint32_t mode_t;
typedef int64_t ssize_t;
#define S_IFMT 0170000
#define S_IFSOCK 0140000
#define S_IFCHR 0020000
#define S_IFDIR 0040000
#define S_IFREG 0100000
#define S_IRUSR 0000400
#define S_IWUSR 0000200
#define S_IXUSR 0000100
#define S_IRGRP 0000040
#define S_IWGRP 0000020
#define S_IXGRP 0000010
#define S_IROTH 0000004
#define S_IWOTH 0000002
#define S_IXOTH 0000001
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
struct stat {
uint64_t st_dev;
uint64_t st_ino;
mode_t st_mode;
uint32_t st_nlink;
uint32_t st_uid;
uint32_t st_gid;
uint64_t st_rdev;
off_t st_size;
int64_t st_atime;
int64_t st_mtime;
int64_t st_ctime;
};
__declspec(dllexport) int stat(const char* path, struct stat* out);
__declspec(dllexport) int fstat(int fd, struct stat* out);
__declspec(dllexport) char* realpath(const char* path, char* resolved_path);
__declspec(dllexport) void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset);
__declspec(dllexport) int munmap(void* addr, size_t length);
@@ -101,13 +138,17 @@ typedef uint32_t in_addr_t;
typedef uint32_t socklen_t;
#define AF_INET 2
#define AF_INET6 10
#define AF_UNSPEC 0
#define PF_INET AF_INET
#define PF_INET6 AF_INET6
#define SOCK_STREAM 1
#define SOCK_DGRAM 2
#define SOCK_RAW 3
#define IPPROTO_ICMP 1
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17
#define IPPROTO_ICMPV6 58
#define SHUT_RD 0
#define SHUT_WR 1
@@ -125,6 +166,10 @@ struct in_addr {
in_addr_t s_addr;
};
struct in6_addr {
uint8_t s6_addr[16];
};
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
@@ -137,6 +182,14 @@ struct sockaddr_in {
char sin_zero[8];
};
struct sockaddr_in6 {
sa_family_t sin6_family;
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
struct hostent {
char* h_name;
char** h_aliases;
@@ -184,6 +237,8 @@ __declspec(dllexport) uint32_t htonl(uint32_t x);
__declspec(dllexport) uint32_t ntohl(uint32_t x);
__declspec(dllexport) in_addr_t inet_addr(const char* text);
__declspec(dllexport) char* inet_ntoa(struct in_addr in);
__declspec(dllexport) int inet_pton(int af, const char* src, void* dst);
__declspec(dllexport) const char* inet_ntop(int af, const void* src, char* dst, socklen_t size);
__declspec(dllexport) int socket(int domain, int type, int protocol);
__declspec(dllexport) int bind(int fd, const struct sockaddr* addr, socklen_t addrlen);
@@ -208,13 +263,13 @@ typedef struct { uint32_t Handle; } pthread_mutex_t;
typedef void* pthread_attr_t; // unused in v1
typedef void* pthread_mutexattr_t;
// posix_spawn types (file_actions and attrp both ignored in v1).
// posix_spawn types (file_actions and attrp both ignored in v1).
typedef void* posix_spawn_file_actions_t;
typedef void* posix_spawnattr_t;
__declspec(dllexport) const char* posix_get_command_line(void);
// dirent (used by posix_dir.c).
// dirent (used by posix_dir.c).
#define NAME_MAX 259
typedef struct dirent {
+155 -8
View File
@@ -4,11 +4,11 @@
#include <ntlite/syscall.h>
#include <stdlib.h>
#include "posix.h"
#include "posix_fd.h"
#include "posix_path.h"
typedef SYS_DIR_ENTRY KDIR_ENTRY;
#define KATTR_DIR 0x10
// Implements the chdir operation.
@@ -24,16 +24,84 @@ __declspec(dllexport) int chdir(const char* path) {
// Implements the getcwd operation.
__declspec(dllexport) char* getcwd(char* buf, size_t size) {
if (!buf || size == 0) { errno = EINVAL; return (char*)0; }
char native[512];
size_t nativeLen = 0;
SYS_GET_CWD_REQUEST req = { native, sizeof(native) - 1, &nativeLen };
uint32_t s = SysGetCwd(&req);
if (s == 0) native[nativeLen] = '\0';
if (s != 0) { errno = ERANGE; return (char*)0; }
NativeToPosix(native, buf, size);
SYS_STRING_VIEW cwd = SysGetCurrentDirectoryView();
if ((cwd.Length && !cwd.Data) || cwd.Length > SYS_CWD_MAX) {
errno = ERANGE;
return (char*)0;
}
char native[SYS_CWD_MAX + 1];
if (cwd.Length) memcpy(native, cwd.Data, cwd.Length);
native[cwd.Length] = '\0';
char posix[SYS_CWD_MAX + 1];
NativeToPosix(native, posix, sizeof(posix));
size_t needed = strlen(posix) + 1;
if (needed > size) {
errno = ERANGE;
return (char*)0;
}
memcpy(buf, posix, needed);
return buf;
}
// Copies one path string into either caller storage or a new allocation.
static char* PosixCopyResolvedPath(const char* value, char* resolvedPath) {
if (!value) { errno = EINVAL; return (char*)0; }
size_t needed = strlen(value) + 1;
char* out = resolvedPath;
if (!out) {
out = (char*)malloc(needed);
if (!out) { errno = ENOMEM; return (char*)0; }
}
memcpy(out, value, needed);
return out;
}
// Implements the realpath operation over the kernel lexical resolver.
__declspec(dllexport) char* realpath(const char* path, char* resolved_path) {
if (!path) { errno = EINVAL; return (char*)0; }
char native[SYS_PATH_MAX + 1];
const char* translated = PosixTranslatePath(path, native, sizeof(native));
if (strcmp(translated, SYNTH_DEVNULL) == 0 || strcmp(translated, SYNTH_DEVZERO) == 0)
return PosixCopyResolvedPath(path, resolved_path);
size_t required = 0;
SYS_REAL_PATH_REQUEST query = {
sizeof(query),
SYS_REAL_PATH_FLAG_NONE,
SysSvFromCstr(translated),
(char*)0,
0,
&required,
};
uint32_t status = SysRealPath(&query);
if (status != STATUS_SUCCESS) { errno = PosixStatusToErrno(status); return (char*)0; }
if (required == 0 || required > SYS_CWD_MAX) { errno = ERANGE; return (char*)0; }
char nativeResolved[SYS_CWD_MAX];
SYS_REAL_PATH_REQUEST request = {
sizeof(request),
SYS_REAL_PATH_FLAG_NONE,
SysSvFromCstr(translated),
nativeResolved,
sizeof(nativeResolved),
&required,
};
status = SysRealPath(&request);
if (status != STATUS_SUCCESS) { errno = PosixStatusToErrno(status); return (char*)0; }
char posix[SYS_CWD_MAX];
NativeToPosix(nativeResolved, posix, sizeof(posix));
return PosixCopyResolvedPath(posix, resolved_path);
}
// Implements the opendir operation.
__declspec(dllexport) DIR* opendir(const char* path) {
char native[512];
@@ -74,3 +142,82 @@ __declspec(dllexport) int closedir(DIR* d) {
free(d);
return 0;
}
// Converts native file metadata to a POSIX stat structure.
static int PosixFillStat(const SYS_FILE_STAT* native, struct stat* out) {
if (!native || !out) { errno = EINVAL; return -1; }
if (native->FileSize > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
memset(out, 0, sizeof(*out));
out->st_dev = native->VolumeId;
out->st_ino = native->FileId;
out->st_nlink = native->LinkCount ? native->LinkCount : 1;
out->st_size = (off_t)native->FileSize;
out->st_atime = (int64_t)native->LastAccessTime.EpochSeconds;
out->st_mtime = (int64_t)native->LastWriteTime.EpochSeconds;
out->st_ctime = (int64_t)native->ChangeTime.EpochSeconds;
if (native->Type == SYS_FILE_TYPE_DIRECTORY) {
out->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR |
S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
} else {
out->st_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
}
if (native->Attributes & SYS_FILE_ATTR_READ_ONLY)
out->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
return 0;
}
// Fills stat for one synthetic descriptor.
static int PosixFillSyntheticStat(mode_t type, struct stat* out) {
if (!out) { errno = EINVAL; return -1; }
memset(out, 0, sizeof(*out));
out->st_mode = type | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
out->st_nlink = 1;
return 0;
}
// Implements the stat operation.
__declspec(dllexport) int stat(const char* path, struct stat* out) {
if (!path || !out) { errno = EINVAL; return -1; }
char native[512];
const char* p = PosixTranslatePath(path, native, sizeof(native));
if (strcmp(p, SYNTH_DEVNULL) == 0 || strcmp(p, SYNTH_DEVZERO) == 0)
return PosixFillSyntheticStat(S_IFCHR, out);
SYS_FILE_STAT nativeStat;
SYS_STAT_FILE_REQUEST req = { SysSvFromCstr(p), &nativeStat };
uint32_t s = SysStatFile(&req);
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
return PosixFillStat(&nativeStat, out);
}
// Implements the fstat operation.
__declspec(dllexport) int fstat(int fd, struct stat* out) {
if (!out) { errno = EINVAL; return -1; }
uint32_t pid = SysGetCurrentPid();
FD_ENTRY* e = FdGet(pid, fd);
if (!e) return -1;
switch (e->Type) {
case FD_CONSOLE_R:
case FD_CONSOLE_W:
case FD_DEVNULL:
case FD_DEVZERO:
return PosixFillSyntheticStat(S_IFCHR, out);
case FD_SOCKET:
return PosixFillSyntheticStat(S_IFSOCK, out);
case FD_FILE: {
SYS_FILE_STAT nativeStat;
uint32_t s = SysGetFileStat(e->KHandle, &nativeStat);
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
return PosixFillStat(&nativeStat, out);
}
default:
errno = EBADF;
return -1;
}
}
+18 -14
View File
@@ -9,20 +9,24 @@ int errno = 0;
// Implements the status To Errno operation.
int PosixStatusToErrno(uint32_t s) {
switch (s) {
case 0x0000: return 0; // STATUS_SUCCESS
case 0x0002: return ENOSYS; // STATUS_NOT_SUPPORTED
case 0x0003: return EAGAIN; // STATUS_TIMEOUT / would block
case 0x0010: return EINVAL; // STATUS_INVALID_PARAMETER
case 0x0011: return EBADF; // STATUS_INVALID_HANDLE
case 0x0012: return ERANGE; // STATUS_BUFFER_TOO_SMALL
case 0x0020: return ENOMEM; // STATUS_NO_MEMORY
case 0x0021: return EACCES; // STATUS_ACCESS_DENIED
case 0x0022: return ENOENT; // STATUS_NOT_FOUND
case 0x0023: return EEXIST; // STATUS_ALREADY_EXISTS
case 0x0030: return ECONNRESET; // STATUS_OBJECT_DEAD
case 0x0031: return EAGAIN; // STATUS_QUEUE_FULL
case 0x0032: return EAGAIN; // STATUS_QUEUE_EMPTY
default: return EIO;
case STATUS_SUCCESS: return 0;
case STATUS_NOT_SUPPORTED: return ENOSYS;
case STATUS_TIMEOUT: return EAGAIN;
case STATUS_WOULD_BLOCK: return EAGAIN;
case STATUS_INVALID_PARAMETER: return EINVAL;
case STATUS_INVALID_HANDLE: return EBADF;
case STATUS_BUFFER_TOO_SMALL: return ERANGE;
case STATUS_NO_MEMORY: return ENOMEM;
case STATUS_ACCESS_DENIED: return EACCES;
case STATUS_NOT_FOUND: return ENOENT;
case STATUS_ALREADY_EXISTS: return EEXIST;
case STATUS_OBJECT_NAME_COLLISION: return EEXIST;
case STATUS_OBJECT_DEAD: return ECONNRESET;
case STATUS_QUEUE_FULL: return EAGAIN;
case STATUS_QUEUE_EMPTY: return EAGAIN;
case STATUS_CANCELLED: return ECANCELED;
case STATUS_NO_MORE_FILES: return ENOENT;
default: return EIO;
}
}
+1 -1
View File
@@ -8,7 +8,7 @@
typedef enum {
FD_FREE,
FD_FILE,
FD_CONSOLE_R, // fd 0 - reads from keyboard via SysReadKeyboardChar
FD_CONSOLE_R, // fd 0 - reads from keyboard through SysReadKeyboardChar
FD_CONSOLE_W, // fd 1, 2 - writes via SysWriteConsole
FD_DEVNULL, // /dev/null
FD_DEVZERO, // /dev/zero
+39 -14
View File
@@ -14,7 +14,11 @@ __declspec(dllexport) void clearscreen(void) {
// Implements the clearkeyboard operation.
__declspec(dllexport) void clearkeyboard(void) {
while (SysPollKeyboard()) (void)SysReadKeyboardChar();
INPUT_KEY key;
while (SysPollKeyboard(&key) == STATUS_SUCCESS) {
char ignored;
(void)SysReadKeyboardChar(&ignored);
}
}
// Opens the requested operation.
@@ -37,17 +41,33 @@ __declspec(dllexport) int open(const char* path, int flags, ...) {
}
uint32_t kh = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(p), &kh };
uint32_t s;
if (flags & O_WRONLY) {
s = SysCreateFile(&req);
} else {
s = SysOpenFile(&req);
}
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
uint32_t access = SYS_FILE_ACCESS_QUERY;
uint32_t disposition = SYS_FILE_OPEN_EXISTING;
if ((flags & O_TRUNC) && (flags & O_WRONLY))
SysTruncateFile(kh, 0);
if ((flags & O_RDWR) == O_RDWR)
access |= SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE;
else if (flags & O_WRONLY)
access |= SYS_FILE_ACCESS_WRITE;
else
access |= SYS_FILE_ACCESS_READ;
if (flags & O_APPEND)
access |= SYS_FILE_ACCESS_APPEND;
if ((flags & O_CREAT) && (flags & O_TRUNC))
disposition = SYS_FILE_CREATE_ALWAYS;
else if (flags & O_CREAT)
disposition = SYS_FILE_OPEN_ALWAYS;
else if (flags & O_TRUNC)
disposition = SYS_FILE_TRUNCATE_EXISTING;
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(p),
access,
disposition,
SYS_FILE_FLAG_NONE,
&kh);
uint32_t s = SysOpenFile(&req);
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
int fd = FdAlloc(pid, type, kh);
if (fd < 0) {
@@ -69,7 +89,8 @@ __declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) {
// Read up to count chars from keyboard, blocking per char.
char* dst = (char*)buf;
for (size_t i = 0; i < count; i++) {
dst[i] = SysReadKeyboardChar();
uint32_t s = SysReadKeyboardChar(&dst[i]);
if (s != STATUS_SUCCESS) { errno = PosixStatusToErrno(s); return -1; }
if (dst[i] == '\n' || dst[i] == '\r') return (ssize_t)(i + 1);
}
return (ssize_t)count;
@@ -174,13 +195,17 @@ __declspec(dllexport) off_t lseek(int fd, off_t offset, int whence) {
target = (int64_t)offset;
break;
case SEEK_CUR: {
uint64_t cur = SysGetFilePos(e->KHandle);
uint64_t cur = 0;
uint32_t s = SysGetFilePos(e->KHandle, &cur);
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
if (cur > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
target = (int64_t)cur + (int64_t)offset;
break;
}
case SEEK_END: {
uint64_t size = SysGetFileSize(e->KHandle);
uint64_t size = 0;
uint32_t s = SysGetFileSize(e->KHandle, &size);
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
if (size > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
target = (int64_t)size + (int64_t)offset;
break;
+3 -3
View File
@@ -52,7 +52,7 @@ __declspec(dllexport) void* mmap(void* addr, size_t length, int prot,
uint32_t mapping = 0;
uint32_t s = SysCreateFileMapping(e->KHandle, protect, SYS_MAP_FLAG_NONE,
maxSize, &mapping);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
errno = PosixStatusToErrno(s);
return MAP_FAILED;
}
@@ -62,7 +62,7 @@ __declspec(dllexport) void* mmap(void* addr, size_t length, int prot,
PosixProtToSysMapAccess(prot, flags),
SYS_MAP_FLAG_NONE, &base);
SysCloseHandle(mapping);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
errno = PosixStatusToErrno(s);
return MAP_FAILED;
}
@@ -73,7 +73,7 @@ __declspec(dllexport) int munmap(void* addr, size_t length) {
(void)length;
if (!addr) { errno = EINVAL; return -1; }
uint32_t s = SysFreeVm(addr);
if (s != SYS_STATUS_SUCCESS) { errno = PosixStatusToErrno(s); return -1; }
if (s != STATUS_SUCCESS) { errno = PosixStatusToErrno(s); return -1; }
return 0;
}
+1 -2
View File
@@ -6,7 +6,6 @@
#include "posix.h"
#include "posix_path.h"
static SYS_PEB* gProcessPeb = NULL;
// Implements the peb operation.
@@ -128,7 +127,7 @@ __declspec(dllexport) pid_t waitpid(pid_t pid, int* status, int options) {
SysWait(handle, SYS_WAIT_FLAG_NONE);
int exitCode = 0;
int32_t exitCode = 0;
SysGetProcessExitCode(handle, &exitCode);
if (status) *status = exitCode << 8; // WEXITSTATUS convention
+245 -59
View File
@@ -58,6 +58,95 @@ static int ParseIpv4Bytes(const char* text, uint8_t out[4]) {
return *p == '\0';
}
static int ParseHex(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}
static int ParseIpv6Bytes(const char* text, uint8_t out[16]) {
uint16_t words[8];
int count = 0;
int compress = -1;
const char* p = text;
if (!text || !*text || !out)
return 0;
memset(words, 0, sizeof(words));
if (p[0] == ':' && p[1] == ':') {
compress = 0;
p += 2;
if (*p == '\0')
count = 0;
}
while (*p) {
if (count >= 8)
return 0;
unsigned value = 0;
int digits = 0;
while (digits < 4) {
int h = ParseHex(*p);
if (h < 0)
break;
value = (value << 4) | (unsigned)h;
p++;
digits++;
}
if (digits == 0)
return 0;
words[count++] = (uint16_t)value;
if (*p == '\0')
break;
if (*p != ':')
return 0;
p++;
if (*p == ':') {
if (compress >= 0)
return 0;
compress = count;
p++;
if (*p == '\0')
break;
}
}
if (compress >= 0) {
int zeros = 8 - count;
if (zeros <= 0)
return 0;
for (int i = count - 1; i >= compress; --i)
words[i + zeros] = words[i];
for (int i = 0; i < zeros; ++i)
words[compress + i] = 0;
} else if (count != 8) {
return 0;
}
for (int i = 0; i < 8; ++i) {
out[i * 2 + 0] = (uint8_t)(words[i] >> 8);
out[i * 2 + 1] = (uint8_t)words[i];
}
return 1;
}
static char* AppendHex16(char* out, uint16_t value) {
static const char hex[] = "0123456789abcdef";
int started = 0;
for (int shift = 12; shift >= 0; shift -= 4) {
unsigned nibble = (value >> shift) & 0xfu;
if (nibble || started || shift == 0) {
*out++ = hex[nibble];
started = 1;
}
}
return out;
}
// Implements the inet addr operation.
__declspec(dllexport) in_addr_t inet_addr(const char* text) {
uint8_t b[4];
@@ -85,41 +174,114 @@ __declspec(dllexport) char* inet_ntoa(struct in_addr in) {
return buf;
}
// Implements the sock Addr To Sys operation.
static int SockAddrToSys(const struct sockaddr* addr, socklen_t addrlen, SYS_SOCKET_ADDRESS_IPV4* out) {
if (!addr || !out || addrlen < sizeof(struct sockaddr_in)) { errno = EINVAL; return -1; }
const struct sockaddr_in* in = (const struct sockaddr_in*)addr;
if (in->sin_family != AF_INET) { errno = EINVAL; return -1; }
// Implements the inet pton operation.
__declspec(dllexport) int inet_pton(int af, const char* src, void* dst) {
if (!src || !dst) { errno = EINVAL; return -1; }
if (af == AF_INET) {
uint8_t b[4];
if (!ParseIpv4Bytes(src, b))
return 0;
memcpy(dst, b, 4);
return 1;
}
if (af == AF_INET6) {
uint8_t b[16];
if (!ParseIpv6Bytes(src, b))
return 0;
memcpy(dst, b, 16);
return 1;
}
errno = EAFNOSUPPORT;
return -1;
}
// Implements the inet ntop operation.
__declspec(dllexport) const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) {
if (!src || !dst) { errno = EINVAL; return (const char*)0; }
if (af == AF_INET) {
if (size < 16) { errno = ERANGE; return (const char*)0; }
struct in_addr in;
memcpy(&in, src, sizeof(in));
strcpy(dst, inet_ntoa(in));
return dst;
}
if (af == AF_INET6) {
if (size < 40) { errno = ERANGE; return (const char*)0; }
const uint8_t* b = (const uint8_t*)src;
char* w = dst;
for (int i = 0; i < 8; ++i) {
uint16_t value = ((uint16_t)b[i * 2] << 8) | b[i * 2 + 1];
w = AppendHex16(w, value);
if (i != 7)
*w++ = ':';
}
*w = '\0';
return dst;
}
errno = EAFNOSUPPORT;
return (const char*)0;
}
// Implements the sock Addr To Sys operation.
static int SockAddrToSys(const struct sockaddr* addr, socklen_t addrlen, SYS_SOCKET_ADDRESS* out) {
if (!addr || !out) { errno = EINVAL; return -1; }
memset(out, 0, sizeof(*out));
out->Family = SYS_AF_INET;
out->Port = ntohs(in->sin_port);
const uint8_t* p = (const uint8_t*)&in->sin_addr.s_addr;
out->Address[0] = p[0];
out->Address[1] = p[1];
out->Address[2] = p[2];
out->Address[3] = p[3];
return 0;
out->Size = sizeof(*out);
if (addr->sa_family == AF_INET) {
if (addrlen < sizeof(struct sockaddr_in)) { errno = EINVAL; return -1; }
const struct sockaddr_in* in = (const struct sockaddr_in*)addr;
out->Family = SYS_AF_INET;
out->Port = ntohs(in->sin_port);
memcpy(out->Address, &in->sin_addr.s_addr, 4);
return 0;
}
if (addr->sa_family == AF_INET6) {
if (addrlen < sizeof(struct sockaddr_in6)) { errno = EINVAL; return -1; }
const struct sockaddr_in6* in6 = (const struct sockaddr_in6*)addr;
out->Family = SYS_AF_INET6;
out->Port = ntohs(in6->sin6_port);
memcpy(out->Address, in6->sin6_addr.s6_addr, 16);
out->ScopeId = in6->sin6_scope_id;
return 0;
}
errno = EAFNOSUPPORT;
return -1;
}
// Implements the to Sock Addr operation.
static void SysToSockAddr(const SYS_SOCKET_ADDRESS_IPV4* in, struct sockaddr* addr, socklen_t* addrlen) {
if (!addr || !addrlen) return;
if (*addrlen < sizeof(struct sockaddr_in)) {
static void SysToSockAddr(const SYS_SOCKET_ADDRESS* in, struct sockaddr* addr, socklen_t* addrlen) {
if (!in || !addr || !addrlen) return;
if (in->Family == SYS_AF_INET) {
if (*addrlen < sizeof(struct sockaddr_in)) {
*addrlen = sizeof(struct sockaddr_in);
return;
}
struct sockaddr_in* out = (struct sockaddr_in*)addr;
memset(out, 0, sizeof(*out));
out->sin_family = AF_INET;
out->sin_port = htons(in->Port);
memcpy(&out->sin_addr.s_addr, in->Address, 4);
*addrlen = sizeof(struct sockaddr_in);
return;
}
struct sockaddr_in* out = (struct sockaddr_in*)addr;
memset(out, 0, sizeof(*out));
out->sin_family = AF_INET;
out->sin_port = htons(in->Port);
uint8_t* p = (uint8_t*)&out->sin_addr.s_addr;
p[0] = in->Address[0];
p[1] = in->Address[1];
p[2] = in->Address[2];
p[3] = in->Address[3];
*addrlen = sizeof(struct sockaddr_in);
if (in->Family == SYS_AF_INET6) {
if (*addrlen < sizeof(struct sockaddr_in6)) {
*addrlen = sizeof(struct sockaddr_in6);
return;
}
struct sockaddr_in6* out = (struct sockaddr_in6*)addr;
memset(out, 0, sizeof(*out));
out->sin6_family = AF_INET6;
out->sin6_port = htons(in->Port);
memcpy(out->sin6_addr.s6_addr, in->Address, 16);
out->sin6_scope_id = in->ScopeId;
*addrlen = sizeof(struct sockaddr_in6);
}
}
// Implements the socket Fd Get operation.
@@ -148,7 +310,7 @@ __declspec(dllexport) int socket(int domain, int type, int protocol) {
__declspec(dllexport) int bind(int fd, const struct sockaddr* addr, socklen_t addrlen) {
FD_ENTRY* e = SocketFdGet(fd);
if (!e) return -1;
SYS_SOCKET_ADDRESS_IPV4 a;
SYS_SOCKET_ADDRESS a;
if (SockAddrToSys(addr, addrlen, &a) != 0) return -1;
uint32_t s = SysBindSocket(e->KHandle, &a, sizeof(a));
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
@@ -169,10 +331,10 @@ __declspec(dllexport) int accept(int fd, struct sockaddr* addr, socklen_t* addrl
FD_ENTRY* e = SocketFdGet(fd);
if (!e) return -1;
SYS_SOCKET_ADDRESS_IPV4 remote;
SYS_SOCKET_ADDRESS remote;
size_t remoteSize = sizeof(remote);
uint32_t kh = 0;
uint32_t s = SysAcceptSocket(e->KHandle, addr ? &remote : (SYS_SOCKET_ADDRESS_IPV4*)0,
uint32_t s = SysAcceptSocket(e->KHandle, addr ? &remote : (SYS_SOCKET_ADDRESS*)0,
addr ? &remoteSize : (size_t*)0, &kh);
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
@@ -189,7 +351,7 @@ __declspec(dllexport) int accept(int fd, struct sockaddr* addr, socklen_t* addrl
__declspec(dllexport) int connect(int fd, const struct sockaddr* addr, socklen_t addrlen) {
FD_ENTRY* e = SocketFdGet(fd);
if (!e) return -1;
SYS_SOCKET_ADDRESS_IPV4 a;
SYS_SOCKET_ADDRESS a;
if (SockAddrToSys(addr, addrlen, &a) != 0) return -1;
uint32_t s = SysConnectSocket(e->KHandle, &a, sizeof(a));
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
@@ -220,7 +382,7 @@ __declspec(dllexport) ssize_t sendto(int fd, const void* buf, size_t len, int fl
const struct sockaddr* dest, socklen_t destlen) {
FD_ENTRY* e = SocketFdGet(fd);
if (!e) return -1;
SYS_SOCKET_ADDRESS_IPV4 remote;
SYS_SOCKET_ADDRESS remote;
if (SockAddrToSys(dest, destlen, &remote) != 0) return -1;
size_t sent = 0;
@@ -243,7 +405,7 @@ __declspec(dllexport) ssize_t recvfrom(int fd, void* buf, size_t len, int flags,
FD_ENTRY* e = SocketFdGet(fd);
if (!e) return -1;
SYS_SOCKET_ADDRESS_IPV4 remote;
SYS_SOCKET_ADDRESS remote;
size_t remoteSize = sizeof(remote);
size_t got = 0;
SYS_SOCKET_RECV_FROM_REQUEST req;
@@ -251,7 +413,7 @@ __declspec(dllexport) ssize_t recvfrom(int fd, void* buf, size_t len, int flags,
req.Buffer = buf;
req.Length = len;
req.Flags = (uint32_t)flags;
req.RemoteAddressOut = src ? &remote : (SYS_SOCKET_ADDRESS_IPV4*)0;
req.RemoteAddressOut = src ? &remote : (SYS_SOCKET_ADDRESS*)0;
req.RemoteAddressSizeInOut = src ? &remoteSize : (size_t*)0;
req.ReceivedOut = &got;
@@ -408,21 +570,36 @@ static unsigned ParsePort(const char* s) {
return *s == '\0' ? v : 0;
}
// Implements the resolve Name Ipv4 operation.
static int ResolveNameIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4* out) {
uint8_t b[4];
if (ParseIpv4Bytes(name, b)) {
memset(out, 0, sizeof(*out));
out->Family = SYS_AF_INET;
out->Address[0] = b[0]; out->Address[1] = b[1]; out->Address[2] = b[2]; out->Address[3] = b[3];
return 0;
// Implements the resolve Name operation.
static int ResolveNameFamily(const char* name, int family, SYS_SOCKET_ADDRESS* out) {
if (!name || !out) { errno = EINVAL; return -1; }
memset(out, 0, sizeof(*out));
out->Size = sizeof(*out);
if (family == AF_INET) {
uint8_t b[4];
if (ParseIpv4Bytes(name, b)) {
out->Family = SYS_AF_INET;
memcpy(out->Address, b, 4);
return 0;
}
} else if (family == AF_INET6) {
uint8_t b[16];
if (ParseIpv6Bytes(name, b)) {
out->Family = SYS_AF_INET6;
memcpy(out->Address, b, 16);
return 0;
}
} else {
errno = EAFNOSUPPORT;
return -1;
}
size_t size = sizeof(*out);
SYS_RESOLVE_HOST_REQUEST req;
memset(&req, 0, sizeof(req));
req.Name = SysSvFromCstr(name);
req.Family = SYS_AF_INET;
req.Family = family == AF_INET6 ? SYS_AF_INET6 : SYS_AF_INET;
req.AddressOut = out;
req.AddressSizeInOut = &size;
uint32_t s = SysResolveHost(&req);
@@ -430,6 +607,16 @@ static int ResolveNameIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4* out) {
return 0;
}
static int ResolveNameAny(const char* name, int family, SYS_SOCKET_ADDRESS* out) {
if (family == AF_INET || family == AF_INET6)
return ResolveNameFamily(name, family, out);
if (family != AF_UNSPEC) { errno = EAFNOSUPPORT; return -1; }
if (ResolveNameFamily(name, AF_INET6, out) == 0)
return 0;
return ResolveNameFamily(name, AF_INET, out);
}
// Implements the gethostbyname operation.
__declspec(dllexport) struct hostent* gethostbyname(const char* name) {
static struct hostent h;
@@ -440,8 +627,8 @@ __declspec(dllexport) struct hostent* gethostbyname(const char* name) {
if (!name || strlen(name) >= sizeof(hname)) { errno = EINVAL; return (struct hostent*)0; }
SYS_SOCKET_ADDRESS_IPV4 a;
if (ResolveNameIpv4(name, &a) != 0) return (struct hostent*)0;
SYS_SOCKET_ADDRESS a;
if (ResolveNameFamily(name, AF_INET, &a) != 0) return (struct hostent*)0;
strcpy(hname, name);
memcpy(addr, a.Address, 4);
@@ -462,35 +649,34 @@ __declspec(dllexport) int getaddrinfo(const char* node, const char* service,
if (!res) return EINVAL;
*res = (struct addrinfo*)0;
int family = hints ? hints->ai_family : AF_INET;
if (family != AF_INET && family != 0) return EINVAL;
int family = hints ? hints->ai_family : AF_UNSPEC;
if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC) return EAFNOSUPPORT;
SYS_SOCKET_ADDRESS_IPV4 a;
SYS_SOCKET_ADDRESS a;
memset(&a, 0, sizeof(a));
a.Family = SYS_AF_INET;
a.Size = sizeof(a);
if (!node) {
a.Address[0] = 0; a.Address[1] = 0; a.Address[2] = 0; a.Address[3] = 0;
} else if (ResolveNameIpv4(node, &a) != 0) {
a.Family = family == AF_INET6 ? SYS_AF_INET6 : SYS_AF_INET;
} else if (ResolveNameAny(node, family, &a) != 0) {
return errno;
}
a.Port = service ? ParsePort(service) : 0;
size_t total = sizeof(struct addrinfo) + sizeof(struct sockaddr_in);
socklen_t addrLen = a.Family == SYS_AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
size_t total = sizeof(struct addrinfo) + addrLen;
struct addrinfo* ai = (struct addrinfo*)malloc(total);
if (!ai) return ENOMEM;
memset(ai, 0, total);
struct sockaddr_in* sin = (struct sockaddr_in*)((uint8_t*)ai + sizeof(struct addrinfo));
sin->sin_family = AF_INET;
sin->sin_port = htons(a.Port);
uint8_t* p = (uint8_t*)&sin->sin_addr.s_addr;
p[0] = a.Address[0]; p[1] = a.Address[1]; p[2] = a.Address[2]; p[3] = a.Address[3];
struct sockaddr* sa = (struct sockaddr*)((uint8_t*)ai + sizeof(struct addrinfo));
socklen_t cap = addrLen;
SysToSockAddr(&a, sa, &cap);
ai->ai_family = AF_INET;
ai->ai_family = a.Family == SYS_AF_INET6 ? AF_INET6 : AF_INET;
ai->ai_socktype = hints ? hints->ai_socktype : 0;
ai->ai_protocol = hints ? hints->ai_protocol : 0;
ai->ai_addrlen = sizeof(struct sockaddr_in);
ai->ai_addr = (struct sockaddr*)sin;
ai->ai_addrlen = cap;
ai->ai_addr = sa;
ai->ai_next = (struct addrinfo*)0;
*res = ai;
return 0;
-1
View File
@@ -54,7 +54,6 @@ typedef struct tagBITMAPINFO {
RGBQUAD bmiColors[1];
} BITMAPINFO;
// GDI Exports.
// Static buffers to persist between function calls.
+136 -145
View File
@@ -13,11 +13,8 @@
// multiple heaps, and NULL is a legal (no-op) heap argument under Win32.
// - Win32 per-thread state lives behind SYS_TEB::SubsystemData so the native
// TEB ABI stays subsystem-neutral.
// - File I/O uses SYS_OPEN_FILE / SYS_READ_FILE / SYS_SEEK_FILE /
// SYS_GET_FILE_SIZE / SYS_CLOSE_HANDLE (unified close path - no separate
// SysCloseFile).
// - WriteFile / CreateFileA in write-mode / SetEndOfFile are stubs because
// the VFS has no write path yet.
// - File I/O uses the flags-based SYS_OPEN_FILE request plus the common
// read/write/seek/query/close syscalls.
#include <stdint.h>
#include <stddef.h>
@@ -221,19 +218,19 @@ static void TranslatePath(const char* input, char* output) {
}
}
// Implements the k32 Open File Native operation.
static uint32_t K32OpenFileNative(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
static uint32_t K32OpenFileNative(const char* path,
uint32_t access,
uint32_t disposition,
uint32_t* handleOut) {
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(path),
access,
disposition,
SYS_FILE_FLAG_NONE,
handleOut);
return SysOpenFile(&req);
}
// Implements the k32 Create File Native operation.
static uint32_t K32CreateFileNative(const char* path, uint32_t* handleOut) {
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
return SysCreateFile(&req);
}
// Implements the k32 Delete File Native operation.
static uint32_t K32DeleteFileNative(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
@@ -254,13 +251,14 @@ static uint32_t K32SetCwdNative(const char* path) {
// Implements the k32 Get Cwd Native operation.
static uint32_t K32GetCwdNative(char* buffer, size_t capacity, size_t* lengthOut) {
size_t len = 0;
size_t writable = (buffer && capacity) ? capacity - 1 : 0;
SYS_GET_CWD_REQUEST req = { buffer, writable, &len };
uint32_t s = SysGetCwd(&req);
if (lengthOut) *lengthOut = len;
if (s == 0 && buffer && capacity) buffer[len] = '\0';
return s;
SYS_STRING_VIEW cwd = SysGetCurrentDirectoryView();
if ((cwd.Length && !cwd.Data) || cwd.Length > SYS_CWD_MAX) return STATUS_INVALID_PARAMETER;
if (lengthOut) *lengthOut = cwd.Length;
if (!buffer || cwd.Length + 1 > capacity) return STATUS_BUFFER_TOO_SMALL;
if (cwd.Length) memcpy(buffer, cwd.Data, cwd.Length);
buffer[cwd.Length] = '\0';
return STATUS_SUCCESS;
}
// Implements the k32 Create Process Native operation.
@@ -323,7 +321,10 @@ static BOOL KiFileExistsA(const char* path) {
char native[MAX_PATH];
TranslatePath(path, native);
uint32_t h = 0;
if (K32OpenFileNative(native, &h) != 0) return FALSE;
if (K32OpenFileNative(native,
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
&h) != 0) return FALSE;
SysCloseHandle(h);
return TRUE;
}
@@ -396,7 +397,7 @@ __declspec(dllexport) DWORD SearchPathA(const char* lpPath, const char* lpFileNa
return 0;
}
// GetLastError / SetLastError (single-threaded stub).
// GetLastError / SetLastError (single-threaded stub).
static SYS_PEB* gProcessPeb = NULL;
// Implements the peb operation.
@@ -416,7 +417,7 @@ __declspec(dllexport) void SetLastError(DWORD err) {
// Process termination.
__declspec(dllexport) _Noreturn void ExitProcess(unsigned int code) {
SysExitProcess((int)code);
SysExitProcess((int32_t)code);
while (true) ; // unreachable
}
@@ -425,17 +426,31 @@ __declspec(dllexport) void OutputDebugStringA(const char* str) {
if (str) K32DebugPrintA(str);
}
// Queries the native monotonic tick frequency.
static uint64_t K32TickFrequency(void) {
uint64_t frequency = 0;
if (SysGetTickFrequency(&frequency) != STATUS_SUCCESS) return 0;
return frequency;
}
// Returns monotonic milliseconds since boot.
static uint64_t K32TickMilliseconds(void) {
uint64_t frequency = K32TickFrequency();
if (!frequency) return 0;
return SysTicksToMilliseconds(SysGetTick(), frequency);
}
// Timer.
__declspec(dllexport) DWORD GetTickCount(void) {
return (DWORD)(SysGetTick() / 1000);
return (DWORD)K32TickMilliseconds();
}
// Returns tick Count64.
__declspec(dllexport) uint64_t GetTickCount64(void) {
return SysGetTick() / 1000;
return K32TickMilliseconds();
}
// Memory (heap argument is ignored - single user-mode heap).
// Memory (heap argument is ignored - single user-mode heap).
#define HEAP_ZERO_MEMORY 0x00000008UL
#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010UL
@@ -511,8 +526,8 @@ __declspec(dllexport) void* VirtualAlloc(void* lpAddress, size_t dwSize,
void* base = NULL;
uint32_t flags = lpAddress ? SYS_VM_FLAG_FIXED : SYS_VM_FLAG_NONE;
uint32_t s = SysAllocVm(lpAddress, pages, WinProtectToSysVm(flProtect), flags, &base);
if (s != SYS_STATUS_SUCCESS) {
gLastError = (s == SYS_STATUS_NOT_FOUND || s == SYS_STATUS_INVALID_PARAMETER) ? 87 : 8;
if (s != STATUS_SUCCESS) {
gLastError = (s == STATUS_NOT_FOUND || s == STATUS_INVALID_PARAMETER) ? 87 : 8;
return NULL;
}
return base;
@@ -523,7 +538,7 @@ __declspec(dllexport) BOOL VirtualFree(void* lpAddress, size_t dwSize, DWORD dwF
(void)dwSize; (void)dwFreeType;
if (!lpAddress) return TRUE;
uint32_t s = SysFreeVm(lpAddress);
if (s != SYS_STATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (s != STATUS_SUCCESS) { gLastError = 87; return FALSE; }
return TRUE;
}
@@ -534,7 +549,7 @@ __declspec(dllexport) BOOL VirtualProtect(void* lpAddress, size_t dwSize,
uint32_t oldSysProtect = 0;
uint32_t s = SysProtectVm(lpAddress, pages, WinProtectToSysVm(flNewProtect),
lpflOldProtect ? &oldSysProtect : NULL);
if (s != SYS_STATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (s != STATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (lpflOldProtect) *lpflOldProtect = SysProtectVmToWin(oldSysProtect);
return TRUE;
}
@@ -549,7 +564,7 @@ __declspec(dllexport) size_t VirtualQuery(const void* lpAddress,
SYS_VM_REGION_INFO info;
uint32_t s = SysQueryVm((void*)lpAddress, &info);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
gLastError = 87;
return 0;
}
@@ -585,8 +600,8 @@ __declspec(dllexport) HANDLE CreateFileMappingA(HANDLE hFile,
SYS_MAP_FLAG_NONE,
maximumSize,
&kh);
if (s != SYS_STATUS_SUCCESS) {
gLastError = (s == SYS_STATUS_NOT_SUPPORTED) ? 120 : ((s == SYS_STATUS_NO_MEMORY) ? 8 : 87);
if (s != STATUS_SUCCESS) {
gLastError = (s == STATUS_NOT_SUPPORTED) ? 120 : ((s == STATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return KH_TO_HANDLE(kh);
@@ -621,8 +636,8 @@ __declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject,
WinFileMapAccessToSys(dwDesiredAccess),
SYS_MAP_FLAG_NONE,
&base);
if (s != SYS_STATUS_SUCCESS) {
gLastError = (s == SYS_STATUS_NOT_SUPPORTED) ? 120 : ((s == SYS_STATUS_NO_MEMORY) ? 8 : 87);
if (s != STATUS_SUCCESS) {
gLastError = (s == STATUS_NOT_SUPPORTED) ? 120 : ((s == STATUS_NO_MEMORY) ? 8 : 87);
return NULL;
}
return base;
@@ -631,7 +646,7 @@ __declspec(dllexport) void* MapViewOfFile(HANDLE hFileMappingObject,
__declspec(dllexport) BOOL UnmapViewOfFile(const void* lpBaseAddress) {
if (!lpBaseAddress) { gLastError = 87; return FALSE; }
uint32_t s = SysFreeVm((void*)lpBaseAddress);
if (s != SYS_STATUS_SUCCESS) { gLastError = 87; return FALSE; }
if (s != STATUS_SUCCESS) { gLastError = 87; return FALSE; }
return TRUE;
}
@@ -645,16 +660,22 @@ __declspec(dllexport) BOOL FlushViewOfFile(const void* lpBaseAddress, size_t dwN
// --- Win32 API ---.
// Returns whether one input key is currently held for Win32 adapters.
static bool Win32IsInputKeyDown(INPUT_KEY key) {
bool down = false;
return SysIsKeyDown(key, &down) == STATUS_SUCCESS && down;
}
// GetAsyncKeyState implementation over the canonical input key namespace.
__declspec(dllexport) SHORT GetAsyncKeyState(int vKey) {
if (vKey == 0x11) { // VK_CONTROL
return (SysIsKeyDown(INPUT_KEY_LEFT_CTRL) || SysIsKeyDown(INPUT_KEY_RIGHT_CTRL)) ? (SHORT)0x8000 : 0;
return (Win32IsInputKeyDown(INPUT_KEY_LEFT_CTRL) || Win32IsInputKeyDown(INPUT_KEY_RIGHT_CTRL)) ? (SHORT)0x8000 : 0;
}
if (vKey == 0x10) { // VK_SHIFT
return (SysIsKeyDown(INPUT_KEY_LEFT_SHIFT) || SysIsKeyDown(INPUT_KEY_RIGHT_SHIFT)) ? (SHORT)0x8000 : 0;
return (Win32IsInputKeyDown(INPUT_KEY_LEFT_SHIFT) || Win32IsInputKeyDown(INPUT_KEY_RIGHT_SHIFT)) ? (SHORT)0x8000 : 0;
}
if (vKey == 0x12) { // VK_MENU / ALT
return (SysIsKeyDown(INPUT_KEY_LEFT_ALT) || SysIsKeyDown(INPUT_KEY_RIGHT_ALT)) ? (SHORT)0x8000 : 0;
return (Win32IsInputKeyDown(INPUT_KEY_LEFT_ALT) || Win32IsInputKeyDown(INPUT_KEY_RIGHT_ALT)) ? (SHORT)0x8000 : 0;
}
static const struct { int vk; INPUT_KEY key; } vk_to_key[] = {
@@ -697,7 +718,7 @@ __declspec(dllexport) SHORT GetAsyncKeyState(int vKey) {
for (size_t i = 0; i < sizeof(vk_to_key) / sizeof(vk_to_key[0]); ++i) {
if (vk_to_key[i].vk == vKey) {
if (SysIsKeyDown(vk_to_key[i].key))
if (Win32IsInputKeyDown(vk_to_key[i].key))
return (SHORT)0x8000;
break;
}
@@ -709,8 +730,10 @@ __declspec(dllexport) SHORT GetAsyncKeyState(int vKey) {
__declspec(dllexport) BOOL FlushConsoleInputBuffer(HANDLE hConsoleInput) {
(void)hConsoleInput;
// Drain the keyboard buffer by reading until empty.
while (SysPollKeyboard()) {
(void)SysReadKeyboardChar();
INPUT_KEY key;
while (SysPollKeyboard(&key) == STATUS_SUCCESS) {
char ignored;
(void)SysReadKeyboardChar(&ignored);
}
return TRUE;
}
@@ -788,71 +811,42 @@ __declspec(dllexport) HANDLE CreateFileA(const char* name,
char translatedName[260];
TranslatePath(name, translatedName);
int wantWrite = (access & GENERIC_WRITE) != 0;
uint32_t sysAccess = SYS_FILE_ACCESS_QUERY;
if (access & GENERIC_READ)
sysAccess |= SYS_FILE_ACCESS_READ;
if (access & GENERIC_WRITE)
sysAccess |= SYS_FILE_ACCESS_WRITE;
if (disposition == OPEN_EXISTING || disposition == TRUNCATE_EXISTING) {
// Open the file via the read path (it must already exist).
uint32_t kh = 0;
uint32_t s = K32OpenFileNative(translatedName, &kh);
if (s != 0) { gLastError = 2; return INVALID_HANDLE_VALUE; }
if (disposition == TRUNCATE_EXISTING && wantWrite) {
// Immediately truncate to zero.
uint32_t ts = SysTruncateFile(kh, 0);
if (ts != 0) {
SysCloseHandle(kh);
gLastError = 5;
return INVALID_HANDLE_VALUE;
}
}
return KH_TO_HANDLE(kh);
uint32_t sysDisposition;
switch (disposition) {
case OPEN_EXISTING: sysDisposition = SYS_FILE_OPEN_EXISTING; break;
case OPEN_ALWAYS: sysDisposition = SYS_FILE_OPEN_ALWAYS; break;
case CREATE_ALWAYS: sysDisposition = SYS_FILE_CREATE_ALWAYS; break;
case CREATE_NEW: sysDisposition = SYS_FILE_CREATE_NEW; break;
case TRUNCATE_EXISTING: sysDisposition = SYS_FILE_TRUNCATE_EXISTING; break;
default:
gLastError = 87;
return INVALID_HANDLE_VALUE;
}
if (disposition == CREATE_ALWAYS && wantWrite) {
// Create-or-replace: try to create; if the file already exists,
// open it and truncate it to zero instead.
uint32_t kh = 0;
uint32_t s = K32CreateFileNative(translatedName, &kh);
if (s != 0) {
// File may already exist - open and truncate.
s = K32OpenFileNative(translatedName, &kh);
if (s != 0) { gLastError = 5; return INVALID_HANDLE_VALUE; }
SysTruncateFile(kh, 0);
}
return KH_TO_HANDLE(kh);
uint32_t kh = 0;
uint32_t s = K32OpenFileNative(translatedName, sysAccess, sysDisposition, &kh);
if (s != 0) {
gLastError = (s == STATUS_OBJECT_NAME_COLLISION) ? 80 :
(s == STATUS_NOT_FOUND) ? 2 : 5;
return INVALID_HANDLE_VALUE;
}
if (disposition == OPEN_ALWAYS && wantWrite) {
// Open if exists, create if not.
uint32_t kh = 0;
if (K32OpenFileNative(translatedName, &kh) != 0) {
if (K32CreateFileNative(translatedName, &kh) != 0) {
gLastError = 5;
return INVALID_HANDLE_VALUE;
}
}
return KH_TO_HANDLE(kh);
}
if (disposition == CREATE_NEW && wantWrite) {
// Fail if the file already exists (we can't check atomically,
// but SysCreateFile returns NAME_COLLISION for existing files).
uint32_t kh = 0;
uint32_t s = K32CreateFileNative(translatedName, &kh);
if (s != 0) { gLastError = 80; return INVALID_HANDLE_VALUE; } // ERROR_FILE_EXISTS
return KH_TO_HANDLE(kh);
}
// Any other combination (e.g. read-only CREATE_ALWAYS) is not supported.
gLastError = 87; // ERROR_INVALID_PARAMETER
return INVALID_HANDLE_VALUE;
return KH_TO_HANDLE(kh);
}
// GetFileSize - fills optional high-DWORD pointer, returns low DWORD.
// Returns 0xFFFFFFFF on error, matching Win32 behaviour.
__declspec(dllexport) DWORD GetFileSize(HANDLE hFile, DWORD* fileSizeHigh) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t sz = SysGetFileSize(HANDLE_TO_KH(hFile));
uint64_t sz = 0;
uint32_t s = SysGetFileSize(HANDLE_TO_KH(hFile), &sz);
if (s != 0) { gLastError = 6; return 0xFFFFFFFFUL; }
if (fileSizeHigh) *fileSizeHigh = (DWORD)(sz >> 32);
return (DWORD)(sz & 0xFFFFFFFFULL);
}
@@ -922,12 +916,14 @@ __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
if (method == FILE_BEGIN) {
target = delta;
} else if (method == FILE_CURRENT) {
uint64_t cur = SysGetFilePos(kh);
if (cur == (uint64_t)-1 || cur > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t cur = 0;
uint32_t s = SysGetFilePos(kh, &cur);
if (s != 0 || cur > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
target = (int64_t)cur + delta;
} else if (method == FILE_END) {
uint64_t sz = SysGetFileSize(kh);
if (sz > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
uint64_t sz = 0;
uint32_t s = SysGetFileSize(kh, &sz);
if (s != 0 || sz > 0x7FFFFFFFFFFFFFFFULL) { gLastError = 6; return 0xFFFFFFFFUL; }
target = (int64_t)sz + delta;
} else {
gLastError = 87; return 0xFFFFFFFFUL;
@@ -949,9 +945,10 @@ __declspec(dllexport) DWORD SetFilePointer(HANDLE hFile, long dist,
__declspec(dllexport) BOOL SetEndOfFile(HANDLE hFile) {
if (!hFile || hFile == INVALID_HANDLE_VALUE) { gLastError = 6; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hFile);
uint64_t pos = SysGetFilePos(kh);
if (pos == (uint64_t)-1) { gLastError = 6; return FALSE; }
uint32_t s = SysTruncateFile(kh, pos);
uint64_t pos = 0;
uint32_t s = SysGetFilePos(kh, &pos);
if (s != 0) { gLastError = 6; return FALSE; }
s = SysTruncateFile(kh, pos);
if (s != 0) { gLastError = 1; return FALSE; }
return TRUE;
}
@@ -1184,7 +1181,7 @@ __declspec(dllexport) DWORD GetModuleFileNameA(HANDLE hModule,
// Implements the terminate Process operation.
__declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitCode) {
if (hProcess == GetCurrentProcess()) {
SysExitProcess((int)uExitCode);
SysExitProcess((int32_t)uExitCode);
while (true) ;
}
@@ -1193,16 +1190,9 @@ __declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitC
return FALSE;
}
uint32_t pid = 0;
uint32_t s = SysGetProcessPid(HANDLE_TO_KH(hProcess), &pid);
if (s != 0 || pid == 0) {
gLastError = 6; // ERROR_INVALID_HANDLE
return FALSE;
}
s = SysKillProcess(pid, (int)uExitCode);
uint32_t s = SysKillProcess(HANDLE_TO_KH(hProcess), (int32_t)uExitCode);
if (s != 0) {
gLastError = 5; // ERROR_ACCESS_DENIED / generic failure for now
gLastError = (s == STATUS_INVALID_HANDLE) ? 6 : 5;
return FALSE;
}
return TRUE;
@@ -1290,7 +1280,6 @@ __declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMillisec
return 0xFFFFFFFFUL;
}
// Implements the extract Application Path A operation.
static BOOL ExtractApplicationPathA(const char* commandLine, char* out, size_t outSize) {
if (!commandLine || !out || outSize == 0) return FALSE;
@@ -1411,7 +1400,7 @@ __declspec(dllexport) BOOL GetExitCodeProcess(HANDLE hProcess, DWORD* lpExitCode
if (!lpExitCode) { gLastError = 87; return FALSE; }
uint32_t kh = HANDLE_TO_KH(hProcess);
if (!kh) { gLastError = 6; return FALSE; } // ERROR_INVALID_HANDLE
int code = 0;
int32_t code = 0;
uint32_t s = SysGetProcessExitCode(kh, &code);
if (s != 0) { gLastError = 6; return FALSE; }
*lpExitCode = (DWORD)code;
@@ -1471,13 +1460,13 @@ static BOOL K32IsConsoleOutputHandle(HANDLE h) {
// Implements the k32 Set Console Last Error operation.
static void K32SetConsoleLastError(uint32_t status) {
switch (status) {
case SYS_STATUS_SUCCESS:
case STATUS_SUCCESS:
gLastError = 0;
break;
case SYS_STATUS_INVALID_PARAMETER:
case STATUS_INVALID_PARAMETER:
gLastError = 87;
break;
case SYS_STATUS_NO_MEMORY:
case STATUS_NO_MEMORY:
gLastError = 8;
break;
default:
@@ -1506,7 +1495,8 @@ __declspec(dllexport) BOOL ReadConsoleA(HANDLE hConsoleInput,
DWORD i = 0; // Notice we switched to a while loop
while (i < nNumberOfCharsToRead) {
char c = SysReadKeyboardChar();
char c = 0;
if (SysReadKeyboardChar(&c) != STATUS_SUCCESS) continue;
// Ignore unmapped keys (Shift, Ctrl, etc.).
if (c == 0) continue;
@@ -1518,7 +1508,7 @@ __declspec(dllexport) BOOL ReadConsoleA(HANDLE hConsoleInput,
// Stop reading when the user presses Enter.
if (c == '\n' || c == '\r') {
// If your shell expects strings to be \n terminated, leave this.
// If your shell expects \0 termination without a newline,
// If your shell expects \0 termination without a newline,
// you might want to replace buf[i-1] with \0 here!
break;
}
@@ -1558,7 +1548,7 @@ __declspec(dllexport) BOOL GetConsoleScreenBufferInfo(HANDLE hConsoleOutput,
SYS_CONSOLE_INFO info;
uint32_t s = SysGetConsoleInfo(&info);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
@@ -1589,7 +1579,7 @@ __declspec(dllexport) BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput,
}
uint32_t s = SysSetConsoleCursor((int)dwCursorPosition.X, (int)dwCursorPosition.Y);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
@@ -1618,7 +1608,7 @@ __declspec(dllexport) BOOL FillConsoleOutputCharacterA(HANDLE hConsoleOutput,
(int)dwWriteCoord.Y, (size_t)nLength,
&written);
if (lpNumberOfCharsWritten) *lpNumberOfCharsWritten = (DWORD)written;
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
@@ -1647,18 +1637,17 @@ __declspec(dllexport) BOOL FillConsoleOutputAttribute(HANDLE hConsoleOutput,
(int)dwWriteCoord.Y, (size_t)nLength,
&written);
if (lpNumberOfAttrsWritten) *lpNumberOfAttrsWritten = (DWORD)written;
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
K32SetConsoleLastError(s);
return FALSE;
}
return TRUE;
}
// Directory enumeration (FindFirstFileA / FindNextFileA / FindClose).
// Directory enumeration (FindFirstFileA / FindNextFileA / FindClose).
typedef SYS_DIR_ENTRY KDIR_ENTRY;
// FIND_HANDLE is a user-side wrapper: it stores the kernel dir handle and
// the wildcard pattern so FindNextFileA can filter subsequent entries.
// We return a pointer to this struct cast to HANDLE; it is NOT a kernel
@@ -1912,16 +1901,16 @@ __declspec(dllexport) BOOL SetCurrentDirectoryA(const char* lpPathName) {
// Returns current Directory A.
__declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBuffer) {
if (!lpBuffer || nBufferLength == 0) {
// Return required size (including NUL). Max CWD is 512 bytes + "C:\\".
return 515;
// Return a conservative required size including NUL.
return SYS_CWD_MAX + 1;
}
// Kernel returns native "N:/path". Present it as a Win32 drive letter.
char raw[512];
char raw[SYS_CWD_MAX + 1];
uint32_t s = K32GetCwdNative(raw, sizeof(raw), NULL);
if (s != 0) { gLastError = 1; return 0; }
char full[515];
char full[SYS_CWD_MAX + 1];
char drive = 'C';
const char* rel = raw;
if (raw[0] >= '0' && raw[0] <= '7' && raw[1] == ':' &&
@@ -1948,7 +1937,6 @@ __declspec(dllexport) DWORD GetCurrentDirectoryA(DWORD nBufferLength, char* lpBu
return (DWORD)(needed - 1); // length without NUL, matching Win32
}
__declspec(dllexport) HANDLE CreateMutexA(void* lpMutexAttributes, BOOL bInitialOwner,
const char* lpName) {
(void)lpMutexAttributes; (void)lpName;
@@ -2108,26 +2096,28 @@ static long long TscFreqFromCpuid(void) {
return (long long)crystal_hz * numer / denom;
}
// Calibrate TSC against SysGetTick (1 unit = 1 microsecond).
// Calibrates TSC against the native monotonic tick source.
static long long TscFreqCalibrate(void) {
// We'll wait for 100ms (100,000 microseconds) to get a stable sample.
const uint64_t CALIBRATION_US = 100000;
uint64_t frequency = K32TickFrequency();
if (!frequency) return 0;
uint64_t calibrationTicks = frequency / 10ull;
if (!calibrationTicks) calibrationTicks = 1;
uint64_t t0 = SysGetTick();
uint64_t tsc0 = __rdtsc();
// Simple busy wait for the duration.
while ((SysGetTick() - t0) < CALIBRATION_US) {
_mm_pause();
while ((SysGetTick() - t0) < calibrationTicks) {
_mm_pause();
}
uint64_t t1 = SysGetTick();
uint64_t tsc1 = __rdtsc();
uint64_t delta_tsc = tsc1 - tsc0;
uint64_t delta_us = t1 - t0;
uint64_t delta_us = SysTicksToMicroseconds(t1 - t0, frequency);
if (!delta_us) return 0;
// Hz = (Delta TSC / Delta Microseconds) * 1,000,000.
return (long long)(delta_tsc * 1000000ULL / delta_us);
}
@@ -2199,7 +2189,7 @@ __declspec(dllexport) void GetSystemTime(SYSTEMTIME* lpSystemTime) {
if (!lpSystemTime) return;
SYS_SYSTEM_TIME st;
if (SysGetSystemTime(&st) != SYS_STATUS_SUCCESS) {
if (SysGetSystemTime(&st) != STATUS_SUCCESS) {
memset(lpSystemTime, 0, sizeof(*lpSystemTime));
return;
}
@@ -2237,11 +2227,12 @@ __declspec(dllexport) void GetSystemTimeAsFileTime(void* lpSystemTimeAsFileTime)
SYS_SYSTEM_TIME st;
uint64_t ft;
if (SysGetSystemTime(&st) == SYS_STATUS_SUCCESS) {
if (SysGetSystemTime(&st) == STATUS_SUCCESS) {
ft = K32EpochToFileTime(st.EpochSeconds, st.Nanosecond);
} else {
uint64_t frequency = K32TickFrequency();
const uint64_t FALLBACK_2024_01_01_FT = 133485408000000000ULL;
ft = FALLBACK_2024_01_01_FT + (SysGetTick() * 10ULL);
ft = FALLBACK_2024_01_01_FT + SysTicksToMicroseconds(SysGetTick(), frequency) * 10ULL;
}
memcpy(lpSystemTimeAsFileTime, &ft, sizeof(ft));
}
@@ -2461,7 +2452,7 @@ __declspec(dllexport) BOOL IsDebuggerPresent(void) {
return FALSE;
}
// RTL Exception / Unwind (SEH infrastructure - all stubs).
// RTL Exception / Unwind (SEH infrastructure - all stubs).
typedef struct _RUNTIME_FUNCTION {
unsigned int BeginAddress;
@@ -2635,7 +2626,7 @@ __declspec(dllexport) BOOL FlsFree(DWORD dwFlsIndex) {
return TRUE;
}
// Pointer encoding (security cookie obfuscation)
// Pointer encoding (security cookie obfuscation)
// We have no security cookie; return the pointer unchanged.
__declspec(dllexport) void* EncodePointer(void* Ptr) { return Ptr; }
-1
View File
@@ -14,7 +14,6 @@ typedef struct tagPAINTSTRUCT {
HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; char res[32];
} PAINTSTRUCT;
// Implements the user32 Debug Print A operation.
static void User32DebugPrintA(const char* s) {
if (!s) return;
+3 -1
View File
@@ -49,7 +49,9 @@ typedef struct {
// Timer.
__declspec(dllexport) unsigned long timeGetTime(void) {
return (unsigned long)(SysGetTick() / 1000u);
uint64_t frequency = 0;
if (SysGetTickFrequency(&frequency) != STATUS_SUCCESS || !frequency) return 0;
return (unsigned long)SysTicksToMilliseconds(SysGetTick(), frequency);
}
// Waveform audio (stubs - no audio hardware at this stage).
+1 -2
View File
@@ -117,7 +117,7 @@ static HEAP_SEGMENT* AddSegment(size_t payloadSize) {
void* base = 0;
uint32_t s = SysAllocVm(0, pages, SYS_VM_READWRITE, SYS_VM_FLAG_NONE, &base);
if (s != SYS_STATUS_SUCCESS || !base)
if (s != STATUS_SUCCESS || !base)
return (HEAP_SEGMENT*)0;
HEAP_SEGMENT* seg = (HEAP_SEGMENT*)base;
@@ -210,7 +210,6 @@ void* _calloc(size_t count, size_t size) {
return ptr;
}
// Returns the usable payload size of an allocation made by this heap.
size_t _malloc_usable_size(const void* ptr) {
if (!ptr) return (size_t)-1;
+133 -111
View File
@@ -1,112 +1,134 @@
; syscall_nr.inc
; Syscall numbers shared by MASM user-mode stubs.
SYS_EXIT_PROCESS EQU 0
SYS_EXIT_THREAD EQU 1
SYS_CREATE_PROCESS EQU 2
SYS_WAIT_SINGLE EQU 3
SYS_GET_PROCESS_EXIT_CODE EQU 4
SYS_KILL_PROCESS EQU 5
SYS_GET_PROCESS_LIST EQU 6
SYS_GET_PROCESS_PID EQU 7
SYS_CREATE_THREAD EQU 8
SYS_RETIRED_WAIT_THREAD EQU 9
SYS_GET_CURRENT_TID EQU 10
SYS_GET_CURRENT_PID EQU 11
SYS_SET_THREAD_PRIORITY EQU 12
SYS_YIELD EQU 13
SYS_SLEEP EQU 14
SYS_SET_USER_APC_DISPATCHER EQU 15
SYS_APC_RETURN EQU 16
SYS_ALLOC_VM EQU 17
SYS_FREE_VM EQU 18
SYS_PROTECT_VM EQU 19
SYS_QUERY_VM EQU 20
SYS_CLOSE_HANDLE EQU 21
SYS_CREATE_MUTEX EQU 22
SYS_RETIRED_ACQUIRE_MUTEX EQU 23
SYS_TRY_ACQUIRE_MUTEX EQU 24
SYS_RELEASE_MUTEX EQU 25
SYS_CREATE_SEM EQU 26
SYS_RETIRED_WAIT_SEM EQU 27
SYS_TRY_WAIT_SEM EQU 28
SYS_SIGNAL_SEM EQU 29
SYS_CREATE_EVENT EQU 30
SYS_SIGNAL_EVENT EQU 31
SYS_CLEAR_EVENT EQU 32
SYS_RETIRED_WAIT_EVENT EQU 33
SYS_CREATE_QUEUE EQU 34
SYS_SEND_QUEUE EQU 35
SYS_RECV_QUEUE EQU 36
SYS_PUBLISH_QUEUE EQU 37
SYS_OPEN_NAMED_QUEUE EQU 38
SYS_DUPLICATE_QUEUE EQU 39
SYS_GET_NAMED_QUEUE_OWNER_PID EQU 40
SYS_OPEN_FILE EQU 41
SYS_CREATE_FILE EQU 42
SYS_READ_FILE EQU 43
SYS_WRITE_FILE EQU 44
SYS_READ_FILE_ASYNC EQU 45
SYS_WRITE_FILE_ASYNC EQU 46
SYS_CANCEL_IO EQU 47
SYS_SEEK_FILE EQU 48
SYS_GET_FILE_POS EQU 49
SYS_GET_FILE_SIZE EQU 50
SYS_TRUNCATE_FILE EQU 51
SYS_DELETE_FILE EQU 52
SYS_RENAME_FILE EQU 53
SYS_OPEN_DIR EQU 54
SYS_READ_DIR EQU 55
SYS_CREATE_DIR EQU 56
SYS_REMOVE_DIR EQU 57
SYS_SET_CWD EQU 58
SYS_GET_CWD EQU 59
SYS_WRITE_CONSOLE EQU 60
SYS_GET_CONSOLE_INFO EQU 61
SYS_SET_CONSOLE_CURSOR EQU 62
SYS_FILL_CONSOLE_CHARACTER EQU 63
SYS_FILL_CONSOLE_ATTRIBUTE EQU 64
SYS_PUT_SCREEN_CHAR EQU 65
SYS_CLEAR_SCREEN EQU 66
SYS_GET_SCREEN_SIZE EQU 67
SYS_QUERY_DISPLAY_INFO EQU 68
SYS_POLL_KEYBOARD EQU 69
SYS_IS_KEY_DOWN EQU 70
SYS_READ_KEYBOARD_CHAR EQU 71
SYS_READ_KEY EQU 72
SYS_GET_KEY_MODIFIERS EQU 73
SYS_POLL_MOUSE EQU 74
SYS_READ_MOUSE EQU 75
SYS_GET_MOUSE_POS EQU 76
SYS_GET_TICK EQU 77
SYS_PRINT_DEBUG EQU 78
SYS_QUERY_TRAP_FRAME EQU 79
SYS_CREATE_SOCKET EQU 80
SYS_BIND_SOCKET EQU 81
SYS_LISTEN_SOCKET EQU 82
SYS_ACCEPT_SOCKET EQU 83
SYS_CONNECT_SOCKET EQU 84
SYS_SEND_SOCKET EQU 85
SYS_RECV_SOCKET EQU 86
SYS_SHUTDOWN_SOCKET EQU 87
SYS_SEND_TO_SOCKET EQU 88
SYS_RECV_FROM_SOCKET EQU 89
SYS_RESOLVE_HOST EQU 90
SYS_SET_SOCKET_OPTION EQU 91
SYS_GET_SOCKET_OPTION EQU 92
SYS_POLL EQU 93
SYS_GET_MEMORY_STATUS EQU 94
SYS_QUERY_KERNEL_STATS EQU 95
SYS_GET_SYSTEM_TIME EQU 96
SYS_CREATE_FILE_MAPPING EQU 97
SYS_MAP_FILE EQU 98
SYS_CREATE_SURFACE EQU 99
SYS_MAP_SURFACE EQU 100
SYS_QUERY_SURFACE EQU 101
SYS_PRESENT_SURFACE EQU 102
SYS_GET_THREAD_LIST EQU 103
SYS_READ_INPUT_EVENT EQU 104
SYS_PEEK_INPUT_EVENT EQU 105
SYS_WAIT_MANY EQU 106
SYS_OPEN_INPUT_QUEUE EQU 107
SYS_FLUSH_FILE EQU 108
SYS_MAX EQU 109
; Process, threads, waits, and APC.
SYS_EXIT_PROCESS EQU 0
SYS_EXIT_THREAD EQU 1
SYS_CREATE_PROCESS EQU 2
SYS_OPEN_PROCESS EQU 3
SYS_GET_PROCESS_EXIT_CODE EQU 4
SYS_KILL_PROCESS EQU 5
SYS_GET_PROCESS_LIST EQU 6
SYS_GET_PROCESS_PID EQU 7
SYS_CREATE_THREAD EQU 8
SYS_GET_THREAD_LIST EQU 9
SYS_GET_CURRENT_TID EQU 10
SYS_GET_CURRENT_PID EQU 11
SYS_SET_THREAD_PRIORITY EQU 12
SYS_YIELD EQU 13
SYS_SLEEP EQU 14
SYS_WAIT_SINGLE EQU 15
SYS_WAIT_MANY EQU 16
SYS_SET_USER_APC_DISPATCHER EQU 17
SYS_APC_RETURN EQU 18
; Virtual memory, mappings, and handles.
SYS_ALLOC_VM EQU 19
SYS_FREE_VM EQU 20
SYS_PROTECT_VM EQU 21
SYS_QUERY_VM EQU 22
SYS_CLOSE_HANDLE EQU 23
SYS_DUPLICATE_HANDLE EQU 24
SYS_CREATE_FILE_MAPPING EQU 25
SYS_MAP_FILE EQU 26
; Synchronization objects.
SYS_CREATE_MUTEX EQU 27
SYS_TRY_ACQUIRE_MUTEX EQU 28
SYS_RELEASE_MUTEX EQU 29
SYS_CREATE_SEM EQU 30
SYS_TRY_WAIT_SEM EQU 31
SYS_SIGNAL_SEM EQU 32
SYS_CREATE_EVENT EQU 33
SYS_SIGNAL_EVENT EQU 34
SYS_CLEAR_EVENT EQU 35
; Queues and named queues.
SYS_CREATE_QUEUE EQU 36
SYS_SEND_QUEUE EQU 37
SYS_RECV_QUEUE EQU 38
SYS_PUBLISH_QUEUE EQU 39
SYS_OPEN_NAMED_QUEUE EQU 40
SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS EQU 41
; Files, directories, and current directory.
SYS_OPEN_FILE EQU 42
SYS_READ_FILE EQU 43
SYS_WRITE_FILE EQU 44
SYS_READ_FILE_ASYNC EQU 45
SYS_WRITE_FILE_ASYNC EQU 46
SYS_CANCEL_IO EQU 47
SYS_SEEK_FILE EQU 48
SYS_GET_FILE_POS EQU 49
SYS_GET_FILE_SIZE EQU 50
SYS_GET_FILE_STAT EQU 51
SYS_STAT_FILE EQU 52
SYS_TRUNCATE_FILE EQU 53
SYS_FLUSH_FILE EQU 54
SYS_DELETE_FILE EQU 55
SYS_RENAME_FILE EQU 56
SYS_LOCK_FILE EQU 57
SYS_UNLOCK_FILE EQU 58
SYS_OPEN_DIR EQU 59
SYS_READ_DIR EQU 60
SYS_CREATE_DIR EQU 61
SYS_REMOVE_DIR EQU 62
SYS_SET_CWD EQU 63
SYS_REAL_PATH EQU 64
; Console, display, and surfaces.
SYS_WRITE_CONSOLE EQU 65
SYS_GET_CONSOLE_INFO EQU 66
SYS_SET_CONSOLE_CURSOR EQU 67
SYS_FILL_CONSOLE_CHARACTER EQU 68
SYS_FILL_CONSOLE_ATTRIBUTE EQU 69
SYS_PUT_SCREEN_CHAR EQU 70
SYS_CLEAR_SCREEN EQU 71
SYS_GET_SCREEN_SIZE EQU 72
SYS_QUERY_DISPLAY_INFO EQU 73
SYS_CREATE_SURFACE EQU 74
SYS_MAP_SURFACE EQU 75
SYS_QUERY_SURFACE EQU 76
SYS_PRESENT_SURFACE EQU 77
; Input.
SYS_POLL_KEYBOARD EQU 78
SYS_IS_KEY_DOWN EQU 79
SYS_READ_KEYBOARD_CHAR EQU 80
SYS_READ_KEY EQU 81
SYS_GET_KEY_MODIFIERS EQU 82
SYS_OPEN_INPUT_QUEUE EQU 83
SYS_READ_INPUT_EVENT EQU 84
SYS_PEEK_INPUT_EVENT EQU 85
SYS_PEEK_MOUSE EQU 86
SYS_READ_MOUSE EQU 87
SYS_GET_MOUSE_POS EQU 88
; Time, debug, and system information.
SYS_CREATE_TIMER EQU 89
SYS_SET_TIMER EQU 90
SYS_CANCEL_TIMER EQU 91
SYS_GET_TICK EQU 92
SYS_GET_TICK_FREQUENCY EQU 93
SYS_GET_SYSTEM_TIME EQU 94
SYS_PRINT_DEBUG EQU 95
SYS_QUERY_TRAP_FRAME EQU 96
SYS_GET_MEMORY_STATUS EQU 97
SYS_QUERY_KERNEL_STATS EQU 98
; Native sockets and polling.
SYS_CREATE_SOCKET EQU 99
SYS_BIND_SOCKET EQU 100
SYS_LISTEN_SOCKET EQU 101
SYS_ACCEPT_SOCKET EQU 102
SYS_CONNECT_SOCKET EQU 103
SYS_SEND_SOCKET EQU 104
SYS_RECV_SOCKET EQU 105
SYS_SHUTDOWN_SOCKET EQU 106
SYS_SEND_TO_SOCKET EQU 107
SYS_RECV_FROM_SOCKET EQU 108
SYS_RESOLVE_HOST EQU 109
SYS_SET_SOCKET_OPTION EQU 110
SYS_GET_SOCKET_OPTION EQU 111
SYS_POLL EQU 112
SYS_MAX EQU 113
+20 -15
View File
@@ -13,8 +13,8 @@
; args 5+ on the stack above the home area ([rsp+28h], [rsp+30h], ...).
;
; SYSCALL_STUB2/3 - 4 or fewer args: pure register passing, no stack read.
; SYSCALL_STUB5 - exactly 5 args: loads [rsp+28h] -> R10 only.
; SYSCALL_STUB6 - 5 or 6 args: loads [rsp+28h] -> R10 and [rsp+30h] -> R11.
; SYSCALL_STUB5 - exactly 5 args: loads [rsp+28h] -> R10 only.
; SYSCALL_STUB6 - 5 or 6 args: loads [rsp+28h] -> R10 and [rsp+30h] -> R11.
;
; Handle lifecycle:
; SysCloseHandle is the single close stub for every object type.
@@ -24,14 +24,11 @@
INCLUDE syscall_nr.inc
SYS_USER_APC_FRAME_Magic EQU 0
SYS_USER_APC_FRAME_OriginalRip EQU 8
SYS_USER_APC_FRAME_OriginalRsp EQU 16
SYS_USER_APC_FRAME_OriginalRflags EQU 24
SYS_USER_APC_FRAME_OriginalRax EQU 32
SYS_USER_APC_FRAME_Routine EQU 40
SYS_USER_APC_FRAME_NormalContext EQU 48
SYS_USER_APC_FRAME_SystemArgument1 EQU 56
SYS_USER_APC_FRAME_SystemArgument2 EQU 64
SYS_USER_APC_FRAME_ReturnToken EQU 8
SYS_USER_APC_FRAME_Routine EQU 16
SYS_USER_APC_FRAME_NormalContext EQU 24
SYS_USER_APC_FRAME_SystemArgument1 EQU 32
SYS_USER_APC_FRAME_SystemArgument2 EQU 40
.CODE
@@ -88,6 +85,7 @@ SYSCALL_STUB3 WaitSingle, SYS_WAIT_SINGLE
SYSCALL_STUB2 WaitMany, SYS_WAIT_MANY
SYSCALL_STUB2 OpenInputQueue, SYS_OPEN_INPUT_QUEUE
SYSCALL_STUB2 GetProcessExitCode, SYS_GET_PROCESS_EXIT_CODE
SYSCALL_STUB2 OpenProcess, SYS_OPEN_PROCESS
SYSCALL_STUB2 KillProcess, SYS_KILL_PROCESS
SYSCALL_STUB2 GetProcessList, SYS_GET_PROCESS_LIST
SYSCALL_STUB2 GetProcessPid, SYS_GET_PROCESS_PID
@@ -139,6 +137,7 @@ SYSCALL_STUB2 FreeVm, SYS_FREE_VM
SYSCALL_STUB2 ProtectVm, SYS_PROTECT_VM
SYSCALL_STUB2 QueryVm, SYS_QUERY_VM
SYSCALL_STUB2 CloseHandle, SYS_CLOSE_HANDLE
SYSCALL_STUB2 DuplicateHandle, SYS_DUPLICATE_HANDLE
; ---------------------------------------------------------------------------
; Synchronization objects
@@ -163,14 +162,12 @@ SYSCALL_STUB2 SendQueue, SYS_SEND_QUEUE
SYSCALL_STUB2 RecvQueue, SYS_RECV_QUEUE
SYSCALL_STUB2 PublishQueue, SYS_PUBLISH_QUEUE
SYSCALL_STUB2 OpenNamedQueue, SYS_OPEN_NAMED_QUEUE
SYSCALL_STUB2 DuplicateQueue, SYS_DUPLICATE_QUEUE
SYSCALL_STUB2 GetNamedQueueOwnerPid, SYS_GET_NAMED_QUEUE_OWNER_PID
SYSCALL_STUB2 OpenNamedQueueOwnerProcess, SYS_OPEN_NAMED_QUEUE_OWNER_PROCESS
; ---------------------------------------------------------------------------
; Files, directories, and current directory
; ---------------------------------------------------------------------------
SYSCALL_STUB2 OpenFile, SYS_OPEN_FILE
SYSCALL_STUB2 CreateFile, SYS_CREATE_FILE
SYSCALL_STUB2 ReadFile, SYS_READ_FILE
SYSCALL_STUB2 WriteFile, SYS_WRITE_FILE
SYSCALL_STUB2 ReadFileAsync, SYS_READ_FILE_ASYNC
@@ -179,17 +176,21 @@ SYSCALL_STUB2 CancelIo, SYS_CANCEL_IO
SYSCALL_STUB2 SeekFile, SYS_SEEK_FILE
SYSCALL_STUB2 GetFilePos, SYS_GET_FILE_POS
SYSCALL_STUB2 GetFileSize, SYS_GET_FILE_SIZE
SYSCALL_STUB2 GetFileStat, SYS_GET_FILE_STAT
SYSCALL_STUB2 StatFile, SYS_STAT_FILE
SYSCALL_STUB2 TruncateFile, SYS_TRUNCATE_FILE
SYSCALL_STUB2 FlushFile, SYS_FLUSH_FILE
SYSCALL_STUB2 DeleteFile, SYS_DELETE_FILE
SYSCALL_STUB2 RenameFile, SYS_RENAME_FILE
SYSCALL_STUB2 LockFile, SYS_LOCK_FILE
SYSCALL_STUB2 UnlockFile, SYS_UNLOCK_FILE
SYSCALL_STUB2 OpenDir, SYS_OPEN_DIR
SYSCALL_STUB2 ReadDir, SYS_READ_DIR
SYSCALL_STUB2 CreateDir, SYS_CREATE_DIR
SYSCALL_STUB2 RemoveDir, SYS_REMOVE_DIR
SYSCALL_STUB2 SetCwd, SYS_SET_CWD
SYSCALL_STUB2 GetCwd, SYS_GET_CWD
SYSCALL_STUB2 RealPath, SYS_REAL_PATH
; ---------------------------------------------------------------------------
; Console and graphics
@@ -219,14 +220,18 @@ SYSCALL_STUB2 GetKeyModifiers, SYS_GET_KEY_MODIFIERS
SYSCALL_STUB2 ReadInputEvent, SYS_READ_INPUT_EVENT
SYSCALL_STUB2 PeekInputEvent, SYS_PEEK_INPUT_EVENT
SYSCALL_STUB2 PollMouse, SYS_POLL_MOUSE
SYSCALL_STUB2 PeekMouse, SYS_PEEK_MOUSE
SYSCALL_STUB2 ReadMouse, SYS_READ_MOUSE
SYSCALL_STUB2 GetMousePos, SYS_GET_MOUSE_POS
; ---------------------------------------------------------------------------
; Time and debug
; ---------------------------------------------------------------------------
SYSCALL_STUB2 CreateTimer, SYS_CREATE_TIMER
SYSCALL_STUB2 SetTimer, SYS_SET_TIMER
SYSCALL_STUB2 CancelTimer, SYS_CANCEL_TIMER
SYSCALL_STUB2 GetTick, SYS_GET_TICK
SYSCALL_STUB2 GetTickFrequency, SYS_GET_TICK_FREQUENCY
SYSCALL_STUB2 PrintDebug, SYS_PRINT_DEBUG
SYSCALL_STUB2 QueryTrapFrame, SYS_QUERY_TRAP_FRAME
+1 -3
View File
@@ -5,8 +5,6 @@
#include <stddef.h>
#include <ntlite/syscall.h>
#define STATUS_SUCCESS 0u
// Implements the itoa operation.
void itoa(int num, char* str) {
int i = 0;
@@ -80,7 +78,7 @@ void _start(void)
if (s != STATUS_SUCCESS)
debug_print("[init] WARNING: SysWait returned error\n");
int exitCode = 0;
int32_t exitCode = 0;
if (SysGetProcessExitCode(h, &exitCode) != STATUS_SUCCESS) {
debug_print("[init] WARNING: failed to read exit code\n");
exitCode = 1;
@@ -0,0 +1,172 @@
// User-mode smoke test for APC return hardening.
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/syscall.h>
#define EVENT_AUTO_RESET 0u
// Prints a C string to the native console.
static void print(const char* text) {
SYS_WRITE_CONSOLE_REQUEST request = { SysSvFromCstr(text) };
SysWriteConsole(&request);
}
// Prints an unsigned integer.
static void print_u64(uint64_t value) {
char buffer[32];
int count = 0;
if (value == 0) {
print("0");
return;
}
while (value && count < (int)sizeof(buffer)) {
buffer[count++] = (char)('0' + (value % 10));
value /= 10;
}
while (count) {
char c[2] = { buffer[--count], 0 };
print(c);
}
}
// Opens a stable payload for async read completion.
static uint32_t open_test_file(uint32_t* handleOut) {
static const char* paths[] = {
"apc_return_smoke.exe",
"/apps/apc_return_smoke.exe",
"1:/apps/apc_return_smoke.exe",
"0:/apps/apc_return_smoke.exe",
"async_apc.exe",
"/apps/async_apc.exe",
"1:/apps/async_apc.exe",
"0:/apps/async_apc.exe",
"/system/init.exe",
"0:/system/init.exe",
"1:/system/init.exe",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t handle = 0;
SYS_OPEN_FILE_REQUEST request = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&handle);
uint32_t status = SysOpenFile(&request);
if (status == STATUS_SUCCESS) {
*handleOut = handle;
return STATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
}
typedef struct APC_RETURN_STATE {
volatile uint32_t Delivered;
volatile uint32_t Status;
volatile uint64_t Information;
} APC_RETURN_STATE;
// Records that the user APC callback ran and observed the I/O status block.
static void file_apc(void* context, SYS_IO_STATUS_BLOCK* iosb, uint64_t information) {
APC_RETURN_STATE* state = (APC_RETURN_STATE*)context;
state->Status = iosb ? iosb->Status : STATUS_FAILURE;
state->Information = information;
state->Delivered++;
}
// Exits after printing a status mismatch.
static void fail_status(const char* what, uint32_t got, uint32_t expected) {
print("apc_return_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
SysExitProcess(1);
}
// Runs the APC return hardening smoke test.
int main(void) {
print("apc_return_smoke: start\n");
uint32_t status = SysApcReturn((const SYS_USER_APC_FRAME*)0);
if (status != STATUS_INVALID_PARAMETER)
fail_status("inactive null return", status, STATUS_INVALID_PARAMETER);
SYS_USER_APC_FRAME fakeFrame;
memset(&fakeFrame, 0, sizeof(fakeFrame));
fakeFrame.Magic = SYS_USER_APC_FRAME_MAGIC;
status = SysApcReturn(&fakeFrame);
if (status != STATUS_INVALID_PARAMETER)
fail_status("inactive forged return", status, STATUS_INVALID_PARAMETER);
status = SysSetUserApcDispatcher((void*)SysUserApcDispatcher);
if (status != STATUS_SUCCESS)
fail_status("set dispatcher", status, STATUS_SUCCESS);
uint32_t file = 0;
status = open_test_file(&file);
if (status != STATUS_SUCCESS)
fail_status("open", status, STATUS_SUCCESS);
uint32_t event = 0;
status = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (status != STATUS_SUCCESS)
fail_status("create event", status, STATUS_SUCCESS);
uint8_t* buffer = (uint8_t*)malloc(512);
if (!buffer) {
print("apc_return_smoke: allocation failed\n");
SysExitProcess(1);
}
memset(buffer, 0, 512);
SYS_IO_STATUS_BLOCK iosb;
iosb.Status = 0;
iosb.Information = 0;
APC_RETURN_STATE state;
memset(&state, 0, sizeof(state));
SYS_ASYNC_FILE_REQUEST request;
memset(&request, 0, sizeof(request));
request.FileHandle = file;
request.EventHandle = event;
request.Buffer = buffer;
request.Size = 512;
request.Iosb = &iosb;
request.ApcRoutine = file_apc;
request.ApcContext = &state;
status = SysReadFileAsync(&request);
if (status != STATUS_PENDING)
fail_status("submit", status, STATUS_PENDING);
while (!state.Delivered) {
status = SysWait(event, SYS_WAIT_FLAG_ALERTABLE);
if (status != STATUS_USER_APC && status != STATUS_SUCCESS && status != STATUS_PENDING)
fail_status("alertable wait", status, STATUS_USER_APC);
}
uint32_t ok = state.Delivered == 1 && state.Status == STATUS_SUCCESS && state.Information == 512;
SysCloseHandle(event);
SysCloseHandle(file);
free(buffer);
if (!ok) {
print("apc_return_smoke: FAIL\n");
SysExitProcess(1);
}
print("apc_return_smoke: PASS\n");
SysExitProcess(0);
return 0;
}
+14 -10
View File
@@ -33,17 +33,21 @@ static uint32_t open_test_file(uint32_t* handleOut, const char** pathOut) {
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS) {
if (s == STATUS_SUCCESS) {
*handleOut = h;
if (pathOut)
*pathOut = paths[i];
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
return STATUS_NOT_FOUND;
}
// Prints hex8.
@@ -91,7 +95,7 @@ int main(void) {
print("async_apc: start\n");
uint32_t s = SysSetUserApcDispatcher((void*)SysUserApcDispatcher);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
print("async_apc: dispatcher registration failed status=");
print_u64(s);
print("\n");
@@ -101,7 +105,7 @@ int main(void) {
uint32_t file = 0;
const char* openedPath = 0;
s = open_test_file(&file, &openedPath);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
print("async_apc: open failed status=");
print_u64(s);
print("\n");
@@ -114,7 +118,7 @@ int main(void) {
uint32_t event = 0;
s = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
print("async_apc: event create failed status=");
print_u64(s);
print("\n");
@@ -147,7 +151,7 @@ int main(void) {
req.ApcContext = &state;
s = SysReadFileAsync(&req);
if (s != SYS_STATUS_PENDING) {
if (s != STATUS_PENDING) {
print("async_apc: submit failed status=");
print_u64(s);
print("\n");
@@ -159,7 +163,7 @@ int main(void) {
print("async_apc: alertable wait returned ");
print_u64(s);
print("\n");
if (s != SYS_STATUS_USER_APC && s != SYS_STATUS_SUCCESS && s != SYS_STATUS_PENDING)
if (s != STATUS_USER_APC && s != STATUS_SUCCESS && s != STATUS_PENDING)
break;
}
@@ -175,7 +179,7 @@ int main(void) {
print_hex8(buffer[2]); print(" ");
print_hex8(buffer[3]); print("\n");
uint32_t ok = (state.Delivered == 1 && state.Status == SYS_STATUS_SUCCESS &&
uint32_t ok = (state.Delivered == 1 && state.Status == STATUS_SUCCESS &&
state.Information == 512 && buffer[0] == 'M' && buffer[1] == 'Z');
SysCloseHandle(event);
+16 -12
View File
@@ -32,17 +32,21 @@ static uint32_t open_test_file(uint32_t* handleOut, const char** pathOut) {
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS) {
if (s == STATUS_SUCCESS) {
*handleOut = h;
if (pathOut)
*pathOut = paths[i];
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
return STATUS_NOT_FOUND;
}
// Prints u64.
@@ -73,7 +77,7 @@ int main(void) {
const char* openedPath = 0;
uint32_t s = open_test_file(&file, &openedPath);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
print("async_cancel: open failed status="); print_u64(s); print("\n");
SysExitProcess(1);
}
@@ -83,7 +87,7 @@ int main(void) {
print("\n");
s = SysCreateEvent(EVENT_AUTO_RESET, 0, &event);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
print("async_cancel: event create failed status="); print_u64(s); print("\n");
SysExitProcess(1);
}
@@ -110,7 +114,7 @@ int main(void) {
req.RequestHandleOut = &request;
s = SysReadFileAsync(&req);
if (s != SYS_STATUS_PENDING || request == 0) {
if (s != STATUS_PENDING || request == 0) {
print("async_cancel: submit failed status="); print_u64(s);
print(" request="); print_u64(request); print("\n");
SysExitProcess(1);
@@ -119,10 +123,10 @@ int main(void) {
uint32_t cs = SysCancelIo(request);
print("async_cancel: cancel returned "); print_u64(cs); print("\n");
while (iosb.Status == SYS_STATUS_PENDING) {
while (iosb.Status == STATUS_PENDING) {
s = SysWait(event, SYS_WAIT_FLAG_ALERTABLE);
print("async_cancel: wait returned "); print_u64(s); print("\n");
if (s != SYS_STATUS_SUCCESS && s != SYS_STATUS_KERNEL_APC && s != SYS_STATUS_USER_APC && s != SYS_STATUS_ALERTED)
if (s != STATUS_SUCCESS && s != STATUS_KERNEL_APC && s != STATUS_USER_APC && s != STATUS_ALERTED)
break;
}
@@ -131,9 +135,9 @@ int main(void) {
// If the worker had already started, cancellation is best-effort and the
// request may complete successfully. If it was still queued, it must report
// SYS_STATUS_CANCELLED. Both outcomes are valid; SYS_STATUS_PENDING is not.
uint32_t ok = (iosb.Status == SYS_STATUS_CANCELLED) ||
(iosb.Status == SYS_STATUS_SUCCESS && iosb.Information == 512);
// STATUS_CANCELLED. Both outcomes are valid; STATUS_PENDING is not.
uint32_t ok = (iosb.Status == STATUS_CANCELLED) ||
(iosb.Status == STATUS_SUCCESS && iosb.Information == 512);
SysCloseHandle(request);
SysCloseHandle(event);
@@ -0,0 +1,108 @@
// Current-directory PEB cache smoke test.
// Verifies that SysSetCwd updates the user-visible PEB cache without a getter syscall.
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <ntlite/syscall.h>
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("cwd_peb_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Fails the test with a simple message.
static void fail(const char* what) {
print("cwd_peb_smoke: ");
print(what);
print("\n");
exit_now(1);
}
// Sets the current directory to one native path.
static void set_cwd_or_fail(const char* path, const char* what) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
uint32_t status = SysSetCwd(&req);
if (status != STATUS_SUCCESS) fail_status(what, status);
}
// Verifies the current-directory PEB view against one native path.
static void expect_cwd(const char* expected) {
SYS_STRING_VIEW cwd = SysGetCurrentDirectoryView();
size_t expectedLen = strlen(expected);
if (cwd.Length != expectedLen) fail("cwd length mismatch");
if (cwd.Length && !cwd.Data) fail("cwd data missing");
if (cwd.Length && memcmp(cwd.Data, expected, cwd.Length) != 0) fail("cwd value mismatch");
}
// Program entry point.
int main(void) {
static const char rootPath[] = "0:/";
static const char tempDir[] = "0:/cwd_peb_smoke_dir";
print("cwd_peb_smoke: start\n");
SYS_PEB* peb = SysGetPeb();
if (!peb) fail("peb missing");
if (peb->Magic != SYS_PEB_MAGIC) fail("peb magic mismatch");
if (peb->Version != SYS_ENV_VERSION) fail("peb version mismatch");
set_cwd_or_fail(rootPath, "set root cwd");
expect_cwd(rootPath);
SYS_PATH_REQUEST dirReq = { SysSvFromCstr(tempDir) };
uint32_t status = SysRemoveDir(&dirReq);
if (status != STATUS_SUCCESS && status != STATUS_NOT_FOUND)
fail_status("remove stale temp dir", status);
status = SysCreateDir(&dirReq);
if (status != STATUS_SUCCESS) fail_status("create temp dir", status);
set_cwd_or_fail(tempDir, "set temp cwd");
expect_cwd(tempDir);
set_cwd_or_fail(rootPath, "restore root cwd");
expect_cwd(rootPath);
status = SysRemoveDir(&dirReq);
if (status != STATUS_SUCCESS) fail_status("remove temp dir", status);
print("cwd_peb_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -78,23 +78,31 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
delete_path(paths[i]);
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysCreateFile(&req);
if (s == SYS_STATUS_SUCCESS) {
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_CREATE_NEW,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
return STATUS_NOT_FOUND;
}
static uint32_t open_path(const char* path, uint32_t* fileOut) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), &h };
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(path),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS)
if (s == STATUS_SUCCESS)
*fileOut = h;
return s;
}
@@ -110,7 +118,7 @@ static void checked_write(uint32_t file, const void* buffer, size_t size, const
size_t done = 0;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done);
uint32_t s = SysWriteFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (done != size) fail("short write");
}
@@ -119,13 +127,13 @@ static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOu
memset(buffer, 0xCC, size);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got);
uint32_t s = SysReadFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (gotOut) *gotOut = got;
}
static void checked_seek(uint32_t file, uint64_t offset, const char* what) {
uint32_t s = SysSeekFile(file, offset);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (s != STATUS_SUCCESS) fail_status(what, s);
}
static void expect_writeback_progress(const SYS_FILE_CACHE_STATS* before,
@@ -150,7 +158,7 @@ int main(void) {
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != SYS_STATUS_SUCCESS) fail_status("create temp", s);
if (s != STATUS_SUCCESS) fail_status("create temp", s);
print("dirty_writeback: using ");
print(path ? path : "<unknown>");
@@ -162,11 +170,11 @@ int main(void) {
SYS_FILE_CACHE_STATS beforeClose;
SYS_FILE_CACHE_STATS afterClose;
if (query_cache(&before) != SYS_STATUS_SUCCESS) fail("initial stats query failed");
if (query_cache(&before) != STATUS_SUCCESS) fail("initial stats query failed");
checked_write(file, gPatternA, sizeof(gPatternA), "write pattern A");
if (query_cache(&afterWrite) != SYS_STATUS_SUCCESS) fail("post-write stats query failed");
if (query_cache(&afterWrite) != STATUS_SUCCESS) fail("post-write stats query failed");
if (afterWrite.DirtyMarks <= before.DirtyMarks) fail("write did not mark dirty cache pages");
if (afterWrite.DirtyPages <= before.DirtyPages) fail("write did not leave dirty pages resident");
@@ -177,9 +185,9 @@ int main(void) {
if (memcmp(gReadback, gPatternA, sizeof(gPatternA)) != 0) fail("cached read before flush mismatch");
s = SysFlushFile(file);
if (s != SYS_STATUS_SUCCESS) fail_status("explicit flush", s);
if (s != STATUS_SUCCESS) fail_status("explicit flush", s);
if (query_cache(&afterFlush) != SYS_STATUS_SUCCESS) fail("post-flush stats query failed");
if (query_cache(&afterFlush) != STATUS_SUCCESS) fail("post-flush stats query failed");
if (afterFlush.FlushFileCalls <= afterWrite.FlushFileCalls) fail("flush call counter did not increase");
expect_writeback_progress(&afterWrite, &afterFlush, "explicit flush");
@@ -188,19 +196,19 @@ int main(void) {
checked_seek(file, REWRITE_SIZE, "seek close-tail");
checked_write(file, gPatternA + REWRITE_SIZE, CLOSE_SIZE - REWRITE_SIZE, "write close tail");
if (query_cache(&beforeClose) != SYS_STATUS_SUCCESS) fail("pre-close stats query failed");
if (query_cache(&beforeClose) != STATUS_SUCCESS) fail("pre-close stats query failed");
if (beforeClose.DirtyMarks <= afterFlush.DirtyMarks) fail("rewrite did not mark dirty pages");
s = SysCloseHandle(file);
file = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("close", s);
if (s != STATUS_SUCCESS) fail_status("close", s);
if (query_cache(&afterClose) != SYS_STATUS_SUCCESS) fail("post-close stats query failed");
if (query_cache(&afterClose) != STATUS_SUCCESS) fail("post-close stats query failed");
if (afterClose.FlushFileCalls <= beforeClose.FlushFileCalls) fail("close did not flush file");
expect_writeback_progress(&beforeClose, &afterClose, "close flush");
s = open_path(path, &file);
if (s != SYS_STATUS_SUCCESS) fail_status("reopen", s);
if (s != STATUS_SUCCESS) fail_status("reopen", s);
checked_read(file, gReadback, CLOSE_SIZE, &got, "read after reopen");
if (got != CLOSE_SIZE) fail("read after reopen size mismatch");
@@ -210,7 +218,7 @@ int main(void) {
s = SysCloseHandle(file);
file = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("close reopened", s);
if (s != STATUS_SUCCESS) fail_status("close reopened", s);
delete_path(path);
print("dirty_writeback: dirty-marks+=");
@@ -0,0 +1,377 @@
// Advisory file-lock smoke test.
// Verifies process-owned byte-range conflicts, timeout behavior, and exit cleanup.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define LOCK_OFFSET 0u
#define LOCK_LENGTH 128u
#define NONOVERLAP_OFFSET 512u
#define CHILD_PREFIX_MAX 32u
#define CHILD_PATH_MAX 256u
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int32_t code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("file_lock_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Fails the test with one plain message.
static void fail(const char* what) {
print("file_lock_smoke: ");
print(what);
print("\n");
exit_now(1);
}
// Deletes one path if it already exists.
static void delete_path(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
(void)SysDeleteFile(&req);
}
// Opens one path with lock rights.
static uint32_t open_existing_lockable(const char* path, uint32_t* fileOut) {
uint32_t h = 0;
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(path),
SYS_FILE_ACCESS_QUERY | SYS_FILE_ACCESS_LOCK,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS)
*fileOut = h;
return s;
}
// Creates a temporary file on the first writable path that works.
static uint32_t create_temp_file(uint32_t* fileOut, const char** pathOut) {
static const char* paths[] = {
"file_lock_smoke.tmp",
"/file_lock_smoke.tmp",
"1:/file_lock_smoke.tmp",
"0:/file_lock_smoke.tmp",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
uint32_t h = 0;
delete_path(paths[i]);
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY | SYS_FILE_ACCESS_LOCK,
SYS_FILE_CREATE_NEW,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
*fileOut = h;
*pathOut = paths[i];
return STATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
}
// Locks a range through the public request ABI.
static uint32_t lock_range(uint32_t file, uint32_t mode, uint64_t offset, uint64_t length, uint64_t timeoutMs) {
SYS_LOCK_FILE_REQUEST req = {
sizeof(req),
file,
mode,
SYS_FILE_LOCK_FLAG_NONE,
offset,
length,
timeoutMs,
};
return SysLockFile(&req);
}
// Unlocks a range through the public request ABI.
static uint32_t unlock_range(uint32_t file, uint64_t offset, uint64_t length) {
SYS_UNLOCK_FILE_REQUEST req = {
sizeof(req),
file,
SYS_FILE_LOCK_FLAG_NONE,
offset,
length,
};
return SysUnlockFile(&req);
}
// Splits the child command line into mode and path.
static int parse_child_command(SYS_STRING_VIEW view, char* mode, size_t modeCap, char* path, size_t pathCap) {
if (!view.Data || view.Length == 0)
return 0;
size_t sep = 0;
while (sep < view.Length && view.Data[sep] != ':')
sep++;
if (sep == view.Length || sep + 1 >= view.Length)
return 0;
if (sep + 1 > modeCap || view.Length - sep > pathCap)
return 0;
memcpy(mode, view.Data, sep);
mode[sep] = '\0';
memcpy(path, view.Data + sep + 1, view.Length - sep - 1);
path[view.Length - sep - 1] = '\0';
return 1;
}
// Returns whether two NUL-terminated strings are equal.
static int str_eq(const char* a, const char* b) {
return strcmp(a, b) == 0;
}
// Returns whether one mode name belongs to the child command protocol.
static int is_child_mode(const char* mode) {
return str_eq(mode, "excl-blocked") ||
str_eq(mode, "excl-timeout") ||
str_eq(mode, "excl-wait") ||
str_eq(mode, "shared-ok") ||
str_eq(mode, "excl-nonoverlap") ||
str_eq(mode, "hold-exit");
}
// Child process entry for cross-process lock checks.
static void run_child(SYS_STRING_VIEW commandLine) {
char mode[CHILD_PREFIX_MAX];
char path[CHILD_PATH_MAX];
if (!parse_child_command(commandLine, mode, sizeof(mode), path, sizeof(path)))
exit_now(2);
uint32_t file = 0;
uint32_t s = open_existing_lockable(path, &file);
if (s != STATUS_SUCCESS)
exit_now(3);
if (str_eq(mode, "excl-blocked")) {
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
SysCloseHandle(file);
exit_now(s == STATUS_WOULD_BLOCK ? 0 : 4);
}
if (str_eq(mode, "excl-timeout")) {
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 20);
SysCloseHandle(file);
exit_now(s == STATUS_TIMEOUT ? 0 : 5);
}
if (str_eq(mode, "excl-wait")) {
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, SYS_WAIT_TIMEOUT_INFINITE);
if (s == STATUS_SUCCESS)
(void)unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
SysCloseHandle(file);
exit_now(s == STATUS_SUCCESS ? 0 : 6);
}
if (str_eq(mode, "shared-ok")) {
s = lock_range(file, SYS_FILE_LOCK_SHARED, LOCK_OFFSET, LOCK_LENGTH, 0);
if (s == STATUS_SUCCESS)
(void)unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
SysCloseHandle(file);
exit_now(s == STATUS_SUCCESS ? 0 : 7);
}
if (str_eq(mode, "excl-nonoverlap")) {
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, NONOVERLAP_OFFSET, LOCK_LENGTH, 0);
if (s == STATUS_SUCCESS)
(void)unlock_range(file, NONOVERLAP_OFFSET, LOCK_LENGTH);
SysCloseHandle(file);
exit_now(s == STATUS_SUCCESS ? 0 : 8);
}
if (str_eq(mode, "hold-exit")) {
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
SysCloseHandle(file);
exit_now(s == STATUS_SUCCESS ? 0 : 9);
}
SysCloseHandle(file);
exit_now(10);
}
// Builds a child command line of the form mode:path.
static void build_child_command(char* out, size_t outCap, const char* mode, const char* path) {
size_t modeLen = strlen(mode);
size_t pathLen = strlen(path);
if (modeLen + 1 + pathLen + 1 > outCap)
fail("child command too long");
memcpy(out, mode, modeLen);
out[modeLen] = ':';
memcpy(out + modeLen + 1, path, pathLen + 1);
}
// Runs this image as a child and requires a zero exit code.
static void run_child_expect_success(const char* mode, const char* path) {
SYS_PEB* peb = SysGetPeb();
if (!peb || !peb->ImagePath.Data || peb->ImagePath.Length == 0)
fail("missing image path");
char command[CHILD_PREFIX_MAX + CHILD_PATH_MAX];
build_child_command(command, sizeof(command), mode, path);
uint32_t child = 0;
SYS_CREATE_PROCESS_REQUEST req = {
peb->ImagePath,
SysSvFromCstr(command),
&child,
};
uint32_t s = SysCreateProcess(&req);
if (s != STATUS_SUCCESS) fail_status("create child", s);
s = SysWait(child, SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS) fail_status("wait child", s);
int32_t exitCode = 0;
s = SysGetProcessExitCode(child, &exitCode);
if (s != STATUS_SUCCESS) fail_status("query child", s);
SysCloseHandle(child);
if (exitCode != 0) {
print("file_lock_smoke: child ");
print(mode);
print(" exited ");
print_u64((uint32_t)exitCode);
print("\n");
exit_now(1);
}
}
// Verifies validation paths that should not depend on filesystem state.
static void test_validation(uint32_t file) {
SYS_LOCK_FILE_REQUEST lockReq = {
0,
file,
SYS_FILE_LOCK_EXCLUSIVE,
SYS_FILE_LOCK_FLAG_NONE,
LOCK_OFFSET,
LOCK_LENGTH,
0,
};
uint32_t s = SysLockFile(&lockReq);
if (s != STATUS_INVALID_PARAMETER) fail_status("bad lock size", s);
lockReq.Size = sizeof(lockReq);
lockReq.Length = 0;
s = SysLockFile(&lockReq);
if (s != STATUS_INVALID_PARAMETER) fail_status("zero lock length", s);
SYS_UNLOCK_FILE_REQUEST unlockReq = {
sizeof(unlockReq),
file,
SYS_FILE_LOCK_FLAG_NONE,
LOCK_OFFSET,
LOCK_LENGTH,
};
s = SysUnlockFile(&unlockReq);
if (s != STATUS_NOT_FOUND) fail_status("unlock missing range", s);
lockReq.Length = LOCK_LENGTH;
lockReq.FileHandle = 0xeeeeeeeeu;
s = SysLockFile(&lockReq);
if (s != STATUS_INVALID_HANDLE) fail_status("bad lock handle", s);
}
// Program entry point.
int main(void) {
SYS_PEB* peb = SysGetPeb();
if (peb && peb->CommandLine.Data && peb->CommandLine.Length != 0) {
char mode[CHILD_PREFIX_MAX];
char path[CHILD_PATH_MAX];
if (parse_child_command(peb->CommandLine, mode, sizeof(mode), path, sizeof(path)) &&
is_child_mode(mode)) {
run_child(peb->CommandLine);
}
}
print("file_lock_smoke: start\n");
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp_file(&file, &path);
if (s != STATUS_SUCCESS) fail_status("create temp", s);
test_validation(file);
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
if (s != STATUS_SUCCESS) fail_status("exclusive parent lock", s);
run_child_expect_success("excl-blocked", path);
run_child_expect_success("excl-timeout", path);
run_child_expect_success("excl-nonoverlap", path);
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
if (s != STATUS_SUCCESS) fail_status("exclusive parent unlock", s);
s = lock_range(file, SYS_FILE_LOCK_SHARED, LOCK_OFFSET, LOCK_LENGTH, 0);
if (s != STATUS_SUCCESS) fail_status("shared parent lock", s);
run_child_expect_success("shared-ok", path);
run_child_expect_success("excl-blocked", path);
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
if (s != STATUS_SUCCESS) fail_status("shared parent unlock", s);
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
if (s != STATUS_SUCCESS) fail_status("blocking setup lock", s);
SYS_PEB* selfPeb = SysGetPeb();
if (!selfPeb || !selfPeb->ImagePath.Data || selfPeb->ImagePath.Length == 0) fail("missing self image");
char command[CHILD_PREFIX_MAX + CHILD_PATH_MAX];
build_child_command(command, sizeof(command), "excl-wait", path);
uint32_t child = 0;
SYS_CREATE_PROCESS_REQUEST createReq = { selfPeb->ImagePath, SysSvFromCstr(command), &child };
s = SysCreateProcess(&createReq);
if (s != STATUS_SUCCESS) fail_status("create blocking child", s);
SysSleep(30);
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
if (s != STATUS_SUCCESS) fail_status("wake unlock", s);
s = SysWait(child, SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS) fail_status("wait blocking child", s);
int32_t exitCode = 0;
s = SysGetProcessExitCode(child, &exitCode);
if (s != STATUS_SUCCESS) fail_status("query blocking child", s);
SysCloseHandle(child);
if (exitCode != 0) fail("blocking child failed");
run_child_expect_success("hold-exit", path);
s = lock_range(file, SYS_FILE_LOCK_EXCLUSIVE, LOCK_OFFSET, LOCK_LENGTH, 0);
if (s != STATUS_SUCCESS) fail_status("exit released lock", s);
s = unlock_range(file, LOCK_OFFSET, LOCK_LENGTH);
if (s != STATUS_SUCCESS) fail_status("exit release cleanup unlock", s);
SysCloseHandle(file);
delete_path(path);
print("file_lock_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,258 @@
// Native file metadata smoke test.
// Verifies path stat, handle stat, directory metadata, missing paths, and invalid handles.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#define TEST_FILE_SIZE 1537u
static uint8_t gPattern[TEST_FILE_SIZE];
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one plain message.
static void fail(const char* msg) {
print("file_stat_smoke: ");
print(msg);
print("\n");
exit_now(1);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("file_stat_smoke: ");
print(what);
print(" failed status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Removes a file path if it already exists.
static void delete_file_path(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
(void)SysDeleteFile(&req);
}
// Removes a directory path if it already exists.
static void remove_dir_path(const char* path) {
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
(void)SysRemoveDir(&req);
}
// Opens a new temporary file on the first writable path that works.
static uint32_t create_temp_file(uint32_t* fileOut, const char** pathOut) {
static const char* paths[] = {
"file_stat_smoke.tmp",
"/file_stat_smoke.tmp",
"1:/file_stat_smoke.tmp",
"0:/file_stat_smoke.tmp",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
delete_file_path(paths[i]);
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_CREATE_NEW,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
*fileOut = h;
*pathOut = paths[i];
return STATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
}
// Creates a temporary directory on the first writable path that works.
static uint32_t create_temp_dir(const char** pathOut) {
static const char* paths[] = {
"file_stat_smoke.dir",
"/file_stat_smoke.dir",
"1:/file_stat_smoke.dir",
"0:/file_stat_smoke.dir",
};
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
remove_dir_path(paths[i]);
SYS_PATH_REQUEST req = { SysSvFromCstr(paths[i]) };
uint32_t s = SysCreateDir(&req);
if (s == STATUS_SUCCESS) {
*pathOut = paths[i];
return STATUS_SUCCESS;
}
}
return STATUS_NOT_FOUND;
}
// Fills the write pattern used by the test file.
static void fill_pattern(void) {
for (size_t i = 0; i < sizeof(gPattern); ++i)
gPattern[i] = (uint8_t)(0x31u + (uint8_t)(i * 17u + (i >> 3)));
}
// Writes the test file contents and flushes them to the filesystem.
static void write_test_file(uint32_t file) {
size_t done = 0;
SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(file, gPattern, sizeof(gPattern), &done);
uint32_t s = SysWriteFile(&writeReq);
if (s != STATUS_SUCCESS) fail_status("write test file", s);
if (done != sizeof(gPattern)) fail("short write");
s = SysFlushFile(file);
if (s != STATUS_SUCCESS) fail_status("flush test file", s);
}
// Checks one regular-file metadata result.
static void check_regular_stat(const SYS_FILE_STAT* stat, uint64_t expectedSize, const char* what) {
if (stat->Size != sizeof(*stat)) fail("stat abi size mismatch");
if (stat->Type != SYS_FILE_TYPE_REGULAR) {
print("file_stat_smoke: ");
print(what);
print(" type=");
print_u64(stat->Type);
print("\n");
exit_now(1);
}
if ((stat->Attributes & SYS_FILE_ATTR_DIRECTORY) != 0) fail("regular file has directory attribute");
if (stat->LinkCount == 0) fail("regular file has zero links");
if (stat->FileSize != expectedSize) {
print("file_stat_smoke: ");
print(what);
print(" size=");
print_u64(stat->FileSize);
print(" expected=");
print_u64(expectedSize);
print("\n");
exit_now(1);
}
}
// Checks one directory metadata result.
static void check_directory_stat(const SYS_FILE_STAT* stat) {
if (stat->Size != sizeof(*stat)) fail("directory stat abi size mismatch");
if (stat->Type != SYS_FILE_TYPE_DIRECTORY) fail("directory stat type mismatch");
if ((stat->Attributes & SYS_FILE_ATTR_DIRECTORY) == 0) fail("directory attribute missing");
if (stat->LinkCount == 0) fail("directory has zero links");
}
// Exercises handle and path stat for one regular file.
static void test_file_stat(uint32_t file, const char* path) {
SYS_FILE_STAT byHandle;
memset(&byHandle, 0xA5, sizeof(byHandle));
uint32_t s = SysGetFileStat(file, &byHandle);
if (s != STATUS_SUCCESS) fail_status("get file stat", s);
check_regular_stat(&byHandle, sizeof(gPattern), "handle stat");
uint64_t size = 0;
s = SysGetFileSize(file, &size);
if (s != STATUS_SUCCESS) fail_status("get file size", s);
if (size != byHandle.FileSize) fail("file size and file stat disagree");
SYS_FILE_STAT byPath;
memset(&byPath, 0x5A, sizeof(byPath));
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(path), &byPath };
s = SysStatFile(&statReq);
if (s != STATUS_SUCCESS) fail_status("path stat", s);
check_regular_stat(&byPath, sizeof(gPattern), "path stat");
if (byHandle.VolumeId != 0 && byPath.VolumeId != 0 && byHandle.VolumeId != byPath.VolumeId)
fail("handle and path volume ids differ");
if (byHandle.FileId != 0 && byPath.FileId != 0 && byHandle.FileId != byPath.FileId)
fail("handle and path file ids differ");
}
// Exercises path stat for one directory.
static void test_directory_stat(const char* path) {
SYS_FILE_STAT stat;
memset(&stat, 0xCC, sizeof(stat));
SYS_STAT_FILE_REQUEST statReq = { SysSvFromCstr(path), &stat };
uint32_t s = SysStatFile(&statReq);
if (s != STATUS_SUCCESS) fail_status("directory stat", s);
check_directory_stat(&stat);
}
// Checks failure status paths that stat callers depend on.
static void test_failure_paths(void) {
SYS_FILE_STAT stat;
SYS_STAT_FILE_REQUEST missingReq = { SysSvFromCstr("file_stat_smoke_missing.nope"), &stat };
uint32_t s = SysStatFile(&missingReq);
if (s != STATUS_NOT_FOUND) fail_status("missing path status", s);
s = SysGetFileStat(0xFFFFFFFFu, &stat);
if (s != STATUS_INVALID_HANDLE) fail_status("invalid handle status", s);
SYS_STAT_FILE_REQUEST badReq = { SysSvFromCstr("file_stat_smoke_missing.nope"), 0 };
s = SysStatFile(&badReq);
if (s != STATUS_INVALID_PARAMETER) fail_status("null stat output status", s);
}
// Runs the native file metadata smoke test.
int main(void) {
print("file_stat_smoke: start\n");
fill_pattern();
uint32_t file = 0;
const char* filePath = 0;
uint32_t s = create_temp_file(&file, &filePath);
if (s != STATUS_SUCCESS) fail_status("create temp file", s);
write_test_file(file);
test_file_stat(file, filePath);
SysCloseHandle(file);
delete_file_path(filePath);
const char* dirPath = 0;
s = create_temp_dir(&dirPath);
if (s != STATUS_SUCCESS) fail_status("create temp dir", s);
test_directory_stat(dirPath);
remove_dir_path(dirPath);
SYS_PATH_REQUEST dirReq = { SysSvFromCstr(dirPath) };
s = SysCreateDir(&dirReq);
if (s != STATUS_SUCCESS) fail_status("recreate temp dir", s);
test_directory_stat(dirPath);
remove_dir_path(dirPath);
test_failure_paths();
print("file_stat_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,205 @@
// Native handle duplication smoke test.
// Verifies explicit rights reduction, same-rights duplication, and invalid duplicate requests.
#include <stdint.h>
#include <stdbool.h>
#include <ntlite/syscall.h>
typedef struct TEST_QUEUE_MESSAGE {
uint32_t Type;
uint32_t Flags;
uint64_t Param[6];
} TEST_QUEUE_MESSAGE;
typedef char TEST_QUEUE_MESSAGE_SIZE_CHECK[
(sizeof(TEST_QUEUE_MESSAGE) == SYS_QUEUE_MESSAGE_SIZE) ? 1 : -1
];
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one plain message.
static void fail(const char* msg) {
print("handle_dup_smoke: ");
print(msg);
print("\n");
exit_now(1);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("handle_dup_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Requires one exact status value.
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("handle_dup_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
exit_now(1);
}
// Duplicates one handle into the current process.
static uint32_t duplicate_handle(uint32_t source, uint32_t process, uint32_t desiredRights, uint32_t flags, uint32_t* out) {
SYS_DUPLICATE_HANDLE_REQUEST req = {
source,
process,
desiredRights,
flags,
out,
};
return SysDuplicateHandle(&req);
}
// Sends one queue message.
static uint32_t send_message(uint32_t queue, uint32_t type, uint64_t value) {
TEST_QUEUE_MESSAGE msg = {0};
msg.Type = type;
msg.Param[0] = value;
SYS_QUEUE_SEND_REQUEST req = {
sizeof(req),
queue,
SYS_QUEUE_FLAG_NONE,
0,
&msg,
};
return SysSendQueue(&req);
}
// Receives one queue message.
static uint32_t recv_message(uint32_t queue, TEST_QUEUE_MESSAGE* msg) {
SYS_QUEUE_RECV_REQUEST req = {
sizeof(req),
queue,
SYS_QUEUE_FLAG_NONE,
0,
msg,
};
return SysRecvQueue(&req);
}
// Verifies one received queue message.
static void check_message(const TEST_QUEUE_MESSAGE* msg, uint32_t type, uint64_t value) {
if (msg->Type != type || msg->Param[0] != value) fail("message payload mismatch");
}
// Runs the native handle duplication smoke test.
int main(void) {
print("handle_dup_smoke: start\n");
uint32_t currentPid = SysGetCurrentPid();
uint32_t process = 0;
uint32_t s = SysOpenProcess(currentPid, &process);
if (s != STATUS_SUCCESS) fail_status("open current process", s);
uint32_t queue = 0;
s = SysCreateQueue(&queue);
if (s != STATUS_SUCCESS) fail_status("create queue", s);
uint32_t readOnly = 0;
s = duplicate_handle(queue, process, SYS_HANDLE_RIGHT_READ, SYS_DUPLICATE_HANDLE_FLAG_NONE, &readOnly);
if (s != STATUS_SUCCESS) fail_status("duplicate read-only", s);
uint32_t writeOnly = 0;
s = duplicate_handle(queue, process, SYS_HANDLE_RIGHT_WRITE, SYS_DUPLICATE_HANDLE_FLAG_NONE, &writeOnly);
if (s != STATUS_SUCCESS) fail_status("duplicate write-only", s);
uint32_t sameRights = 0;
s = duplicate_handle(queue, process, 0, SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS, &sameRights);
if (s != STATUS_SUCCESS) fail_status("duplicate same rights", s);
s = send_message(readOnly, 10, 100);
if (s == STATUS_SUCCESS) fail("read-only duplicate allowed send");
TEST_QUEUE_MESSAGE msg;
s = recv_message(writeOnly, &msg);
if (s == STATUS_SUCCESS) fail("write-only duplicate allowed receive");
s = send_message(queue, 11, 101);
if (s != STATUS_SUCCESS) fail_status("send original", s);
s = recv_message(readOnly, &msg);
if (s != STATUS_SUCCESS) fail_status("receive read-only", s);
check_message(&msg, 11, 101);
s = send_message(writeOnly, 12, 102);
if (s != STATUS_SUCCESS) fail_status("send write-only", s);
s = recv_message(queue, &msg);
if (s != STATUS_SUCCESS) fail_status("receive original", s);
check_message(&msg, 12, 102);
s = send_message(sameRights, 13, 103);
if (s != STATUS_SUCCESS) fail_status("send same-rights", s);
s = recv_message(sameRights, &msg);
if (s != STATUS_SUCCESS) fail_status("receive same-rights", s);
check_message(&msg, 13, 103);
uint32_t ignored = 0;
s = duplicate_handle(readOnly, process, SYS_HANDLE_RIGHT_READ, SYS_DUPLICATE_HANDLE_FLAG_NONE, &ignored);
expect_status("duplicate non-duplicable handle", s, STATUS_ACCESS_DENIED);
s = duplicate_handle(queue, process, SYS_HANDLE_RIGHT_READ | 0x80000000u, SYS_DUPLICATE_HANDLE_FLAG_NONE, &ignored);
expect_status("duplicate unknown rights", s, STATUS_INVALID_PARAMETER);
s = duplicate_handle(queue, process, SYS_HANDLE_RIGHT_READ, SYS_DUPLICATE_HANDLE_FLAG_SAME_RIGHTS, &ignored);
expect_status("duplicate same-rights with desired rights", s, STATUS_INVALID_PARAMETER);
s = duplicate_handle(queue, process, 0, SYS_DUPLICATE_HANDLE_FLAG_NONE, &ignored);
expect_status("duplicate zero rights", s, STATUS_INVALID_PARAMETER);
SYS_DUPLICATE_HANDLE_REQUEST badOut = {
queue,
process,
SYS_HANDLE_RIGHT_READ,
SYS_DUPLICATE_HANDLE_FLAG_NONE,
0,
};
s = SysDuplicateHandle(&badOut);
expect_status("duplicate null output", s, STATUS_INVALID_PARAMETER);
SysCloseHandle(sameRights);
SysCloseHandle(writeOnly);
SysCloseHandle(readOnly);
SysCloseHandle(queue);
SysCloseHandle(process);
print("handle_dup_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,122 @@
// Native input API smoke test.
// Verifies status-returning keyboard and mouse polling contracts without requiring live input.
#include <stdint.h>
#include <stdbool.h>
#include <ntlite/syscall.h>
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("input_api_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Requires one exact status value.
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("input_api_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
exit_now(1);
}
// Program entry point.
int main(void) {
print("input_api_smoke: start\n");
expect_status("poll keyboard null", SysPollKeyboard((INPUT_KEY*)0), STATUS_INVALID_PARAMETER);
expect_status("is key down null", SysIsKeyDown(INPUT_KEY_A, (bool*)0), STATUS_INVALID_PARAMETER);
expect_status("read key null", SysReadKey((INPUT_KEY*)0), STATUS_INVALID_PARAMETER);
expect_status("read char null", SysReadKeyboardChar((char*)0), STATUS_INVALID_PARAMETER);
bool down = true;
uint32_t status = SysIsKeyDown(INPUT_KEY_A, &down);
if (status != STATUS_SUCCESS)
fail_status("is key down", status);
INPUT_KEY key = INPUT_KEY_A;
status = SysPollKeyboard(&key);
if (status != STATUS_SUCCESS && status != STATUS_WOULD_BLOCK)
fail_status("poll keyboard", status);
if (status == STATUS_WOULD_BLOCK && key != INPUT_KEY_NONE)
fail_status("poll keyboard would-block key", key);
INPUT_KEY_RECORD input;
SYS_INPUT_EVENT_REQUEST inputReq;
inputReq.Size = sizeof(inputReq);
inputReq.QueueHandle = 0;
inputReq.Flags = SYS_INPUT_EVENT_FLAG_NONE;
inputReq.TimeoutMilliseconds = 0;
inputReq.RecordOut = &input;
expect_status("peek input null", SysPeekInputEvent((const SYS_INPUT_EVENT_REQUEST*)0), STATUS_INVALID_PARAMETER);
status = SysPeekInputEvent(&inputReq);
if (status != STATUS_SUCCESS && status != STATUS_WOULD_BLOCK)
fail_status("peek input", status);
INPUT_MOUSE_EVENT mouse;
expect_status("peek mouse null", SysPeekMouse((INPUT_MOUSE_EVENT*)0), STATUS_INVALID_PARAMETER);
expect_status("read mouse null", SysReadMouse((INPUT_MOUSE_EVENT*)0), STATUS_INVALID_PARAMETER);
status = SysPeekMouse(&mouse);
if (status != STATUS_SUCCESS && status != STATUS_WOULD_BLOCK)
fail_status("peek mouse", status);
uint32_t readStatus = SysReadMouse(&mouse);
if (status == STATUS_SUCCESS) {
if (readStatus != STATUS_SUCCESS)
fail_status("read mouse after peek", readStatus);
} else if (readStatus != STATUS_WOULD_BLOCK) {
fail_status("read mouse would-block", readStatus);
}
int x = 0;
int y = 0;
expect_status("get mouse pos null x", SysGetMousePos((int*)0, &y), STATUS_INVALID_PARAMETER);
expect_status("get mouse pos null y", SysGetMousePos(&x, (int*)0), STATUS_INVALID_PARAMETER);
status = SysGetMousePos(&x, &y);
if (status != STATUS_SUCCESS)
fail_status("get mouse pos", status);
print("input_api_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,114 @@
// Input model smoke test.
// Verifies that input queue handles and request-based reads target one keyboard queue.
#include <stdint.h>
#include <ntlite/syscall.h>
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("input_model_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Requires one exact status value.
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("input_model_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
exit_now(1);
}
// Program entry point.
int main(void) {
print("input_model_smoke: start\n");
uint32_t inputQueue = 0;
expect_status("open input queue null", SysOpenInputQueue(SYS_INPUT_QUEUE_FLAG_NONE, (uint32_t*)0), STATUS_INVALID_PARAMETER);
expect_status("open input queue bad flags", SysOpenInputQueue(1, &inputQueue), STATUS_INVALID_PARAMETER);
uint32_t status = SysOpenInputQueue(SYS_INPUT_QUEUE_FLAG_NONE, &inputQueue);
if (status != STATUS_SUCCESS)
fail_status("open input queue", status);
INPUT_KEY_RECORD record;
SYS_INPUT_EVENT_REQUEST req;
req.Size = sizeof(req);
req.QueueHandle = inputQueue;
req.Flags = SYS_INPUT_EVENT_FLAG_NONE;
req.TimeoutMilliseconds = 0;
req.RecordOut = &record;
SYS_INPUT_EVENT_REQUEST badReq = req;
badReq.Size = 0;
expect_status("read input bad size", SysReadInputEvent(&badReq), STATUS_INVALID_PARAMETER);
badReq = req;
badReq.RecordOut = (INPUT_KEY_RECORD*)0;
expect_status("peek input null out", SysPeekInputEvent(&badReq), STATUS_INVALID_PARAMETER);
badReq = req;
badReq.QueueHandle = 0xdeadbeefu;
expect_status("read input bad handle", SysReadInputEvent(&badReq), STATUS_INVALID_HANDLE);
status = SysPeekInputEvent(&req);
if (status != STATUS_SUCCESS && status != STATUS_WOULD_BLOCK)
fail_status("peek input immediate", status);
uint32_t waitStatus = SysWaitSingle(inputQueue, SYS_WAIT_FLAG_NONE, 0);
if (status == STATUS_SUCCESS) {
if (waitStatus != STATUS_SUCCESS)
fail_status("wait input after readable peek", waitStatus);
} else if (waitStatus != STATUS_TIMEOUT) {
fail_status("wait empty input immediate", waitStatus);
}
req.TimeoutMilliseconds = 1;
status = SysReadInputEvent(&req);
if (status != STATUS_SUCCESS && status != STATUS_TIMEOUT)
fail_status("read input finite timeout", status);
SysCloseHandle(inputQueue);
print("input_model_smoke: PASS\n");
exit_now(0);
return 0;
}
+13 -9
View File
@@ -70,17 +70,21 @@ static uint32_t open_first_existing(uint32_t* fileOut, const char** pathOut) {
for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); ++i) {
uint32_t file = 0;
SYS_PATH_HANDLE_REQUEST openReq = { SysSvFromCstr(candidates[i]), &file };
SYS_OPEN_FILE_REQUEST openReq = SysMakeOpenFileRequest(SysSvFromCstr(candidates[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&file);
uint32_t s = SysOpenFile(&openReq);
if (s == SYS_STATUS_SUCCESS) {
if (s == STATUS_SUCCESS) {
*fileOut = file;
if (pathOut)
*pathOut = candidates[i];
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
return STATUS_NOT_FOUND;
}
int main(void) {
@@ -89,7 +93,7 @@ int main(void) {
uint32_t file = 0;
const char* openedPath = 0;
uint32_t s = open_first_existing(&file, &openedPath);
if (s != SYS_STATUS_SUCCESS)
if (s != STATUS_SUCCESS)
fail_status("open test file", s);
print("mmap_smoke: using ");
@@ -101,7 +105,7 @@ int main(void) {
SYS_FILE_IO_REQUEST ioReq = SysMakeFileIoRequest(file, gExpected, sizeof(gExpected), &got);
s = SysReadFile(&ioReq);
if (s != SYS_STATUS_SUCCESS)
if (s != STATUS_SUCCESS)
fail_status("read baseline", s);
if (got < 64)
fail_msg("baseline read was unexpectedly short");
@@ -114,7 +118,7 @@ int main(void) {
SYS_MAP_FLAG_NONE,
sizeof(gExpected),
&mapping);
if (s != SYS_STATUS_SUCCESS)
if (s != STATUS_SUCCESS)
fail_status("create file mapping", s);
print("mmap_smoke: mapping created\n");
@@ -137,7 +141,7 @@ int main(void) {
SysCloseHandle(mapping);
mapping = 0;
if (s != SYS_STATUS_SUCCESS)
if (s != STATUS_SUCCESS)
fail_status("map file view", s);
if (!base)
fail_msg("map returned null base");
@@ -187,7 +191,7 @@ int main(void) {
print("mmap_smoke: EOF fill OK\n");
s = SysFreeVm(base);
if (s != SYS_STATUS_SUCCESS)
if (s != STATUS_SUCCESS)
fail_status("unmap", s);
print("mmap_smoke: PASS\n");
@@ -73,16 +73,20 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
delete_path(paths[i]);
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysCreateFile(&req);
if (s == SYS_STATUS_SUCCESS) {
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_CREATE_NEW,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
return STATUS_NOT_FOUND;
}
static void fill_patterns(void) {
@@ -96,7 +100,7 @@ static void checked_write(uint32_t file, const void* buffer, size_t size, const
size_t done = 0;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done);
uint32_t s = SysWriteFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (done != size) fail("short write");
}
@@ -105,7 +109,7 @@ static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOu
memset(buffer, 0xCC, size);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got);
uint32_t s = SysReadFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (gotOut) *gotOut = got;
}
@@ -116,7 +120,7 @@ int main(void) {
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != SYS_STATUS_SUCCESS) fail_status("create temp", s);
if (s != STATUS_SUCCESS) fail_status("create temp", s);
print("pcache_invalidate: using ");
print(path ? path : "<unknown>");
@@ -125,7 +129,7 @@ int main(void) {
checked_write(file, gOriginal, sizeof(gOriginal), "write original");
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek fill", s);
if (s != STATUS_SUCCESS) fail_status("seek fill", s);
SYS_FILE_CACHE_STATS before;
SYS_FILE_CACHE_STATS afterFill;
@@ -133,44 +137,44 @@ int main(void) {
SYS_FILE_CACHE_STATS afterWrite;
SYS_FILE_CACHE_STATS afterRewriteRead;
SYS_FILE_CACHE_STATS afterTruncate;
if (query_cache(&before) != SYS_STATUS_SUCCESS) fail("initial stats query failed");
if (query_cache(&before) != STATUS_SUCCESS) fail("initial stats query failed");
size_t got = 0;
checked_read(file, gReadback, sizeof(gReadback), &got, "first read");
if (got != sizeof(gOriginal)) fail("first read size mismatch");
if (memcmp(gReadback, gOriginal, sizeof(gOriginal)) != 0) fail("first read data mismatch");
if (query_cache(&afterFill) != SYS_STATUS_SUCCESS) fail("fill stats query failed");
if (query_cache(&afterFill) != STATUS_SUCCESS) fail("fill stats query failed");
if (afterFill.Loads <= before.Loads) fail("initial read did not populate cache");
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek second", s);
if (s != STATUS_SUCCESS) fail_status("seek second", s);
checked_read(file, gReadback, sizeof(gReadback), &got, "second read");
if (got != sizeof(gOriginal)) fail("second read size mismatch");
if (memcmp(gReadback, gOriginal, sizeof(gOriginal)) != 0) fail("second read data mismatch");
if (query_cache(&afterSecond) != SYS_STATUS_SUCCESS) fail("second stats query failed");
if (query_cache(&afterSecond) != STATUS_SUCCESS) fail("second stats query failed");
// Do not require this second read to hit: the cache is intentionally
// bounded and pcache_pressure.exe may have left it at the soft limit, so
// the newly-populated temp pages are eligible for immediate pressure
// eviction. This test is about write/truncate freshness; mmap/read reuse
// eviction. This test is about write/truncate freshness; mmap/read reuse
// is covered by pcache_mmap_read_reuse.exe.
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek rewrite", s);
if (s != STATUS_SUCCESS) fail_status("seek rewrite", s);
checked_write(file, gReplacement, sizeof(gReplacement), "rewrite first page");
if (query_cache(&afterWrite) != SYS_STATUS_SUCCESS) fail("post-write stats query failed");
if (query_cache(&afterWrite) != STATUS_SUCCESS) fail("post-write stats query failed");
if (afterWrite.Invalidations <= afterSecond.Invalidations) fail("write did not invalidate cached page");
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, sizeof(gReplacement), &mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("create mapping after rewrite", s);
if (s != STATUS_SUCCESS) fail_status("create mapping after rewrite", s);
void* mappedBase = 0;
s = SysMapFile(mapping, 0, sizeof(gReplacement), SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &mappedBase);
SysCloseHandle(mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("map after rewrite", s);
if (s != STATUS_SUCCESS) fail_status("map after rewrite", s);
if (!mappedBase) fail("map after rewrite returned null");
volatile uint8_t* mappedView = (volatile uint8_t*)mappedBase;
@@ -181,27 +185,30 @@ int main(void) {
fail("stale mmap data after write invalidation");
}
s = SysFreeVm(mappedBase);
if (s != SYS_STATUS_SUCCESS) fail_status("unmap after rewrite", s);
if (s != STATUS_SUCCESS) fail_status("unmap after rewrite", s);
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek verify rewrite", s);
if (s != STATUS_SUCCESS) fail_status("seek verify rewrite", s);
checked_read(file, gReadback, sizeof(gReadback), &got, "read after rewrite");
if (got != sizeof(gOriginal)) fail("rewrite read size mismatch");
if (memcmp(gReadback, gReplacement, sizeof(gReplacement)) != 0) fail("stale data after write invalidation");
if (memcmp(gReadback + sizeof(gReplacement), gOriginal + sizeof(gReplacement),
sizeof(gOriginal) - sizeof(gReplacement)) != 0) fail("unwritten page changed after rewrite");
if (query_cache(&afterRewriteRead) != SYS_STATUS_SUCCESS) fail("rewrite-read stats query failed");
if (query_cache(&afterRewriteRead) != STATUS_SUCCESS) fail("rewrite-read stats query failed");
s = SysTruncateFile(file, TRUNC_SIZE);
if (s != SYS_STATUS_SUCCESS) fail_status("truncate", s);
if (SysGetFileSize(file) != TRUNC_SIZE) fail("truncate size mismatch");
if (s != STATUS_SUCCESS) fail_status("truncate", s);
uint64_t truncatedSize = 0;
s = SysGetFileSize(file, &truncatedSize);
if (s != STATUS_SUCCESS) fail_status("get truncated size", s);
if (truncatedSize != TRUNC_SIZE) fail("truncate size mismatch");
if (query_cache(&afterTruncate) != SYS_STATUS_SUCCESS) fail("truncate stats query failed");
if (query_cache(&afterTruncate) != STATUS_SUCCESS) fail("truncate stats query failed");
if (afterTruncate.Invalidations <= afterRewriteRead.Invalidations) fail("truncate did not invalidate cached tail");
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek truncated", s);
if (s != STATUS_SUCCESS) fail_status("seek truncated", s);
checked_read(file, gReadback, sizeof(gReadback), &got, "read truncated");
if (got != TRUNC_SIZE) fail("truncated read length mismatch");
if (memcmp(gReadback, gReplacement, TRUNC_SIZE) != 0) fail("truncated data mismatch");
@@ -72,15 +72,19 @@ static uint32_t open_test_file(uint32_t* out, const char** pathOut) {
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS) {
if (s == STATUS_SUCCESS) {
*out = h;
if (pathOut) *pathOut = paths[i];
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
return STATUS_NOT_FOUND;
}
int main(void) {
@@ -90,14 +94,16 @@ int main(void) {
SYS_FILE_CACHE_STATS afterFault;
SYS_FILE_CACHE_STATS afterRead;
uint32_t s = query_cache(&before);
if (s != SYS_STATUS_SUCCESS) fail_status("query initial stats", s);
if (s != STATUS_SUCCESS) fail_status("query initial stats", s);
uint32_t file = 0;
const char* path = 0;
s = open_test_file(&file, &path);
if (s != SYS_STATUS_SUCCESS) fail_status("open test file", s);
if (s != STATUS_SUCCESS) fail_status("open test file", s);
uint64_t fileSize = SysGetFileSize(file);
uint64_t fileSize = 0;
s = SysGetFileSize(file, &fileSize);
if (s != STATUS_SUCCESS) fail_status("get file size", s);
if (fileSize < 4096) fail("test file too small");
size_t mapSize = (fileSize < TEST_SIZE) ? (size_t)fileSize : TEST_SIZE;
@@ -109,12 +115,12 @@ int main(void) {
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, mapSize, &mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("create mapping", s);
if (s != STATUS_SUCCESS) fail_status("create mapping", s);
void* base = 0;
s = SysMapFile(mapping, 0, mapSize, SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base);
SysCloseHandle(mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("map file", s);
if (s != STATUS_SUCCESS) fail_status("map file", s);
if (!base) fail("map returned null");
volatile uint8_t* view = (volatile uint8_t*)base;
@@ -123,14 +129,14 @@ int main(void) {
(void)view[mapSize - 1];
s = query_cache(&afterFault);
if (s != SYS_STATUS_SUCCESS) fail_status("query after mmap faults", s);
if (s != STATUS_SUCCESS) fail_status("query after mmap faults", s);
if (afterFault.Loads <= before.Loads) {
SysFreeVm(base);
fail("mmap faults did not populate file cache");
}
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
SysFreeVm(base);
fail_status("seek", s);
}
@@ -139,7 +145,7 @@ int main(void) {
memset(gReadback, 0xCC, sizeof(gReadback));
SYS_FILE_IO_REQUEST readReq = SysMakeFileIoRequest(file, gReadback, mapSize, &got);
s = SysReadFile(&readReq);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
SysFreeVm(base);
fail_status("read", s);
}
@@ -153,7 +159,7 @@ int main(void) {
}
s = query_cache(&afterRead);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
SysFreeVm(base);
fail_status("query after read", s);
}
@@ -81,16 +81,20 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
delete_path(paths[i]);
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysCreateFile(&req);
if (s == SYS_STATUS_SUCCESS) {
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_CREATE_NEW,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
return STATUS_NOT_FOUND;
}
static void fill_pattern(void) {
@@ -103,7 +107,7 @@ static uint32_t validate_mapping(const uint8_t* base) {
if (base[i] != gPattern[i])
return 0x10000000u | (uint32_t)(i & 0x0FFFFFFFu);
}
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
static void thread_main(uintptr_t arg) {
@@ -114,8 +118,8 @@ static void thread_main(uintptr_t arg) {
void* base = 0;
uint32_t s = SysMapFile(ctx->Mapping, 0, TEST_SIZE,
SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base);
if (s != SYS_STATUS_SUCCESS || !base) {
ctx->Status = s ? s : SYS_STATUS_INVALID_PARAMETER;
if (s != STATUS_SUCCESS || !base) {
ctx->Status = s ? s : STATUS_INVALID_PARAMETER;
SysExitThread(1);
}
@@ -126,18 +130,18 @@ static void thread_main(uintptr_t arg) {
s = validate_mapping((const uint8_t*)base);
uint32_t fs = SysFreeVm(base);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
ctx->Status = s;
SysExitThread(1);
}
if (fs != SYS_STATUS_SUCCESS) {
if (fs != STATUS_SUCCESS) {
ctx->Status = fs;
SysExitThread(1);
}
ctx->IterationsDone++;
}
ctx->Status = SYS_STATUS_SUCCESS;
ctx->Status = STATUS_SUCCESS;
SysExitThread(0);
}
@@ -148,21 +152,21 @@ int main(void) {
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != SYS_STATUS_SUCCESS) fail_status("create temp", s);
if (s != STATUS_SUCCESS) fail_status("create temp", s);
size_t written = 0;
SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(file, gPattern, sizeof(gPattern), &written);
s = SysWriteFile(&writeReq);
if (s != SYS_STATUS_SUCCESS) fail_status("write pattern", s);
if (s != STATUS_SUCCESS) fail_status("write pattern", s);
if (written != sizeof(gPattern)) fail("short write");
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, TEST_SIZE, &mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("create mapping", s);
if (s != STATUS_SUCCESS) fail_status("create mapping", s);
SYS_FILE_CACHE_STATS before;
SYS_FILE_CACHE_STATS after;
if (query_cache(&before) != SYS_STATUS_SUCCESS) fail("initial stats query failed");
if (query_cache(&before) != STATUS_SUCCESS) fail("initial stats query failed");
uint32_t handles[THREAD_COUNT];
uint32_t tids[THREAD_COUNT];
@@ -173,7 +177,7 @@ int main(void) {
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
gThreads[i].Index = i;
gThreads[i].Mapping = mapping;
gThreads[i].Status = SYS_STATUS_PENDING;
gThreads[i].Status = STATUS_PENDING;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)thread_main,
(uintptr_t)&gThreads[i],
@@ -183,17 +187,17 @@ int main(void) {
&tids[i]
};
s = SysCreateThread(&req);
if (s != SYS_STATUS_SUCCESS) fail_status("create thread", s);
if (s != STATUS_SUCCESS) fail_status("create thread", s);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
s = SysWait(handles[i], SYS_WAIT_FLAG_NONE);
if (s != SYS_STATUS_SUCCESS) fail_status("wait thread", s);
if (s != STATUS_SUCCESS) fail_status("wait thread", s);
SysCloseHandle(handles[i]);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
if (gThreads[i].Status != SYS_STATUS_SUCCESS) {
if (gThreads[i].Status != STATUS_SUCCESS) {
print("pcache_multithread: worker ");
print_u64(i);
print(" status=");
@@ -206,7 +210,7 @@ int main(void) {
if (gThreads[i].IterationsDone != ITERATIONS) fail("worker iteration count mismatch");
}
if (query_cache(&after) != SYS_STATUS_SUCCESS) fail("final stats query failed");
if (query_cache(&after) != STATUS_SUCCESS) fail("final stats query failed");
if (after.Loads <= before.Loads) fail("concurrent mmap did not load cache pages");
if (after.Hits <= before.Hits) fail("concurrent mmap did not reuse cache pages");
@@ -71,23 +71,32 @@ static uint32_t open_first_large(uint32_t* fileOut, const char** pathOut, uint64
for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); ++i) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(candidates[i]), &h };
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(candidates[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s != SYS_STATUS_SUCCESS)
if (s != STATUS_SUCCESS)
continue;
uint64_t size = SysGetFileSize(h);
uint64_t size = 0;
s = SysGetFileSize(h, &size);
if (s != STATUS_SUCCESS) {
SysCloseHandle(h);
continue;
}
if (size >= MIN_PRESSURE_BYTES) {
*fileOut = h;
*pathOut = candidates[i];
*sizeOut = size;
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
SysCloseHandle(h);
}
return SYS_STATUS_NOT_FOUND;
return STATUS_NOT_FOUND;
}
int main(void) {
@@ -95,14 +104,14 @@ int main(void) {
SYS_FILE_CACHE_STATS before;
SYS_FILE_CACHE_STATS after;
if (query_cache(&before) != SYS_STATUS_SUCCESS)
if (query_cache(&before) != STATUS_SUCCESS)
fail("initial cache stats query failed");
uint32_t file = 0;
const char* path = 0;
uint64_t size = 0;
uint32_t s = open_first_large(&file, &path, &size);
if (s != SYS_STATUS_SUCCESS)
if (s != STATUS_SUCCESS)
fail_status("open large asset", s);
print("pcache_pressure: using ");
@@ -117,7 +126,7 @@ int main(void) {
size_t got = 0;
SYS_FILE_IO_REQUEST readReq = SysMakeFileIoRequest(file, gBuf, sizeof(gBuf), &got);
s = SysReadFile(&readReq);
if (s != SYS_STATUS_SUCCESS)
if (s != STATUS_SUCCESS)
fail_status("read", s);
if (got == 0)
break;
@@ -129,7 +138,7 @@ int main(void) {
if (total < MIN_PRESSURE_BYTES)
fail("asset read was too short to pressure the cache");
if (query_cache(&after) != SYS_STATUS_SUCCESS)
if (query_cache(&after) != STATUS_SUCCESS)
fail("final cache stats query failed");
uint64_t loads = after.Loads - before.Loads;
+18 -14
View File
@@ -66,15 +66,19 @@ static uint32_t open_self(uint32_t* out) {
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS) {
if (s == STATUS_SUCCESS) {
*out = h;
print("pcache_smoke: using "); print(paths[i]); print("\n");
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
return STATUS_NOT_FOUND;
}
int main(void) {
@@ -82,52 +86,52 @@ int main(void) {
SYS_FILE_CACHE_STATS before, afterFirst, afterSecond, afterMap;
uint32_t s = query_cache(&before);
if (s != SYS_STATUS_SUCCESS) fail_status("query initial stats", s);
if (s != STATUS_SUCCESS) fail_status("query initial stats", s);
uint32_t file = 0;
s = open_self(&file);
if (s != SYS_STATUS_SUCCESS) fail_status("open test file", s);
if (s != STATUS_SUCCESS) fail_status("open test file", s);
size_t gotA = 0;
memset(gA, 0xA5, sizeof(gA));
SYS_FILE_IO_REQUEST readReqA = SysMakeFileIoRequest(file, gA, sizeof(gA), &gotA);
s = SysReadFile(&readReqA);
if (s != SYS_STATUS_SUCCESS) fail_status("first read", s);
if (s != STATUS_SUCCESS) fail_status("first read", s);
if (gotA < 4096) fail("first read too short");
print("pcache_smoke: first read OK\n");
s = query_cache(&afterFirst);
if (s != SYS_STATUS_SUCCESS) fail_status("query after first read", s);
if (s != STATUS_SUCCESS) fail_status("query after first read", s);
if (afterFirst.Loads <= before.Loads)
fail("first read did not load file-cache pages");
s = SysSeekFile(file, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("seek", s);
if (s != STATUS_SUCCESS) fail_status("seek", s);
size_t gotB = 0;
memset(gB, 0x5A, sizeof(gB));
SYS_FILE_IO_REQUEST readReqB = SysMakeFileIoRequest(file, gB, sizeof(gB), &gotB);
s = SysReadFile(&readReqB);
if (s != SYS_STATUS_SUCCESS) fail_status("second read", s);
if (s != STATUS_SUCCESS) fail_status("second read", s);
if (gotB != gotA) fail("second read length mismatch");
if (memcmp(gA, gB, gotA) != 0) fail("second read data mismatch");
print("pcache_smoke: second read OK\n");
s = query_cache(&afterSecond);
if (s != SYS_STATUS_SUCCESS) fail_status("query after second read", s);
if (s != STATUS_SUCCESS) fail_status("query after second read", s);
if (afterSecond.Hits <= afterFirst.Hits)
fail("second read did not hit file cache");
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, sizeof(gA), &mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("create mapping", s);
if (s != STATUS_SUCCESS) fail_status("create mapping", s);
SysCloseHandle(file);
file = 0;
void* base = 0;
s = SysMapFile(mapping, 0, sizeof(gA), SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base);
SysCloseHandle(mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("map file", s);
if (s != STATUS_SUCCESS) fail_status("map file", s);
if (!base) fail("map returned null");
volatile uint8_t* view = (volatile uint8_t*)base;
@@ -141,7 +145,7 @@ int main(void) {
}
s = query_cache(&afterMap);
if (s != SYS_STATUS_SUCCESS) fail_status("query after map", s);
if (s != STATUS_SUCCESS) fail_status("query after map", s);
if (afterMap.Hits <= afterSecond.Hits)
fail("mmap faults did not hit already cached pages");
@@ -0,0 +1,103 @@
// Protected-process smoke test.
// Verifies that kernel-owned processes remain queryable but cannot be terminated.
#include <stddef.h>
#include <stdint.h>
#include <ntlite/syscall.h>
#define MAX_PROCESSES 16u
// Prints one NUL-terminated string to the console.
static void print(const char* text) {
SYS_WRITE_CONSOLE_REQUEST request = { SysSvFromCstr(text) };
(void)SysWriteConsole(&request);
}
// Prints one unsigned integer to the console.
static void print_u32(uint32_t value) {
char buffer[16];
size_t length = 0;
if (value == 0) {
print("0");
return;
}
while (value && length < sizeof(buffer)) {
buffer[length++] = (char)('0' + (value % 10u));
value /= 10u;
}
while (length) {
char digit = buffer[--length];
SYS_WRITE_CONSOLE_REQUEST request = { SysSvFromParts(&digit, 1) };
(void)SysWriteConsole(&request);
}
}
// Terminates the current test process.
static void exit_now(int32_t code) {
for (;;) (void)SysExitProcess(code);
}
// Fails the test with one status value.
static void fail_status(const char* operation, uint32_t status) {
print("process_protection_smoke: ");
print(operation);
print(" status=");
print_u32(status);
print("\n");
exit_now(1);
}
// Verifies one protected process remains alive after a kill attempt.
static void verify_protected_process(const SYS_PROCESS_INFO* process) {
uint32_t handle = 0;
uint32_t status = SysOpenProcess(process->ProcessId, &handle);
if (status != STATUS_SUCCESS)
fail_status("open protected process", status);
status = SysKillProcess(handle, (int32_t)STATUS_PROCESS_KILLED);
if (status != STATUS_ACCESS_DENIED) {
SysCloseHandle(handle);
fail_status("kill protected process", status);
}
status = SysWaitSingle(handle, SYS_WAIT_FLAG_NONE, 0);
SysCloseHandle(handle);
if (status != STATUS_TIMEOUT)
fail_status("protected process remained signaled", status);
}
// Runs the protected-process contract checks.
void main(void) {
print("process_protection_smoke: start\n");
SYS_PROCESS_INFO processes[MAX_PROCESSES];
size_t count = 0;
uint32_t status = SysGetProcessList(processes, MAX_PROCESSES, &count);
if (status != STATUS_SUCCESS)
fail_status("query process list", status);
int sawIdle = 0;
int sawSystem = 0;
size_t limit = count < MAX_PROCESSES ? count : MAX_PROCESSES;
for (size_t i = 0; i < limit; i++) {
const SYS_PROCESS_INFO* process = &processes[i];
if (!(process->Flags & (SYS_PROCESS_FLAG_IDLE | SYS_PROCESS_FLAG_SYSTEM)))
continue;
if (!(process->Flags & SYS_PROCESS_FLAG_TERMINATION_PROTECTED))
fail_status("missing protected flag", process->ProcessId);
verify_protected_process(process);
if (process->Flags & SYS_PROCESS_FLAG_IDLE) sawIdle = 1;
if (process->Flags & SYS_PROCESS_FLAG_SYSTEM) sawSystem = 1;
}
if (!sawIdle || !sawSystem)
fail_status("missing kernel process", (uint32_t)((sawIdle ? 1u : 0u) | (sawSystem ? 2u : 0u)));
print("process_protection_smoke: PASS\n");
exit_now(0);
}
@@ -0,0 +1,177 @@
// Realpath ABI smoke test.
// Verifies lexical canonicalization, size reporting, and root clamping.
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <ntlite/syscall.h>
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("realpath_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Fails the test with one plain message.
static void fail(const char* what) {
print("realpath_smoke: ");
print(what);
print("\n");
exit_now(1);
}
// Appends one string to a fixed test buffer.
static void append(char* out, size_t cap, const char* suffix) {
size_t len = strlen(out);
size_t add = strlen(suffix);
if (len + add + 1 > cap) fail("test buffer too small");
memcpy(out + len, suffix, add + 1);
}
// Builds the current drive root path.
static void get_current_drive_root(char* out, size_t cap) {
char current[SYS_CWD_MAX];
size_t required = 0;
SYS_REAL_PATH_REQUEST req = {
sizeof(req),
SYS_REAL_PATH_FLAG_NONE,
SysSvFromCstr("."),
current,
sizeof(current),
&required,
};
uint32_t status = SysRealPath(&req);
if (status != STATUS_SUCCESS) fail_status("query current path", status);
if (required < 4 || current[0] < '0' || current[0] > '7' || current[1] != ':' || current[2] != '/')
fail("current path is not native drive-rooted");
if (cap < 4) fail("drive root output too small");
out[0] = current[0];
out[1] = ':';
out[2] = '/';
out[3] = '\0';
}
// Resolves a path and compares the exact native string.
static void expect_realpath(const char* input, const char* expected) {
char out[SYS_CWD_MAX];
size_t required = 0;
SYS_REAL_PATH_REQUEST req = {
sizeof(req),
SYS_REAL_PATH_FLAG_NONE,
SysSvFromCstr(input),
out,
sizeof(out),
&required,
};
uint32_t status = SysRealPath(&req);
if (status != STATUS_SUCCESS) fail_status("realpath", status);
if (required != strlen(expected) + 1) fail("required size mismatch");
if (strcmp(out, expected) != 0) fail("resolved path mismatch");
}
// Verifies size-only and too-small buffer behavior.
static void check_size_reporting(const char* input, const char* expected) {
size_t required = 0;
SYS_REAL_PATH_REQUEST query = {
sizeof(query),
SYS_REAL_PATH_FLAG_NONE,
SysSvFromCstr(input),
(char*)0,
0,
&required,
};
uint32_t status = SysRealPath(&query);
if (status != STATUS_SUCCESS) fail_status("size query", status);
if (required != strlen(expected) + 1) fail("size query required mismatch");
char small[4];
size_t smallRequired = 0;
SYS_REAL_PATH_REQUEST smallReq = {
sizeof(smallReq),
SYS_REAL_PATH_FLAG_NONE,
SysSvFromCstr(input),
small,
sizeof(small),
&smallRequired,
};
status = SysRealPath(&smallReq);
if (status != STATUS_BUFFER_TOO_SMALL) fail_status("small buffer", status);
if (smallRequired != required) fail("small required mismatch");
}
// Program entry point.
int main(void) {
print("realpath_smoke: start\n");
char root[4];
get_current_drive_root(root, sizeof(root));
char input[SYS_CWD_MAX];
char expected[SYS_CWD_MAX];
memcpy(input, root, sizeof(root));
append(input, sizeof(input), "//foo///bar\\baz/.");
memcpy(expected, root, sizeof(root));
append(expected, sizeof(expected), "foo/bar/baz");
expect_realpath(input, expected);
check_size_reporting(input, expected);
memcpy(input, root, sizeof(root));
append(input, sizeof(input), "../../root_escape");
memcpy(expected, root, sizeof(root));
append(expected, sizeof(expected), "root_escape");
expect_realpath(input, expected);
SYS_REAL_PATH_REQUEST currentReq = {
sizeof(currentReq),
SYS_REAL_PATH_FLAG_NONE,
SysSvFromCstr("."),
expected,
sizeof(expected),
(size_t*)0,
};
uint32_t status = SysRealPath(&currentReq);
if (status != STATUS_SUCCESS) fail_status("current realpath", status);
memcpy(input, "missing_realpath_dir/../missing_file", sizeof("missing_realpath_dir/../missing_file"));
append(expected, sizeof(expected), expected[strlen(expected) - 1] == '/' ? "missing_file" : "/missing_file");
expect_realpath(input, expected);
print("realpath_smoke: PASS\n");
exit_now(0);
return 0;
}
+30 -22
View File
@@ -73,36 +73,44 @@ static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) {
for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) {
uint32_t h = 0;
delete_path(paths[i]);
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(paths[i]), &h };
uint32_t s = SysCreateFile(&req);
if (s == SYS_STATUS_SUCCESS) {
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_CREATE_NEW,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == STATUS_SUCCESS) {
*fileOut = h;
if (pathOut) *pathOut = paths[i];
return SYS_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
}
return SYS_STATUS_NOT_FOUND;
return STATUS_NOT_FOUND;
}
static uint32_t open_path(const char* path, uint32_t* fileOut) {
uint32_t h = 0;
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), &h };
SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(path),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&h);
uint32_t s = SysOpenFile(&req);
if (s == SYS_STATUS_SUCCESS)
if (s == STATUS_SUCCESS)
*fileOut = h;
return s;
}
static void checked_seek(uint32_t file, uint64_t offset, const char* what) {
uint32_t s = SysSeekFile(file, offset);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (s != STATUS_SUCCESS) fail_status(what, s);
}
static void checked_write(uint32_t file, const void* buffer, size_t size, const char* what) {
size_t done = 0;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &done);
uint32_t s = SysWriteFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (done != size) fail("short write");
}
@@ -111,7 +119,7 @@ static void checked_read(uint32_t file, void* buffer, size_t size, size_t* gotOu
memset(buffer, 0xCC, size);
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(file, buffer, size, &got);
uint32_t s = SysReadFile(&req);
if (s != SYS_STATUS_SUCCESS) fail_status(what, s);
if (s != STATUS_SUCCESS) fail_status(what, s);
if (gotOut) *gotOut = got;
}
@@ -129,7 +137,7 @@ int main(void) {
uint32_t file = 0;
const char* path = 0;
uint32_t s = create_temp(&file, &path);
if (s != SYS_STATUS_SUCCESS) fail_status("create temp", s);
if (s != STATUS_SUCCESS) fail_status("create temp", s);
print("shared_mmap: creating temporary file ");
print(path ? path : "<unknown>");
@@ -137,16 +145,16 @@ int main(void) {
checked_write(file, gInitial, TEST_SIZE, "initial write");
s = SysFlushFile(file);
if (s != SYS_STATUS_SUCCESS) fail_status("initial flush", s);
if (s != STATUS_SUCCESS) fail_status("initial flush", s);
uint32_t mapping = 0;
s = SysCreateFileMapping(file, SYS_VM_READWRITE, SYS_MAP_FLAG_NONE, TEST_SIZE, &mapping);
if (s != SYS_STATUS_SUCCESS) fail_status("create mapping", s);
if (s != STATUS_SUCCESS) fail_status("create mapping", s);
uint8_t* view = 0;
s = SysMapFile(mapping, 0, TEST_SIZE, SYS_MAP_ACCESS_READ | SYS_MAP_ACCESS_WRITE,
SYS_MAP_FLAG_NONE, (void**)&view);
if (s != SYS_STATUS_SUCCESS) fail_status("map shared write", s);
if (s != STATUS_SUCCESS) fail_status("map shared write", s);
for (size_t i = 128; i < 128 + 3000; ++i) {
view[i] = (uint8_t)(0xA0u + (uint8_t)i);
@@ -159,12 +167,12 @@ int main(void) {
SYS_FILE_CACHE_STATS beforeFlush;
SYS_FILE_CACHE_STATS afterFlush;
if (query_cache(&beforeFlush) != SYS_STATUS_SUCCESS) fail("pre-flush stats failed");
if (query_cache(&beforeFlush) != STATUS_SUCCESS) fail("pre-flush stats failed");
s = SysFlushFile(file);
if (s != SYS_STATUS_SUCCESS) fail_status("flush mapped writes", s);
if (s != STATUS_SUCCESS) fail_status("flush mapped writes", s);
if (query_cache(&afterFlush) != SYS_STATUS_SUCCESS) fail("post-flush stats failed");
if (query_cache(&afterFlush) != STATUS_SUCCESS) fail("post-flush stats failed");
if (afterFlush.MappedDirtyPages <= beforeFlush.MappedDirtyPages)
fail("mapped dirty pages were not harvested");
if (afterFlush.WritebackBytes <= beforeFlush.WritebackBytes)
@@ -183,18 +191,18 @@ int main(void) {
s = SysFreeVm(view);
view = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("unmap", s);
if (s != STATUS_SUCCESS) fail_status("unmap", s);
s = SysCloseHandle(mapping);
mapping = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("close mapping", s);
if (s != STATUS_SUCCESS) fail_status("close mapping", s);
s = SysCloseHandle(file);
file = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("close file", s);
if (s != STATUS_SUCCESS) fail_status("close file", s);
s = open_path(path, &file);
if (s != SYS_STATUS_SUCCESS) fail_status("reopen", s);
if (s != STATUS_SUCCESS) fail_status("reopen", s);
checked_read(file, gReadback, TEST_SIZE, &got, "read after close");
if (got != TEST_SIZE) fail("read after close size mismatch");
@@ -202,7 +210,7 @@ int main(void) {
s = SysCloseHandle(file);
file = 0;
if (s != SYS_STATUS_SUCCESS) fail_status("close reopened", s);
if (s != STATUS_SUCCESS) fail_status("close reopened", s);
delete_path(path);
print("shared_mmap: mapped-dirty-pages+=");
@@ -0,0 +1,150 @@
// Native socket address ABI smoke test.
// Verifies generic IPv4/IPv6 address records and basic socket validation.
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <ntlite/syscall.h>
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current process.
static void exit_now(int32_t code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one message.
static void fail(const char* message) {
print("socket_addr_smoke: ");
print(message);
print("\n");
exit_now(1);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("socket_addr_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Verifies public address record shape.
static void check_address_shape(void) {
if (sizeof(SYS_SOCKET_ADDRESS) != SYS_SOCKET_ADDRESS_SIZE)
fail("SYS_SOCKET_ADDRESS size mismatch");
}
// Verifies numeric IPv4 resolution returns the generic address shape.
static void check_ipv4_resolve(void) {
SYS_SOCKET_ADDRESS address;
size_t addressSize = sizeof(address);
SYS_RESOLVE_HOST_REQUEST req;
memset(&address, 0, sizeof(address));
memset(&req, 0, sizeof(req));
req.Name = SysSvFromCstr("127.0.0.1");
req.Family = SYS_AF_INET;
req.AddressOut = &address;
req.AddressSizeInOut = &addressSize;
uint32_t status = SysResolveHost(&req);
if (status != STATUS_SUCCESS) fail_status("resolve ipv4", status);
if (addressSize != sizeof(address)) fail("resolve ipv4 size mismatch");
if (address.Family != SYS_AF_INET || address.Size != sizeof(address)) fail("resolve ipv4 tag mismatch");
if (address.Address[0] != 127 || address.Address[1] != 0 || address.Address[2] != 0 || address.Address[3] != 1)
fail("resolve ipv4 bytes mismatch");
}
// Verifies numeric IPv6 resolution returns the generic address shape.
static void check_ipv6_resolve(void) {
SYS_SOCKET_ADDRESS address;
size_t addressSize = sizeof(address);
SYS_RESOLVE_HOST_REQUEST req;
memset(&address, 0, sizeof(address));
memset(&req, 0, sizeof(req));
req.Name = SysSvFromCstr("::1");
req.Family = SYS_AF_INET6;
req.AddressOut = &address;
req.AddressSizeInOut = &addressSize;
uint32_t status = SysResolveHost(&req);
if (status != STATUS_SUCCESS) fail_status("resolve ipv6", status);
if (addressSize != sizeof(address)) fail("resolve ipv6 size mismatch");
if (address.Family != SYS_AF_INET6 || address.Size != sizeof(address)) fail("resolve ipv6 tag mismatch");
for (int i = 0; i < 15; i++) {
if (address.Address[i] != 0) fail("resolve ipv6 prefix mismatch");
}
if (address.Address[15] != 1) fail("resolve ipv6 loopback mismatch");
}
// Verifies IPv4 and IPv6 sockets accept only matching generic address records.
static void check_socket_address_validation(void) {
uint32_t ipv4 = 0;
uint32_t ipv6 = 0;
uint32_t status = SysCreateSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &ipv4);
if (status != STATUS_SUCCESS) fail_status("create ipv4 udp", status);
status = SysCreateSocket(SYS_AF_INET6, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &ipv6);
if (status != STATUS_SUCCESS) fail_status("create ipv6 udp", status);
SYS_SOCKET_ADDRESS ipv4Address;
memset(&ipv4Address, 0, sizeof(ipv4Address));
ipv4Address.Size = sizeof(ipv4Address);
ipv4Address.Family = SYS_AF_INET;
ipv4Address.Address[0] = 127;
ipv4Address.Address[3] = 1;
SYS_SOCKET_ADDRESS ipv6Address;
memset(&ipv6Address, 0, sizeof(ipv6Address));
ipv6Address.Size = sizeof(ipv6Address);
ipv6Address.Family = SYS_AF_INET6;
ipv6Address.Address[15] = 1;
SYS_SOCKET_ADDRESS badSize = ipv6Address;
badSize.Size = sizeof(badSize) - 1;
status = SysBindSocket(ipv6, &badSize, sizeof(badSize));
if (status == STATUS_SUCCESS) fail("bind accepted wrong ipv6 size");
status = SysConnectSocket(ipv4, &ipv6Address, sizeof(ipv6Address));
if (status == STATUS_SUCCESS) fail("connect accepted wrong address family");
SysCloseHandle(ipv4);
SysCloseHandle(ipv6);
}
// Runs the socket address ABI smoke test.
int main(void) {
print("socket_addr_smoke: start\n");
check_address_shape();
check_ipv4_resolve();
check_ipv6_resolve();
check_socket_address_validation();
print("socket_addr_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,141 @@
// Kernel statistics ABI smoke test.
// Verifies size queries, trailing CPU records, and generic filesystem stats.
#include <stdint.h>
#include <stddef.h>
#include <ntlite/syscall.h>
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one plain message.
static void fail(const char* what) {
print("stats_abi_smoke: ");
print(what);
print("\n");
exit_now(1);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("stats_abi_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Requires one exact status value.
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("stats_abi_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
exit_now(1);
}
// Verifies fixed-size stats classes report and fill their public header.
static void check_fixed_stats(void) {
size_t required = 0;
uint32_t status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, 0, 0, &required);
if (status != STATUS_SUCCESS) fail_status("scheduler size", status);
if (required != sizeof(SYS_SCHEDULER_STATS)) fail("scheduler required size mismatch");
uint8_t small[sizeof(SYS_SCHEDULER_STATS) - 1u];
size_t smallRequired = 0;
status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, small, sizeof(small), &smallRequired);
expect_status("scheduler small buffer", status, STATUS_BUFFER_TOO_SMALL);
if (smallRequired != sizeof(SYS_SCHEDULER_STATS)) fail("scheduler small required mismatch");
SYS_SCHEDULER_STATS scheduler;
status = SysQueryKernelStats(SYS_KERNEL_STATS_SCHEDULER, &scheduler, sizeof(scheduler), 0);
if (status != STATUS_SUCCESS) fail_status("scheduler exact", status);
if (scheduler.Size != sizeof(scheduler)) fail("scheduler size field mismatch");
if (scheduler.Version != SYS_STATS_VERSION) fail("scheduler version mismatch");
SYS_FILESYSTEM_STATS filesystem;
status = SysQueryKernelStats(SYS_KERNEL_STATS_FILESYSTEM, &filesystem, sizeof(filesystem), 0);
if (status != STATUS_SUCCESS) fail_status("filesystem exact", status);
if (filesystem.Size != sizeof(filesystem)) fail("filesystem size field mismatch");
if (filesystem.Version != SYS_STATS_VERSION) fail("filesystem version mismatch");
}
// Verifies CPU stats use a size-reported trailing-record layout.
static void check_cpu_stats(void) {
size_t required = 0;
uint32_t status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, 0, 0, &required);
if (status != STATUS_SUCCESS) fail_status("cpu size", status);
if (required < sizeof(SYS_CPU_STATS) + sizeof(SYS_CPU_STAT)) fail("cpu required size too small");
SYS_CPU_STATS headerOnly;
size_t smallRequired = 0;
status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, &headerOnly, sizeof(headerOnly), &smallRequired);
expect_status("cpu header-only buffer", status, STATUS_BUFFER_TOO_SMALL);
if (smallRequired != required) fail("cpu small required mismatch");
void* buffer = 0;
size_t pages = (required + 4095u) / 4096u;
if (pages == 0) pages = 1;
status = SysAllocVm(0, pages, SYS_VM_READWRITE, SYS_VM_FLAG_NONE, &buffer);
if (status != STATUS_SUCCESS || !buffer) fail_status("cpu buffer alloc", status);
SYS_CPU_STATS* cpu = (SYS_CPU_STATS*)buffer;
status = SysQueryKernelStats(SYS_KERNEL_STATS_CPU, cpu, pages * 4096u, 0);
if (status != STATUS_SUCCESS) {
SysFreeVm(buffer);
fail_status("cpu exact", status);
}
if (cpu->Size != required) fail("cpu size field mismatch");
if (cpu->Version != SYS_STATS_VERSION) fail("cpu version mismatch");
if (cpu->HeaderSize != sizeof(SYS_CPU_STATS)) fail("cpu header size mismatch");
if (cpu->RecordSize != sizeof(SYS_CPU_STAT)) fail("cpu record size mismatch");
if (cpu->RecordCount == 0 || cpu->RecordCount > cpu->CpuCount) fail("cpu record count mismatch");
const SYS_CPU_STAT* records = SYS_CPU_STATS_CONST_RECORDS(cpu);
if (records[0].CpuIndex >= cpu->CpuCount) fail("cpu first record index mismatch");
SysFreeVm(buffer);
}
// Runs the stats ABI smoke test.
int main(void) {
print("stats_abi_smoke: start\n");
check_fixed_stats();
check_cpu_stats();
print("stats_abi_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -66,37 +66,37 @@ int main(void) {
uint32_t bad = 0;
uint32_t s = SysCreateSurface(0, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888, SYS_SURFACE_FLAG_NONE, &bad);
expect_status("zero width", s, SYS_STATUS_INVALID_PARAMETER);
expect_status("zero width", s, STATUS_INVALID_PARAMETER);
s = SysCreateSurface(SURFACE_W, SURFACE_H, 0x9999u, SYS_SURFACE_FLAG_NONE, &bad);
expect_status("bad format", s, SYS_STATUS_NOT_SUPPORTED);
expect_status("bad format", s, STATUS_NOT_SUPPORTED);
uint32_t surface = 0;
s = SysCreateSurface(SURFACE_W, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (s != SYS_STATUS_SUCCESS) fail_status("create surface", s);
if (s != STATUS_SUCCESS) fail_status("create surface", s);
void* base = 0;
uint32_t pitch = 0;
s = SysMapSurface(surface, &base, &pitch);
if (s != SYS_STATUS_SUCCESS) fail_status("map surface", s);
if (s != STATUS_SUCCESS) fail_status("map surface", s);
if (!base || pitch < SURFACE_W * sizeof(uint32_t)) fail("invalid mapping");
s = SysPresentSurface(surface, 0, 1);
expect_status("null rect array with nonzero count", s, SYS_STATUS_INVALID_PARAMETER);
expect_status("null rect array with nonzero count", s, STATUS_INVALID_PARAMETER);
DISPLAY_RECT rects[SYS_SURFACE_MAX_RECTS + 1];
memset(rects, 0, sizeof(rects));
s = SysPresentSurface(surface, rects, SYS_SURFACE_MAX_RECTS + 1);
expect_status("too many rects", s, SYS_STATUS_INVALID_PARAMETER);
expect_status("too many rects", s, STATUS_INVALID_PARAMETER);
DISPLAY_RECT clipped = { -8, -8, 24, 24 };
s = SysPresentSurface(surface, &clipped, 1);
if (s != SYS_STATUS_SUCCESS) fail_status("present clipped rect", s);
if (s != STATUS_SUCCESS) fail_status("present clipped rect", s);
DISPLAY_RECT empty = { 10000, 10000, 16, 16 };
s = SysPresentSurface(surface, &empty, 1);
if (s != SYS_STATUS_SUCCESS) fail_status("present empty clipped rect", s);
if (s != STATUS_SUCCESS) fail_status("present empty clipped rect", s);
SysCloseHandle(surface);
print("surface_invalid_rect: PASS\n");
@@ -80,7 +80,7 @@ static void worker_main(uintptr_t arg) {
DISPLAY_RECT rect = { 0, (int32_t)y0, (int32_t)SURFACE_W, (int32_t)h };
uint32_t s = SysPresentSurface(st->Surface, &rect, 1);
if (s != SYS_STATUS_SUCCESS) {
if (s != STATUS_SUCCESS) {
st->Status = s;
SysExitThread(1);
}
@@ -88,7 +88,7 @@ static void worker_main(uintptr_t arg) {
SysYield();
}
st->Status = SYS_STATUS_SUCCESS;
st->Status = STATUS_SUCCESS;
SysExitThread(0);
}
@@ -98,12 +98,12 @@ int main(void) {
uint32_t surface = 0;
uint32_t s = SysCreateSurface(SURFACE_W, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (s != SYS_STATUS_SUCCESS) fail_status("create surface", s);
if (s != STATUS_SUCCESS) fail_status("create surface", s);
void* base = 0;
uint32_t pitch = 0;
s = SysMapSurface(surface, &base, &pitch);
if (s != SYS_STATUS_SUCCESS) fail_status("map surface", s);
if (s != STATUS_SUCCESS) fail_status("map surface", s);
if (!base || pitch < SURFACE_W * sizeof(uint32_t)) fail("invalid mapping");
uint32_t handles[THREAD_COUNT];
@@ -117,7 +117,7 @@ int main(void) {
gWorkers[i].Surface = surface;
gWorkers[i].Base = (uint8_t*)base;
gWorkers[i].Pitch = pitch;
gWorkers[i].Status = SYS_STATUS_PENDING;
gWorkers[i].Status = STATUS_PENDING;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)worker_main,
(uintptr_t)&gWorkers[i],
@@ -127,17 +127,17 @@ int main(void) {
&tids[i]
};
s = SysCreateThread(&req);
if (s != SYS_STATUS_SUCCESS) fail_status("create thread", s);
if (s != STATUS_SUCCESS) fail_status("create thread", s);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
s = SysWait(handles[i], SYS_WAIT_FLAG_NONE);
if (s != SYS_STATUS_SUCCESS) fail_status("wait thread", s);
if (s != STATUS_SUCCESS) fail_status("wait thread", s);
SysCloseHandle(handles[i]);
}
for (uint32_t i = 0; i < THREAD_COUNT; ++i) {
if (gWorkers[i].Status != SYS_STATUS_SUCCESS) {
if (gWorkers[i].Status != STATUS_SUCCESS) {
print("surface_multithread_present: worker ");
print_u64(i);
print(" status=");
@@ -55,19 +55,19 @@ int main(void) {
uint32_t surface = 0;
uint32_t s = SysCreateSurface(SURFACE_W, SURFACE_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &surface);
if (s != SYS_STATUS_SUCCESS) fail_status("create surface", s);
if (s != STATUS_SUCCESS) fail_status("create surface", s);
if (!surface) fail("surface handle is zero");
void* base = 0;
uint32_t pitch = 0;
s = SysMapSurface(surface, &base, &pitch);
if (s != SYS_STATUS_SUCCESS) fail_status("map surface", s);
if (s != STATUS_SUCCESS) fail_status("map surface", s);
if (!base || pitch < SURFACE_W * sizeof(uint32_t)) fail("invalid mapping");
SYS_SURFACE_DESC desc;
memset(&desc, 0, sizeof(desc));
s = SysQuerySurface(surface, &desc);
if (s != SYS_STATUS_SUCCESS) fail_status("query surface", s);
if (s != STATUS_SUCCESS) fail_status("query surface", s);
if (desc.Width != SURFACE_W || desc.Height != SURFACE_H) fail("query dimensions mismatch");
if (desc.PitchBytes != pitch || desc.Format != SYS_SURFACE_FORMAT_XRGB8888) fail("query metadata mismatch");
if (desc.Base != base) fail("query base mismatch");
@@ -81,10 +81,10 @@ int main(void) {
DISPLAY_RECT rect = { 0, 0, (int32_t)SURFACE_W, (int32_t)SURFACE_H };
s = SysPresentSurface(surface, &rect, 1);
if (s != SYS_STATUS_SUCCESS) fail_status("present surface", s);
if (s != STATUS_SUCCESS) fail_status("present surface", s);
s = SysPresentSurface(surface, 0, 0);
if (s != SYS_STATUS_SUCCESS) fail_status("present full surface", s);
if (s != STATUS_SUCCESS) fail_status("present full surface", s);
SysCloseHandle(surface);
print("surface_smoke: PASS\n");
@@ -0,0 +1,125 @@
// Native syscall contract smoke test.
// Verifies public tick units and process exit-code round trips.
#include <stdint.h>
#include <stddef.h>
#include <ntlite/syscall.h>
#define CHILD_EXIT_CODE ((int32_t)-1234567)
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int32_t code) {
for (;;) SysExitProcess(code);
}
// Checks whether a string view equals a NUL-terminated string.
static int view_equals_cstr(SYS_STRING_VIEW view, const char* text) {
size_t n = 0;
while (text[n]) n++;
if (view.Length != n || (!view.Data && n != 0)) return 0;
for (size_t i = 0; i < n; i++) {
if (view.Data[i] != text[i]) return 0;
}
return 1;
}
// Fails the test with one plain message.
static void fail(const char* msg) {
print("syscall_contract_smoke: ");
print(msg);
print("\n");
exit_now(1);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("syscall_contract_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Verifies the documented tick unit without relying on wall-clock precision.
static void check_tick_contract(void) {
uint64_t frequency = 0;
uint32_t s = SysGetTickFrequency(&frequency);
if (s != STATUS_SUCCESS) fail_status("query tick frequency", s);
if (!frequency) fail("tick frequency was zero");
uint64_t t0 = SysGetTick();
s = SysSleep(1);
if (s != STATUS_SUCCESS) fail_status("sleep", s);
uint64_t t1 = SysGetTick();
if (t1 < t0) fail("tick moved backwards across sleep");
if (SysTicksToMicroseconds(t1 - t0, frequency) < 1000ULL)
fail("tick delta was smaller than one millisecond");
}
// Verifies process exit-code storage, wait, and query agree on int32_t.
static void check_exit_code_contract(void) {
SYS_PEB* peb = SysGetPeb();
if (!peb || !peb->ImagePath.Data || peb->ImagePath.Length == 0) fail("missing image path");
uint32_t child = 0;
SYS_CREATE_PROCESS_REQUEST req = {
peb->ImagePath,
SysSvFromCstr("child"),
&child,
};
uint32_t s = SysCreateProcess(&req);
if (s != STATUS_SUCCESS) fail_status("create child", s);
s = SysWait(child, SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS) fail_status("wait child", s);
int32_t exitCode = 0;
s = SysGetProcessExitCode(child, &exitCode);
if (s != STATUS_SUCCESS) fail_status("query child exit code", s);
if (exitCode != CHILD_EXIT_CODE) fail("child exit code mismatch");
SysCloseHandle(child);
}
// Runs the syscall ABI contract smoke test.
int main(void) {
SYS_PEB* peb = SysGetPeb();
if (peb && view_equals_cstr(peb->CommandLine, "child")) {
exit_now(CHILD_EXIT_CODE);
}
print("syscall_contract_smoke: start\n");
check_tick_contract();
check_exit_code_contract();
print("syscall_contract_smoke: PASS\n");
exit_now(0);
return 0;
}
+193
View File
@@ -0,0 +1,193 @@
// Waitable timer smoke test.
// Verifies one-shot, cancellation, reset, invalid handle, and wait timeout behavior.
#include <stdint.h>
#include <ntlite/syscall.h>
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("timer_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Requires one exact status value.
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("timer_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
exit_now(1);
}
// Arms a timer through the public request ABI.
static uint32_t set_timer(uint32_t timer, uint64_t dueMs, uint64_t periodMs) {
SYS_SET_TIMER_REQUEST req = {
sizeof(req),
timer,
SYS_TIMER_FLAG_NONE,
dueMs,
periodMs,
};
return SysSetTimer(&req);
}
// Verifies timer request validation paths.
static void test_validation(void) {
uint32_t timer = 0;
expect_status("create timer null", SysCreateTimer((uint32_t*)0), STATUS_INVALID_PARAMETER);
uint32_t s = SysCreateTimer(&timer);
if (s != STATUS_SUCCESS) fail_status("create validation timer", s);
expect_status("set timer null", SysSetTimer((const SYS_SET_TIMER_REQUEST*)0), STATUS_INVALID_PARAMETER);
expect_status("cancel invalid", SysCancelTimer(0xdeadbeefu), STATUS_INVALID_HANDLE);
expect_status("wait invalid", SysWaitSingle(0xdeadbeefu, SYS_WAIT_FLAG_NONE, 0), STATUS_INVALID_HANDLE);
SYS_SET_TIMER_REQUEST req = {
0,
timer,
SYS_TIMER_FLAG_NONE,
1,
0,
};
expect_status("set bad size", SysSetTimer(&req), STATUS_INVALID_PARAMETER);
req.Size = sizeof(req);
req.Flags = 1;
expect_status("set bad flags", SysSetTimer(&req), STATUS_INVALID_PARAMETER);
SysCloseHandle(timer);
}
// Verifies one-shot timers wake waiters and are consumed by the wait.
static void test_one_shot(void) {
uint32_t timer = 0;
uint32_t s = SysCreateTimer(&timer);
if (s != STATUS_SUCCESS) fail_status("create one-shot timer", s);
expect_status("unset immediate wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), STATUS_TIMEOUT);
s = set_timer(timer, 20, 0);
if (s != STATUS_SUCCESS) fail_status("set one-shot", s);
s = SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, SYS_WAIT_TIMEOUT_INFINITE);
if (s != STATUS_SUCCESS) fail_status("wait one-shot", s);
expect_status("one-shot consumed", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), STATUS_TIMEOUT);
SysCloseHandle(timer);
}
// Verifies cancellation clears an armed timer and its retained signal.
static void test_cancel(void) {
uint32_t timer = 0;
uint32_t s = SysCreateTimer(&timer);
if (s != STATUS_SUCCESS) fail_status("create cancel timer", s);
s = set_timer(timer, 100, 0);
if (s != STATUS_SUCCESS) fail_status("set cancel timer", s);
s = SysCancelTimer(timer);
if (s != STATUS_SUCCESS) fail_status("cancel timer", s);
expect_status("cancelled timer wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 30), STATUS_TIMEOUT);
s = set_timer(timer, 0, 0);
if (s != STATUS_SUCCESS) fail_status("set immediate", s);
expect_status("immediate timer wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), STATUS_SUCCESS);
s = set_timer(timer, 0, 0);
if (s != STATUS_SUCCESS) fail_status("set immediate before cancel", s);
s = SysCancelTimer(timer);
if (s != STATUS_SUCCESS) fail_status("cancel signaled", s);
expect_status("cancel clears signal", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 0), STATUS_TIMEOUT);
SysCloseHandle(timer);
}
// Verifies resetting an active timer replaces its previous deadline.
static void test_reset_changes_deadline(void) {
uint32_t timer = 0;
uint32_t s = SysCreateTimer(&timer);
if (s != STATUS_SUCCESS) fail_status("create reset timer", s);
s = set_timer(timer, 100, 0);
if (s != STATUS_SUCCESS) fail_status("set reset timer long", s);
s = set_timer(timer, 20, 0);
if (s != STATUS_SUCCESS) fail_status("set reset timer short", s);
s = SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 80);
if (s != STATUS_SUCCESS) fail_status("wait reset timer", s);
SysCloseHandle(timer);
}
// Verifies a periodic timer can wake more than once.
static void test_periodic(void) {
uint32_t timer = 0;
uint32_t s = SysCreateTimer(&timer);
if (s != STATUS_SUCCESS) fail_status("create periodic timer", s);
s = set_timer(timer, 10, 20);
if (s != STATUS_SUCCESS) fail_status("set periodic timer", s);
s = SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 80);
if (s != STATUS_SUCCESS) fail_status("wait periodic first", s);
s = SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 80);
if (s != STATUS_SUCCESS) fail_status("wait periodic second", s);
s = SysCancelTimer(timer);
if (s != STATUS_SUCCESS) fail_status("cancel periodic", s);
expect_status("periodic cancelled wait", SysWaitSingle(timer, SYS_WAIT_FLAG_NONE, 30), STATUS_TIMEOUT);
SysCloseHandle(timer);
}
// Program entry point.
int main(void) {
print("timer_smoke: start\n");
test_validation();
test_one_shot();
test_cancel();
test_reset_changes_deadline();
test_periodic();
print("timer_smoke: PASS\n");
exit_now(0);
return 0;
}
@@ -0,0 +1,246 @@
// Native would-block and queue-timeout contract smoke test.
// Verifies that queue try, finite-timeout, and infinite-wait requests return distinct statuses.
#include <stdint.h>
#include <ntlite/syscall.h>
#define TEST_FILL_LIMIT 1024u
#define TEST_MESSAGE_WAKE 0x77616b65u
#define TEST_WAKE_VALUE 0x12345678u
typedef struct TEST_QUEUE_MESSAGE {
uint32_t Type;
uint32_t Flags;
uint64_t Param[6];
} TEST_QUEUE_MESSAGE;
typedef char TEST_QUEUE_MESSAGE_SIZE_CHECK[
(sizeof(TEST_QUEUE_MESSAGE) == SYS_QUEUE_MESSAGE_SIZE) ? 1 : -1
];
typedef struct TEST_SENDER_CONTEXT {
uint32_t Queue;
volatile uint32_t Status;
} TEST_SENDER_CONTEXT;
// Sends one message with the queue timeout request ABI.
static uint32_t send_queue(uint32_t queue, const TEST_QUEUE_MESSAGE* msg, uint64_t timeoutMilliseconds) {
SYS_QUEUE_SEND_REQUEST req = {
sizeof(req),
queue,
SYS_QUEUE_FLAG_NONE,
timeoutMilliseconds,
msg,
};
return SysSendQueue(&req);
}
// Receives one message with the queue timeout request ABI.
static uint32_t recv_queue(uint32_t queue, TEST_QUEUE_MESSAGE* msg, uint64_t timeoutMilliseconds) {
SYS_QUEUE_RECV_REQUEST req = {
sizeof(req),
queue,
SYS_QUEUE_FLAG_NONE,
timeoutMilliseconds,
msg,
};
return SysRecvQueue(&req);
}
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one plain message.
static void fail(const char* msg) {
print("would_block_smoke: ");
print(msg);
print("\n");
exit_now(1);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("would_block_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Requires one exact status value.
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("would_block_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
exit_now(1);
}
// Sends one wake message after the receiver has had a chance to block.
static void sender_thread(uintptr_t arg) {
TEST_SENDER_CONTEXT* ctx = (TEST_SENDER_CONTEXT*)arg;
if (!ctx) SysExitThread(1);
SysSleep(20);
TEST_QUEUE_MESSAGE msg = {0};
msg.Type = TEST_MESSAGE_WAKE;
msg.Param[0] = TEST_WAKE_VALUE;
ctx->Status = send_queue(ctx->Queue, &msg, SYS_WAIT_TIMEOUT_INFINITE);
SysExitThread(ctx->Status == STATUS_SUCCESS ? 0 : 1);
}
// Fills one queue with immediate sends until the queue reports would-block.
static void fill_until_would_block(uint32_t queue) {
TEST_QUEUE_MESSAGE msg = {0};
msg.Type = 1;
uint32_t sends = 0;
for (; sends < TEST_FILL_LIMIT; sends++) {
msg.Param[0] = sends;
uint32_t s = send_queue(queue, &msg, 0);
if (s == STATUS_WOULD_BLOCK)
break;
if (s != STATUS_SUCCESS)
fail_status("fill send", s);
}
if (sends == 0 || sends == TEST_FILL_LIMIT)
fail("queue fill did not reach would-block");
}
// Verifies queue try and finite-timeout status distinctions.
static void test_queue_empty_and_full(void) {
uint32_t queue = 0;
uint32_t s = SysCreateQueue(&queue);
if (s != STATUS_SUCCESS) fail_status("create empty queue", s);
TEST_QUEUE_MESSAGE msg = {0};
expect_status("empty nonblocking receive", recv_queue(queue, &msg, 0), STATUS_WOULD_BLOCK);
expect_status("empty finite receive", recv_queue(queue, &msg, 20), STATUS_TIMEOUT);
SysCloseHandle(queue);
queue = 0;
s = SysCreateQueue(&queue);
if (s != STATUS_SUCCESS) fail_status("create full queue", s);
fill_until_would_block(queue);
expect_status("full nonblocking send", send_queue(queue, &msg, 0), STATUS_WOULD_BLOCK);
expect_status("full finite send", send_queue(queue, &msg, 20), STATUS_TIMEOUT);
SysCloseHandle(queue);
}
// Verifies that an infinite receive composes with a sender wakeup.
static void test_queue_infinite_receive_wakeup(void) {
uint32_t queue = 0;
uint32_t s = SysCreateQueue(&queue);
if (s != STATUS_SUCCESS) fail_status("create wake queue", s);
TEST_SENDER_CONTEXT ctx = { queue, STATUS_PENDING };
uint32_t threadHandle = 0;
uint32_t threadId = 0;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)sender_thread,
(uintptr_t)&ctx,
1,
0,
&threadHandle,
&threadId
};
s = SysCreateThread(&req);
if (s != STATUS_SUCCESS) fail_status("create sender", s);
TEST_QUEUE_MESSAGE msg = {0};
s = recv_queue(queue, &msg, SYS_WAIT_TIMEOUT_INFINITE);
if (s != STATUS_SUCCESS) fail_status("infinite receive", s);
if (msg.Type != TEST_MESSAGE_WAKE || msg.Param[0] != TEST_WAKE_VALUE)
fail("wake message payload mismatch");
s = SysWait(threadHandle, SYS_WAIT_FLAG_NONE);
if (s != STATUS_SUCCESS) fail_status("wait sender", s);
if (ctx.Status != STATUS_SUCCESS) fail_status("sender status", ctx.Status);
SysCloseHandle(threadHandle);
SysCloseHandle(queue);
}
// Verifies that request metadata is part of the queue ABI contract.
static void test_queue_request_validation(void) {
uint32_t queue = 0;
uint32_t s = SysCreateQueue(&queue);
if (s != STATUS_SUCCESS) fail_status("create validation queue", s);
TEST_QUEUE_MESSAGE msg = {0};
expect_status("null send request", SysSendQueue((const SYS_QUEUE_SEND_REQUEST*)0), STATUS_INVALID_PARAMETER);
expect_status("null recv request", SysRecvQueue((const SYS_QUEUE_RECV_REQUEST*)0), STATUS_INVALID_PARAMETER);
SYS_QUEUE_SEND_REQUEST sendReq = {
0,
queue,
SYS_QUEUE_FLAG_NONE,
0,
&msg,
};
expect_status("bad send size", SysSendQueue(&sendReq), STATUS_INVALID_PARAMETER);
sendReq.Size = sizeof(sendReq);
sendReq.Flags = 1;
expect_status("bad send flags", SysSendQueue(&sendReq), STATUS_INVALID_PARAMETER);
sendReq.Flags = SYS_QUEUE_FLAG_NONE;
sendReq.Message = (const void*)0;
expect_status("bad send message", SysSendQueue(&sendReq), STATUS_INVALID_PARAMETER);
SYS_QUEUE_RECV_REQUEST recvReq = {
sizeof(recvReq),
queue,
SYS_QUEUE_FLAG_NONE,
0,
(void*)0,
};
expect_status("bad recv message", SysRecvQueue(&recvReq), STATUS_INVALID_PARAMETER);
SysCloseHandle(queue);
}
// Runs the native would-block smoke test.
int main(void) {
print("would_block_smoke: start\n");
test_queue_request_validation();
test_queue_empty_and_full();
test_queue_infinite_receive_wakeup();
print("would_block_smoke: PASS\n");
exit_now(0);
}