Files
nt-lite/code/user/personalities/posix/posix_proc.c
T
maksw 73abd6f90d isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
2026-09-16 10:33:20 +02:00

457 lines
17 KiB
C

// POSIX compatibility support for posix proc.
#include <stddef.h>
#include <string.h>
#include <ntlite/syscall.h>
#include <ntlite/path.h>
#include <ntlite/process_loader.h>
#include "posix_internal.h"
#include "posix_path.h"
// Returns the native PEB retained by the process-local POSIX runtime state.
static SYS_PEB* PosixPeb(void) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
return process ? process->Peb : (SYS_PEB*)0;
}
// Implements the posix get command line operation.
__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;
}
// Records one spawn association in process-local POSIX state.
static void ProcTableInsert(pid_t pid, SYS_HANDLE handle, uint64_t inheritanceToken) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process) {
SysCloseHandle(handle);
return;
}
for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) {
if (!process->SpawnTable[i].Used) {
process->SpawnTable[i] = (POSIX_SPAWN_ENTRY){ pid, handle, inheritanceToken, true };
return;
}
}
SysCloseHandle(handle);
if (inheritanceToken != 0) PosixFdCancelInheritanceSnapshot(inheritanceToken);
}
// Finds a spawned process handle retained by the current POSIX process.
static SYS_HANDLE ProcTableFind(pid_t pid) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process) return SYS_HANDLE_INVALID;
for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) {
if (process->SpawnTable[i].Used && process->SpawnTable[i].Pid == pid)
return process->SpawnTable[i].Handle;
}
return SYS_HANDLE_INVALID;
}
// Removes one spawn association from process-local POSIX state.
static void ProcTableRemove(pid_t pid) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process) return;
for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) {
if (process->SpawnTable[i].Used && process->SpawnTable[i].Pid == pid) {
uint64_t inheritanceToken = process->SpawnTable[i].FdInheritanceToken;
memset(&process->SpawnTable[i], 0, sizeof(process->SpawnTable[i]));
if (inheritanceToken != 0) PosixFdCancelInheritanceSnapshot(inheritanceToken);
return;
}
}
}
// 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) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread || !thread->LogicalProcess || thread->LogicalProcess->PosixPid == 0)
return (pid_t)-1;
return (pid_t)thread->LogicalProcess->PosixPid;
}
__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];
uint32_t pathStatus = PosixProcessPath(path, native, sizeof(native));
if (pathStatus != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(pathStatus); return -1; }
SYS_HANDLE imageFile = SYS_HANDLE_INVALID;
pathStatus = NtlOpenFile(SysSvFromCstr(native), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, &imageFile);
if (pathStatus != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(pathStatus); return -1; }
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';
}
uint64_t inheritanceToken = 0;
uint32_t snapshotStatus =
PosixFdCreateInheritanceSnapshot(POSIX_FD_INHERIT_SKIP_CLOEXEC, &inheritanceToken);
if (snapshotStatus != NTLSTATUS_SUCCESS) {
(void)SysCloseHandle(imageFile);
errno = PosixStatusToErrno(snapshotStatus);
return -1;
}
NTL_POSIX_BOOTSTRAP_DATA bootstrap;
memset(&bootstrap, 0, sizeof(bootstrap));
bootstrap.Magic = NTL_POSIX_BOOTSTRAP_MAGIC;
bootstrap.Version = NTL_POSIX_BOOTSTRAP_VERSION;
bootstrap.Size = sizeof(bootstrap);
bootstrap.FdInheritanceToken = inheritanceToken;
uint32_t procHandle = 0;
NTL_CREATE_SYSTEM_PROCESS_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
req.ImageFile = imageFile;
req.Path = SysSvFromCstr(native);
req.CommandLine = SysSvFromCstr(cmdline);
req.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT;
req.ResolveModuleName = PosixResolveModuleName;
req.StartupData = &bootstrap;
req.StartupDataSize = sizeof(bootstrap);
req.ProcessHandleOut = &procHandle;
uint32_t s = NtlCreateSystemProcess(&req);
if (imageFile != SYS_HANDLE_INVALID) (void)SysCloseHandle(imageFile);
if (s != NTLSTATUS_SUCCESS) {
PosixFdCancelInheritanceSnapshot(inheritanceToken);
errno = PosixStatusToErrno(s);
return -1;
}
KSTATUS armStatus = PosixFdArmInheritanceSnapshot(inheritanceToken, procHandle);
if (!KSUCCESS(armStatus) && armStatus != NTLSTATUS_NOT_FOUND) {
(void)SysKillProcess(procHandle, (int32_t)NTLSTATUS_PROCESS_KILLED);
(void)SysCloseHandle(procHandle);
PosixFdCancelInheritanceSnapshot(inheritanceToken);
errno = PosixStatusToErrno(armStatus);
return -1;
}
uint32_t kpid = 0;
SysGetProcessPid(procHandle, &kpid);
ProcTableInsert((pid_t)kpid, procHandle, inheritanceToken);
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;
SYS_HANDLE handle = ProcTableFind(pid);
if (handle == SYS_HANDLE_INVALID) { errno = ESRCH; return -1; }
SysWait(handle, SYS_WAIT_FLAG_NONE);
int32_t exitCode = 0;
SysGetProcessExitCode(handle, &exitCode);
if (status) *status = exitCode << 8; // WEXITSTATUS convention
SysCloseHandle(handle);
ProcTableRemove(pid);
return pid;
}
// Implements the exit operation.
__declspec(dllexport) void exit(int status) {
SysExitProcess(status);
while (true) {}
}
// Initializes one ordinary shared-attribute request.
static void PosixProcessRequest(NTL_POSIX_REQUEST* request, uint32_t operation) {
memset(request, 0, sizeof(*request));
request->Size = sizeof(*request);
request->Version = NTL_POSIX_PROTOCOL_VERSION;
request->Operation = operation;
request->Flags = NTL_POSIX_REQUEST_FLAG_NONE;
}
// Executes one shared-attribute request and converts transport/server failure to errno.
static int PosixProcessCall(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* reply) {
KSTATUS status = PosixRuntimeCallServer(request, reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return -1;
}
if (!KSUCCESS(reply->Status)) {
errno = PosixStatusToErrno(reply->Status);
return -1;
}
return 0;
}
// Executes one operation whose permission failures are POSIX EPERM rather than EACCES.
static int PosixProcessCallPermission(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* reply) {
KSTATUS status = PosixRuntimeCallServer(request, reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return -1;
}
if (!KSUCCESS(reply->Status)) {
errno = reply->Status == NTLSTATUS_ACCESS_DENIED ? EPERM : PosixStatusToErrno(reply->Status);
return -1;
}
return 0;
}
// Returns the real POSIX user id owned by posixsrv.
__declspec(dllexport) uid_t getuid(void) {
NTL_POSIX_REPLY reply;
if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (uid_t)-1;
return (uid_t)reply.UserId;
}
// Returns the effective POSIX user id owned by posixsrv.
__declspec(dllexport) uid_t geteuid(void) {
NTL_POSIX_REPLY reply;
if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (uid_t)-1;
return (uid_t)reply.EffectiveUserId;
}
// Returns the real POSIX group id owned by posixsrv.
__declspec(dllexport) gid_t getgid(void) {
NTL_POSIX_REPLY reply;
if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (gid_t)-1;
return (gid_t)reply.GroupId;
}
// Returns the effective POSIX group id owned by posixsrv.
__declspec(dllexport) gid_t getegid(void) {
NTL_POSIX_REPLY reply;
if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (gid_t)-1;
return (gid_t)reply.EffectiveGroupId;
}
// Changes synthetic user credentials; uid zero is the only credential-changing authority.
__declspec(dllexport) int setuid(uid_t uid) {
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_SET_UID);
request.UserId = (uint32_t)uid;
return PosixProcessCallPermission(&request, &reply);
}
// Changes synthetic group credentials; effective uid zero is the authority.
__declspec(dllexport) int setgid(gid_t gid) {
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_SET_GID);
request.GroupId = (uint32_t)gid;
return PosixProcessCallPermission(&request, &reply);
}
// Returns the supplementary group list attached to the current POSIX process.
__declspec(dllexport) int getgroups(int size, gid_t list[]) {
if (size < 0 || (size != 0 && !list)) {
errno = EINVAL;
return -1;
}
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeGetProcessAttributes(&reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return -1;
}
if (reply.SupplementaryGroupCount > NTL_POSIX_SUPPLEMENTARY_GROUP_MAX) {
errno = EIO;
return -1;
}
if (size == 0) return (int)reply.SupplementaryGroupCount;
if ((uint32_t)size < reply.SupplementaryGroupCount) {
errno = EINVAL;
return -1;
}
for (uint32_t i = 0; i < reply.SupplementaryGroupCount; ++i)
list[i] = (gid_t)reply.SupplementaryGroups[i];
return (int)reply.SupplementaryGroupCount;
}
// Returns the login identity fixed when the POSIX process record is created.
__declspec(dllexport) char* getlogin(void) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread) return (char*)0;
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeGetProcessAttributes(&reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return (char*)0;
}
if (reply.NameLength == 0 || reply.NameLength > NTL_POSIX_IDENTITY_NAME_MAX ||
reply.Name[reply.NameLength] != '\0') {
errno = ENOENT;
return (char*)0;
}
memcpy(thread->LoginName, reply.Name, reply.NameLength + 1u);
return thread->LoginName;
}
// Returns the current POSIX process-group id.
__declspec(dllexport) pid_t getpgrp(void) {
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeGetProcessAttributes(&reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return (pid_t)-1;
}
return (pid_t)reply.ProcessGroupId;
}
// Changes process-group membership under posixsrv's parent/session invariants.
__declspec(dllexport) int setpgid(pid_t pid, pid_t pgid) {
if (pid < 0 || pgid < 0) {
errno = EINVAL;
return -1;
}
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_SET_PROCESS_GROUP);
request.TargetPosixPid = (uint32_t)pid;
request.ProcessGroupId = (uint32_t)pgid;
return PosixProcessCallPermission(&request, &reply);
}
// Creates a new POSIX session and process group when the caller is not a group leader.
__declspec(dllexport) pid_t setsid(void) {
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_CREATE_SESSION);
if (PosixProcessCallPermission(&request, &reply) != 0) return (pid_t)-1;
return (pid_t)reply.SessionId;
}
// Performs one passwd lookup and builds a thread-local POSIX result view.
static struct passwd* PosixLookupUser(uint32_t lookupKind, uid_t uid, const char* name) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread) return (struct passwd*)0;
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_LOOKUP_USER);
request.LookupKind = lookupKind;
request.UserId = (uint32_t)uid;
if (lookupKind == NTL_POSIX_LOOKUP_BY_NAME) {
if (!name) { errno = EINVAL; return (struct passwd*)0; }
size_t length = strlen(name);
if (length == 0 || length > NTL_POSIX_IDENTITY_NAME_MAX) {
errno = ENOENT;
return (struct passwd*)0;
}
memcpy(request.Name, name, length + 1u);
request.NameLength = (uint32_t)length;
}
if (PosixProcessCall(&request, &reply) != 0) return (struct passwd*)0;
if (reply.NameLength == 0 || reply.NameLength > NTL_POSIX_IDENTITY_NAME_MAX ||
reply.HomeLength > NTL_POSIX_IDENTITY_HOME_MAX ||
reply.ShellLength > NTL_POSIX_IDENTITY_SHELL_MAX ||
reply.Name[reply.NameLength] != '\0' || reply.Home[reply.HomeLength] != '\0' ||
reply.Shell[reply.ShellLength] != '\0') {
errno = EIO;
return (struct passwd*)0;
}
memcpy(thread->PasswordName, reply.Name, reply.NameLength + 1u);
memcpy(thread->PasswordHome, reply.Home, reply.HomeLength + 1u);
memcpy(thread->PasswordShell, reply.Shell, reply.ShellLength + 1u);
thread->PasswordResult.pw_name = thread->PasswordName;
thread->PasswordResult.pw_uid = (uid_t)reply.UserId;
thread->PasswordResult.pw_gid = (gid_t)reply.GroupId;
thread->PasswordResult.pw_dir = thread->PasswordHome;
thread->PasswordResult.pw_shell = thread->PasswordShell;
return &thread->PasswordResult;
}
// Looks up one synthesized passwd entry by name.
__declspec(dllexport) struct passwd* getpwnam(const char* name) {
return PosixLookupUser(NTL_POSIX_LOOKUP_BY_NAME, 0, name);
}
// Looks up one synthesized passwd entry by uid.
__declspec(dllexport) struct passwd* getpwuid(uid_t uid) {
return PosixLookupUser(NTL_POSIX_LOOKUP_BY_ID, uid, (const char*)0);
}
// Performs one group lookup and builds a thread-local POSIX result view.
static struct group* PosixLookupGroup(uint32_t lookupKind, gid_t gid, const char* name) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread) return (struct group*)0;
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_LOOKUP_GROUP);
request.LookupKind = lookupKind;
request.GroupId = (uint32_t)gid;
if (lookupKind == NTL_POSIX_LOOKUP_BY_NAME) {
if (!name) { errno = EINVAL; return (struct group*)0; }
size_t length = strlen(name);
if (length == 0 || length > NTL_POSIX_IDENTITY_NAME_MAX) {
errno = ENOENT;
return (struct group*)0;
}
memcpy(request.Name, name, length + 1u);
request.NameLength = (uint32_t)length;
}
if (PosixProcessCall(&request, &reply) != 0) return (struct group*)0;
if (reply.NameLength == 0 || reply.NameLength > NTL_POSIX_IDENTITY_NAME_MAX ||
reply.Name[reply.NameLength] != '\0') {
errno = EIO;
return (struct group*)0;
}
memcpy(thread->GroupName, reply.Name, reply.NameLength + 1u);
thread->GroupMembers[0] = (char*)0;
thread->GroupResult.gr_name = thread->GroupName;
thread->GroupResult.gr_gid = (gid_t)reply.GroupId;
thread->GroupResult.gr_mem = thread->GroupMembers;
return &thread->GroupResult;
}
// Looks up one synthesized group entry by name.
__declspec(dllexport) struct group* getgrnam(const char* name) {
return PosixLookupGroup(NTL_POSIX_LOOKUP_BY_NAME, 0, name);
}
// Looks up one synthesized group entry by gid.
__declspec(dllexport) struct group* getgrgid(gid_t gid) {
return PosixLookupGroup(NTL_POSIX_LOOKUP_BY_ID, gid, (const char*)0);
}