808 lines
34 KiB
C
808 lines
34 KiB
C
// POSIX personality process/thread runtime state backed by the native TEB extension slot.
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ntlite/posix_service.h>
|
|
#include <ntlite/process_loader.h>
|
|
#include <ntlite/path.h>
|
|
#include <ntlite/resource.h>
|
|
#include <ntlite/status.h>
|
|
#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;
|
|
}
|