major restructuring, prepare for smp
This commit is contained in:
1 parent
b3569a2b90
commit
6bff37746d
229 files changed
+1617
-1489
No files matched your search
@@ -0,0 +1,232 @@
|
||||
#pragma once
|
||||
// posix.h - POSIX type and constant definitions for posix.dll
|
||||
// Covers just what open/read/write/close/lseek/spawn/pthread/chdir need.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// errno
|
||||
// ---------------------------------------------------------------------------
|
||||
#ifdef errno
|
||||
#undef errno
|
||||
#endif
|
||||
#ifdef EAGAIN
|
||||
#undef EAGAIN
|
||||
#endif
|
||||
#ifdef ENOSYS
|
||||
#undef ENOSYS
|
||||
#endif
|
||||
#ifdef EWOULDBLOCK
|
||||
#undef EWOULDBLOCK
|
||||
#endif
|
||||
#ifdef EINPROGRESS
|
||||
#undef EINPROGRESS
|
||||
#endif
|
||||
#ifdef ETIMEDOUT
|
||||
#undef ETIMEDOUT
|
||||
#endif
|
||||
#ifdef ECONNRESET
|
||||
#undef ECONNRESET
|
||||
#endif
|
||||
#ifdef ENOTSOCK
|
||||
#undef ENOTSOCK
|
||||
#endif
|
||||
extern int errno; // defined in posix_fd.c; single global in v1
|
||||
|
||||
#define EPERM 1
|
||||
#define ENOENT 2
|
||||
#define ESRCH 3
|
||||
#define EINTR 4
|
||||
#define EIO 5
|
||||
#define EBADF 9
|
||||
#define ENOMEM 12
|
||||
#define EACCES 13
|
||||
#define EEXIST 17
|
||||
#define ENODEV 19
|
||||
#define ENOTDIR 20
|
||||
#define EINVAL 22
|
||||
#define ESPIPE 29
|
||||
#define EMFILE 24
|
||||
#define ENOSPC 28
|
||||
#define ERANGE 34
|
||||
#define EAGAIN 35
|
||||
#define EWOULDBLOCK EAGAIN
|
||||
#define ENOSYS 38
|
||||
#define ENOTSOCK 88
|
||||
#define EINPROGRESS 115
|
||||
#define ETIMEDOUT 116
|
||||
#define ECONNRESET 104
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// open() flags
|
||||
// ---------------------------------------------------------------------------
|
||||
#define O_RDONLY 0x0000
|
||||
#define O_WRONLY 0x0001
|
||||
#define O_RDWR 0x0002
|
||||
#define O_CREAT 0x0040
|
||||
#define O_TRUNC 0x0200
|
||||
#define O_APPEND 0x0400
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// lseek() whence
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SEEK_SET 0
|
||||
#define SEEK_CUR 1
|
||||
#define SEEK_END 2
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Scalar types
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef int32_t pid_t;
|
||||
typedef int32_t off_t;
|
||||
typedef uint32_t mode_t;
|
||||
typedef int64_t ssize_t;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Sockets
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef uint16_t sa_family_t;
|
||||
typedef uint16_t in_port_t;
|
||||
typedef uint32_t in_addr_t;
|
||||
typedef uint32_t socklen_t;
|
||||
|
||||
#define AF_INET 2
|
||||
#define PF_INET AF_INET
|
||||
#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 SHUT_RD 0
|
||||
#define SHUT_WR 1
|
||||
#define SHUT_RDWR 2
|
||||
|
||||
#define SOL_SOCKET 1
|
||||
#define SO_RCVTIMEO 1
|
||||
#define SO_SNDTIMEO 2
|
||||
#define SO_NONBLOCK 3
|
||||
|
||||
#define INADDR_ANY ((in_addr_t)0x00000000u)
|
||||
#define INADDR_NONE ((in_addr_t)0xFFFFFFFFu)
|
||||
|
||||
struct in_addr {
|
||||
in_addr_t s_addr;
|
||||
};
|
||||
|
||||
struct sockaddr {
|
||||
sa_family_t sa_family;
|
||||
char sa_data[14];
|
||||
};
|
||||
|
||||
struct sockaddr_in {
|
||||
sa_family_t sin_family;
|
||||
in_port_t sin_port;
|
||||
struct in_addr sin_addr;
|
||||
char sin_zero[8];
|
||||
};
|
||||
|
||||
struct hostent {
|
||||
char* h_name;
|
||||
char** h_aliases;
|
||||
int h_addrtype;
|
||||
int h_length;
|
||||
char** h_addr_list;
|
||||
};
|
||||
#define h_addr h_addr_list[0]
|
||||
|
||||
struct addrinfo {
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
socklen_t ai_addrlen;
|
||||
struct sockaddr* ai_addr;
|
||||
char* ai_canonname;
|
||||
struct addrinfo* ai_next;
|
||||
};
|
||||
|
||||
#define AI_PASSIVE 0x0001
|
||||
|
||||
struct pollfd {
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
};
|
||||
|
||||
#define POLLIN 0x0001
|
||||
#define POLLOUT 0x0002
|
||||
#define POLLERR 0x0004
|
||||
#define POLLHUP 0x0008
|
||||
#define POLLNVAL 0x0010
|
||||
|
||||
typedef unsigned long nfds_t;
|
||||
|
||||
typedef struct timeval {
|
||||
long tv_sec;
|
||||
long tv_usec;
|
||||
} timeval;
|
||||
|
||||
__declspec(dllexport) uint16_t htons(uint16_t x);
|
||||
__declspec(dllexport) uint16_t ntohs(uint16_t x);
|
||||
__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 socket(int domain, int type, int protocol);
|
||||
__declspec(dllexport) int bind(int fd, const struct sockaddr* addr, socklen_t addrlen);
|
||||
__declspec(dllexport) int listen(int fd, int backlog);
|
||||
__declspec(dllexport) int accept(int fd, struct sockaddr* addr, socklen_t* addrlen);
|
||||
__declspec(dllexport) int connect(int fd, const struct sockaddr* addr, socklen_t addrlen);
|
||||
__declspec(dllexport) ssize_t send(int fd, const void* buf, size_t len, int flags);
|
||||
__declspec(dllexport) ssize_t recv(int fd, void* buf, size_t len, int flags);
|
||||
__declspec(dllexport) ssize_t sendto(int fd, const void* buf, size_t len, int flags, const struct sockaddr* dest, socklen_t destlen);
|
||||
__declspec(dllexport) ssize_t recvfrom(int fd, void* buf, size_t len, int flags, struct sockaddr* src, socklen_t* srclen);
|
||||
__declspec(dllexport) int shutdown(int fd, int how);
|
||||
__declspec(dllexport) int setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen);
|
||||
__declspec(dllexport) int getsockopt(int fd, int level, int optname, void* optval, socklen_t* optlen);
|
||||
__declspec(dllexport) int poll(struct pollfd* fds, nfds_t nfds, int timeout);
|
||||
__declspec(dllexport) struct hostent* gethostbyname(const char* name);
|
||||
__declspec(dllexport) int getaddrinfo(const char* node, const char* service, const struct addrinfo* hints, struct addrinfo** res);
|
||||
__declspec(dllexport) void freeaddrinfo(struct addrinfo* res);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// pthread types
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef struct { uint32_t Tid; } pthread_t;
|
||||
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)
|
||||
// ---------------------------------------------------------------------------
|
||||
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)
|
||||
// ---------------------------------------------------------------------------
|
||||
#define NAME_MAX 259
|
||||
|
||||
typedef struct dirent {
|
||||
char d_name[NAME_MAX + 1];
|
||||
unsigned char d_type; // DT_REG or DT_DIR
|
||||
} dirent;
|
||||
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_REG 8
|
||||
#define DT_DIR 4
|
||||
|
||||
typedef struct DIR {
|
||||
uint32_t KHandle; // kernel DIR_OBJECT handle
|
||||
dirent Current;
|
||||
bool Valid;
|
||||
} DIR;
|
||||
@@ -0,0 +1,69 @@
|
||||
// posix_dir.c - chdir, getcwd, opendir, readdir, closedir
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
#include "posix_path.h"
|
||||
|
||||
typedef SYS_DIR_ENTRY KDIR_ENTRY;
|
||||
|
||||
|
||||
#define KATTR_DIR 0x10
|
||||
|
||||
__declspec(dllexport) int chdir(const char* path) {
|
||||
char native[512];
|
||||
PosixTranslatePath(path, native, sizeof(native));
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(native) };
|
||||
uint32_t s = SysSetCwd(&req);
|
||||
if (s != 0) { errno = ENOENT; return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__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);
|
||||
return buf;
|
||||
}
|
||||
|
||||
__declspec(dllexport) DIR* opendir(const char* path) {
|
||||
char native[512];
|
||||
PosixTranslatePath(path, native, sizeof(native));
|
||||
uint32_t kh;
|
||||
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(native), &kh };
|
||||
uint32_t s = SysOpenDir(&req);
|
||||
if (s != 0) { errno = ENOENT; return (DIR*)0; }
|
||||
|
||||
// Allocate DIR on the kernel heap (SysAlloc) so it outlives the stack.
|
||||
DIR* d = (DIR*)SysAlloc(sizeof(DIR));
|
||||
if (!d) { SysCloseHandle(kh); errno = ENOMEM; return (DIR*)0; }
|
||||
d->KHandle = kh;
|
||||
d->Valid = false;
|
||||
return d;
|
||||
}
|
||||
|
||||
__declspec(dllexport) struct dirent* readdir(DIR* d) {
|
||||
if (!d) { errno = EBADF; return (struct dirent*)0; }
|
||||
KDIR_ENTRY ke;
|
||||
uint32_t s = SysReadDir(d->KHandle, &ke);
|
||||
if (s != 0) return (struct dirent*)0; // end of directory or error
|
||||
|
||||
size_t len = strlen(ke.Name);
|
||||
if (len > NAME_MAX) len = NAME_MAX;
|
||||
memcpy(d->Current.d_name, ke.Name, len);
|
||||
d->Current.d_name[len] = '\0';
|
||||
d->Current.d_type = (ke.Attr & KATTR_DIR) ? DT_DIR : DT_REG;
|
||||
d->Valid = true;
|
||||
return &d->Current;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int closedir(DIR* d) {
|
||||
if (!d) { errno = EBADF; return -1; }
|
||||
SysCloseHandle(d->KHandle);
|
||||
SysFree(d);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// posix_fd.c - file descriptor table
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
#include "posix_fd.h"
|
||||
|
||||
int errno = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static FD_ENTRY gFdTable[FD_TABLE_PIDS][FD_MAX];
|
||||
|
||||
__declspec(dllexport) void PosixFdInit(void) {
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||
|
||||
// Clear this process's slice (handles inherited state from a previous
|
||||
// process that happened to land on the same slot - unlikely but safe).
|
||||
for (int i = 0; i < FD_MAX; i++)
|
||||
gFdTable[slot][i] = (FD_ENTRY){ FD_FREE, 0, false };
|
||||
|
||||
gFdTable[slot][0] = (FD_ENTRY){ FD_CONSOLE_R, 0, true };
|
||||
gFdTable[slot][1] = (FD_ENTRY){ FD_CONSOLE_W, 0, true };
|
||||
gFdTable[slot][2] = (FD_ENTRY){ FD_CONSOLE_W, 0, true };
|
||||
}
|
||||
|
||||
__declspec(dllexport) int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle) {
|
||||
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||
for (int fd = 3; fd < FD_MAX; fd++) {
|
||||
if (!gFdTable[slot][fd].Used) {
|
||||
gFdTable[slot][fd] = (FD_ENTRY){ type, khandle, true };
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
errno = EMFILE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__declspec(dllexport) FD_ENTRY* FdGet(uint32_t pid, int fd) {
|
||||
if (fd < 0 || fd >= FD_MAX) { errno = EBADF; return (FD_ENTRY*)0; }
|
||||
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||
FD_ENTRY* e = &gFdTable[slot][fd];
|
||||
if (!e->Used) { errno = EBADF; return (FD_ENTRY*)0; }
|
||||
return e;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void FdFree(uint32_t pid, int fd) {
|
||||
if (fd < 0 || fd >= FD_MAX) return;
|
||||
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||
gFdTable[slot][fd] = (FD_ENTRY){ FD_FREE, 0, false };
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
// posix_fd.h - file descriptor table (internal to posix.dll)
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum {
|
||||
FD_FREE,
|
||||
FD_FILE,
|
||||
FD_CONSOLE_R, // fd 0 - reads from keyboard via SysKbReadChar
|
||||
FD_CONSOLE_W, // fd 1, 2 - writes via SysWriteConsole
|
||||
FD_DEVNULL, // /dev/null
|
||||
FD_DEVZERO, // /dev/zero
|
||||
FD_SOCKET, // kernel socket handle
|
||||
} FD_TYPE;
|
||||
|
||||
typedef struct {
|
||||
FD_TYPE Type;
|
||||
uint32_t KHandle; // kernel handle; 0 for console/devnull/devzero
|
||||
bool Used;
|
||||
} FD_ENTRY;
|
||||
|
||||
#define FD_MAX 64
|
||||
#define FD_TABLE_PIDS 32 // circular, indexed by pid % FD_TABLE_PIDS
|
||||
|
||||
// Called from _start before main(); sets up fds 0/1/2 for this process.
|
||||
__declspec(dllexport) void PosixFdInit(void);
|
||||
|
||||
// Allocate the lowest free fd >= 3. Returns -1 and sets errno=EMFILE on failure.
|
||||
__declspec(dllexport) int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle);
|
||||
|
||||
// Look up an fd. Returns NULL and sets errno=EBADF if invalid.
|
||||
__declspec(dllexport) FD_ENTRY* FdGet(uint32_t pid, int fd);
|
||||
|
||||
// Release an fd slot (does NOT close the kernel handle).
|
||||
__declspec(dllexport) void FdFree(uint32_t pid, int fd);
|
||||
|
||||
int PosixStatusToErrno(uint32_t s);
|
||||
@@ -0,0 +1,187 @@
|
||||
// posix_io.c - open, read, write, close, lseek
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
#include "posix_fd.h"
|
||||
#include "posix_path.h"
|
||||
|
||||
__declspec(dllexport) void clearscreen(void) {
|
||||
SysScreenClear(0x00000000);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void clearkeyboard(void) {
|
||||
while (SysKbPoll()) (void)SysKbReadChar();
|
||||
}
|
||||
|
||||
__declspec(dllexport) int open(const char* path, int flags, ...) {
|
||||
char native[512];
|
||||
const char* p = PosixTranslatePath(path, native, sizeof(native));
|
||||
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
FD_TYPE type = FD_FILE;
|
||||
|
||||
// Synthetic devices
|
||||
if (strcmp(p, SYNTH_DEVNULL) == 0) {
|
||||
int fd = FdAlloc(pid, FD_DEVNULL, 0);
|
||||
return fd;
|
||||
}
|
||||
|
||||
if (strcmp(p, SYNTH_DEVZERO) == 0) {
|
||||
int fd = FdAlloc(pid, FD_DEVZERO, 0);
|
||||
return fd;
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
if ((flags & O_TRUNC) && (flags & O_WRONLY))
|
||||
SysTruncateFile(kh, 0);
|
||||
|
||||
int fd = FdAlloc(pid, type, kh);
|
||||
if (fd < 0) {
|
||||
SysCloseHandle(kh); // FdAlloc already set errno
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) {
|
||||
if (count == 0) return 0;
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
FD_ENTRY* e = FdGet(pid, fd);
|
||||
if (!e) return -1;
|
||||
|
||||
switch (e->Type) {
|
||||
case FD_CONSOLE_R: {
|
||||
// Read up to count chars from keyboard, blocking per char.
|
||||
char* dst = (char*)buf;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
dst[i] = SysKbReadChar();
|
||||
if (dst[i] == '\n' || dst[i] == '\r') return (ssize_t)(i + 1);
|
||||
}
|
||||
return (ssize_t)count;
|
||||
}
|
||||
case FD_DEVNULL:
|
||||
return 0;
|
||||
case FD_DEVZERO:
|
||||
memset(buf, 0, count);
|
||||
return (ssize_t)count;
|
||||
case FD_FILE: {
|
||||
size_t got = 0;
|
||||
uint32_t s = SysReadFile(e->KHandle, buf, count, &got);
|
||||
if (s != 0 && got == 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)got;
|
||||
}
|
||||
case FD_SOCKET: {
|
||||
size_t got = 0;
|
||||
uint32_t s = SysRecv(e->KHandle, buf, count, 0, &got);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)got;
|
||||
}
|
||||
default:
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) {
|
||||
if (count == 0) return 0;
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
FD_ENTRY* e = FdGet(pid, fd);
|
||||
if (!e) return -1;
|
||||
|
||||
switch (e->Type) {
|
||||
case FD_CONSOLE_W: {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts((const char*)buf, count) };
|
||||
SysWriteConsole(&req);
|
||||
return (ssize_t)count;
|
||||
}
|
||||
case FD_DEVNULL:
|
||||
case FD_DEVZERO:
|
||||
return (ssize_t)count;
|
||||
case FD_FILE: {
|
||||
size_t written = 0;
|
||||
uint32_t s = SysWriteFile(e->KHandle, buf, count, &written);
|
||||
if (s != 0 && written == 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)written;
|
||||
}
|
||||
case FD_SOCKET: {
|
||||
size_t sent = 0;
|
||||
uint32_t s = SysSend(e->KHandle, buf, count, 0, &sent);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)sent;
|
||||
}
|
||||
default:
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(dllexport) int close(int fd) {
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
FD_ENTRY* e = FdGet(pid, fd);
|
||||
if (!e) return -1;
|
||||
if (e->Type == FD_FILE || e->Type == FD_SOCKET)
|
||||
SysCloseHandle(e->KHandle);
|
||||
FdFree(pid, fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int unlink(const char* path) {
|
||||
if (!path) { 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) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(p) };
|
||||
uint32_t s = SysDeleteFile(&req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) off_t lseek(int fd, off_t offset, int whence) {
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
FD_ENTRY* e = FdGet(pid, fd);
|
||||
if (!e || e->Type != FD_FILE) { errno = (e ? ESPIPE : EBADF); return -1; }
|
||||
|
||||
int64_t target = 0;
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
target = (int64_t)offset;
|
||||
break;
|
||||
case SEEK_CUR: {
|
||||
uint64_t cur = SysGetFilePos(e->KHandle);
|
||||
if (cur > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
|
||||
target = (int64_t)cur + (int64_t)offset;
|
||||
break;
|
||||
}
|
||||
case SEEK_END: {
|
||||
uint64_t size = SysGetFileSize(e->KHandle);
|
||||
if (size > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
|
||||
target = (int64_t)size + (int64_t)offset;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (target < 0 || target > 0x7FFFFFFFLL) { errno = EINVAL; return -1; }
|
||||
|
||||
uint32_t s = SysSeekFile(e->KHandle, (uint64_t)target);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (off_t)target;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// posix_path.c - POSIX <-> native path translation
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "posix_path.h"
|
||||
|
||||
static void FlipSlashes(char* s) {
|
||||
for (; *s; s++) if (*s == '/') *s = '\\';
|
||||
}
|
||||
|
||||
static int IsDriveMount(const char* p) {
|
||||
return p && p[0] == '/' && p[1] >= '0' && p[1] <= '7' &&
|
||||
(p[2] == '\0' || p[2] == '/');
|
||||
}
|
||||
|
||||
const char* PosixTranslatePath(const char* in, char* out, size_t outSize) {
|
||||
if (!in || !out || outSize < 4) return out;
|
||||
|
||||
if (strcmp(in, "/dev/null") == 0) return SYNTH_DEVNULL;
|
||||
if (strcmp(in, "/dev/zero") == 0) return SYNTH_DEVZERO;
|
||||
|
||||
if (IsDriveMount(in)) {
|
||||
out[0] = in[1];
|
||||
out[1] = ':';
|
||||
out[2] = '/';
|
||||
const char* rest = (in[2] == '/') ? in + 3 : "";
|
||||
size_t restLen = strlen(rest);
|
||||
if (restLen + 4 > outSize) restLen = outSize - 4;
|
||||
memcpy(out + 3, rest, restLen);
|
||||
out[3 + restLen] = '\0';
|
||||
FlipSlashes(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
if (in[0] == '/') {
|
||||
out[0] = '0';
|
||||
out[1] = ':';
|
||||
out[2] = '/';
|
||||
size_t rest = strlen(in + 1);
|
||||
if (rest + 4 > outSize) rest = outSize - 4;
|
||||
memcpy(out + 3, in + 1, rest);
|
||||
out[3 + rest] = '\0';
|
||||
} else {
|
||||
size_t len = strlen(in);
|
||||
if (len + 1 > outSize) len = outSize - 1;
|
||||
memcpy(out, in, len);
|
||||
out[len] = '\0';
|
||||
}
|
||||
|
||||
FlipSlashes(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
void NativeToPosix(const char* in, char* out, size_t outSize) {
|
||||
if (!in || !out || outSize == 0) return;
|
||||
|
||||
const char* src = in;
|
||||
char drive = 0;
|
||||
if (src[0] >= '0' && src[0] <= '7' && src[1] == ':' &&
|
||||
(src[2] == '\\' || src[2] == '/')) {
|
||||
drive = src[0];
|
||||
src += 3;
|
||||
}
|
||||
|
||||
size_t pos = 0;
|
||||
if (drive && drive != '0') {
|
||||
if (outSize < 4) { out[0] = '\0'; return; }
|
||||
out[pos++] = '/';
|
||||
out[pos++] = drive;
|
||||
if (*src) out[pos++] = '/';
|
||||
} else {
|
||||
out[pos++] = '/';
|
||||
}
|
||||
|
||||
while (*src && pos + 1 < outSize) {
|
||||
out[pos++] = (*src == '\\') ? '/' : *src;
|
||||
src++;
|
||||
}
|
||||
out[pos] = '\0';
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
// posix_path.h - POSIX <-> native path translation
|
||||
|
||||
// SYNTHETIC sentinel returned from PosixTranslatePath for /dev/null and
|
||||
// /dev/zero; the caller must handle these before passing to any syscall.
|
||||
#define SYNTH_DEVNULL "\x01<devnull>"
|
||||
#define SYNTH_DEVZERO "\x01<devzero>"
|
||||
|
||||
// Translate a POSIX path to a native kernel path.
|
||||
// Returns a pointer to 'out' on success, or SYNTH_DEVNULL / SYNTH_DEVZERO
|
||||
// for those synthetic devices. 'out' must be at least 512 bytes.
|
||||
const char* PosixTranslatePath(const char* in, char* out, size_t outSize);
|
||||
|
||||
// Translate a native kernel path back to a POSIX-style path.
|
||||
// Strips the drive letter prefix and flips backslashes.
|
||||
void NativeToPosix(const char* in, char* out, size_t outSize);
|
||||
@@ -0,0 +1,138 @@
|
||||
// posix_proc.c - posix_spawn, waitpid, getpid
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
#include "posix_path.h"
|
||||
|
||||
|
||||
static SYS_PEB* gProcessPeb = NULL;
|
||||
|
||||
static SYS_PEB* PosixPeb(void) {
|
||||
if (!gProcessPeb) gProcessPeb = SysGetPeb();
|
||||
return gProcessPeb;
|
||||
}
|
||||
|
||||
__declspec(dllexport) const char* posix_get_command_line(void) {
|
||||
SYS_PEB* peb = PosixPeb();
|
||||
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return "";
|
||||
return peb->CommandLine.Data;
|
||||
}
|
||||
|
||||
// pid -> proc handle table (user-mode side, for waitpid)
|
||||
#define PROC_TABLE_SIZE 32
|
||||
typedef struct {
|
||||
pid_t pid;
|
||||
uint32_t handle;
|
||||
bool used;
|
||||
} ProcEntry;
|
||||
|
||||
static ProcEntry gProcTable[PROC_TABLE_SIZE];
|
||||
|
||||
static void ProcTableInsert(pid_t pid, uint32_t handle) {
|
||||
for (int i = 0; i < PROC_TABLE_SIZE; i++) {
|
||||
if (!gProcTable[i].used) {
|
||||
gProcTable[i] = (ProcEntry){ pid, handle, true };
|
||||
return;
|
||||
}
|
||||
}
|
||||
SysCloseHandle(handle);
|
||||
}
|
||||
|
||||
static uint32_t ProcTableFind(pid_t pid) {
|
||||
for (int i = 0; i < PROC_TABLE_SIZE; i++)
|
||||
if (gProcTable[i].used && gProcTable[i].pid == pid)
|
||||
return gProcTable[i].handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ProcTableRemove(pid_t pid) {
|
||||
for (int i = 0; i < PROC_TABLE_SIZE; i++)
|
||||
if (gProcTable[i].used && gProcTable[i].pid == pid)
|
||||
gProcTable[i].used = false;
|
||||
}
|
||||
|
||||
// Build a flat Windows-style cmdline from a NULL-terminated argv array.
|
||||
// Each token that contains a space is wrapped in double-quotes.
|
||||
// argv[0] is included (it becomes the executable token visible to _start).
|
||||
static void BuildCmdline(char* const argv[], char* out, size_t outSize) {
|
||||
size_t pos = 0;
|
||||
for (int i = 0; argv && argv[i]; i++) {
|
||||
if (i > 0 && pos < outSize - 1) out[pos++] = ' ';
|
||||
const char* tok = argv[i];
|
||||
bool needQuote = false;
|
||||
for (const char* c = tok; *c; c++) if (*c == ' ' || *c == '\t') { needQuote = true; break; }
|
||||
if (needQuote && pos < outSize - 1) out[pos++] = '"';
|
||||
for (; *tok && pos < outSize - 2; tok++) {
|
||||
if (*tok == '"') { out[pos++] = '\\'; }
|
||||
out[pos++] = *tok;
|
||||
}
|
||||
if (needQuote && pos < outSize - 1) out[pos++] = '"';
|
||||
}
|
||||
if (pos < outSize) out[pos] = '\0';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// POSIX API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
__declspec(dllexport) pid_t getpid(void) {
|
||||
return (pid_t)SysGetCurrentPid();
|
||||
}
|
||||
|
||||
__declspec(dllexport) int posix_spawn(pid_t* pidOut,
|
||||
const char* path,
|
||||
const posix_spawn_file_actions_t* file_actions, // ignored v1
|
||||
const posix_spawnattr_t* attrp, // ignored v1
|
||||
char* const argv[],
|
||||
char* const envp[]) // ignored v1
|
||||
{
|
||||
(void)file_actions; (void)attrp; (void)envp;
|
||||
|
||||
char native[512];
|
||||
PosixTranslatePath(path, native, sizeof(native));
|
||||
|
||||
char cmdline[512];
|
||||
BuildCmdline(argv, cmdline, sizeof(cmdline));
|
||||
// If argv was NULL or empty, fall back to just the path so _start still
|
||||
// gets a meaningful argv[0].
|
||||
if (cmdline[0] == '\0') {
|
||||
size_t len = strlen(path);
|
||||
if (len >= sizeof(cmdline)) len = sizeof(cmdline) - 1;
|
||||
memcpy(cmdline, path, len);
|
||||
cmdline[len] = '\0';
|
||||
}
|
||||
|
||||
uint32_t procHandle;
|
||||
SYS_CREATE_PROCESS_REQUEST req = { SysSvFromCstr(native), SysSvFromCstr(cmdline), &procHandle };
|
||||
uint32_t s = SysCreateProcess(&req);
|
||||
if (s != 0) { errno = ENOENT; return -1; }
|
||||
|
||||
uint32_t kpid = 0;
|
||||
SysGetProcessPid(procHandle, &kpid);
|
||||
ProcTableInsert((pid_t)kpid, procHandle);
|
||||
if (pidOut) *pidOut = (pid_t)kpid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// options: WNOHANG not supported in v1 (always blocks).
|
||||
__declspec(dllexport) pid_t waitpid(pid_t pid, int* status, int options) {
|
||||
(void)options;
|
||||
|
||||
uint32_t handle = ProcTableFind(pid);
|
||||
if (!handle) { errno = ESRCH; return -1; }
|
||||
|
||||
SysWaitForProcess(handle);
|
||||
|
||||
int exitCode = 0;
|
||||
SysGetProcessExitCode(handle, &exitCode);
|
||||
if (status) *status = exitCode << 8; // WEXITSTATUS convention
|
||||
|
||||
SysCloseHandle(handle);
|
||||
ProcTableRemove(pid);
|
||||
return pid;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void exit(int status) {
|
||||
SysExitProcess(status);
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
// posix_socket.c - POSIX-ish socket wrappers over native Sys* sockets.
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
#include "posix_fd.h"
|
||||
|
||||
static uint16_t bswap16(uint16_t x) {
|
||||
return (uint16_t)((x << 8) | (x >> 8));
|
||||
}
|
||||
|
||||
static uint32_t bswap32(uint32_t x) {
|
||||
return ((x & 0x000000FFu) << 24) |
|
||||
((x & 0x0000FF00u) << 8) |
|
||||
((x & 0x00FF0000u) >> 8) |
|
||||
((x & 0xFF000000u) >> 24);
|
||||
}
|
||||
|
||||
__declspec(dllexport) uint16_t htons(uint16_t x) { return bswap16(x); }
|
||||
__declspec(dllexport) uint16_t ntohs(uint16_t x) { return bswap16(x); }
|
||||
__declspec(dllexport) uint32_t htonl(uint32_t x) { return bswap32(x); }
|
||||
__declspec(dllexport) uint32_t ntohl(uint32_t x) { return bswap32(x); }
|
||||
|
||||
static int ParseByte(const char** p, uint8_t* out) {
|
||||
const char* s = *p;
|
||||
if (*s < '0' || *s > '9') return 0;
|
||||
unsigned v = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
v = v * 10 + (unsigned)(*s - '0');
|
||||
if (v > 255) return 0;
|
||||
s++;
|
||||
}
|
||||
*out = (uint8_t)v;
|
||||
*p = s;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ParseIpv4Bytes(const char* text, uint8_t out[4]) {
|
||||
const char* p = text;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!ParseByte(&p, &out[i])) return 0;
|
||||
if (i != 3) {
|
||||
if (*p != '.') return 0;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
return *p == '\0';
|
||||
}
|
||||
|
||||
__declspec(dllexport) in_addr_t inet_addr(const char* text) {
|
||||
uint8_t b[4];
|
||||
if (!text || !ParseIpv4Bytes(text, b)) return INADDR_NONE;
|
||||
in_addr_t out = 0;
|
||||
uint8_t* p = (uint8_t*)&out;
|
||||
p[0] = b[0]; p[1] = b[1]; p[2] = b[2]; p[3] = b[3];
|
||||
return out;
|
||||
}
|
||||
|
||||
__declspec(dllexport) char* inet_ntoa(struct in_addr in) {
|
||||
static char buf[16];
|
||||
const uint8_t* p = (const uint8_t*)&in.s_addr;
|
||||
// Tiny decimal formatter, avoiding stdio dependency.
|
||||
char* w = buf;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
unsigned v = p[i];
|
||||
if (v >= 100) { *w++ = (char)('0' + v / 100); v %= 100; *w++ = (char)('0' + v / 10); *w++ = (char)('0' + v % 10); }
|
||||
else if (v >= 10) { *w++ = (char)('0' + v / 10); *w++ = (char)('0' + v % 10); }
|
||||
else { *w++ = (char)('0' + v); }
|
||||
if (i != 3) *w++ = '.';
|
||||
}
|
||||
*w = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)) {
|
||||
*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);
|
||||
}
|
||||
|
||||
static FD_ENTRY* SocketFdGet(int fd) {
|
||||
FD_ENTRY* e = FdGet(SysGetCurrentPid(), fd);
|
||||
if (!e) return (FD_ENTRY*)0;
|
||||
if (e->Type != FD_SOCKET) { errno = ENOTSOCK; return (FD_ENTRY*)0; }
|
||||
return e;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int socket(int domain, int type, int protocol) {
|
||||
uint32_t kh = 0;
|
||||
uint32_t s = SysSocket((uint32_t)domain, (uint32_t)type, (uint32_t)protocol, &kh);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
|
||||
int fd = FdAlloc(SysGetCurrentPid(), FD_SOCKET, kh);
|
||||
if (fd < 0) {
|
||||
SysCloseHandle(kh);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
__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;
|
||||
if (SockAddrToSys(addr, addrlen, &a) != 0) return -1;
|
||||
uint32_t s = SysBind(e->KHandle, &a, sizeof(a));
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int listen(int fd, int backlog) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
uint32_t s = SysListen(e->KHandle, backlog < 0 ? 0u : (uint32_t)backlog);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int accept(int fd, struct sockaddr* addr, socklen_t* addrlen) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 remote;
|
||||
size_t remoteSize = sizeof(remote);
|
||||
uint32_t kh = 0;
|
||||
uint32_t s = SysAccept(e->KHandle, addr ? &remote : (SYS_SOCKET_ADDRESS_IPV4*)0,
|
||||
addr ? &remoteSize : (size_t*)0, &kh);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
|
||||
int outfd = FdAlloc(SysGetCurrentPid(), FD_SOCKET, kh);
|
||||
if (outfd < 0) {
|
||||
SysCloseHandle(kh);
|
||||
return -1;
|
||||
}
|
||||
if (addr) SysToSockAddr(&remote, addr, addrlen);
|
||||
return outfd;
|
||||
}
|
||||
|
||||
__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;
|
||||
if (SockAddrToSys(addr, addrlen, &a) != 0) return -1;
|
||||
uint32_t s = SysConnect(e->KHandle, &a, sizeof(a));
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t send(int fd, const void* buf, size_t len, int flags) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
size_t sent = 0;
|
||||
uint32_t s = SysSend(e->KHandle, buf, len, (uint32_t)flags, &sent);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)sent;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t recv(int fd, void* buf, size_t len, int flags) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
size_t got = 0;
|
||||
uint32_t s = SysRecv(e->KHandle, buf, len, (uint32_t)flags, &got);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)got;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t sendto(int fd, const void* buf, size_t len, int flags,
|
||||
const struct sockaddr* dest, socklen_t destlen) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
SYS_SOCKET_ADDRESS_IPV4 remote;
|
||||
if (SockAddrToSys(dest, destlen, &remote) != 0) return -1;
|
||||
|
||||
size_t sent = 0;
|
||||
SYS_SOCKET_SEND_TO_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Buffer = buf;
|
||||
req.Length = len;
|
||||
req.Flags = (uint32_t)flags;
|
||||
req.RemoteAddress = &remote;
|
||||
req.RemoteAddressSize = sizeof(remote);
|
||||
req.SentOut = &sent;
|
||||
|
||||
uint32_t s = SysSendTo(e->KHandle, &req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)sent;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t recvfrom(int fd, void* buf, size_t len, int flags,
|
||||
struct sockaddr* src, socklen_t* srclen) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 remote;
|
||||
size_t remoteSize = sizeof(remote);
|
||||
size_t got = 0;
|
||||
SYS_SOCKET_RECV_FROM_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Buffer = buf;
|
||||
req.Length = len;
|
||||
req.Flags = (uint32_t)flags;
|
||||
req.RemoteAddressOut = src ? &remote : (SYS_SOCKET_ADDRESS_IPV4*)0;
|
||||
req.RemoteAddressSizeInOut = src ? &remoteSize : (size_t*)0;
|
||||
req.ReceivedOut = &got;
|
||||
|
||||
uint32_t s = SysRecvFrom(e->KHandle, &req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
if (src) SysToSockAddr(&remote, src, srclen);
|
||||
return (ssize_t)got;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int shutdown(int fd, int how) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
uint32_t s = SysShutdown(e->KHandle, (uint32_t)how);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t TimevalToMs(const struct timeval* tv) {
|
||||
if (!tv) return 0;
|
||||
uint64_t ms = (uint64_t)tv->tv_sec * 1000u + (uint64_t)(tv->tv_usec / 1000);
|
||||
return ms > 0xFFFFFFFFu ? 0xFFFFFFFFu : (uint32_t)ms;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen) {
|
||||
if (level != SOL_SOCKET || !optval) { errno = EINVAL; return -1; }
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
|
||||
uint32_t option = 0;
|
||||
uint32_t value = 0;
|
||||
if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) {
|
||||
option = (optname == SO_RCVTIMEO) ? SYS_SOCKET_OPT_RECV_TIMEOUT_MS : SYS_SOCKET_OPT_SEND_TIMEOUT_MS;
|
||||
if (optlen == sizeof(struct timeval)) value = TimevalToMs((const struct timeval*)optval);
|
||||
else if (optlen == sizeof(uint32_t)) memcpy(&value, optval, sizeof(value));
|
||||
else { errno = EINVAL; return -1; }
|
||||
} else if (optname == SO_NONBLOCK) {
|
||||
option = SYS_SOCKET_OPT_NONBLOCKING;
|
||||
if (optlen < sizeof(int)) { errno = EINVAL; return -1; }
|
||||
value = (*(const int*)optval) ? 1u : 0u;
|
||||
} else {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
SYS_SOCKET_OPTION_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Option = option;
|
||||
req.Value = &value;
|
||||
req.ValueSize = sizeof(value);
|
||||
uint32_t s = SysSetSocketOption(e->KHandle, &req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int getsockopt(int fd, int level, int optname, void* optval, socklen_t* optlen) {
|
||||
if (level != SOL_SOCKET || !optval || !optlen) { errno = EINVAL; return -1; }
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
|
||||
uint32_t option = 0;
|
||||
if (optname == SO_RCVTIMEO) option = SYS_SOCKET_OPT_RECV_TIMEOUT_MS;
|
||||
else if (optname == SO_SNDTIMEO) option = SYS_SOCKET_OPT_SEND_TIMEOUT_MS;
|
||||
else if (optname == SO_NONBLOCK) option = SYS_SOCKET_OPT_NONBLOCKING;
|
||||
else { errno = ENOSYS; return -1; }
|
||||
|
||||
uint32_t value = 0;
|
||||
size_t actual = sizeof(value);
|
||||
SYS_SOCKET_OPTION_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Option = option;
|
||||
req.Value = &value;
|
||||
req.ValueSize = sizeof(value);
|
||||
req.ActualSizeOut = &actual;
|
||||
uint32_t s = SysGetSocketOption(e->KHandle, &req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
|
||||
if ((optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) && *optlen >= sizeof(struct timeval)) {
|
||||
struct timeval* tv = (struct timeval*)optval;
|
||||
tv->tv_sec = (long)(value / 1000u);
|
||||
tv->tv_usec = (long)((value % 1000u) * 1000u);
|
||||
*optlen = sizeof(struct timeval);
|
||||
} else if (*optlen >= sizeof(uint32_t)) {
|
||||
memcpy(optval, &value, sizeof(value));
|
||||
*optlen = sizeof(value);
|
||||
} else {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int poll(struct pollfd* fds, nfds_t nfds, int timeout) {
|
||||
if (!fds || nfds == 0 || nfds > 64) { errno = EINVAL; return -1; }
|
||||
|
||||
SYS_POLLFD* kfds = (SYS_POLLFD*)SysAlloc(sizeof(SYS_POLLFD) * nfds);
|
||||
if (!kfds) { errno = ENOMEM; return -1; }
|
||||
|
||||
for (nfds_t i = 0; i < nfds; i++) {
|
||||
kfds[i].Handle = 0;
|
||||
kfds[i].Events = 0;
|
||||
kfds[i].ReturnedEvents = 0;
|
||||
|
||||
fds[i].revents = 0;
|
||||
FD_ENTRY* e = FdGet(SysGetCurrentPid(), fds[i].fd);
|
||||
if (!e || e->Type != FD_SOCKET) {
|
||||
continue;
|
||||
}
|
||||
kfds[i].Handle = e->KHandle;
|
||||
if (fds[i].events & POLLIN) kfds[i].Events |= SYS_POLL_READ;
|
||||
if (fds[i].events & POLLOUT) kfds[i].Events |= SYS_POLL_WRITE;
|
||||
if (fds[i].events & POLLERR) kfds[i].Events |= SYS_POLL_ERROR;
|
||||
if (fds[i].events & POLLHUP) kfds[i].Events |= SYS_POLL_HANGUP;
|
||||
}
|
||||
|
||||
size_t ready = 0;
|
||||
uint32_t timeoutMs = timeout < 0 ? SYS_POLL_TIMEOUT_INFINITE : (uint32_t)timeout;
|
||||
uint32_t s = SysPoll(kfds, nfds, timeoutMs, &ready);
|
||||
if (s != 0) {
|
||||
SysFree(kfds);
|
||||
errno = PosixStatusToErrno(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int out = 0;
|
||||
for (nfds_t i = 0; i < nfds; i++) {
|
||||
short re = 0;
|
||||
if (kfds[i].ReturnedEvents & SYS_POLL_READ) re |= POLLIN;
|
||||
if (kfds[i].ReturnedEvents & SYS_POLL_WRITE) re |= POLLOUT;
|
||||
if (kfds[i].ReturnedEvents & SYS_POLL_ERROR) re |= POLLERR;
|
||||
if (kfds[i].ReturnedEvents & SYS_POLL_HANGUP) re |= POLLHUP;
|
||||
if (kfds[i].ReturnedEvents & SYS_POLL_INVALID) re |= POLLNVAL;
|
||||
fds[i].revents = re;
|
||||
if (re) out++;
|
||||
}
|
||||
|
||||
SysFree(kfds);
|
||||
return out;
|
||||
}
|
||||
|
||||
static unsigned ParsePort(const char* s) {
|
||||
unsigned v = 0;
|
||||
if (!s) return 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
v = v * 10 + (unsigned)(*s - '0');
|
||||
if (v > 65535u) return 0;
|
||||
s++;
|
||||
}
|
||||
return *s == '\0' ? v : 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.AddressOut = out;
|
||||
req.AddressSizeInOut = &size;
|
||||
uint32_t s = SysResolveHost(&req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) struct hostent* gethostbyname(const char* name) {
|
||||
static struct hostent h;
|
||||
static char hname[256];
|
||||
static char* aliases[1];
|
||||
static char* addr_list[2];
|
||||
static uint8_t addr[4];
|
||||
|
||||
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;
|
||||
|
||||
strcpy(hname, name);
|
||||
memcpy(addr, a.Address, 4);
|
||||
aliases[0] = (char*)0;
|
||||
addr_list[0] = (char*)addr;
|
||||
addr_list[1] = (char*)0;
|
||||
|
||||
h.h_name = hname;
|
||||
h.h_aliases = aliases;
|
||||
h.h_addrtype = AF_INET;
|
||||
h.h_length = 4;
|
||||
h.h_addr_list = addr_list;
|
||||
return &h;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int getaddrinfo(const char* node, const char* service,
|
||||
const struct addrinfo* hints, struct addrinfo** res) {
|
||||
if (!res) return EINVAL;
|
||||
*res = (struct addrinfo*)0;
|
||||
|
||||
int family = hints ? hints->ai_family : AF_INET;
|
||||
if (family != AF_INET && family != 0) return EINVAL;
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 a;
|
||||
memset(&a, 0, sizeof(a));
|
||||
a.Family = SYS_AF_INET;
|
||||
if (!node) {
|
||||
a.Address[0] = 0; a.Address[1] = 0; a.Address[2] = 0; a.Address[3] = 0;
|
||||
} else if (ResolveNameIpv4(node, &a) != 0) {
|
||||
return errno;
|
||||
}
|
||||
a.Port = service ? ParsePort(service) : 0;
|
||||
|
||||
size_t total = sizeof(struct addrinfo) + sizeof(struct sockaddr_in);
|
||||
struct addrinfo* ai = (struct addrinfo*)SysAlloc(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];
|
||||
|
||||
ai->ai_family = 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_next = (struct addrinfo*)0;
|
||||
*res = ai;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void freeaddrinfo(struct addrinfo* res) {
|
||||
if (res) SysFree(res);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// posix_thread.c - pthread_create, pthread_join, pthread_exit, pthread_mutex_*
|
||||
#include <stddef.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
|
||||
// Thread return-value table (PID-keyed like the fd table)
|
||||
#define PTHREAD_TABLE_PIDS 32
|
||||
#define PTHREAD_TABLE_SIZE 32
|
||||
|
||||
typedef struct {
|
||||
uint32_t Tid;
|
||||
void* RetVal;
|
||||
bool Used;
|
||||
} PTHREAD_SLOT;
|
||||
|
||||
static PTHREAD_SLOT gPthreadTable[PTHREAD_TABLE_PIDS][PTHREAD_TABLE_SIZE];
|
||||
|
||||
static PTHREAD_SLOT* PthreadSlotAlloc(uint32_t pid, uint32_t tid) {
|
||||
PTHREAD_SLOT* table = gPthreadTable[pid % PTHREAD_TABLE_PIDS];
|
||||
for (int i = 0; i < PTHREAD_TABLE_SIZE; i++) {
|
||||
if (!table[i].Used) {
|
||||
table[i] = (PTHREAD_SLOT){ tid, (void*)0, true };
|
||||
return &table[i];
|
||||
}
|
||||
}
|
||||
return (PTHREAD_SLOT*)0;
|
||||
}
|
||||
|
||||
static PTHREAD_SLOT* PthreadSlotFind(uint32_t pid, uint32_t tid) {
|
||||
PTHREAD_SLOT* table = gPthreadTable[pid % PTHREAD_TABLE_PIDS];
|
||||
for (int i = 0; i < PTHREAD_TABLE_SIZE; i++)
|
||||
if (table[i].Used && table[i].Tid == tid)
|
||||
return &table[i];
|
||||
return (PTHREAD_SLOT*)0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Trampoline - this is what actually runs as the new thread
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef struct {
|
||||
void* (*Fn)(void*);
|
||||
void* Arg;
|
||||
uint32_t Pid; // owner pid, for the retval table
|
||||
} PTHREAD_TRAMP;
|
||||
|
||||
static void PthreadTrampoline(uintptr_t raw) {
|
||||
PTHREAD_TRAMP* tr = (PTHREAD_TRAMP*)raw;
|
||||
void* (*fn)(void*) = tr->Fn;
|
||||
void* arg = tr->Arg;
|
||||
uint32_t pid = tr->Pid;
|
||||
uint32_t tid = SysGetCurrentTid();
|
||||
|
||||
// Create the join slot from inside the new thread. KeCreateThread makes the
|
||||
// thread runnable before the creator regains control, so the old design
|
||||
// raced on tr->Tid and could observe zero/stale memory.
|
||||
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
|
||||
if (!slot) slot = PthreadSlotAlloc(pid, tid);
|
||||
|
||||
SysFree(tr);
|
||||
|
||||
void* retval = fn(arg);
|
||||
|
||||
// Store retval for pthread_join.
|
||||
if (slot) slot->RetVal = retval;
|
||||
|
||||
SysExitThread(0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// pthread API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
__declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* attr,
|
||||
void* (*fn)(void*), void* arg) {
|
||||
(void)attr;
|
||||
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
|
||||
PTHREAD_TRAMP* tr = (PTHREAD_TRAMP*)SysAlloc(sizeof(PTHREAD_TRAMP));
|
||||
if (!tr) return ENOMEM;
|
||||
tr->Fn = fn;
|
||||
tr->Arg = arg;
|
||||
tr->Pid = pid;
|
||||
|
||||
uint32_t tid = 0;
|
||||
uint32_t s = SysCreateThread((uint64_t)PthreadTrampoline,
|
||||
(uint64_t)tr, 1 /*PriorityNormal*/, &tid);
|
||||
if (s != 0) {
|
||||
SysFree(tr);
|
||||
return EAGAIN;
|
||||
}
|
||||
// The trampoline allocates the retval slot itself after it knows its real
|
||||
// TID, avoiding the creator/new-thread race.
|
||||
if (t) t->Tid = tid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_join(pthread_t t, void** retval) {
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
uint32_t s = SysWaitThread(t.Tid);
|
||||
// STATUS_NOT_FOUND (0x22) means already dead - treat as success
|
||||
if (s != 0 && s != 0x0022) return ESRCH;
|
||||
|
||||
PTHREAD_SLOT* slot = PthreadSlotFind(pid, t.Tid);
|
||||
if (retval) *retval = slot ? slot->RetVal : (void*)0;
|
||||
if (slot) slot->Used = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void pthread_exit(void* retval) {
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
uint32_t tid = SysGetCurrentTid();
|
||||
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
|
||||
if (slot) slot->RetVal = retval;
|
||||
SysExitThread(0);
|
||||
while (1); // unreachable
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr) {
|
||||
(void)attr;
|
||||
uint32_t h;
|
||||
uint32_t s = SysMutexCreate(&h);
|
||||
if (s != 0) return ENOMEM;
|
||||
m->Handle = h;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_mutex_destroy(pthread_mutex_t* m) {
|
||||
SysCloseHandle(m->Handle);
|
||||
m->Handle = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_mutex_lock(pthread_mutex_t* m) {
|
||||
uint32_t s = SysMutexAcquire(m->Handle);
|
||||
return s == 0 ? 0 : EINVAL;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_mutex_trylock(pthread_mutex_t* m) {
|
||||
uint32_t s = SysMutexTryAcquire(m->Handle);
|
||||
return s == 0 ? 0 : EAGAIN;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_mutex_unlock(pthread_mutex_t* m) {
|
||||
uint32_t s = SysMutexRelease(m->Handle);
|
||||
return s == 0 ? 0 : EINVAL;
|
||||
}
|
||||
Reference in new issue
Block a user