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:
1 parent
2c4a68a75f
commit
ed3d065abd
156 files changed
+10509
-2643
No files matched your search
+44
-13
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -54,7 +54,6 @@ typedef struct tagBITMAPINFO {
|
||||
RGBQUAD bmiColors[1];
|
||||
} BITMAPINFO;
|
||||
|
||||
|
||||
// GDI Exports.
|
||||
|
||||
// Static buffers to persist between function calls.
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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(¤tReq);
|
||||
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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user