439 lines
18 KiB
C
439 lines
18 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;
|
|
|
|
// 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);
|
|
}
|
|
|
|
// 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)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
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;
|
|
|
|
SYS_HANDLE selfProcess = SYS_HANDLE_INVALID;
|
|
status = SysOpenProcess(SysGetCurrentPid(),
|
|
SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_RIGHT_TRANSFER,
|
|
&selfProcess);
|
|
if (!KSUCCESS(status)) {
|
|
(void)SysDisconnectPort(endpoint);
|
|
(void)SysCloseHandle(endpoint);
|
|
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);
|
|
(void)SysCloseHandle(selfProcess);
|
|
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
}
|
|
request.PathLength = (uint32_t)strlen(request.Path);
|
|
|
|
SYS_PORT_HANDLE_TRANSFER transfer;
|
|
memset(&transfer, 0, sizeof(transfer));
|
|
transfer.Handle = selfProcess;
|
|
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT;
|
|
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
|
|
|
|
NTL_POSIX_REPLY reply;
|
|
status = PosixRuntimeCallEndpoint(endpoint, &request, &transfer, 1u,
|
|
(SYS_HANDLE*)0, 0, 0, (uint32_t*)0, &reply);
|
|
(void)SysCloseHandle(selfProcess);
|
|
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) {
|
|
(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.
|
|
uint32_t 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->FdLock = SYS_HANDLE_INVALID;
|
|
gPosixProcessState = process;
|
|
|
|
NTL_POSIX_BOOTSTRAP_DATA bootstrap;
|
|
uint32_t status = PosixRuntimeReadBootstrap(&bootstrap);
|
|
if (status != NTLSTATUS_SUCCESS) {
|
|
gPosixProcessState = (POSIX_PROCESS_STATE*)0;
|
|
free(process);
|
|
return status;
|
|
}
|
|
|
|
status = PosixRuntimeRegisterProcess(process, &bootstrap);
|
|
if (status != NTLSTATUS_SUCCESS) {
|
|
gPosixProcessState = (POSIX_PROCESS_STATE*)0;
|
|
free(process);
|
|
return status;
|
|
}
|
|
|
|
status = PosixFdInitializeProcess(process, bootstrap.FdInheritanceToken);
|
|
if (status != NTLSTATUS_SUCCESS) {
|
|
(void)SysDisconnectPort(process->ServerEndpoint);
|
|
(void)SysCloseHandle(process->ServerEndpoint);
|
|
process->ServerEndpoint = SYS_HANDLE_INVALID;
|
|
gPosixProcessState = (POSIX_PROCESS_STATE*)0;
|
|
free(process);
|
|
return status;
|
|
}
|
|
|
|
status = PosixRuntimeAttachThread();
|
|
if (status != NTLSTATUS_SUCCESS) {
|
|
PosixFdDestroyProcess(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;
|
|
}
|
|
|
|
// Attaches the native thread to the process's default logical POSIX identity.
|
|
uint32_t PosixRuntimeAttachThread(void) {
|
|
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;
|
|
teb->SubsystemData = thread;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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;
|
|
|
|
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;
|
|
}
|
|
|
|
// Initializes all POSIX process state required before application main.
|
|
NTL_POSIX_API uint32_t __ntl_posix_process_start(void) {
|
|
uint32_t status = PosixRuntimeInitializeProcess();
|
|
if (status != NTLSTATUS_SUCCESS)
|
|
return status;
|
|
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|