// POSIX personality process/thread runtime state backed by the native TEB extension slot. #include #include #include #include #include #include #include #include "posix_internal.h" #include "posix_fd.h" static POSIX_PROCESS_STATE* gPosixProcessState; static int gEmergencyErrno; NTL_POSIX_API char** environ; // Reports one startup failure without depending on the POSIX descriptor layer. static void PosixRuntimeReportStartupFailure(const char* stage, KSTATUS status) { static const char hex[] = "0123456789ABCDEF"; char buffer[128]; const char* prefix = "posix startup: "; size_t position = 0; while (*prefix && position + 1u < sizeof(buffer)) buffer[position++] = *prefix++; while (stage && *stage && position + 1u < sizeof(buffer)) buffer[position++] = *stage++; const char* middle = " failed status=0x"; while (*middle && position + 1u < sizeof(buffer)) buffer[position++] = *middle++; for (int shift = 28; shift >= 0 && position + 1u < sizeof(buffer); shift -= 4) buffer[position++] = hex[(status >> shift) & 0xFu]; if (position + 1u < sizeof(buffer)) buffer[position++] = '\n'; buffer[position] = '\0'; SYS_WRITE_CONSOLE_REQUEST write; memset(&write, 0, sizeof(write)); write.Size = sizeof(write); write.Text = SysSvFromParts(buffer, position); (void)SysWriteConsole(&write); } static void PosixRuntimeDestroyVectors(POSIX_PROCESS_STATE* process); // Copies one immutable loader vector into mutable process-local POSIX strings. static KSTATUS PosixRuntimeCopyVector(int environment, char*** vectorOut, int* countOut) { if (!vectorOut || !countOut) return NTLSTATUS_INVALID_PARAMETER; *vectorOut = (char**)0; *countOut = 0; uint32_t count = 0; KSTATUS status = environment ? NtlGetProcessEnvironmentCount(&count) : NtlGetProcessArgumentCount(&count); if (!KSUCCESS(status)) return status; if (count > INT32_MAX) return NTLSTATUS_LIMIT_REACHED; char** vector = (char**)calloc((size_t)count + 1u, sizeof(char*)); if (!vector) return NTLSTATUS_NO_MEMORY; for (uint32_t i = 0; i < count; ++i) { SYS_STRING_VIEW view = SYS_SV_NULL; status = environment ? NtlGetProcessEnvironment(i, &view) : NtlGetProcessArgument(i, &view); if (!KSUCCESS(status)) { for (uint32_t j = 0; j < i; ++j) free(vector[j]); free(vector); return status; } vector[i] = (char*)malloc(view.Length + 1u); if (!vector[i]) { for (uint32_t j = 0; j < i; ++j) free(vector[j]); free(vector); return NTLSTATUS_NO_MEMORY; } if (view.Length) memcpy(vector[i], view.Data, view.Length); vector[i][view.Length] = '\0'; } *vectorOut = vector; *countOut = (int)count; return NTLSTATUS_SUCCESS; } // Builds the compatibility command-line view from the loader-owned argv vector. static KSTATUS PosixRuntimeBuildCommandLine(POSIX_PROCESS_STATE* process) { if (!process) return NTLSTATUS_INVALID_PARAMETER; if (process->ArgumentCount <= 0 || !process->Arguments) return NTLSTATUS_SUCCESS; size_t required = 1u; for (int i = 0; i < process->ArgumentCount; ++i) { const char* argument = process->Arguments[i] ? process->Arguments[i] : ""; int quote = 0; for (const char* cursor = argument; *cursor; ++cursor) { if (*cursor == ' ' || *cursor == '\t') quote = 1; if (*cursor == '"') { if (required == (size_t)-1) return NTLSTATUS_LIMIT_REACHED; ++required; } if (required == (size_t)-1) return NTLSTATUS_LIMIT_REACHED; ++required; } if (quote) { if (required > (size_t)-1 - 2u) return NTLSTATUS_LIMIT_REACHED; required += 2u; } if (i != 0) { if (required == (size_t)-1) return NTLSTATUS_LIMIT_REACHED; ++required; } } char* commandLine = (char*)malloc(required); if (!commandLine) return NTLSTATUS_NO_MEMORY; size_t position = 0; for (int i = 0; i < process->ArgumentCount; ++i) { const char* argument = process->Arguments[i] ? process->Arguments[i] : ""; int quote = 0; for (const char* cursor = argument; *cursor; ++cursor) { if (*cursor == ' ' || *cursor == '\t') { quote = 1; break; } } if (i != 0) commandLine[position++] = ' '; if (quote) commandLine[position++] = '"'; for (const char* cursor = argument; *cursor; ++cursor) { if (*cursor == '"') commandLine[position++] = '\\'; commandLine[position++] = *cursor; } if (quote) commandLine[position++] = '"'; } commandLine[position] = '\0'; process->CommandLine = commandLine; return NTLSTATUS_SUCCESS; } // Materializes argv and environ before application entry executes. static KSTATUS PosixRuntimeInitializeVectors(POSIX_PROCESS_STATE* process) { if (!process) return NTLSTATUS_INVALID_PARAMETER; int environmentCount = 0; KSTATUS status = PosixRuntimeCopyVector(0, &process->Arguments, &process->ArgumentCount); if (!KSUCCESS(status)) return status; status = PosixRuntimeCopyVector(1, &process->Environment, &environmentCount); if (!KSUCCESS(status)) { for (int i = 0; i < process->ArgumentCount; ++i) free(process->Arguments[i]); free(process->Arguments); process->Arguments = (char**)0; process->ArgumentCount = 0; return status; } environ = process->Environment; status = PosixRuntimeBuildCommandLine(process); if (!KSUCCESS(status)) { PosixRuntimeDestroyVectors(process); return status; } return NTLSTATUS_SUCCESS; } // Releases process-local argv/environ storage during initialization rollback. static void PosixRuntimeDestroyVectors(POSIX_PROCESS_STATE* process) { if (!process) return; if (process->Arguments) { for (int i = 0; i < process->ArgumentCount; ++i) free(process->Arguments[i]); free(process->Arguments); } if (process->Environment) { for (char** entry = process->Environment; *entry; ++entry) free(*entry); free(process->Environment); } free(process->CommandLine); process->Arguments = (char**)0; process->Environment = (char**)0; process->CommandLine = (char*)0; process->ArgumentCount = 0; environ = (char**)0; } // Resolves the published POSIX service and opens one private client endpoint. static KSTATUS PosixRuntimeConnectServer(SYS_HANDLE* endpointOut) { if (!endpointOut) return NTLSTATUS_INVALID_PARAMETER; *endpointOut = SYS_HANDLE_INVALID; SYS_HANDLE resource = SYS_HANDLE_INVALID; KSTATUS status = NtlResourceConnect(&resource); if (!KSUCCESS(status)) return status; SYS_HANDLE listener = SYS_HANDLE_INVALID; status = NtlResourceLookup(resource, SYS_SV_LITERAL(NTL_POSIX_SERVICE_PATH), SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT, &listener); (void)NtlResourceClose(resource); if (!KSUCCESS(status)) return status; SYS_CONNECT_PORT_REQUEST connect; memset(&connect, 0, sizeof(connect)); connect.Size = sizeof(connect); connect.ListenerHandle = listener; connect.Flags = SYS_PORT_CONNECT_FLAG_NONE; connect.EndpointHandleOut = endpointOut; status = SysConnectPort(&connect); (void)SysCloseHandle(listener); return status; } // Calls one fixed-size posixsrv request over an explicitly supplied endpoint. static KSTATUS PosixRuntimeCallEndpoint(SYS_HANDLE endpoint, const NTL_POSIX_REQUEST* request, const SYS_PORT_HANDLE_TRANSFER* requestHandles, uint32_t requestHandleCount, SYS_HANDLE* replyHandlesOut, uint32_t replyHandleCapacity, uint32_t expectedReplyHandleCount, uint32_t* actualReplyHandleCountOut, NTL_POSIX_REPLY* replyOut) { if (endpoint == SYS_HANDLE_INVALID || !request || !replyOut || (expectedReplyHandleCount != UINT32_MAX && expectedReplyHandleCount > replyHandleCapacity) || (replyHandleCapacity != 0 && !replyHandlesOut)) return NTLSTATUS_INVALID_PARAMETER; memset(replyOut, 0, sizeof(*replyOut)); if (replyHandlesOut) { for (uint32_t i = 0; i < replyHandleCapacity; ++i) replyHandlesOut[i] = SYS_HANDLE_INVALID; } SYS_PORT_MESSAGE_INFO info; memset(&info, 0, sizeof(info)); info.Size = sizeof(info); SYS_CALL_PORT_REQUEST call; memset(&call, 0, sizeof(call)); call.Size = sizeof(call); call.EndpointHandle = endpoint; call.Flags = SYS_PORT_FLAG_NONE; call.RequestPayload = request; call.RequestLength = sizeof(*request); call.RequestHandles = requestHandles; call.RequestHandleCount = requestHandleCount; call.ReplyPayload = replyOut; call.ReplyCapacity = sizeof(*replyOut); call.ReplyHandlesOut = replyHandlesOut; call.ReplyHandleCapacity = replyHandleCapacity; call.ReplyInfoOut = &info; call.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE; KSTATUS status = SysCallPort(&call); if (!KSUCCESS(status)) return status; uint32_t expectedHandles = KSUCCESS(replyOut->Status) ? expectedReplyHandleCount : 0u; int handlesValid = expectedReplyHandleCount == UINT32_MAX ? info.HandleCount <= replyHandleCapacity : info.HandleCount == expectedHandles; if (info.Type != SYS_PORT_MESSAGE_REPLY || !handlesValid || info.PayloadLength != sizeof(*replyOut) || replyOut->Size != sizeof(*replyOut) || replyOut->Version != NTL_POSIX_PROTOCOL_VERSION || replyOut->Operation != request->Operation || replyOut->Flags != NTL_POSIX_REQUEST_FLAG_NONE) { if (replyHandlesOut) { uint32_t closeCount = info.HandleCount < replyHandleCapacity ? info.HandleCount : replyHandleCapacity; for (uint32_t i = 0; i < closeCount; ++i) { if (replyHandlesOut[i] != SYS_HANDLE_INVALID) (void)SysCloseHandle(replyHandlesOut[i]); replyHandlesOut[i] = SYS_HANDLE_INVALID; } } return NTLSTATUS_FAILURE; } if (actualReplyHandleCountOut) *actualReplyHandleCountOut = info.HandleCount; return NTLSTATUS_SUCCESS; } // Calls posixsrv while optionally transferring native capabilities. KSTATUS PosixRuntimeCallServerWithHandles(const NTL_POSIX_REQUEST* request, const SYS_PORT_HANDLE_TRANSFER* handles, uint32_t handleCount, NTL_POSIX_REPLY* replyOut) { POSIX_PROCESS_STATE* process = gPosixProcessState; if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC) return NTLSTATUS_NOT_FOUND; return PosixRuntimeCallEndpoint(process->ServerEndpoint, request, handles, handleCount, (SYS_HANDLE*)0, 0, 0, (uint32_t*)0, replyOut); } // Calls posixsrv with independent request/reply capability transfer. KSTATUS PosixRuntimeCallServerExchange(const NTL_POSIX_REQUEST* request, const SYS_PORT_HANDLE_TRANSFER* requestHandles, uint32_t requestHandleCount, SYS_HANDLE* replyHandlesOut, uint32_t replyHandleCapacity, uint32_t expectedReplyHandleCount, NTL_POSIX_REPLY* replyOut) { POSIX_PROCESS_STATE* process = gPosixProcessState; if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC) return NTLSTATUS_NOT_FOUND; return PosixRuntimeCallEndpoint(process->ServerEndpoint, request, requestHandles, requestHandleCount, replyHandlesOut, replyHandleCapacity, expectedReplyHandleCount, (uint32_t*)0, replyOut); } KSTATUS PosixRuntimeCallServerExchangeVariable(const NTL_POSIX_REQUEST* request, SYS_HANDLE* replyHandlesOut, uint32_t replyHandleCapacity, uint32_t* replyHandleCountOut, NTL_POSIX_REPLY* replyOut) { POSIX_PROCESS_STATE* process = gPosixProcessState; if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC || !replyHandleCountOut) return NTLSTATUS_INVALID_PARAMETER; *replyHandleCountOut = 0; return PosixRuntimeCallEndpoint(process->ServerEndpoint, request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, replyHandlesOut, replyHandleCapacity, UINT32_MAX, replyHandleCountOut, replyOut); } // Calls posixsrv over the registered process endpoint and validates one reply. KSTATUS PosixRuntimeCallServer(const NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* replyOut) { return PosixRuntimeCallServerWithHandles(request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, replyOut); } // Registers a temporary endpoint to the current native/POSIX process identity. static KSTATUS PosixRuntimeRegisterAuxiliaryEndpoint(SYS_HANDLE endpoint) { POSIX_PROCESS_STATE* process = gPosixProcessState; if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC || endpoint == SYS_HANDLE_INVALID) return NTLSTATUS_NOT_FOUND; NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Version = NTL_POSIX_PROTOCOL_VERSION; request.Operation = NTL_POSIX_OP_REGISTER_PROCESS; size_t currentDirectorySize = 0; KSTATUS status = NtlGetCurrentDirectory(request.Path, sizeof(request.Path), ¤tDirectorySize); if (!KSUCCESS(status) || currentDirectorySize == 0 || currentDirectorySize > sizeof(request.Path)) return KSUCCESS(status) ? NTLSTATUS_FAILURE : status; request.PathLength = (uint32_t)strlen(request.Path); NTL_POSIX_REPLY reply; status = PosixRuntimeCallEndpoint(endpoint, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, (SYS_HANDLE*)0, 0, 0, (uint32_t*)0, &reply); if (!KSUCCESS(status)) return status; if (!KSUCCESS(reply.Status)) return reply.Status; if (reply.PosixPid != process->DefaultIdentity.PosixPid || reply.NativeGeneration != process->DefaultIdentity.Generation || (reply.ProcessState != NTL_POSIX_PROCESS_STATE_RUNNING && reply.ProcessState != NTL_POSIX_PROCESS_STATE_EXEC_PREPARING)) return NTLSTATUS_ACCESS_DENIED; return NTLSTATUS_SUCCESS; } // Uses an auxiliary registered connection so one blocking request cannot stall ordinary POSIX calls. KSTATUS PosixRuntimeCallServerBlockingWithHandles(const NTL_POSIX_REQUEST* request, const SYS_PORT_HANDLE_TRANSFER* handles, uint32_t handleCount, NTL_POSIX_REPLY* replyOut) { if (!request || !replyOut || (handleCount != 0 && !handles) || handleCount > SYS_PORT_MAX_TRANSFER_HANDLES) return NTLSTATUS_INVALID_PARAMETER; SYS_HANDLE endpoint = SYS_HANDLE_INVALID; KSTATUS status = PosixRuntimeConnectServer(&endpoint); if (!KSUCCESS(status)) return status; status = PosixRuntimeRegisterAuxiliaryEndpoint(endpoint); if (KSUCCESS(status)) status = PosixRuntimeCallEndpoint(endpoint, request, handles, handleCount, (SYS_HANDLE*)0, 0, 0, (uint32_t*)0, replyOut); (void)SysDisconnectPort(endpoint); (void)SysCloseHandle(endpoint); return status; } // Uses an auxiliary registered connection without transferring capabilities. KSTATUS PosixRuntimeCallServerBlocking(const NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* replyOut) { return PosixRuntimeCallServerBlockingWithHandles( request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, replyOut); } // Queries the current server-owned POSIX process attributes. KSTATUS PosixRuntimeGetProcessAttributes(NTL_POSIX_REPLY* replyOut) { if (!replyOut) return NTLSTATUS_INVALID_PARAMETER; NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Version = NTL_POSIX_PROTOCOL_VERSION; request.Operation = NTL_POSIX_OP_GET_PROCESS_ATTRIBUTES; request.Flags = NTL_POSIX_REQUEST_FLAG_NONE; KSTATUS status = PosixRuntimeCallServer(&request, replyOut); if (!KSUCCESS(status)) return status; return replyOut->Status; } // Reads and validates optional POSIX bootstrap identity from generic userspace loader data. static KSTATUS PosixRuntimeReadBootstrap(NTL_POSIX_BOOTSTRAP_DATA* bootstrapOut) { if (!bootstrapOut) return NTLSTATUS_INVALID_PARAMETER; memset(bootstrapOut, 0, sizeof(*bootstrapOut)); size_t size = 0; KSTATUS status = NtlGetProcessStartupData(bootstrapOut, sizeof(*bootstrapOut), &size); if (status == NTLSTATUS_NOT_FOUND) return NTLSTATUS_SUCCESS; if (!KSUCCESS(status)) return status; if (size != sizeof(*bootstrapOut) || bootstrapOut->Magic != NTL_POSIX_BOOTSTRAP_MAGIC || bootstrapOut->Version != NTL_POSIX_BOOTSTRAP_VERSION || bootstrapOut->Size != sizeof(*bootstrapOut) || bootstrapOut->Reserved != 0 || (bootstrapOut->InitialSignalMask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0 || (bootstrapOut->IgnoredSignals & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0) return NTLSTATUS_INVALID_PARAMETER; bootstrapOut->InitialSignalMask &= ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK; bootstrapOut->IgnoredSignals &= ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK; return NTLSTATUS_SUCCESS; } // Registers the current native process with posixsrv and caches its stable logical identity. static KSTATUS PosixRuntimeRegisterProcess(POSIX_PROCESS_STATE* process, const NTL_POSIX_BOOTSTRAP_DATA* bootstrap) { if (!process || !bootstrap) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = NTLSTATUS_SUCCESS; SYS_HANDLE endpoint = SYS_HANDLE_INVALID; status = PosixRuntimeConnectServer(&endpoint); if (!KSUCCESS(status)) return status; NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Version = NTL_POSIX_PROTOCOL_VERSION; request.Operation = NTL_POSIX_OP_REGISTER_PROCESS; request.Flags = NTL_POSIX_REQUEST_FLAG_NONE; request.TransactionKind = NTL_POSIX_TRANSACTION_NONE; request.BootstrapToken = bootstrap->Token; request.BootstrapPosixPid = bootstrap->PosixPid; request.BootstrapGeneration = bootstrap->Generation; size_t currentDirectorySize = 0; status = NtlGetCurrentDirectory(request.Path, sizeof(request.Path), ¤tDirectorySize); if (!KSUCCESS(status) || currentDirectorySize == 0 || currentDirectorySize > sizeof(request.Path)) { (void)SysDisconnectPort(endpoint); (void)SysCloseHandle(endpoint); return KSUCCESS(status) ? NTLSTATUS_FAILURE : status; } request.PathLength = (uint32_t)strlen(request.Path); NTL_POSIX_REPLY reply; status = PosixRuntimeCallEndpoint(endpoint, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, (SYS_HANDLE*)0, 0, 0, (uint32_t*)0, &reply); if (!KSUCCESS(status)) { (void)SysDisconnectPort(endpoint); (void)SysCloseHandle(endpoint); return status; } if (!KSUCCESS(reply.Status)) { (void)SysDisconnectPort(endpoint); (void)SysCloseHandle(endpoint); return reply.Status; } if (reply.ClientProcessId != SysGetCurrentPid() || reply.PosixPid == 0 || reply.PosixPid > 0x7FFFFFFFu || reply.NativeGeneration == 0 || (reply.ProcessState != NTL_POSIX_PROCESS_STATE_RUNNING && reply.ProcessState != NTL_POSIX_PROCESS_STATE_CONSTRUCTING && reply.ProcessState != NTL_POSIX_PROCESS_STATE_EXEC_PREPARING && reply.ProcessState != NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING)) { (void)SysDisconnectPort(endpoint); (void)SysCloseHandle(endpoint); return NTLSTATUS_FAILURE; } process->ServerEndpoint = endpoint; process->DefaultIdentity.PosixPid = reply.PosixPid; process->DefaultIdentity.Generation = reply.NativeGeneration; return NTLSTATUS_SUCCESS; } // Initializes process-local POSIX state, registers stable identity, and attaches the initial thread. KSTATUS PosixRuntimeInitializeProcess(void) { if (gPosixProcessState) return PosixRuntimeAttachThread(); SYS_PEB* peb = SysGetPeb(); if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION) return NTLSTATUS_INVALID_PARAMETER; POSIX_PROCESS_STATE* process = (POSIX_PROCESS_STATE*)calloc(1, sizeof(*process)); if (!process) return NTLSTATUS_NO_MEMORY; process->Magic = POSIX_PROCESS_STATE_MAGIC; process->Peb = peb; process->ServerEndpoint = SYS_HANDLE_INVALID; process->DefaultIdentity.FdLock = SYS_HANDLE_INVALID; gPosixProcessState = process; NTL_POSIX_BOOTSTRAP_DATA bootstrap; KSTATUS status = PosixRuntimeReadBootstrap(&bootstrap); if (status != NTLSTATUS_SUCCESS) { gPosixProcessState = (POSIX_PROCESS_STATE*)0; free(process); return status; } status = PosixRuntimeRegisterProcess(process, &bootstrap); if (status != NTLSTATUS_SUCCESS) { PosixRuntimeReportStartupFailure("register-process", status); gPosixProcessState = (POSIX_PROCESS_STATE*)0; free(process); return status; } process->BootstrapToken = bootstrap.Token; process->BootstrapPosixPid = bootstrap.PosixPid; process->BootstrapGeneration = bootstrap.Generation; status = PosixRuntimeInitializeVectors(process); if (status != NTLSTATUS_SUCCESS) { PosixRuntimeReportStartupFailure("vectors", status); (void)SysDisconnectPort(process->ServerEndpoint); (void)SysCloseHandle(process->ServerEndpoint); process->ServerEndpoint = SYS_HANDLE_INVALID; gPosixProcessState = (POSIX_PROCESS_STATE*)0; free(process); return status; } for (int signalNumber = NTL_POSIX_SIGNAL_HUP; signalNumber <= NTL_POSIX_SIGNAL_MAX; ++signalNumber) { uint64_t bit = NTL_POSIX_SIGNAL_BIT(signalNumber); if (bootstrap.IgnoredSignals & bit) process->DefaultIdentity.SignalActions[signalNumber].Handler = (uintptr_t)SIG_IGN; } status = PosixFdInitializeLogicalProcess(&process->DefaultIdentity, bootstrap.FdInheritanceToken); if (status != NTLSTATUS_SUCCESS) { PosixRuntimeReportStartupFailure("fd-init", status); PosixRuntimeDestroyVectors(process); (void)SysDisconnectPort(process->ServerEndpoint); (void)SysCloseHandle(process->ServerEndpoint); process->ServerEndpoint = SYS_HANDLE_INVALID; gPosixProcessState = (POSIX_PROCESS_STATE*)0; free(process); return status; } status = PosixRuntimeAttachThreadWithSignalMask(bootstrap.InitialSignalMask); if (status != NTLSTATUS_SUCCESS) { PosixRuntimeReportStartupFailure("thread-attach", status); PosixFdDestroyLogicalProcess(&process->DefaultIdentity); PosixRuntimeDestroyVectors(process); (void)SysDisconnectPort(process->ServerEndpoint); (void)SysCloseHandle(process->ServerEndpoint); process->ServerEndpoint = SYS_HANDLE_INVALID; gPosixProcessState = (POSIX_PROCESS_STATE*)0; free(process); return status; } return NTLSTATUS_SUCCESS; } // Returns the POSIX state for the current native process after startup. POSIX_PROCESS_STATE* PosixRuntimeProcessState(void) { POSIX_PROCESS_STATE* process = gPosixProcessState; if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC) return (POSIX_PROCESS_STATE*)0; return process; } // Registers the current native thread capability with posixsrv. static KSTATUS PosixRuntimeRegisterThread(POSIX_PROCESS_STATE* process, POSIX_THREAD_STATE* thread) { if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC || !thread) return NTLSTATUS_INVALID_PARAMETER; KSTATUS status = SysSetUserApcDispatcher((void*)SysUserApcDispatcher); if (!KSUCCESS(status)) return status; SYS_HANDLE selfThread = SYS_HANDLE_INVALID; status = SysOpenCurrentThread(SYS_RIGHT_QUERY | SYS_RIGHT_TRANSFER | SYS_THREAD_RIGHT_NOTIFY, &selfThread); if (!KSUCCESS(status)) return status; SYS_PORT_HANDLE_TRANSFER transfer; memset(&transfer, 0, sizeof(transfer)); transfer.Handle = selfThread; transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_THREAD_RIGHT_NOTIFY; transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE; NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Version = NTL_POSIX_PROTOCOL_VERSION; request.Operation = NTL_POSIX_OP_REGISTER_THREAD; request.Flags = NTL_POSIX_REQUEST_FLAG_NONE; request.SignalMask = thread->SignalBlocked; request.SignalApcRoutine = (uint64_t)(uintptr_t)PosixSignalApc; NTL_POSIX_REPLY reply; status = PosixRuntimeCallServerWithHandles(&request, &transfer, 1u, &reply); (void)SysCloseHandle(selfThread); if (!KSUCCESS(status)) return status; return reply.Status; } // Removes the current native thread from posixsrv's target registry. static void PosixRuntimeUnregisterThread(POSIX_THREAD_STATE* thread) { if (!thread || !thread->ServerRegistered || !thread->NativeProcess || thread->NativeProcess->Magic != POSIX_PROCESS_STATE_MAGIC) return; NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Version = NTL_POSIX_PROTOCOL_VERSION; request.Operation = NTL_POSIX_OP_UNREGISTER_THREAD; request.Flags = NTL_POSIX_REQUEST_FLAG_NONE; NTL_POSIX_REPLY reply; (void)PosixRuntimeCallServer(&request, &reply); thread->ServerRegistered = 0; } // Attaches the native thread with one inherited persistent POSIX signal mask. KSTATUS PosixRuntimeAttachThreadWithSignalMask(uint64_t signalMask) { SYS_TEB* teb = SysGetTeb(); POSIX_PROCESS_STATE* process = PosixRuntimeProcessState(); if (!teb || teb->Magic != SYS_TEB_MAGIC || !process) return NTLSTATUS_INVALID_PARAMETER; if (teb->SubsystemData) { POSIX_THREAD_STATE* existing = (POSIX_THREAD_STATE*)teb->SubsystemData; return existing->Magic == POSIX_THREAD_STATE_MAGIC && existing->NativeProcess == process && existing->LogicalProcess ? NTLSTATUS_SUCCESS : NTLSTATUS_ACCESS_DENIED; } POSIX_THREAD_STATE* thread = (POSIX_THREAD_STATE*)calloc(1, sizeof(*thread)); if (!thread) return NTLSTATUS_NO_MEMORY; thread->Magic = POSIX_THREAD_STATE_MAGIC; thread->NativeProcess = process; thread->LogicalProcess = &process->DefaultIdentity; thread->NativeThreadId = SysGetCurrentTid(); thread->SignalBlocked = signalMask & NTL_POSIX_SIGNAL_VALID_MASK & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK; if (thread->NativeThreadId == 0) { free(thread); return NTLSTATUS_FAILURE; } KSTATUS status = PosixRuntimeRegisterThread(process, thread); if (!KSUCCESS(status)) { thread->Magic = 0; free(thread); return status; } thread->ServerRegistered = 1u; teb->SubsystemData = thread; return NTLSTATUS_SUCCESS; } // Attaches a native thread with the default empty signal mask. KSTATUS PosixRuntimeAttachThread(void) { return PosixRuntimeAttachThreadWithSignalMask(0); } // Returns the current thread state, attaching lazily when necessary. POSIX_THREAD_STATE* PosixRuntimeThreadState(void) { if (!PosixRuntimeProcessState()) { if (PosixRuntimeInitializeProcess() != NTLSTATUS_SUCCESS) return (POSIX_THREAD_STATE*)0; } SYS_TEB* teb = SysGetTeb(); if (!teb || teb->Magic != SYS_TEB_MAGIC) return (POSIX_THREAD_STATE*)0; if (!teb->SubsystemData && PosixRuntimeAttachThread() != NTLSTATUS_SUCCESS) return (POSIX_THREAD_STATE*)0; POSIX_THREAD_STATE* thread = (POSIX_THREAD_STATE*)teb->SubsystemData; if (!thread || thread->Magic != POSIX_THREAD_STATE_MAGIC || thread->NativeProcess != PosixRuntimeProcessState() || !thread->LogicalProcess) return (POSIX_THREAD_STATE*)0; return thread; } // Returns the logical POSIX process selected by the current native thread. POSIX_LOGICAL_PROCESS_STATE* PosixRuntimeLogicalProcessState(void) { POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); return thread ? thread->LogicalProcess : (POSIX_LOGICAL_PROCESS_STATE*)0; } // Releases POSIX state owned by the current native TEB. void PosixRuntimeDetachThread(void) { SYS_TEB* teb = SysGetTeb(); if (!teb || teb->Magic != SYS_TEB_MAGIC || !teb->SubsystemData) return; POSIX_THREAD_STATE* thread = (POSIX_THREAD_STATE*)teb->SubsystemData; if (thread->Magic != POSIX_THREAD_STATE_MAGIC || thread->NativeProcess != PosixRuntimeProcessState()) return; PosixRuntimeUnregisterThread(thread); teb->SubsystemData = (void*)0; thread->Magic = 0; free(thread); } // Returns the lvalue backing the current thread's errno value. NTL_POSIX_API int* __ntl_posix_errno_location(void) { POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) return &gEmergencyErrno; return &thread->ErrnoValue; } // Clears the current thread's dynamic-loader error state. void PosixRuntimeClearDlError(void) { POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) return; thread->DlError[0] = '\0'; thread->DlErrorPending = 0; } // Stores one dynamic-loader error in current-thread state. void PosixRuntimeSetDlError(const char* message) { POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) return; if (!message) message = "dynamic loader error"; strncpy(thread->DlError, message, sizeof(thread->DlError) - 1u); thread->DlError[sizeof(thread->DlError) - 1u] = '\0'; thread->DlErrorPending = 1; } // Returns and consumes the current thread's dynamic-loader error. char* PosixRuntimeTakeDlError(void) { POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread || !thread->DlErrorPending || !thread->DlError[0]) return (char*)0; thread->DlErrorPending = 0; return thread->DlError; } // Commits a server-built child only after every current personality bootstrap step succeeds. static KSTATUS PosixRuntimeReportBootstrapReady(POSIX_PROCESS_STATE* process) { if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC) return NTLSTATUS_INVALID_PARAMETER; if (process->BootstrapToken == 0) return NTLSTATUS_SUCCESS; NTL_POSIX_REQUEST ready; memset(&ready, 0, sizeof(ready)); ready.Size = sizeof(ready); ready.Version = NTL_POSIX_PROTOCOL_VERSION; ready.Operation = NTL_POSIX_OP_BOOTSTRAP_READY; ready.BootstrapToken = process->BootstrapToken; ready.BootstrapPosixPid = process->BootstrapPosixPid; ready.BootstrapGeneration = process->BootstrapGeneration; NTL_POSIX_REPLY reply; KSTATUS status = PosixRuntimeCallServer(&ready, &reply); if (!KSUCCESS(status)) return status; if (!KSUCCESS(reply.Status)) return reply.Status; process->BootstrapToken = 0; process->BootstrapPosixPid = 0; process->BootstrapGeneration = 0; return NTLSTATUS_SUCCESS; } // Initializes all POSIX process state required before application main. NTL_POSIX_API uint32_t __ntl_posix_process_start(void) { KSTATUS status = PosixRuntimeInitializeProcess(); if (status != NTLSTATUS_SUCCESS) return status; POSIX_PROCESS_STATE* process = PosixRuntimeProcessState(); status = PosixSignalInitializeExceptionTranslation(process); if (status != NTLSTATUS_SUCCESS) { PosixRuntimeReportStartupFailure("signal-init", status); return status; } status = PosixRuntimeReportBootstrapReady(process); if (status != NTLSTATUS_SUCCESS) { PosixRuntimeReportStartupFailure("bootstrap-ready", status); return status; } return NTLSTATUS_SUCCESS; } // Returns process-local vectors to the POSIX startup shim after initialization. NTL_POSIX_API uint32_t __ntl_posix_process_vectors(int* argcOut, char*** argvOut, char*** envpOut) { POSIX_PROCESS_STATE* process = PosixRuntimeProcessState(); if (!process || !argcOut || !argvOut || !envpOut) return NTLSTATUS_INVALID_PARAMETER; *argcOut = process->ArgumentCount; *argvOut = process->Arguments; *envpOut = process->Environment; return NTLSTATUS_SUCCESS; } // Finds one process-local environment entry by exact variable name. NTL_POSIX_API char* getenv(const char* name) { if (!name || !name[0] || strchr(name, '=')) return (char*)0; POSIX_PROCESS_STATE* process = PosixRuntimeProcessState(); if (!process || !process->Environment) return (char*)0; size_t nameLength = strlen(name); for (char** entry = process->Environment; *entry; ++entry) { if (strncmp(*entry, name, nameLength) == 0 && (*entry)[nameLength] == '=') return *entry + nameLength + 1u; } return (char*)0; }