Files

5188 lines
240 KiB
C

// POSIX personality-server bootstrap, stable process identity, shared attributes, and transaction plumbing.
#include <ntlite/atomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <ntlite/native_heap.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/exception.h>
#include <ntlite/path.h>
#include <ntlite/posix_service.h>
#include <ntlite/process_loader.h>
#include <ntlite/resource.h>
#include <ntlite/security.h>
#include <ntlite/service_control.h>
#include <ntlite/syscall.h>
#define POSIXSRV_FIRST_PID 0x00010000u
#define POSIXSRV_LAST_PID 0x7FFFFFFFu
typedef struct POSIXSRV_CONSTRUCTION_STRING {
char* Data;
uint32_t Length;
uint32_t Capacity;
bool Complete;
} POSIXSRV_CONSTRUCTION_STRING;
typedef struct POSIXSRV_SPAWN_ACTION {
uint32_t Kind;
int32_t FileDescriptor;
int32_t NewFileDescriptor;
uint32_t OpenFlags;
uint32_t OpenMode;
char* Path;
} POSIXSRV_SPAWN_ACTION;
typedef struct POSIXSRV_CONSTRUCTION {
char* Executable;
POSIXSRV_CONSTRUCTION_STRING* Arguments;
uint32_t ArgumentCount;
uint32_t ArgumentCapacity;
POSIXSRV_CONSTRUCTION_STRING* Environment;
uint32_t EnvironmentCount;
uint32_t EnvironmentCapacity;
uint32_t TotalDataBytes;
} POSIXSRV_CONSTRUCTION;
typedef struct POSIXSRV_SPAWN_POLICY {
POSIXSRV_SPAWN_ACTION Actions[NTL_POSIX_SPAWN_ACTION_MAX];
uint32_t ActionCount;
uint32_t AttributeFlags;
uint32_t ProcessGroupId;
uint64_t SignalMask;
uint64_t SignalDefaultMask;
} POSIXSRV_SPAWN_POLICY;
typedef struct POSIXSRV_TRANSACTION {
uint32_t Id;
uint32_t Kind;
POSIXSRV_CONSTRUCTION* Construction;
POSIXSRV_SPAWN_POLICY* Spawn;
} POSIXSRV_TRANSACTION;
typedef struct POSIXSRV_OPEN_DESCRIPTION POSIXSRV_OPEN_DESCRIPTION;
typedef struct POSIXSRV_PROCESS_RECORD POSIXSRV_PROCESS_RECORD;
typedef struct POSIXSRV_FD_INHERITANCE POSIXSRV_FD_INHERITANCE;
typedef struct POSIXSRV_THREAD_RECORD POSIXSRV_THREAD_RECORD;
typedef struct POSIXSRV_PROCESS_MONITOR_CONTEXT POSIXSRV_PROCESS_MONITOR_CONTEXT;
typedef struct POSIXSRV_BORROWED_MONITOR_CONTEXT POSIXSRV_BORROWED_MONITOR_CONTEXT;
typedef struct POSIXSRV_WAITER {
struct POSIXSRV_WAITER* Next;
uint32_t ParentPosixPid;
uint32_t ParentProcessGroupId;
int32_t WaitPid;
SYS_HANDLE Event;
} POSIXSRV_WAITER;
typedef struct POSIXSRV_PIPE_BACKING {
SYS_PIPE_SHARED_STATE* Shared;
uint32_t DescriptionCount;
} POSIXSRV_PIPE_BACKING;
typedef struct POSIXSRV_OPEN_HOLDER {
struct POSIXSRV_OPEN_HOLDER* Next;
POSIXSRV_OPEN_DESCRIPTION* Description;
} POSIXSRV_OPEN_HOLDER;
typedef struct POSIXSRV_EXEC_HOLDER_PLAN {
POSIXSRV_OPEN_HOLDER* Holders[NTL_POSIX_FD_INHERIT_MAX];
uint32_t Count;
} POSIXSRV_EXEC_HOLDER_PLAN;
#define POSIXSRV_VFORK_TERMINAL_NONE 0u
#define POSIXSRV_VFORK_TERMINAL_EXIT 1u
#define POSIXSRV_VFORK_TERMINAL_EXEC 2u
typedef struct POSIXSRV_FD_INHERITANCE_ENTRY {
NTL_POSIX_FD_MANIFEST_ENTRY Manifest;
POSIXSRV_OPEN_DESCRIPTION* Description;
} POSIXSRV_FD_INHERITANCE_ENTRY;
struct POSIXSRV_FD_INHERITANCE {
POSIXSRV_FD_INHERITANCE* Next;
uint64_t Token;
POSIXSRV_PROCESS_RECORD* SourceProcess;
POSIXSRV_PROCESS_RECORD* ClaimProcess;
uint32_t SecurityId;
uint32_t EntryCount;
uint32_t DescriptionCount;
POSIXSRV_FD_INHERITANCE_ENTRY Entries[NTL_POSIX_FD_INHERIT_MAX];
POSIXSRV_OPEN_DESCRIPTION* Descriptions[NTL_POSIX_FD_INHERIT_MAX];
};
struct POSIXSRV_THREAD_RECORD {
POSIXSRV_THREAD_RECORD* Next;
uint32_t NativeThreadId;
uint32_t Generation;
SYS_HANDLE ThreadHandle;
uint64_t BlockedSignals;
uint64_t SignalApcRoutine;
};
struct POSIXSRV_OPEN_DESCRIPTION {
POSIXSRV_OPEN_DESCRIPTION* Next;
uint64_t DescriptionId;
uint32_t Type;
uint32_t HolderCount;
uint32_t InheritanceRefCount;
SYS_HANDLE MasterHandle;
NTL_POSIX_SHARED_OPEN_DESCRIPTION* Shared;
POSIXSRV_PIPE_BACKING* PipeBacking;
};
struct POSIXSRV_PROCESS_RECORD {
struct POSIXSRV_PROCESS_RECORD* Next;
uint32_t PosixPid;
uint32_t ParentPosixPid;
uint32_t NativeProcessId;
uint32_t SecurityId;
uint32_t Generation;
uint32_t State;
uint32_t ConnectionCount;
bool MonitorActive;
POSIXSRV_PROCESS_RECORD* BorrowedHostProcess;
uint32_t BorrowedHostGeneration;
uint32_t BorrowedNativeThreadId;
uint64_t BorrowedToken;
bool BorrowedMonitorActive;
uint32_t BorrowedTerminalKind;
int32_t BorrowedExitCode;
POSIXSRV_EXEC_HOLDER_PLAN* PendingExecHolderPlan;
bool AlarmMonitorActive;
uint64_t BootstrapToken;
SYS_HANDLE BootstrapReadyEvent;
uint32_t PendingNativeProcessId;
uint32_t PendingGeneration;
uint64_t PendingBootstrapToken;
SYS_HANDLE PendingBootstrapReadyEvent;
SYS_HANDLE PendingBootstrapReleaseEvent;
bool PendingBootstrapReadyObserved;
SYS_HANDLE PendingProcessHandle;
uint64_t PendingFdInheritanceToken;
uint32_t UserId;
uint32_t EffectiveUserId;
uint32_t GroupId;
uint32_t EffectiveGroupId;
uint32_t ProcessGroupId;
uint32_t SessionId;
uint32_t SupplementaryGroupCount;
uint32_t SupplementaryGroups[NTL_POSIX_SUPPLEMENTARY_GROUP_MAX];
char LoginName[NTL_POSIX_IDENTITY_NAME_MAX + 1u];
char CurrentDirectory[SYS_PATH_MAX + 1u];
int32_t ExitCode;
uint64_t ProcessPendingSignals;
uint64_t IgnoredSignals;
uint64_t ExplicitIgnoredSignals;
SYS_HANDLE AlarmTimer;
SYS_HANDLE AlarmControlEvent;
uint64_t AlarmGeneration;
uint64_t AlarmDeadlineTicks;
SYS_HANDLE ProcessHandle;
POSIXSRV_OPEN_HOLDER* OpenDescriptions;
POSIXSRV_THREAD_RECORD* Threads;
uint32_t NextTransactionId;
uint32_t ActiveTransactions;
POSIXSRV_TRANSACTION Transactions[NTL_POSIX_TRANSACTION_CAPACITY];
};
typedef struct POSIXSRV_USER_RECORD {
const char* Name;
uint32_t UserId;
uint32_t GroupId;
const char* Home;
const char* Shell;
} POSIXSRV_USER_RECORD;
typedef struct POSIXSRV_GROUP_RECORD {
const char* Name;
uint32_t GroupId;
} POSIXSRV_GROUP_RECORD;
typedef struct POSIXSRV_CLIENT {
SYS_HANDLE Endpoint;
SYS_PORT_CONNECTION_INFO Connection;
POSIXSRV_PROCESS_RECORD* Process;
uint32_t NativeGeneration;
} POSIXSRV_CLIENT;
struct POSIXSRV_PROCESS_MONITOR_CONTEXT {
POSIXSRV_PROCESS_RECORD* Process;
SYS_HANDLE ProcessHandle;
uint32_t Generation;
};
struct POSIXSRV_BORROWED_MONITOR_CONTEXT {
POSIXSRV_PROCESS_RECORD* Process;
SYS_HANDLE ThreadHandle;
uint64_t BorrowedToken;
};
static SYS_HANDLE gProcessRegistryMutex = SYS_HANDLE_INVALID;
static POSIXSRV_PROCESS_RECORD* gProcessRegistry;
static POSIXSRV_OPEN_DESCRIPTION* gOpenDescriptionRegistry;
static POSIXSRV_FD_INHERITANCE* gFdInheritanceRegistry;
static POSIXSRV_WAITER* gWaiters;
static uint32_t gNextPosixPid = POSIXSRV_FIRST_PID;
static uint64_t gNextOpenDescriptionId = 1u;
static uint64_t gNextFdInheritanceToken = 1u;
static uint64_t gNextBootstrapToken = 1u;
static uint64_t gNextVforkToken = 1u;
static uint32_t gRegisteredProcesses;
static uint32_t gRegisteredOpenDescriptions;
static uint32_t gRegisteredThreads;
static void PosixsrvDestroyTransaction(POSIXSRV_TRANSACTION* transaction);
static void PosixsrvFreeProcessRecord(POSIXSRV_PROCESS_RECORD* record);
// Returns whether the stable POSIX process still has a committed native generation.
static bool PosixsrvProcessHasCommittedGeneration(const POSIXSRV_PROCESS_RECORD* process) {
return process && (process->State == NTL_POSIX_PROCESS_STATE_RUNNING ||
process->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING ||
process->State == NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING);
}
// Returns whether ordinary operations may still run in the committed old generation.
static bool PosixsrvCommittedGenerationOperational(const POSIXSRV_CLIENT* client) {
return client && client->Process && client->NativeGeneration == client->Process->Generation &&
(client->Process->State == NTL_POSIX_PROCESS_STATE_RUNNING ||
client->Process->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING);
}
// Returns whether this connection belongs to the not-yet-committed replacement generation.
static bool PosixsrvClientIsPendingExecGeneration(const POSIXSRV_CLIENT* client) {
return client && client->Process && client->Process->PendingGeneration != 0 &&
client->NativeGeneration == client->Process->PendingGeneration &&
(client->Process->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING ||
client->Process->State == NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING);
}
// Returns whether this routed client represents one borrowed vfork logical process.
static bool PosixsrvClientIsBorrowedProcess(const POSIXSRV_CLIENT* client) {
return client && client->Process &&
client->Process->State == NTL_POSIX_PROCESS_STATE_VFORK_BORROWED &&
client->NativeGeneration == client->Process->Generation;
}
// Finds the borrowed logical child bound to one native sender thread.
static POSIXSRV_PROCESS_RECORD* PosixsrvFindBorrowedChildLocked(
POSIXSRV_PROCESS_RECORD* host, uint32_t hostGeneration, uint32_t nativeThreadId) {
if (!host || hostGeneration == 0 || nativeThreadId == 0)
return (POSIXSRV_PROCESS_RECORD*)0;
for (POSIXSRV_PROCESS_RECORD* record = gProcessRegistry; record; record = record->Next) {
if (record->State != NTL_POSIX_PROCESS_STATE_VFORK_BORROWED ||
record->BorrowedHostProcess != host ||
record->BorrowedHostGeneration != hostGeneration ||
record->BorrowedNativeThreadId != nativeThreadId || !record->Threads ||
record->Threads->NativeThreadId != nativeThreadId ||
record->Threads->ThreadHandle == SYS_HANDLE_INVALID)
continue;
if (SysWaitSingle(record->Threads->ThreadHandle, SYS_WAIT_FLAG_NONE, 0u) == NTLSTATUS_TIMEOUT)
return record;
}
return (POSIXSRV_PROCESS_RECORD*)0;
}
static const POSIXSRV_USER_RECORD gUsers[] = {
{ "root", 0u, 0u, "/", "/system/shell_posix.exe" },
{ "user", 1000u, 1000u, "/", "/system/shell_posix.exe" },
};
static const POSIXSRV_GROUP_RECORD gGroups[] = {
{ "root", 0u },
{ "users", 1000u },
};
// Terminates the server without depending on a compiler runtime return path.
static void PosixsrvExit(int code) {
for (;;) SysExitProcess(code);
}
// Gives one server thread a best-effort diagnostic name.
static void PosixsrvSetCurrentThreadName(const char* name) {
SYS_SET_THREAD_NAME_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.ThreadHandle = SYS_HANDLE_INVALID;
request.Name = SysSvFromCstr(name);
(void)SysSetThreadName(&request);
}
// Acquires the process-registry mutex.
static KSTATUS PosixsrvLockRegistry(void) {
if (gProcessRegistryMutex == SYS_HANDLE_INVALID) return NTLSTATUS_FAILURE;
return SysWait(gProcessRegistryMutex, SYS_WAIT_FLAG_NONE);
}
// Releases the process-registry mutex.
static void PosixsrvUnlockRegistry(void) {
if (gProcessRegistryMutex != SYS_HANDLE_INVALID)
(void)SysReleaseMutex(gProcessRegistryMutex);
}
// Finds the committed process generation represented by one native identity.
static POSIXSRV_PROCESS_RECORD* PosixsrvFindCommittedProcessLocked(uint32_t nativePid,
uint32_t securityId) {
for (POSIXSRV_PROCESS_RECORD* record = gProcessRegistry; record; record = record->Next) {
if (record->NativeProcessId != nativePid || record->SecurityId != securityId) continue;
if (record->State == NTL_POSIX_PROCESS_STATE_RUNNING ||
record->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING)
return record;
}
return (POSIXSRV_PROCESS_RECORD*)0;
}
// Finds one process by stable POSIX identity while the registry mutex is held.
static POSIXSRV_PROCESS_RECORD* PosixsrvFindPosixProcessLocked(uint32_t posixPid) {
for (POSIXSRV_PROCESS_RECORD* record = gProcessRegistry; record; record = record->Next) {
if (record->PosixPid == posixPid)
return record;
}
return (POSIXSRV_PROCESS_RECORD*)0;
}
// Returns whether a process group currently exists in one POSIX session.
static bool PosixsrvProcessGroupExistsLocked(uint32_t sessionId, uint32_t processGroupId) {
if (processGroupId == 0) return false;
for (POSIXSRV_PROCESS_RECORD* record = gProcessRegistry; record; record = record->Next) {
if (PosixsrvProcessHasCommittedGeneration(record) && record->SessionId == sessionId &&
record->ProcessGroupId == processGroupId)
return true;
}
return false;
}
// Returns whether one protocol open-description type requires a retained native handle.
static bool PosixsrvOpenDescriptionNeedsHandle(uint32_t type) {
return type == NTL_POSIX_OPEN_TYPE_FILE || type == NTL_POSIX_OPEN_TYPE_SOCKET ||
type == NTL_POSIX_OPEN_TYPE_PIPE_READ || type == NTL_POSIX_OPEN_TYPE_PIPE_WRITE;
}
// Returns whether one protocol open-description type is known.
static bool PosixsrvOpenDescriptionTypeValid(uint32_t type) {
return type >= NTL_POSIX_OPEN_TYPE_FILE && type <= NTL_POSIX_OPEN_TYPE_PIPE_WRITE;
}
// Finds one process holder for a shared open description while the registry lock is held.
static POSIXSRV_OPEN_HOLDER* PosixsrvFindOpenHolderLocked(POSIXSRV_PROCESS_RECORD* process,
uint64_t descriptionId) {
if (!process) return (POSIXSRV_OPEN_HOLDER*)0;
for (POSIXSRV_OPEN_HOLDER* holder = process->OpenDescriptions; holder; holder = holder->Next) {
if (holder->Description && holder->Description->DescriptionId == descriptionId)
return holder;
}
return (POSIXSRV_OPEN_HOLDER*)0;
}
// Removes and destroys one globally unheld open description while the registry lock is held.
static void PosixsrvDestroyOpenDescriptionLocked(POSIXSRV_OPEN_DESCRIPTION* description) {
if (!description || description->HolderCount != 0 || description->InheritanceRefCount != 0) return;
POSIXSRV_OPEN_DESCRIPTION** link = &gOpenDescriptionRegistry;
while (*link && *link != description) link = &(*link)->Next;
if (*link != description) return;
*link = description->Next;
description->Next = (POSIXSRV_OPEN_DESCRIPTION*)0;
if (gRegisteredOpenDescriptions != 0) --gRegisteredOpenDescriptions;
if (description->MasterHandle != SYS_HANDLE_INVALID)
(void)SysCloseHandle(description->MasterHandle);
if (description->PipeBacking) {
POSIXSRV_PIPE_BACKING* backing = description->PipeBacking;
description->PipeBacking = (POSIXSRV_PIPE_BACKING*)0;
if (backing->DescriptionCount != 0)
--backing->DescriptionCount;
if (backing->DescriptionCount == 0) {
if (backing->Shared)
(void)SysFreeVm(backing->Shared);
free(backing);
}
}
free(description->Shared);
free(description);
}
// Releases one process holder and destroys the description when this was its final holder.
static KSTATUS PosixsrvReleaseOpenDescriptionLocked(POSIXSRV_PROCESS_RECORD* process,
uint64_t descriptionId) {
if (!process || descriptionId == 0) return NTLSTATUS_INVALID_PARAMETER;
POSIXSRV_OPEN_HOLDER** link = &process->OpenDescriptions;
while (*link && (!(*link)->Description ||
(*link)->Description->DescriptionId != descriptionId))
link = &(*link)->Next;
if (!*link) return NTLSTATUS_NOT_FOUND;
POSIXSRV_OPEN_HOLDER* holder = *link;
POSIXSRV_OPEN_DESCRIPTION* description = holder->Description;
*link = holder->Next;
free(holder);
if (description && description->HolderCount != 0)
--description->HolderCount;
PosixsrvDestroyOpenDescriptionLocked(description);
return NTLSTATUS_SUCCESS;
}
// Releases every open-description holder owned by one terminating POSIX process.
static void PosixsrvReleaseAllOpenDescriptionsLocked(POSIXSRV_PROCESS_RECORD* process) {
if (!process) return;
while (process->OpenDescriptions) {
POSIXSRV_OPEN_HOLDER* holder = process->OpenDescriptions;
POSIXSRV_OPEN_DESCRIPTION* description = holder->Description;
process->OpenDescriptions = holder->Next;
free(holder);
if (description && description->HolderCount != 0)
--description->HolderCount;
PosixsrvDestroyOpenDescriptionLocked(description);
}
}
// Finds one pending descriptor-inheritance snapshot while the registry lock is held.
static POSIXSRV_FD_INHERITANCE* PosixsrvFindFdInheritanceLocked(uint64_t token) {
if (token == 0) return (POSIXSRV_FD_INHERITANCE*)0;
for (POSIXSRV_FD_INHERITANCE* snapshot = gFdInheritanceRegistry; snapshot;
snapshot = snapshot->Next) {
if (snapshot->Token == token) return snapshot;
}
return (POSIXSRV_FD_INHERITANCE*)0;
}
// Drops one snapshot's temporary description references and removes it from the registry.
static void PosixsrvDestroyFdInheritanceLocked(POSIXSRV_FD_INHERITANCE* snapshot) {
if (!snapshot) return;
POSIXSRV_FD_INHERITANCE** link = &gFdInheritanceRegistry;
while (*link && *link != snapshot) link = &(*link)->Next;
if (*link != snapshot) return;
*link = snapshot->Next;
snapshot->Next = (POSIXSRV_FD_INHERITANCE*)0;
for (uint32_t i = 0; i < snapshot->DescriptionCount; ++i) {
POSIXSRV_OPEN_DESCRIPTION* description = snapshot->Descriptions[i];
if (description && description->InheritanceRefCount != 0)
--description->InheritanceRefCount;
PosixsrvDestroyOpenDescriptionLocked(description);
}
free(snapshot);
}
// Cleans snapshots owned or claimed by one native process generation on genuine exit.
static void PosixsrvReleaseFdInheritanceForProcessLocked(POSIXSRV_PROCESS_RECORD* process) {
if (!process) return;
POSIXSRV_FD_INHERITANCE* snapshot = gFdInheritanceRegistry;
while (snapshot) {
POSIXSRV_FD_INHERITANCE* next = snapshot->Next;
if (snapshot->ClaimProcess == process ||
(snapshot->SourceProcess == process && !snapshot->ClaimProcess)) {
PosixsrvDestroyFdInheritanceLocked(snapshot);
} else if (snapshot->SourceProcess == process) {
snapshot->SourceProcess = (POSIXSRV_PROCESS_RECORD*)0;
}
snapshot = next;
}
}
// Returns the process-local rights needed by one imported native capability.
static SYS_RIGHTS PosixsrvOpenDescriptionClientRights(POSIXSRV_OPEN_DESCRIPTION* description) {
if (!description || !description->Shared) return 0;
uint32_t statusFlags = NtlAtomicLoadU32(&description->Shared->StatusFlags);
SYS_RIGHTS rights = SYS_RIGHT_QUERY;
if (description->Type == NTL_POSIX_OPEN_TYPE_FILE) {
rights |= SYS_FILE_RIGHT_LOCK;
uint32_t access = statusFlags & NTL_POSIX_OPEN_STATUS_ACCMODE;
if (access == NTL_POSIX_OPEN_STATUS_RDONLY || access == NTL_POSIX_OPEN_STATUS_RDWR)
rights |= SYS_FILE_RIGHT_READ_DATA | SYS_FILE_RIGHT_MAP_READ;
if (access == NTL_POSIX_OPEN_STATUS_WRONLY || access == NTL_POSIX_OPEN_STATUS_RDWR)
rights |= SYS_FILE_RIGHT_WRITE_DATA | SYS_FILE_RIGHT_MAP_WRITE;
} else if (description->Type == NTL_POSIX_OPEN_TYPE_SOCKET) {
rights |= SYS_SOCKET_RIGHT_BIND | SYS_SOCKET_RIGHT_CONNECT | SYS_SOCKET_RIGHT_LISTEN |
SYS_SOCKET_RIGHT_ACCEPT | SYS_SOCKET_RIGHT_SEND | SYS_SOCKET_RIGHT_RECEIVE |
SYS_SOCKET_RIGHT_SHUTDOWN | SYS_SOCKET_RIGHT_POLL |
SYS_SOCKET_RIGHT_SET_OPTION;
} else if (description->Type == NTL_POSIX_OPEN_TYPE_PIPE_READ) {
rights |= SYS_RIGHT_WAIT | SYS_PIPE_RIGHT_READ | SYS_PIPE_RIGHT_POLL;
} else if (description->Type == NTL_POSIX_OPEN_TYPE_PIPE_WRITE) {
rights |= SYS_RIGHT_WAIT | SYS_PIPE_RIGHT_WRITE | SYS_PIPE_RIGHT_POLL;
}
return rights;
}
// Creates a one-shot server snapshot of the caller's process-local FD table manifest.
static KSTATUS PosixsrvCreateFdInheritance(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request,
uint64_t* tokenOut) {
if (!client || !client->Process || !request || !tokenOut)
return NTLSTATUS_INVALID_PARAMETER;
*tokenOut = 0;
if (request->FdInheritanceFlags != NTL_POSIX_FD_INHERIT_FLAG_NONE ||
request->FdInheritanceIndex != 0 || request->FdInheritanceToken != 0 ||
request->FdInheritanceCount > NTL_POSIX_FD_INHERIT_MAX)
return NTLSTATUS_INVALID_PARAMETER;
POSIXSRV_FD_INHERITANCE* snapshot =
(POSIXSRV_FD_INHERITANCE*)calloc(1, sizeof(*snapshot));
if (!snapshot) return NTLSTATUS_NO_MEMORY;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) { free(snapshot); return status; }
if (!PosixsrvCommittedGenerationOperational(client) && !PosixsrvClientIsBorrowedProcess(client)) {
PosixsrvUnlockRegistry(); free(snapshot); return NTLSTATUS_OBJECT_DEAD;
}
if (gNextFdInheritanceToken == 0) {
PosixsrvUnlockRegistry(); free(snapshot); return NTLSTATUS_ID_EXHAUSTED;
}
snapshot->Token = gNextFdInheritanceToken++;
snapshot->SourceProcess = client->Process;
snapshot->SecurityId = client->Process->SecurityId;
snapshot->EntryCount = request->FdInheritanceCount;
for (uint32_t i = 0; i < request->FdInheritanceCount; ++i) {
const NTL_POSIX_FD_MANIFEST_ENTRY* manifest = &request->FdManifest[i];
if (manifest->Descriptor < 0 || manifest->Descriptor >= (int32_t)NTL_POSIX_FD_INHERIT_MAX ||
(manifest->DescriptorFlags & ~NTL_POSIX_FD_DESCRIPTOR_CLOEXEC) != 0 ||
manifest->OpenDescriptionId == 0) {
status = NTLSTATUS_INVALID_PARAMETER;
break;
}
for (uint32_t j = 0; j < i; ++j) {
if (snapshot->Entries[j].Manifest.Descriptor == manifest->Descriptor) {
status = NTLSTATUS_INVALID_PARAMETER;
break;
}
}
if (!KSUCCESS(status)) break;
POSIXSRV_OPEN_HOLDER* holder =
PosixsrvFindOpenHolderLocked(client->Process, manifest->OpenDescriptionId);
if (!holder || !holder->Description) { status = NTLSTATUS_NOT_FOUND; break; }
snapshot->Entries[i].Manifest = *manifest;
snapshot->Entries[i].Description = holder->Description;
uint32_t found = 0;
for (uint32_t j = 0; j < snapshot->DescriptionCount; ++j) {
if (snapshot->Descriptions[j] == holder->Description) { found = 1; break; }
}
if (!found) snapshot->Descriptions[snapshot->DescriptionCount++] = holder->Description;
}
for (uint32_t i = request->FdInheritanceCount; i < NTL_POSIX_FD_INHERIT_MAX; ++i) {
if (request->FdManifest[i].Descriptor != 0 || request->FdManifest[i].DescriptorFlags != 0 ||
request->FdManifest[i].OpenDescriptionId != 0) {
status = NTLSTATUS_INVALID_PARAMETER;
break;
}
}
if (!KSUCCESS(status)) {
PosixsrvUnlockRegistry();
free(snapshot);
return status;
}
for (uint32_t i = 0; i < snapshot->DescriptionCount; ++i)
++snapshot->Descriptions[i]->InheritanceRefCount;
snapshot->Next = gFdInheritanceRegistry;
gFdInheritanceRegistry = snapshot;
*tokenOut = snapshot->Token;
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
// Cancels one unclaimed snapshot owned by the caller.
static KSTATUS PosixsrvCancelFdInheritance(POSIXSRV_CLIENT* client, uint64_t token) {
if (!client || !client->Process || token == 0) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_FD_INHERITANCE* snapshot = PosixsrvFindFdInheritanceLocked(token);
if (!snapshot) status = NTLSTATUS_NOT_FOUND;
else if (snapshot->SourceProcess != client->Process || snapshot->ClaimProcess)
status = NTLSTATUS_ACCESS_DENIED;
else { PosixsrvDestroyFdInheritanceLocked(snapshot); status = NTLSTATUS_SUCCESS; }
PosixsrvUnlockRegistry();
return status;
}
// Adds one process holder for every unique description before the child imports wrappers.
static KSTATUS PosixsrvClaimFdInheritanceLocked(POSIXSRV_FD_INHERITANCE* snapshot,
POSIXSRV_PROCESS_RECORD* process) {
if (!snapshot || !process) return NTLSTATUS_INVALID_PARAMETER;
if (snapshot->ClaimProcess)
return snapshot->ClaimProcess == process ? NTLSTATUS_SUCCESS : NTLSTATUS_ACCESS_DENIED;
if (process->OpenDescriptions)
return NTLSTATUS_ACCESS_DENIED;
POSIXSRV_OPEN_DESCRIPTION* added[NTL_POSIX_FD_INHERIT_MAX];
uint32_t addedCount = 0;
for (uint32_t i = 0; i < snapshot->DescriptionCount; ++i) {
POSIXSRV_OPEN_DESCRIPTION* description = snapshot->Descriptions[i];
if (PosixsrvFindOpenHolderLocked(process, description->DescriptionId)) continue;
POSIXSRV_OPEN_HOLDER* holder = (POSIXSRV_OPEN_HOLDER*)calloc(1, sizeof(*holder));
if (!holder) {
for (uint32_t j = 0; j < addedCount; ++j)
(void)PosixsrvReleaseOpenDescriptionLocked(process, added[j]->DescriptionId);
return NTLSTATUS_NO_MEMORY;
}
holder->Description = description;
holder->Next = process->OpenDescriptions;
process->OpenDescriptions = holder;
++description->HolderCount;
added[addedCount++] = description;
}
snapshot->ClaimProcess = process;
return NTLSTATUS_SUCCESS;
}
// Preallocates every holder node that exec may need after the point of no return.
static KSTATUS PosixsrvPrepareExecHolderPlanLocked(POSIXSRV_PROCESS_RECORD* process,
POSIXSRV_FD_INHERITANCE* snapshot,
POSIXSRV_EXEC_HOLDER_PLAN* plan) {
if (!process || !snapshot || !plan || snapshot->SourceProcess != process || snapshot->ClaimProcess)
return NTLSTATUS_INVALID_PARAMETER;
memset(plan, 0, sizeof(*plan));
for (uint32_t i = 0; i < snapshot->DescriptionCount; ++i) {
POSIXSRV_OPEN_DESCRIPTION* description = snapshot->Descriptions[i];
if (!description) return NTLSTATUS_OBJECT_DEAD;
if (PosixsrvFindOpenHolderLocked(process, description->DescriptionId)) continue;
POSIXSRV_OPEN_HOLDER* holder = (POSIXSRV_OPEN_HOLDER*)calloc(1, sizeof(*holder));
if (!holder) {
for (uint32_t j = 0; j < plan->Count; ++j) free(plan->Holders[j]);
memset(plan, 0, sizeof(*plan));
return NTLSTATUS_NO_MEMORY;
}
holder->Description = description;
plan->Holders[plan->Count++] = holder;
}
return NTLSTATUS_SUCCESS;
}
// Releases an uncommitted exec holder plan.
static void PosixsrvDestroyExecHolderPlan(POSIXSRV_EXEC_HOLDER_PLAN* plan) {
if (!plan) return;
for (uint32_t i = 0; i < plan->Count; ++i) free(plan->Holders[i]);
memset(plan, 0, sizeof(*plan));
}
// Applies a preallocated holder plan and drops every description excluded by the exec snapshot.
static void PosixsrvApplyExecHolderPlanLocked(POSIXSRV_PROCESS_RECORD* process,
POSIXSRV_FD_INHERITANCE* snapshot,
POSIXSRV_EXEC_HOLDER_PLAN* plan) {
if (!process || !snapshot || !plan) return;
for (uint32_t i = 0; i < plan->Count; ++i) {
POSIXSRV_OPEN_HOLDER* holder = plan->Holders[i];
if (!holder || !holder->Description) continue;
holder->Next = process->OpenDescriptions;
process->OpenDescriptions = holder;
++holder->Description->HolderCount;
plan->Holders[i] = (POSIXSRV_OPEN_HOLDER*)0;
}
plan->Count = 0;
POSIXSRV_OPEN_HOLDER** link = &process->OpenDescriptions;
while (*link) {
POSIXSRV_OPEN_HOLDER* holder = *link;
int retained = 0;
for (uint32_t i = 0; i < snapshot->DescriptionCount; ++i) {
if (snapshot->Descriptions[i] == holder->Description) { retained = 1; break; }
}
if (retained) {
link = &holder->Next;
continue;
}
POSIXSRV_OPEN_DESCRIPTION* description = holder->Description;
*link = holder->Next;
free(holder);
if (description && description->HolderCount != 0) --description->HolderCount;
PosixsrvDestroyOpenDescriptionLocked(description);
}
}
// Releases one holder owned by the calling POSIX process.
static KSTATUS PosixsrvReleaseOpenDescription(POSIXSRV_CLIENT* client, uint64_t descriptionId) {
if (!client || !client->Process || descriptionId == 0)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
if (PosixsrvClientIsPendingExecGeneration(client))
status = NTLSTATUS_SUCCESS;
else if (!PosixsrvCommittedGenerationOperational(client) && !PosixsrvClientIsBorrowedProcess(client))
status = NTLSTATUS_OBJECT_DEAD;
else
status = PosixsrvReleaseOpenDescriptionLocked(client->Process, descriptionId);
PosixsrvUnlockRegistry();
return status;
}
// Creates one server-owned shared open description and one holder for the calling process.
static KSTATUS PosixsrvCreateOpenDescription(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request,
SYS_HANDLE* handles,
uint32_t handleCount,
uint64_t* descriptionIdOut,
NTL_POSIX_SHARED_OPEN_DESCRIPTION** sharedOut) {
if (!client || !client->Process || !request || !descriptionIdOut || !sharedOut)
return NTLSTATUS_INVALID_PARAMETER;
*descriptionIdOut = 0;
*sharedOut = (NTL_POSIX_SHARED_OPEN_DESCRIPTION*)0;
uint32_t type = request->OpenDescriptionType;
uint32_t statusFlags = request->OpenStatusFlags;
if (!PosixsrvOpenDescriptionTypeValid(type) ||
(statusFlags & ~NTL_POSIX_OPEN_STATUS_VALID_MASK) != 0 ||
(statusFlags & NTL_POSIX_OPEN_STATUS_ACCMODE) == NTL_POSIX_OPEN_STATUS_ACCMODE)
return NTLSTATUS_INVALID_PARAMETER;
uint32_t expectedHandles = PosixsrvOpenDescriptionNeedsHandle(type) ? 1u : 0u;
if (handleCount != expectedHandles || (expectedHandles && !handles))
return NTLSTATUS_INVALID_PARAMETER;
POSIXSRV_OPEN_DESCRIPTION* description =
(POSIXSRV_OPEN_DESCRIPTION*)calloc(1, sizeof(*description));
POSIXSRV_OPEN_HOLDER* holder = (POSIXSRV_OPEN_HOLDER*)calloc(1, sizeof(*holder));
NTL_POSIX_SHARED_OPEN_DESCRIPTION* shared =
(NTL_POSIX_SHARED_OPEN_DESCRIPTION*)calloc(1, sizeof(*shared));
if (!description || !holder || !shared) {
free(description);
free(holder);
free(shared);
return NTLSTATUS_NO_MEMORY;
}
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) {
free(description);
free(holder);
free(shared);
return status;
}
if (!PosixsrvCommittedGenerationOperational(client)) {
PosixsrvUnlockRegistry();
free(description);
free(holder);
free(shared);
return NTLSTATUS_OBJECT_DEAD;
}
if (gNextOpenDescriptionId == 0) {
PosixsrvUnlockRegistry();
free(description);
free(holder);
free(shared);
return NTLSTATUS_ID_EXHAUSTED;
}
uint64_t id = gNextOpenDescriptionId++;
description->DescriptionId = id;
description->Type = type;
description->HolderCount = 1u;
description->MasterHandle = expectedHandles ? handles[0] : SYS_HANDLE_INVALID;
description->Shared = shared;
shared->Magic = NTL_POSIX_SHARED_OPEN_MAGIC;
shared->Version = NTL_POSIX_SHARED_OPEN_VERSION;
shared->Type = type;
shared->DescriptionId = id;
shared->StatusFlags = statusFlags;
holder->Description = description;
holder->Next = client->Process->OpenDescriptions;
client->Process->OpenDescriptions = holder;
description->Next = gOpenDescriptionRegistry;
gOpenDescriptionRegistry = description;
++gRegisteredOpenDescriptions;
if (expectedHandles) handles[0] = SYS_HANDLE_INVALID;
*descriptionIdOut = id;
*sharedOut = shared;
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
// Creates one server-owned pipe backing plus read/write shared open descriptions.
static KSTATUS PosixsrvCreatePipe(POSIXSRV_CLIENT* client,
uint64_t* readIdOut,
uint64_t* writeIdOut) {
if (!client || !client->Process || !readIdOut || !writeIdOut)
return NTLSTATUS_INVALID_PARAMETER;
*readIdOut = 0;
*writeIdOut = 0;
SYS_HANDLE readHandle = SYS_HANDLE_INVALID;
SYS_HANDLE writeHandle = SYS_HANDLE_INVALID;
SYS_PIPE_SHARED_STATE* pipeShared = (SYS_PIPE_SHARED_STATE*)0;
SYS_CREATE_PIPE_REQUEST pipeRequest;
memset(&pipeRequest, 0, sizeof(pipeRequest));
pipeRequest.Size = sizeof(pipeRequest);
pipeRequest.ReadHandleOut = &readHandle;
pipeRequest.WriteHandleOut = &writeHandle;
pipeRequest.SharedStateOut = &pipeShared;
KSTATUS status = SysCreatePipe(&pipeRequest);
if (!KSUCCESS(status)) return status;
if (!pipeShared || pipeShared->Magic != SYS_PIPE_SHARED_MAGIC ||
pipeShared->Version != SYS_PIPE_SHARED_VERSION ||
pipeShared->Capacity != SYS_PIPE_BUFFER_CAPACITY ||
pipeShared->HeaderBytes != SYS_PIPE_SHARED_HEADER_BYTES) {
(void)SysCloseHandle(readHandle);
(void)SysCloseHandle(writeHandle);
if (pipeShared) (void)SysFreeVm(pipeShared);
return NTLSTATUS_FAILURE;
}
POSIXSRV_PIPE_BACKING* backing =
(POSIXSRV_PIPE_BACKING*)calloc(1, sizeof(*backing));
POSIXSRV_OPEN_DESCRIPTION* readDescription =
(POSIXSRV_OPEN_DESCRIPTION*)calloc(1, sizeof(*readDescription));
POSIXSRV_OPEN_DESCRIPTION* writeDescription =
(POSIXSRV_OPEN_DESCRIPTION*)calloc(1, sizeof(*writeDescription));
POSIXSRV_OPEN_HOLDER* readHolder =
(POSIXSRV_OPEN_HOLDER*)calloc(1, sizeof(*readHolder));
POSIXSRV_OPEN_HOLDER* writeHolder =
(POSIXSRV_OPEN_HOLDER*)calloc(1, sizeof(*writeHolder));
NTL_POSIX_SHARED_OPEN_DESCRIPTION* readShared =
(NTL_POSIX_SHARED_OPEN_DESCRIPTION*)calloc(1, sizeof(*readShared));
NTL_POSIX_SHARED_OPEN_DESCRIPTION* writeShared =
(NTL_POSIX_SHARED_OPEN_DESCRIPTION*)calloc(1, sizeof(*writeShared));
if (!backing || !readDescription || !writeDescription || !readHolder || !writeHolder ||
!readShared || !writeShared) {
free(backing);
free(readDescription);
free(writeDescription);
free(readHolder);
free(writeHolder);
free(readShared);
free(writeShared);
(void)SysCloseHandle(readHandle);
(void)SysCloseHandle(writeHandle);
(void)SysFreeVm(pipeShared);
return NTLSTATUS_NO_MEMORY;
}
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) {
free(backing);
free(readDescription);
free(writeDescription);
free(readHolder);
free(writeHolder);
free(readShared);
free(writeShared);
(void)SysCloseHandle(readHandle);
(void)SysCloseHandle(writeHandle);
(void)SysFreeVm(pipeShared);
return status;
}
if (!PosixsrvCommittedGenerationOperational(client)) {
PosixsrvUnlockRegistry();
free(backing);
free(readDescription);
free(writeDescription);
free(readHolder);
free(writeHolder);
free(readShared);
free(writeShared);
(void)SysCloseHandle(readHandle);
(void)SysCloseHandle(writeHandle);
(void)SysFreeVm(pipeShared);
return NTLSTATUS_OBJECT_DEAD;
}
if (gNextOpenDescriptionId == 0 || gNextOpenDescriptionId == UINT64_MAX) {
PosixsrvUnlockRegistry();
free(backing);
free(readDescription);
free(writeDescription);
free(readHolder);
free(writeHolder);
free(readShared);
free(writeShared);
(void)SysCloseHandle(readHandle);
(void)SysCloseHandle(writeHandle);
(void)SysFreeVm(pipeShared);
return NTLSTATUS_ID_EXHAUSTED;
}
uint64_t readId = gNextOpenDescriptionId++;
uint64_t writeId = gNextOpenDescriptionId++;
backing->Shared = pipeShared;
backing->DescriptionCount = 2u;
readDescription->DescriptionId = readId;
readDescription->Type = NTL_POSIX_OPEN_TYPE_PIPE_READ;
readDescription->HolderCount = 1u;
readDescription->MasterHandle = readHandle;
readDescription->Shared = readShared;
readDescription->PipeBacking = backing;
readShared->Magic = NTL_POSIX_SHARED_OPEN_MAGIC;
readShared->Version = NTL_POSIX_SHARED_OPEN_VERSION;
readShared->Type = NTL_POSIX_OPEN_TYPE_PIPE_READ;
readShared->DescriptionId = readId;
readShared->StatusFlags = NTL_POSIX_OPEN_STATUS_RDONLY;
readShared->SharedObjectData = (uint64_t)(uintptr_t)pipeShared;
writeDescription->DescriptionId = writeId;
writeDescription->Type = NTL_POSIX_OPEN_TYPE_PIPE_WRITE;
writeDescription->HolderCount = 1u;
writeDescription->MasterHandle = writeHandle;
writeDescription->Shared = writeShared;
writeDescription->PipeBacking = backing;
writeShared->Magic = NTL_POSIX_SHARED_OPEN_MAGIC;
writeShared->Version = NTL_POSIX_SHARED_OPEN_VERSION;
writeShared->Type = NTL_POSIX_OPEN_TYPE_PIPE_WRITE;
writeShared->DescriptionId = writeId;
writeShared->StatusFlags = NTL_POSIX_OPEN_STATUS_WRONLY;
writeShared->SharedObjectData = (uint64_t)(uintptr_t)pipeShared;
readHolder->Description = readDescription;
writeHolder->Description = writeDescription;
writeHolder->Next = client->Process->OpenDescriptions;
readHolder->Next = writeHolder;
client->Process->OpenDescriptions = readHolder;
writeDescription->Next = gOpenDescriptionRegistry;
readDescription->Next = writeDescription;
gOpenDescriptionRegistry = readDescription;
gRegisteredOpenDescriptions += 2u;
*readIdOut = readId;
*writeIdOut = writeId;
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
// Applies F_SETFL mutable state through the master native capability and publishes the result.
static KSTATUS PosixsrvSetOpenStatus(POSIXSRV_CLIENT* client,
uint64_t descriptionId,
uint32_t requestedFlags,
uint32_t* statusFlagsOut,
uint32_t* typeOut) {
if (!client || !client->Process || descriptionId == 0 || !statusFlagsOut || !typeOut)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
if (!PosixsrvCommittedGenerationOperational(client)) {
PosixsrvUnlockRegistry();
return NTLSTATUS_OBJECT_DEAD;
}
POSIXSRV_OPEN_HOLDER* holder =
PosixsrvFindOpenHolderLocked(client->Process, descriptionId);
if (!holder || !holder->Description || !holder->Description->Shared) {
PosixsrvUnlockRegistry();
return NTLSTATUS_NOT_FOUND;
}
POSIXSRV_OPEN_DESCRIPTION* description = holder->Description;
uint32_t current = NtlAtomicLoadU32(&description->Shared->StatusFlags);
uint32_t updated = (current & ~(NTL_POSIX_OPEN_STATUS_APPEND |
NTL_POSIX_OPEN_STATUS_NONBLOCK)) |
(requestedFlags & (NTL_POSIX_OPEN_STATUS_APPEND |
NTL_POSIX_OPEN_STATUS_NONBLOCK));
status = NTLSTATUS_SUCCESS;
if (description->Type == NTL_POSIX_OPEN_TYPE_FILE) {
SYS_SET_FILE_STATUS_REQUEST fileRequest;
memset(&fileRequest, 0, sizeof(fileRequest));
fileRequest.Size = sizeof(fileRequest);
fileRequest.FileHandle = description->MasterHandle;
fileRequest.Mask = SYS_FILE_STATUS_APPEND;
fileRequest.Flags = (updated & NTL_POSIX_OPEN_STATUS_APPEND) ?
SYS_FILE_STATUS_APPEND : 0;
status = SysSetFileStatus(&fileRequest);
} else if (description->Type == NTL_POSIX_OPEN_TYPE_SOCKET) {
uint32_t nonblocking = (updated & NTL_POSIX_OPEN_STATUS_NONBLOCK) ? 1u : 0u;
SYS_SOCKET_OPTION_REQUEST socketRequest;
memset(&socketRequest, 0, sizeof(socketRequest));
socketRequest.Size = sizeof(socketRequest);
socketRequest.Option = SYS_SOCKET_OPT_NONBLOCKING;
socketRequest.Value = &nonblocking;
socketRequest.ValueSize = sizeof(nonblocking);
status = SysSetSocketOption(description->MasterHandle, &socketRequest);
}
if (KSUCCESS(status))
NtlAtomicExchangeU32(&description->Shared->StatusFlags, updated);
*statusFlagsOut = KSUCCESS(status) ? updated : current;
*typeOut = description->Type;
PosixsrvUnlockRegistry();
return status;
}
// Finds one synthesized passwd record by uid or exact name.
static const POSIXSRV_USER_RECORD* PosixsrvFindUser(uint32_t lookupKind,
uint32_t userId,
const char* name) {
for (uint32_t i = 0; i < (uint32_t)(sizeof(gUsers) / sizeof(gUsers[0])); ++i) {
if ((lookupKind == NTL_POSIX_LOOKUP_BY_ID && gUsers[i].UserId == userId) ||
(lookupKind == NTL_POSIX_LOOKUP_BY_NAME && name && strcmp(gUsers[i].Name, name) == 0))
return &gUsers[i];
}
return (const POSIXSRV_USER_RECORD*)0;
}
// Finds one synthesized group record by gid or exact name.
static const POSIXSRV_GROUP_RECORD* PosixsrvFindGroup(uint32_t lookupKind,
uint32_t groupId,
const char* name) {
for (uint32_t i = 0; i < (uint32_t)(sizeof(gGroups) / sizeof(gGroups[0])); ++i) {
if ((lookupKind == NTL_POSIX_LOOKUP_BY_ID && gGroups[i].GroupId == groupId) ||
(lookupKind == NTL_POSIX_LOOKUP_BY_NAME && name && strcmp(gGroups[i].Name, name) == 0))
return &gGroups[i];
}
return (const POSIXSRV_GROUP_RECORD*)0;
}
// Validates one length-delimited canonical native current-directory string.
static bool PosixsrvValidateCurrentDirectory(const NTL_POSIX_REQUEST* request) {
if (!request || request->PathLength == 0 || request->PathLength > SYS_PATH_MAX)
return false;
if (request->Path[0] != '/' || request->Path[request->PathLength] != '\0')
return false;
return strlen(request->Path) == request->PathLength;
}
// Validates one optional length-delimited identity lookup name.
static bool PosixsrvValidateLookupName(const NTL_POSIX_REQUEST* request) {
if (!request || request->NameLength == 0 || request->NameLength > NTL_POSIX_IDENTITY_NAME_MAX)
return false;
if (request->Name[request->NameLength] != '\0')
return false;
return strlen(request->Name) == request->NameLength;
}
// Retries process-pending delivery after thread registration or mask changes.
static void PosixsrvDrainPendingSignalsLocked(POSIXSRV_PROCESS_RECORD* process);
// Finds one registered native thread while the process registry lock is held.
static POSIXSRV_THREAD_RECORD* PosixsrvFindThreadLocked(POSIXSRV_PROCESS_RECORD* process,
uint32_t nativeThreadId,
uint32_t generation) {
if (!process || nativeThreadId == 0 || generation == 0) return (POSIXSRV_THREAD_RECORD*)0;
for (POSIXSRV_THREAD_RECORD* thread = process->Threads; thread; thread = thread->Next) {
if (thread->NativeThreadId == nativeThreadId && thread->Generation == generation)
return thread;
}
return (POSIXSRV_THREAD_RECORD*)0;
}
// Releases every retained native thread capability owned by one POSIX process.
static void PosixsrvReleaseThreadsLocked(POSIXSRV_PROCESS_RECORD* process) {
if (!process) return;
while (process->Threads) {
POSIXSRV_THREAD_RECORD* thread = process->Threads;
process->Threads = thread->Next;
if (thread->ThreadHandle != SYS_HANDLE_INVALID)
(void)SysCloseHandle(thread->ThreadHandle);
if (gRegisteredThreads != 0) --gRegisteredThreads;
free(thread);
}
}
// Releases retained native thread capabilities for one retired native generation.
static void PosixsrvReleaseThreadsForGenerationLocked(POSIXSRV_PROCESS_RECORD* process,
uint32_t generation) {
if (!process || generation == 0) return;
POSIXSRV_THREAD_RECORD** link = &process->Threads;
while (*link) {
POSIXSRV_THREAD_RECORD* thread = *link;
if (thread->Generation != generation) {
link = &thread->Next;
continue;
}
*link = thread->Next;
if (thread->ThreadHandle != SYS_HANDLE_INVALID)
(void)SysCloseHandle(thread->ThreadHandle);
if (gRegisteredThreads != 0) --gRegisteredThreads;
free(thread);
}
}
// Registers one thread capability and verifies it against the kernel-stamped sender TID.
static KSTATUS PosixsrvRegisterThread(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request,
const SYS_PORT_MESSAGE_INFO* info,
SYS_HANDLE* transferredThreadHandle) {
if (!client || !client->Process || !request || !info || !transferredThreadHandle ||
*transferredThreadHandle == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
if (client->NativeGeneration == 0 ||
(client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING &&
client->Process->State != NTL_POSIX_PROCESS_STATE_CONSTRUCTING &&
client->Process->State != NTL_POSIX_PROCESS_STATE_EXEC_PREPARING &&
client->Process->State != NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING))
return NTLSTATUS_OBJECT_DEAD;
uint32_t nativeThreadId = 0;
KSTATUS status = SysGetThreadTid(*transferredThreadHandle, &nativeThreadId);
if (!KSUCCESS(status)) return status;
if (nativeThreadId == 0 || nativeThreadId != info->SenderThreadId)
return NTLSTATUS_ACCESS_DENIED;
POSIXSRV_THREAD_RECORD* candidate =
(POSIXSRV_THREAD_RECORD*)calloc(1u, sizeof(*candidate));
if (!candidate) return NTLSTATUS_NO_MEMORY;
candidate->NativeThreadId = nativeThreadId;
candidate->Generation = client->NativeGeneration;
candidate->ThreadHandle = *transferredThreadHandle;
candidate->BlockedSignals = request->SignalMask & NTL_POSIX_SIGNAL_VALID_MASK &
~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
candidate->SignalApcRoutine = request->SignalApcRoutine;
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) {
free(candidate);
return status;
}
POSIXSRV_THREAD_RECORD* existing = PosixsrvFindThreadLocked(client->Process, nativeThreadId, client->NativeGeneration);
if (existing) {
existing->BlockedSignals = candidate->BlockedSignals;
existing->SignalApcRoutine = candidate->SignalApcRoutine;
PosixsrvDrainPendingSignalsLocked(client->Process);
PosixsrvUnlockRegistry();
free(candidate);
(void)SysCloseHandle(*transferredThreadHandle);
*transferredThreadHandle = SYS_HANDLE_INVALID;
return NTLSTATUS_SUCCESS;
}
candidate->Next = client->Process->Threads;
client->Process->Threads = candidate;
++gRegisteredThreads;
*transferredThreadHandle = SYS_HANDLE_INVALID;
PosixsrvDrainPendingSignalsLocked(client->Process);
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
// Unregisters the calling native thread using the kernel-stamped Port sender TID.
static KSTATUS PosixsrvUnregisterThread(POSIXSRV_CLIENT* client,
const SYS_PORT_MESSAGE_INFO* info) {
if (!client || !client->Process || !info || info->SenderThreadId == 0)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_THREAD_RECORD** link = &client->Process->Threads;
while (*link && ((*link)->NativeThreadId != info->SenderThreadId ||
(*link)->Generation != client->NativeGeneration))
link = &(*link)->Next;
if (!*link) {
PosixsrvUnlockRegistry();
return NTLSTATUS_NOT_FOUND;
}
POSIXSRV_THREAD_RECORD* thread = *link;
*link = thread->Next;
thread->Next = (POSIXSRV_THREAD_RECORD*)0;
if (gRegisteredThreads != 0) --gRegisteredThreads;
PosixsrvUnlockRegistry();
if (thread->ThreadHandle != SYS_HANDLE_INVALID)
(void)SysCloseHandle(thread->ThreadHandle);
free(thread);
return NTLSTATUS_SUCCESS;
}
// Returns whether the server should discard this signal for one process.
static bool PosixsrvSignalIgnoredLocked(const POSIXSRV_PROCESS_RECORD* process, int signalNumber) {
if (!process || !NtlPosixSignalValid(signalNumber)) return false;
return (process->IgnoredSignals & NTL_POSIX_SIGNAL_BIT(signalNumber)) != 0;
}
// Queues one POSIX signal notification through a retained native thread capability.
static KSTATUS PosixsrvQueueSignalToThreadLocked(POSIXSRV_THREAD_RECORD* thread,
int signalNumber,
uint32_t route) {
if (!thread || thread->ThreadHandle == SYS_HANDLE_INVALID || thread->SignalApcRoutine == 0 ||
!NtlPosixSignalValid(signalNumber))
return NTLSTATUS_INVALID_PARAMETER;
SYS_QUEUE_USER_APC_REQUEST queue;
memset(&queue, 0, sizeof(queue));
queue.Size = sizeof(queue);
queue.ThreadHandle = thread->ThreadHandle;
queue.Routine = (SYS_USER_APC_CALLBACK)(uintptr_t)thread->SignalApcRoutine;
queue.NormalContext = (void*)(uintptr_t)(uint32_t)signalNumber;
queue.SystemArgument1 = (void*)(uintptr_t)route;
queue.Flags = SYS_QUEUE_USER_APC_FLAG_INTERRUPT;
return SysQueueUserApc(&queue);
}
// Selects one unblocked registered thread for a process-directed signal.
static POSIXSRV_THREAD_RECORD* PosixsrvSelectSignalThreadLocked(POSIXSRV_PROCESS_RECORD* process,
int signalNumber) {
if (!process || !NtlPosixSignalValid(signalNumber)) return (POSIXSRV_THREAD_RECORD*)0;
uint64_t bit = NTL_POSIX_SIGNAL_BIT(signalNumber);
for (POSIXSRV_THREAD_RECORD* thread = process->Threads; thread; thread = thread->Next) {
if (thread->Generation != process->Generation || thread->SignalApcRoutine == 0) continue;
if (signalNumber == NTL_POSIX_SIGNAL_KILL || (thread->BlockedSignals & bit) == 0)
return thread;
}
return (POSIXSRV_THREAD_RECORD*)0;
}
// Delivers one process-directed signal or leaves it pending when every thread blocks it.
static KSTATUS PosixsrvRouteSignalLocked(POSIXSRV_PROCESS_RECORD* process, int signalNumber) {
if (!process || !PosixsrvProcessHasCommittedGeneration(process) ||
!NtlPosixSignalValid(signalNumber))
return NTLSTATUS_INVALID_PARAMETER;
uint64_t bit = NTL_POSIX_SIGNAL_BIT(signalNumber);
if (PosixsrvSignalIgnoredLocked(process, signalNumber)) {
process->ProcessPendingSignals &= ~bit;
return NTLSTATUS_SUCCESS;
}
if (process->State == NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING) {
process->ProcessPendingSignals |= bit;
return NTLSTATUS_SUCCESS;
}
POSIXSRV_THREAD_RECORD* thread = PosixsrvSelectSignalThreadLocked(process, signalNumber);
if (!thread) {
process->ProcessPendingSignals |= bit;
return NTLSTATUS_SUCCESS;
}
KSTATUS status = PosixsrvQueueSignalToThreadLocked(thread, signalNumber,
NTL_POSIX_SIGNAL_ROUTE_PROCESS);
if (KSUCCESS(status)) process->ProcessPendingSignals &= ~bit;
else process->ProcessPendingSignals |= bit;
return NTLSTATUS_SUCCESS;
}
// Attempts every process-pending signal that has become deliverable.
static void PosixsrvDrainPendingSignalsLocked(POSIXSRV_PROCESS_RECORD* process) {
if (!process || (process->State != NTL_POSIX_PROCESS_STATE_RUNNING &&
process->State != NTL_POSIX_PROCESS_STATE_EXEC_PREPARING))
return;
for (int signalNumber = NTL_POSIX_SIGNAL_HUP;
signalNumber <= NTL_POSIX_SIGNAL_MAX; ++signalNumber) {
uint64_t bit = NTL_POSIX_SIGNAL_BIT(signalNumber);
if ((process->ProcessPendingSignals & bit) == 0) continue;
if (PosixsrvSignalIgnoredLocked(process, signalNumber)) {
process->ProcessPendingSignals &= ~bit;
continue;
}
POSIXSRV_THREAD_RECORD* thread = PosixsrvSelectSignalThreadLocked(process, signalNumber);
if (!thread) continue;
if (KSUCCESS(PosixsrvQueueSignalToThreadLocked(thread, signalNumber,
NTL_POSIX_SIGNAL_ROUTE_PROCESS)))
process->ProcessPendingSignals &= ~bit;
}
}
// Updates the calling native thread's persistent POSIX signal mask.
static KSTATUS PosixsrvUpdateSignalMask(POSIXSRV_CLIENT* client,
const SYS_PORT_MESSAGE_INFO* info,
uint64_t signalMask) {
if (!client || !client->Process || !info ||
(signalMask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_THREAD_RECORD* thread = PosixsrvFindThreadLocked(client->Process, info->SenderThreadId, client->NativeGeneration);
if (!thread) status = NTLSTATUS_NOT_FOUND;
else {
thread->BlockedSignals = signalMask & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
PosixsrvDrainPendingSignalsLocked(client->Process);
status = NTLSTATUS_SUCCESS;
}
PosixsrvUnlockRegistry();
return status;
}
// Changes the server-side routing view of one process signal disposition.
static KSTATUS PosixsrvSetSignalDisposition(POSIXSRV_CLIENT* client,
int signalNumber,
uint32_t disposition) {
if (!client || !client->Process || !NtlPosixSignalValid(signalNumber) ||
disposition > NTL_POSIX_SIGNAL_DISPOSITION_CAUGHT ||
(signalNumber == NTL_POSIX_SIGNAL_KILL && disposition != NTL_POSIX_SIGNAL_DISPOSITION_DEFAULT))
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
if (!PosixsrvCommittedGenerationOperational(client) ||
client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING) {
PosixsrvUnlockRegistry();
return NTLSTATUS_WOULD_BLOCK;
}
uint64_t bit = NTL_POSIX_SIGNAL_BIT(signalNumber);
int explicitlyIgnored = disposition == NTL_POSIX_SIGNAL_DISPOSITION_IGNORE;
int ignored = explicitlyIgnored ||
(disposition == NTL_POSIX_SIGNAL_DISPOSITION_DEFAULT &&
signalNumber == NTL_POSIX_SIGNAL_CHLD);
if (explicitlyIgnored) client->Process->ExplicitIgnoredSignals |= bit;
else client->Process->ExplicitIgnoredSignals &= ~bit;
if (ignored) {
client->Process->IgnoredSignals |= bit;
client->Process->ProcessPendingSignals &= ~bit;
} else {
client->Process->IgnoredSignals &= ~bit;
PosixsrvDrainPendingSignalsLocked(client->Process);
}
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
// Returns whether a sender is permitted to signal one synthetic POSIX identity.
static bool PosixsrvCanSignalLocked(const POSIXSRV_PROCESS_RECORD* sender,
const POSIXSRV_PROCESS_RECORD* target) {
if (!sender || !target) return false;
if (sender->EffectiveUserId == 0) return true;
return sender->UserId == target->UserId || sender->UserId == target->EffectiveUserId ||
sender->EffectiveUserId == target->UserId ||
sender->EffectiveUserId == target->EffectiveUserId;
}
// Routes kill-style process or process-group selection through stable POSIX identity.
static KSTATUS PosixsrvSendSignal(POSIXSRV_CLIENT* client, int32_t targetPid, int signalNumber) {
if (!client || !client->Process || signalNumber < 0 || signalNumber > NTL_POSIX_SIGNAL_MAX ||
targetPid == INT32_MIN)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_PROCESS_RECORD* sender = client->Process;
uint32_t matched = 0;
uint32_t permitted = 0;
KSTATUS deliveryStatus = NTLSTATUS_SUCCESS;
for (POSIXSRV_PROCESS_RECORD* target = gProcessRegistry; target; target = target->Next) {
if (!PosixsrvProcessHasCommittedGeneration(target)) continue;
bool selected = false;
if (targetPid > 0) selected = target->PosixPid == (uint32_t)targetPid;
else if (targetPid == 0) selected = target->ProcessGroupId == sender->ProcessGroupId;
else if (targetPid == -1) selected = true;
else selected = target->ProcessGroupId == (uint32_t)(-targetPid);
if (!selected) continue;
++matched;
if (!PosixsrvCanSignalLocked(sender, target)) continue;
++permitted;
if (signalNumber != 0) {
KSTATUS one = PosixsrvRouteSignalLocked(target, signalNumber);
if (!KSUCCESS(one) && KSUCCESS(deliveryStatus)) deliveryStatus = one;
}
}
if (matched == 0) status = NTLSTATUS_NOT_FOUND;
else if (permitted == 0) status = NTLSTATUS_ACCESS_DENIED;
else status = deliveryStatus;
PosixsrvUnlockRegistry();
return status;
}
// Removes one process record from the registry while the registry mutex is held.
static void PosixsrvUnlinkProcessLocked(POSIXSRV_PROCESS_RECORD* record) {
if (!record) return;
POSIXSRV_PROCESS_RECORD** link = &gProcessRegistry;
while (*link && *link != record) link = &(*link)->Next;
if (*link == record) {
*link = record->Next;
record->Next = (POSIXSRV_PROCESS_RECORD*)0;
if (gRegisteredProcesses != 0) --gRegisteredProcesses;
}
}
// Releases one detached process record and its retained native capability.
static void PosixsrvFreeProcessRecord(POSIXSRV_PROCESS_RECORD* record) {
if (!record) return;
for (uint32_t i = 0; i < NTL_POSIX_TRANSACTION_CAPACITY; ++i)
PosixsrvDestroyTransaction(&record->Transactions[i]);
record->ActiveTransactions = 0;
while (record->Threads) {
POSIXSRV_THREAD_RECORD* thread = record->Threads;
record->Threads = thread->Next;
if (thread->ThreadHandle != SYS_HANDLE_INVALID)
(void)SysCloseHandle(thread->ThreadHandle);
if (gRegisteredThreads != 0) --gRegisteredThreads;
free(thread);
}
if (record->ProcessHandle != SYS_HANDLE_INVALID)
(void)SysCloseHandle(record->ProcessHandle);
if (record->BootstrapReadyEvent != SYS_HANDLE_INVALID)
(void)SysCloseHandle(record->BootstrapReadyEvent);
if (record->PendingProcessHandle != SYS_HANDLE_INVALID)
(void)SysCloseHandle(record->PendingProcessHandle);
if (record->PendingBootstrapReadyEvent != SYS_HANDLE_INVALID)
(void)SysCloseHandle(record->PendingBootstrapReadyEvent);
if (record->PendingBootstrapReleaseEvent != SYS_HANDLE_INVALID)
(void)SysCloseHandle(record->PendingBootstrapReleaseEvent);
if (record->AlarmTimer != SYS_HANDLE_INVALID)
(void)SysCloseHandle(record->AlarmTimer);
if (record->AlarmControlEvent != SYS_HANDLE_INVALID)
(void)SysCloseHandle(record->AlarmControlEvent);
if (record->PendingExecHolderPlan) {
PosixsrvDestroyExecHolderPlan(record->PendingExecHolderPlan);
free(record->PendingExecHolderPlan);
record->PendingExecHolderPlan = (POSIXSRV_EXEC_HOLDER_PLAN*)0;
}
free(record);
}
// Removes borrowed logical children when their shared host native process dies.
static void PosixsrvReleaseBorrowedChildrenForHostLocked(POSIXSRV_PROCESS_RECORD* host) {
if (!host) return;
POSIXSRV_PROCESS_RECORD** link = &gProcessRegistry;
while (*link) {
POSIXSRV_PROCESS_RECORD* child = *link;
if (child->State != NTL_POSIX_PROCESS_STATE_VFORK_BORROWED ||
child->BorrowedHostProcess != host || child->BorrowedMonitorActive) {
link = &child->Next;
continue;
}
*link = child->Next;
child->Next = (POSIXSRV_PROCESS_RECORD*)0;
if (gRegisteredProcesses != 0) --gRegisteredProcesses;
PosixsrvReleaseFdInheritanceForProcessLocked(child);
PosixsrvReleaseAllOpenDescriptionsLocked(child);
PosixsrvReleaseThreadsLocked(child);
PosixsrvFreeProcessRecord(child);
}
}
// Returns whether one exited process record has no remaining owner that requires retention.
static bool PosixsrvCanFreeProcessLocked(const POSIXSRV_PROCESS_RECORD* record) {
return record && record->State == NTL_POSIX_PROCESS_STATE_EXITED &&
record->ParentPosixPid == 0 && record->ConnectionCount == 0 &&
!record->MonitorActive && !record->AlarmMonitorActive && !record->BorrowedMonitorActive;
}
// Converts one monotonic alarm deadline into POSIX whole seconds remaining.
static uint32_t PosixsrvAlarmRemainingSeconds(uint64_t deadlineTicks,
uint64_t nowTicks,
uint64_t frequency) {
if (deadlineTicks == 0 || deadlineTicks <= nowTicks || frequency == 0) return 0;
uint64_t remainingTicks = deadlineTicks - nowTicks;
uint64_t seconds = remainingTicks / frequency;
if (remainingTicks % frequency) ++seconds;
return seconds > UINT32_MAX ? UINT32_MAX : (uint32_t)seconds;
}
// Waits on one process alarm timer until process teardown ends its ownership.
static void PosixsrvAlarmMonitorThread(uintptr_t argument) {
POSIXSRV_PROCESS_RECORD* record = (POSIXSRV_PROCESS_RECORD*)argument;
if (!record) {
SysExitThread(1);
return;
}
PosixsrvSetCurrentThreadName("posixsrv.alarm");
for (;;) {
SYS_HANDLE timer = SYS_HANDLE_INVALID;
SYS_HANDLE control = SYS_HANDLE_INVALID;
uint64_t generation = 0;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) break;
if (!PosixsrvProcessHasCommittedGeneration(record) ||
record->AlarmTimer == SYS_HANDLE_INVALID ||
record->AlarmControlEvent == SYS_HANDLE_INVALID) {
record->AlarmMonitorActive = 0;
POSIXSRV_PROCESS_RECORD* freeRecord = (POSIXSRV_PROCESS_RECORD*)0;
if (PosixsrvCanFreeProcessLocked(record)) {
PosixsrvUnlinkProcessLocked(record);
freeRecord = record;
}
PosixsrvUnlockRegistry();
if (freeRecord) PosixsrvFreeProcessRecord(freeRecord);
SysExitThread(0);
return;
}
timer = record->AlarmTimer;
control = record->AlarmControlEvent;
generation = record->AlarmGeneration;
PosixsrvUnlockRegistry();
SYS_HANDLE handles[2] = { timer, control };
uint32_t selected = UINT32_MAX;
SYS_WAIT_MANY_REQUEST wait;
memset(&wait, 0, sizeof(wait));
wait.Size = sizeof(wait);
wait.Flags = SYS_WAIT_FLAG_NONE;
wait.Handles = handles;
wait.Count = 2u;
wait.SignaledIndexOut = &selected;
wait.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
status = SysWaitMany(&wait);
POSIXSRV_PROCESS_RECORD* freeRecord = (POSIXSRV_PROCESS_RECORD*)0;
KSTATUS lockStatus = PosixsrvLockRegistry();
if (!KSUCCESS(lockStatus)) break;
if (!KSUCCESS(status)) {
record->AlarmMonitorActive = 0;
} else if (!PosixsrvProcessHasCommittedGeneration(record)) {
record->AlarmMonitorActive = 0;
} else if (selected == 0u && generation == record->AlarmGeneration &&
record->AlarmDeadlineTicks != 0) {
record->AlarmDeadlineTicks = 0;
++record->AlarmGeneration;
(void)PosixsrvRouteSignalLocked(record, NTL_POSIX_SIGNAL_ALRM);
}
int stop = !record->AlarmMonitorActive;
if (stop && PosixsrvCanFreeProcessLocked(record)) {
PosixsrvUnlinkProcessLocked(record);
freeRecord = record;
}
PosixsrvUnlockRegistry();
if (freeRecord) PosixsrvFreeProcessRecord(freeRecord);
if (stop) {
SysExitThread(KSUCCESS(status) ? 0 : 1);
return;
}
}
if (KSUCCESS(PosixsrvLockRegistry())) {
record->AlarmMonitorActive = 0;
PosixsrvUnlockRegistry();
}
SysExitThread(1);
}
// Ensures one running process has the server-owned alarm timer and monitor.
static KSTATUS PosixsrvEnsureAlarmMonitorLocked(POSIXSRV_PROCESS_RECORD* process) {
if (!process || !PosixsrvProcessHasCommittedGeneration(process))
return NTLSTATUS_OBJECT_DEAD;
if (process->AlarmTimer == SYS_HANDLE_INVALID) {
KSTATUS status = SysCreateTimer(&process->AlarmTimer);
if (!KSUCCESS(status)) return status;
}
if (process->AlarmControlEvent == SYS_HANDLE_INVALID) {
KSTATUS status = SysCreateEvent(0u, 0u, &process->AlarmControlEvent);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(process->AlarmTimer);
process->AlarmTimer = SYS_HANDLE_INVALID;
return status;
}
}
if (process->AlarmMonitorActive) return NTLSTATUS_SUCCESS;
process->AlarmMonitorActive = 1u;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)PosixsrvAlarmMonitorThread;
create.Argument = (uintptr_t)process;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
KSTATUS status = SysCreateThread(&create);
if (!KSUCCESS(status)) process->AlarmMonitorActive = 0;
return status;
}
// Replaces one process alarm and reports the previous interval rounded up.
static KSTATUS PosixsrvSetAlarm(POSIXSRV_CLIENT* client,
uint32_t seconds,
uint32_t* previousSecondsOut) {
if (!client || !client->Process || !previousSecondsOut)
return NTLSTATUS_INVALID_PARAMETER;
*previousSecondsOut = 0;
uint64_t frequency = 0;
KSTATUS status = SysGetTickFrequency(&frequency);
if (!KSUCCESS(status) || frequency == 0)
return KSUCCESS(status) ? NTLSTATUS_DEVICE_ERROR : status;
uint64_t now = SysGetTick();
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_PROCESS_RECORD* process = client->Process;
if (!PosixsrvCommittedGenerationOperational(client)) {
PosixsrvUnlockRegistry();
return NTLSTATUS_OBJECT_DEAD;
}
*previousSecondsOut = PosixsrvAlarmRemainingSeconds(process->AlarmDeadlineTicks,
now, frequency);
if (seconds == 0) {
process->AlarmDeadlineTicks = 0;
++process->AlarmGeneration;
if (process->AlarmTimer != SYS_HANDLE_INVALID)
(void)SysCancelTimer(process->AlarmTimer);
if (process->AlarmControlEvent != SYS_HANDLE_INVALID)
(void)SysSignalEvent(process->AlarmControlEvent);
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
status = PosixsrvEnsureAlarmMonitorLocked(process);
if (!KSUCCESS(status)) {
PosixsrvUnlockRegistry();
return status;
}
uint64_t intervalTicks = (uint64_t)seconds * frequency;
process->AlarmDeadlineTicks = intervalTicks > UINT64_MAX - now ? UINT64_MAX : now + intervalTicks;
++process->AlarmGeneration;
SYS_SET_TIMER_REQUEST setTimer;
memset(&setTimer, 0, sizeof(setTimer));
setTimer.Size = sizeof(setTimer);
setTimer.TimerHandle = process->AlarmTimer;
setTimer.Flags = SYS_TIMER_FLAG_NONE;
setTimer.DueTimeMilliseconds = (uint64_t)seconds * 1000u;
status = SysSetTimer(&setTimer);
if (!KSUCCESS(status)) {
process->AlarmDeadlineTicks = 0;
++process->AlarmGeneration;
}
(void)SysSignalEvent(process->AlarmControlEvent);
PosixsrvUnlockRegistry();
return status;
}
// Tests whether one waiter selection includes the supplied direct child.
static bool PosixsrvWaiterMatchesChild(const POSIXSRV_WAITER* waiter,
const POSIXSRV_PROCESS_RECORD* child) {
if (!waiter || !child || child->ParentPosixPid != waiter->ParentPosixPid) return false;
if (waiter->WaitPid > 0) return child->PosixPid == (uint32_t)waiter->WaitPid;
if (waiter->WaitPid == -1) return true;
if (waiter->WaitPid == 0) return child->ProcessGroupId == waiter->ParentProcessGroupId;
uint64_t group = (uint64_t)(-(int64_t)waiter->WaitPid);
return group <= UINT32_MAX && child->ProcessGroupId == (uint32_t)group;
}
// Wakes every blocked wait request whose selection includes one child transition.
static void PosixsrvSignalWaitersForChildLocked(const POSIXSRV_PROCESS_RECORD* child) {
for (POSIXSRV_WAITER* waiter = gWaiters; waiter; waiter = waiter->Next) {
if (PosixsrvWaiterMatchesChild(waiter, child))
(void)SysSignalEvent(waiter->Event);
}
}
// Wakes waits issued by a process that has itself terminated.
static void PosixsrvSignalWaitersForParentLocked(uint32_t parentPid) {
for (POSIXSRV_WAITER* waiter = gWaiters; waiter; waiter = waiter->Next) {
if (waiter->ParentPosixPid == parentPid)
(void)SysSignalEvent(waiter->Event);
}
}
// Drops parent associations when a POSIX parent dies and reclaims already-exited orphans.
static void PosixsrvOrphanChildrenLocked(uint32_t parentPid) {
POSIXSRV_PROCESS_RECORD* record = gProcessRegistry;
while (record) {
POSIXSRV_PROCESS_RECORD* next = record->Next;
if (record->ParentPosixPid == parentPid) {
record->ParentPosixPid = 0;
if (PosixsrvCanFreeProcessLocked(record)) {
PosixsrvUnlinkProcessLocked(record);
PosixsrvFreeProcessRecord(record);
}
}
record = next;
}
PosixsrvSignalWaitersForParentLocked(parentPid);
}
// Allocates one stable positive POSIX PID from the server-owned namespace.
static KSTATUS PosixsrvAllocatePidLocked(uint32_t* pidOut) {
if (!pidOut) return NTLSTATUS_INVALID_PARAMETER;
*pidOut = 0;
if (gNextPosixPid < POSIXSRV_FIRST_PID || gNextPosixPid > POSIXSRV_LAST_PID)
return NTLSTATUS_ID_EXHAUSTED;
*pidOut = gNextPosixPid++;
return NTLSTATUS_SUCCESS;
}
// Verifies that the service runs without ambient privilege or service-registration claims.
static KSTATUS PosixsrvValidateSecurity(void) {
SYS_SECURITY_CONTEXT_INFO security;
memset(&security, 0, sizeof(security));
security.Size = sizeof(security);
KSTATUS status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &security);
if (!KSUCCESS(status)) return status;
if (security.EffectivePrivileges != 0 || security.DelegablePrivileges != 0)
return NTLSTATUS_ACCESS_DENIED;
return NTLSTATUS_SUCCESS;
}
// Accepts init's private service-control connection.
static KSTATUS PosixsrvAcceptControl(SYS_HANDLE listener, SYS_HANDLE* endpointOut) {
if (listener == SYS_HANDLE_INVALID || !endpointOut) return NTLSTATUS_INVALID_PARAMETER;
SYS_ACCEPT_PORT_REQUEST accept;
memset(&accept, 0, sizeof(accept));
accept.Size = sizeof(accept);
accept.ListenerHandle = listener;
accept.Flags = SYS_PORT_ACCEPT_FLAG_NONE;
accept.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
accept.EndpointHandleOut = endpointOut;
return SysAcceptPort(&accept);
}
// Creates the public client listener exported through the generic service READY contract.
static KSTATUS PosixsrvCreateListener(SYS_HANDLE* listenerOut) {
if (!listenerOut) return NTLSTATUS_INVALID_PARAMETER;
*listenerOut = SYS_HANDLE_INVALID;
SYS_CREATE_PORT_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Flags = SYS_PORT_FLAG_NONE;
create.Backlog = SYS_PORT_DEFAULT_BACKLOG;
create.PeerProcessRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_RIGHT_DERIVE |
SYS_PROCESS_RIGHT_TERMINATE | SYS_PROCESS_RIGHT_SECURITY_CONTEXT;
create.HandleOut = listenerOut;
return SysCreatePort(&create);
}
// Copies one bounded NUL-terminated string into a protocol field and returns its byte length.
static uint32_t PosixsrvCopyText(char* destination, uint32_t capacity, const char* source) {
if (!destination || capacity == 0) return 0;
if (!source) source = "";
size_t length = strlen(source);
if (length >= capacity) length = capacity - 1u;
if (length) memcpy(destination, source, length);
destination[length] = '\0';
return (uint32_t)length;
}
// Copies the current process identity and shared attributes into one reply.
static void PosixsrvSnapshotReplyIdentity(POSIXSRV_CLIENT* client, NTL_POSIX_REPLY* reply) {
if (!client || !reply || !KSUCCESS(PosixsrvLockRegistry())) return;
if (client->Process) {
POSIXSRV_PROCESS_RECORD* record = client->Process;
reply->PosixPid = record->PosixPid;
reply->NativeGeneration = client->NativeGeneration ? client->NativeGeneration : record->Generation;
reply->ProcessState = record->State;
reply->ParentPosixPid = record->ParentPosixPid;
reply->UserId = record->UserId;
reply->EffectiveUserId = record->EffectiveUserId;
reply->GroupId = record->GroupId;
reply->EffectiveGroupId = record->EffectiveGroupId;
reply->ProcessGroupId = record->ProcessGroupId;
reply->SessionId = record->SessionId;
reply->ActiveTransactions = record->ActiveTransactions;
reply->SupplementaryGroupCount = record->SupplementaryGroupCount;
if (reply->SupplementaryGroupCount > NTL_POSIX_SUPPLEMENTARY_GROUP_MAX)
reply->SupplementaryGroupCount = NTL_POSIX_SUPPLEMENTARY_GROUP_MAX;
for (uint32_t i = 0; i < reply->SupplementaryGroupCount; ++i)
reply->SupplementaryGroups[i] = record->SupplementaryGroups[i];
reply->NameLength = PosixsrvCopyText(reply->Name, sizeof(reply->Name), record->LoginName);
reply->CurrentDirectoryLength = PosixsrvCopyText(reply->CurrentDirectory,
sizeof(reply->CurrentDirectory),
record->CurrentDirectory);
}
reply->RegisteredProcesses = gRegisteredProcesses;
reply->RegisteredOpenDescriptions = gRegisteredOpenDescriptions;
reply->RegisteredThreads = gRegisteredThreads;
PosixsrvUnlockRegistry();
}
// Initializes the common fields of one protocol reply.
static void PosixsrvInitializeReply(POSIXSRV_CLIENT* client,
NTL_POSIX_REPLY* reply,
uint32_t operation,
KSTATUS status,
uint32_t transactionId) {
memset(reply, 0, sizeof(*reply));
reply->Size = sizeof(*reply);
reply->Version = NTL_POSIX_PROTOCOL_VERSION;
reply->Operation = operation;
reply->Flags = NTL_POSIX_REQUEST_FLAG_NONE;
reply->Status = status;
reply->TransactionId = transactionId;
reply->ClientProcessId = client->Connection.ClientProcessId;
reply->ClientSecurityId = client->Connection.ClientSecurityId;
reply->TransactionCapacity = NTL_POSIX_TRANSACTION_CAPACITY;
PosixsrvSnapshotReplyIdentity(client, reply);
}
// Sends one already-prepared fixed-size protocol reply with no capability transfer.
static KSTATUS PosixsrvSendReply(POSIXSRV_CLIENT* client,
uint32_t messageId,
const NTL_POSIX_REPLY* reply) {
if (!client || !reply) return NTLSTATUS_INVALID_PARAMETER;
SYS_REPLY_PORT_REQUEST out;
memset(&out, 0, sizeof(out));
out.Size = sizeof(out);
out.EndpointHandle = client->Endpoint;
out.MessageId = messageId;
out.Flags = SYS_PORT_FLAG_NONE;
out.Payload = reply;
out.PayloadLength = sizeof(*reply);
return SysReplyPort(&out);
}
// Sends one prepared reply with a bounded set of transferred capabilities.
static KSTATUS PosixsrvSendReplyWithHandles(POSIXSRV_CLIENT* client,
uint32_t messageId,
const NTL_POSIX_REPLY* reply,
const SYS_PORT_HANDLE_TRANSFER* handles,
uint32_t handleCount) {
if (!client || !reply || handleCount > SYS_PORT_MAX_TRANSFER_HANDLES ||
(handleCount != 0 && !handles))
return NTLSTATUS_INVALID_PARAMETER;
SYS_REPLY_PORT_REQUEST out;
memset(&out, 0, sizeof(out));
out.Size = sizeof(out);
out.EndpointHandle = client->Endpoint;
out.MessageId = messageId;
out.Flags = SYS_PORT_FLAG_NONE;
out.Payload = reply;
out.PayloadLength = sizeof(*reply);
out.Handles = handles;
out.HandleCount = handleCount;
return SysReplyPort(&out);
}
// Sends one ordinary process/transaction reply.
static KSTATUS PosixsrvReply(POSIXSRV_CLIENT* client,
uint32_t messageId,
uint32_t operation,
KSTATUS status,
uint32_t transactionId) {
if (!client) return NTLSTATUS_INVALID_PARAMETER;
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, operation, status, transactionId);
return PosixsrvSendReply(client, messageId, &reply);
}
// Sends one open-description result while preserving current-process metadata.
static KSTATUS PosixsrvReplyOpenDescription(POSIXSRV_CLIENT* client,
uint32_t messageId,
uint32_t operation,
KSTATUS status,
uint64_t descriptionId,
uint32_t type,
uint32_t statusFlags,
NTL_POSIX_SHARED_OPEN_DESCRIPTION* shared) {
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, operation, status, 0);
if (KSUCCESS(status)) {
reply.OpenDescriptionId = descriptionId;
reply.OpenDescriptionType = type;
reply.OpenStatusFlags = statusFlags;
reply.SharedOpenDescription = (uint64_t)(uintptr_t)shared;
}
return PosixsrvSendReply(client, messageId, &reply);
}
// Sends one server-created pipe result with endpoint capabilities in read/write order.
static KSTATUS PosixsrvReplyPipe(POSIXSRV_CLIENT* client,
uint32_t messageId,
KSTATUS status,
uint64_t readId,
uint64_t writeId) {
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, NTL_POSIX_OP_CREATE_PIPE, status, 0);
if (!KSUCCESS(status))
return PosixsrvSendReply(client, messageId, &reply);
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_OPEN_HOLDER* readHolder =
PosixsrvFindOpenHolderLocked(client->Process, readId);
POSIXSRV_OPEN_HOLDER* writeHolder =
PosixsrvFindOpenHolderLocked(client->Process, writeId);
POSIXSRV_OPEN_DESCRIPTION* readDescription =
readHolder ? readHolder->Description : (POSIXSRV_OPEN_DESCRIPTION*)0;
POSIXSRV_OPEN_DESCRIPTION* writeDescription =
writeHolder ? writeHolder->Description : (POSIXSRV_OPEN_DESCRIPTION*)0;
if (!readDescription || !writeDescription || !readDescription->Shared ||
!writeDescription->Shared ||
readDescription->Type != NTL_POSIX_OPEN_TYPE_PIPE_READ ||
writeDescription->Type != NTL_POSIX_OPEN_TYPE_PIPE_WRITE ||
readDescription->MasterHandle == SYS_HANDLE_INVALID ||
writeDescription->MasterHandle == SYS_HANDLE_INVALID) {
PosixsrvUnlockRegistry();
return PosixsrvReply(client, messageId, NTL_POSIX_OP_CREATE_PIPE,
NTLSTATUS_OBJECT_DEAD, 0);
}
reply.OpenDescriptionId = readDescription->DescriptionId;
reply.OpenDescriptionType = readDescription->Type;
reply.OpenStatusFlags = NtlAtomicLoadU32(&readDescription->Shared->StatusFlags);
reply.SharedOpenDescription = (uint64_t)(uintptr_t)readDescription->Shared;
reply.OpenDescriptionId2 = writeDescription->DescriptionId;
reply.OpenDescriptionType2 = writeDescription->Type;
reply.OpenStatusFlags2 = NtlAtomicLoadU32(&writeDescription->Shared->StatusFlags);
reply.SharedOpenDescription2 = (uint64_t)(uintptr_t)writeDescription->Shared;
SYS_PORT_HANDLE_TRANSFER transfers[2];
memset(transfers, 0, sizeof(transfers));
transfers[0].Handle = readDescription->MasterHandle;
transfers[0].DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT |
SYS_PIPE_RIGHT_READ | SYS_PIPE_RIGHT_POLL;
transfers[0].Flags = SYS_PORT_TRANSFER_FLAG_NONE;
transfers[1].Handle = writeDescription->MasterHandle;
transfers[1].DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT |
SYS_PIPE_RIGHT_WRITE | SYS_PIPE_RIGHT_POLL;
transfers[1].Flags = SYS_PORT_TRANSFER_FLAG_NONE;
KSTATUS replyStatus = PosixsrvSendReplyWithHandles(client, messageId, &reply, transfers, 2u);
PosixsrvUnlockRegistry();
return replyStatus;
}
// Sends one bounded inherited-description batch and its process-local native aliases.
static KSTATUS PosixsrvReplyFdInheritance(POSIXSRV_CLIENT* client,
uint32_t messageId,
uint64_t token,
uint32_t descriptionIndex) {
if (!client || !client->Process || token == 0) return NTLSTATUS_INVALID_PARAMETER;
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, NTL_POSIX_OP_IMPORT_FD_INHERITANCE,
NTLSTATUS_SUCCESS, 0);
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_FD_INHERITANCE* snapshot = PosixsrvFindFdInheritanceLocked(token);
bool execImport = snapshot && PosixsrvClientIsPendingExecGeneration(client) &&
client->Process->PendingFdInheritanceToken == token &&
snapshot->SourceProcess == client->Process && !snapshot->ClaimProcess;
int processStateValid = client->Process->State == NTL_POSIX_PROCESS_STATE_RUNNING ||
client->Process->State == NTL_POSIX_PROCESS_STATE_CONSTRUCTING ||
client->Process->State == NTL_POSIX_PROCESS_STATE_VFORK_BORROWED ||
execImport;
bool ownershipValid = snapshot && (execImport || snapshot->ClaimProcess == client->Process ||
(!snapshot->ClaimProcess && snapshot->SecurityId == client->Process->SecurityId));
if (!snapshot || !ownershipValid || !processStateValid) {
PosixsrvUnlockRegistry();
return PosixsrvReply(client, messageId, NTL_POSIX_OP_IMPORT_FD_INHERITANCE,
snapshot ? NTLSTATUS_ACCESS_DENIED : NTLSTATUS_NOT_FOUND, 0);
}
if ((snapshot->DescriptionCount != 0 && descriptionIndex >= snapshot->DescriptionCount) ||
(snapshot->DescriptionCount == 0 && descriptionIndex != 0)) {
PosixsrvUnlockRegistry();
return PosixsrvReply(client, messageId, NTL_POSIX_OP_IMPORT_FD_INHERITANCE,
NTLSTATUS_INVALID_PARAMETER, 0);
}
if (!execImport) {
status = PosixsrvClaimFdInheritanceLocked(snapshot, client->Process);
if (!KSUCCESS(status)) {
PosixsrvUnlockRegistry();
return PosixsrvReply(client, messageId, NTL_POSIX_OP_IMPORT_FD_INHERITANCE, status, 0);
}
}
uint32_t count = snapshot->DescriptionCount - descriptionIndex;
if (count > NTL_POSIX_FD_IMPORT_BATCH_MAX) count = NTL_POSIX_FD_IMPORT_BATCH_MAX;
reply.FdInheritanceToken = token;
reply.FdInheritanceIndex = descriptionIndex;
reply.FdInheritanceCount = snapshot->EntryCount;
reply.FdDescriptionIndex = descriptionIndex;
reply.FdDescriptionCount = count;
reply.FdDescriptionTotal = snapshot->DescriptionCount;
for (uint32_t i = 0; i < snapshot->EntryCount; ++i)
reply.FdManifest[i] = snapshot->Entries[i].Manifest;
SYS_PORT_HANDLE_TRANSFER transfers[NTL_POSIX_FD_IMPORT_BATCH_MAX];
memset(transfers, 0, sizeof(transfers));
uint32_t handleCount = 0;
for (uint32_t i = 0; i < count; ++i) {
POSIXSRV_OPEN_DESCRIPTION* description = snapshot->Descriptions[descriptionIndex + i];
if (!description || !description->Shared) { status = NTLSTATUS_OBJECT_DEAD; break; }
NTL_POSIX_FD_DESCRIPTION_ENTRY* out = &reply.FdDescriptions[i];
out->OpenDescriptionId = description->DescriptionId;
out->SharedOpenDescription = (uint64_t)(uintptr_t)description->Shared;
out->OpenDescriptionType = description->Type;
out->OpenStatusFlags = NtlAtomicLoadU32(&description->Shared->StatusFlags);
out->HandleIndex = NTL_POSIX_FD_HANDLE_NONE;
if (PosixsrvOpenDescriptionNeedsHandle(description->Type)) {
if (description->MasterHandle == SYS_HANDLE_INVALID || handleCount >= NTL_POSIX_FD_IMPORT_BATCH_MAX) {
status = NTLSTATUS_OBJECT_DEAD;
break;
}
out->HandleIndex = handleCount;
transfers[handleCount].Handle = description->MasterHandle;
transfers[handleCount].DesiredRights = PosixsrvOpenDescriptionClientRights(description);
transfers[handleCount].Flags = SYS_PORT_TRANSFER_FLAG_NONE;
++handleCount;
}
}
if (!KSUCCESS(status)) {
PosixsrvUnlockRegistry();
return PosixsrvReply(client, messageId, NTL_POSIX_OP_IMPORT_FD_INHERITANCE, status, 0);
}
KSTATUS replyStatus = PosixsrvSendReplyWithHandles(client, messageId, &reply,
transfers, handleCount);
if (KSUCCESS(replyStatus) && descriptionIndex + count == snapshot->DescriptionCount && !execImport)
PosixsrvDestroyFdInheritanceLocked(snapshot);
PosixsrvUnlockRegistry();
return replyStatus;
}
// Sends one passwd-database lookup result while preserving current-process metadata.
static KSTATUS PosixsrvReplyUser(POSIXSRV_CLIENT* client,
uint32_t messageId,
uint32_t operation,
KSTATUS status,
const POSIXSRV_USER_RECORD* user) {
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, operation, status, 0);
if (KSUCCESS(status) && user) {
reply.UserId = user->UserId;
reply.GroupId = user->GroupId;
reply.NameLength = PosixsrvCopyText(reply.Name, sizeof(reply.Name), user->Name);
reply.HomeLength = PosixsrvCopyText(reply.Home, sizeof(reply.Home), user->Home);
reply.ShellLength = PosixsrvCopyText(reply.Shell, sizeof(reply.Shell), user->Shell);
}
return PosixsrvSendReply(client, messageId, &reply);
}
// Sends one group-database lookup result while preserving current-process metadata.
static KSTATUS PosixsrvReplyGroup(POSIXSRV_CLIENT* client,
uint32_t messageId,
uint32_t operation,
KSTATUS status,
const POSIXSRV_GROUP_RECORD* group) {
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, operation, status, 0);
if (KSUCCESS(status) && group) {
reply.GroupId = group->GroupId;
reply.NameLength = PosixsrvCopyText(reply.Name, sizeof(reply.Name), group->Name);
}
return PosixsrvSendReply(client, messageId, &reply);
}
// Waits for one native process generation and records genuine process termination.
static void PosixsrvProcessMonitorThread(uintptr_t argument) {
POSIXSRV_PROCESS_MONITOR_CONTEXT* monitor =
(POSIXSRV_PROCESS_MONITOR_CONTEXT*)argument;
if (!monitor || !monitor->Process || monitor->ProcessHandle == SYS_HANDLE_INVALID ||
monitor->Generation == 0) {
free(monitor);
SysExitThread(1);
return;
}
PosixsrvSetCurrentThreadName("posixsrv.process");
POSIXSRV_PROCESS_RECORD* record = monitor->Process;
SYS_HANDLE processHandle = monitor->ProcessHandle;
uint32_t generation = monitor->Generation;
KSTATUS waitStatus = SysWait(processHandle, SYS_WAIT_FLAG_NONE);
int32_t exitCode = (int32_t)NTLSTATUS_PROCESS_UNKNOWN_FAULT;
if (KSUCCESS(waitStatus))
(void)SysGetProcessExitCode(processHandle, &exitCode);
POSIXSRV_PROCESS_RECORD* freeRecord = (POSIXSRV_PROCESS_RECORD*)0;
SYS_HANDLE closeCommittedHandle = SYS_HANDLE_INVALID;
SYS_HANDLE abortPendingHandle = SYS_HANDLE_INVALID;
int genuineExit = 0;
if (KSUCCESS(PosixsrvLockRegistry())) {
if (KSUCCESS(waitStatus) && record->Generation == generation &&
(record->State == NTL_POSIX_PROCESS_STATE_RUNNING ||
record->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING)) {
genuineExit = 1;
if (record->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING)
abortPendingHandle = record->PendingProcessHandle;
PosixsrvReleaseFdInheritanceForProcessLocked(record);
PosixsrvReleaseAllOpenDescriptionsLocked(record);
PosixsrvReleaseThreadsLocked(record);
for (uint32_t i = 0; i < NTL_POSIX_TRANSACTION_CAPACITY; ++i)
PosixsrvDestroyTransaction(&record->Transactions[i]);
record->ActiveTransactions = 0;
record->State = NTL_POSIX_PROCESS_STATE_EXITED;
record->ExitCode = exitCode;
record->AlarmDeadlineTicks = 0;
++record->AlarmGeneration;
if (record->AlarmTimer != SYS_HANDLE_INVALID)
(void)SysCancelTimer(record->AlarmTimer);
if (record->AlarmControlEvent != SYS_HANDLE_INVALID)
(void)SysSignalEvent(record->AlarmControlEvent);
closeCommittedHandle = record->ProcessHandle;
record->ProcessHandle = SYS_HANDLE_INVALID;
PosixsrvSignalWaitersForChildLocked(record);
PosixsrvReleaseBorrowedChildrenForHostLocked(record);
POSIXSRV_PROCESS_RECORD* parent =
PosixsrvFindPosixProcessLocked(record->ParentPosixPid);
if (parent && PosixsrvProcessHasCommittedGeneration(parent))
(void)PosixsrvRouteSignalLocked(parent, NTL_POSIX_SIGNAL_CHLD);
PosixsrvOrphanChildrenLocked(record->PosixPid);
}
if (record->MonitorActive != 0) --record->MonitorActive;
if (PosixsrvCanFreeProcessLocked(record)) {
PosixsrvUnlinkProcessLocked(record);
freeRecord = record;
}
PosixsrvUnlockRegistry();
}
if (abortPendingHandle != SYS_HANDLE_INVALID)
(void)SysKillProcess(abortPendingHandle, (int32_t)NTLSTATUS_PROCESS_KILLED);
(void)SysCloseHandle(processHandle);
if (closeCommittedHandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(closeCommittedHandle);
free(monitor);
if (freeRecord) PosixsrvFreeProcessRecord(freeRecord);
SysExitThread(KSUCCESS(waitStatus) || genuineExit ? 0 : 1);
}
// Starts one generation-tagged native process monitor and transfers handle ownership to it.
static KSTATUS PosixsrvStartProcessMonitorLocked(POSIXSRV_PROCESS_RECORD* record,
SYS_HANDLE processHandle,
uint32_t generation) {
if (!record || processHandle == SYS_HANDLE_INVALID || generation == 0)
return NTLSTATUS_INVALID_PARAMETER;
SYS_HANDLE monitorHandle = SYS_HANDLE_INVALID;
SYS_DERIVE_HANDLE_REQUEST derive;
memset(&derive, 0, sizeof(derive));
derive.Size = sizeof(derive);
derive.SourceHandle = processHandle;
derive.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT;
derive.DerivedHandleOut = &monitorHandle;
KSTATUS status = SysDeriveHandle(&derive);
if (!KSUCCESS(status)) return status;
POSIXSRV_PROCESS_MONITOR_CONTEXT* monitor =
(POSIXSRV_PROCESS_MONITOR_CONTEXT*)calloc(1, sizeof(*monitor));
if (!monitor) {
(void)SysCloseHandle(monitorHandle);
return NTLSTATUS_NO_MEMORY;
}
monitor->Process = record;
monitor->ProcessHandle = monitorHandle;
monitor->Generation = generation;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)PosixsrvProcessMonitorThread;
create.Argument = (uintptr_t)monitor;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
status = SysCreateThread(&create);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(monitorHandle);
free(monitor);
return status;
}
++record->MonitorActive;
return NTLSTATUS_SUCCESS;
}
// Waits for one borrowed vfork thread and owns the terminal state transition.
static void PosixsrvBorrowedMonitorThread(uintptr_t argument) {
POSIXSRV_BORROWED_MONITOR_CONTEXT* monitor =
(POSIXSRV_BORROWED_MONITOR_CONTEXT*)argument;
if (!monitor || !monitor->Process || monitor->ThreadHandle == SYS_HANDLE_INVALID ||
monitor->BorrowedToken == 0) {
free(monitor);
SysExitThread(1);
return;
}
PosixsrvSetCurrentThreadName("posixsrv.vfork");
POSIXSRV_PROCESS_RECORD* record = monitor->Process;
KSTATUS waitStatus = SysWait(monitor->ThreadHandle, SYS_WAIT_FLAG_NONE);
SYS_HANDLE releaseEvent = SYS_HANDLE_INVALID;
SYS_HANDLE abortPending = SYS_HANDLE_INVALID;
int completed = 0;
if (KSUCCESS(PosixsrvLockRegistry())) {
if (KSUCCESS(waitStatus) && record->BorrowedToken == monitor->BorrowedToken) {
if (record->BorrowedTerminalKind == POSIXSRV_VFORK_TERMINAL_EXEC &&
record->State == NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING &&
record->PendingExecHolderPlan &&
record->PendingProcessHandle != SYS_HANDLE_INVALID &&
record->PendingGeneration != 0 && record->PendingFdInheritanceToken != 0) {
POSIXSRV_FD_INHERITANCE* snapshot =
PosixsrvFindFdInheritanceLocked(record->PendingFdInheritanceToken);
if (snapshot && snapshot->SourceProcess == record && !snapshot->ClaimProcess &&
SysWaitSingle(record->PendingProcessHandle, SYS_WAIT_FLAG_NONE, 0u) == NTLSTATUS_TIMEOUT) {
PosixsrvApplyExecHolderPlanLocked(record, snapshot,
record->PendingExecHolderPlan);
free(record->PendingExecHolderPlan);
record->PendingExecHolderPlan = (POSIXSRV_EXEC_HOLDER_PLAN*)0;
PosixsrvReleaseThreadsLocked(record);
record->ProcessHandle = record->PendingProcessHandle;
record->NativeProcessId = record->PendingNativeProcessId;
record->Generation = record->PendingGeneration;
record->State = NTL_POSIX_PROCESS_STATE_RUNNING;
record->PendingProcessHandle = SYS_HANDLE_INVALID;
record->PendingNativeProcessId = 0;
record->PendingGeneration = 0;
record->PendingBootstrapToken = 0;
record->PendingFdInheritanceToken = 0;
record->BorrowedHostProcess = (POSIXSRV_PROCESS_RECORD*)0;
record->BorrowedHostGeneration = 0;
record->BorrowedNativeThreadId = 0;
record->BorrowedToken = 0;
record->BorrowedTerminalKind = POSIXSRV_VFORK_TERMINAL_NONE;
record->IgnoredSignals = record->ExplicitIgnoredSignals |
NTL_POSIX_SIGNAL_BIT(NTL_POSIX_SIGNAL_CHLD);
PosixsrvDestroyFdInheritanceLocked(snapshot);
PosixsrvDrainPendingSignalsLocked(record);
releaseEvent = record->PendingBootstrapReleaseEvent;
completed = 1;
}
}
if (!completed) {
if (record->PendingProcessHandle != SYS_HANDLE_INVALID) {
abortPending = record->PendingProcessHandle;
record->PendingProcessHandle = SYS_HANDLE_INVALID;
}
if (record->PendingExecHolderPlan) {
PosixsrvDestroyExecHolderPlan(record->PendingExecHolderPlan);
free(record->PendingExecHolderPlan);
record->PendingExecHolderPlan = (POSIXSRV_EXEC_HOLDER_PLAN*)0;
}
if (record->PendingFdInheritanceToken != 0) {
POSIXSRV_FD_INHERITANCE* snapshot =
PosixsrvFindFdInheritanceLocked(record->PendingFdInheritanceToken);
if (snapshot && snapshot->SourceProcess == record && !snapshot->ClaimProcess)
PosixsrvDestroyFdInheritanceLocked(snapshot);
}
PosixsrvReleaseFdInheritanceForProcessLocked(record);
PosixsrvReleaseAllOpenDescriptionsLocked(record);
PosixsrvReleaseThreadsLocked(record);
for (uint32_t i = 0; i < NTL_POSIX_TRANSACTION_CAPACITY; ++i)
PosixsrvDestroyTransaction(&record->Transactions[i]);
record->ActiveTransactions = 0;
record->State = NTL_POSIX_PROCESS_STATE_EXITED;
record->ExitCode = record->BorrowedTerminalKind == POSIXSRV_VFORK_TERMINAL_EXIT
? record->BorrowedExitCode
: (int32_t)NTLSTATUS_PROCESS_UNKNOWN_FAULT;
record->PendingNativeProcessId = 0;
record->PendingGeneration = 0;
record->PendingBootstrapToken = 0;
record->PendingFdInheritanceToken = 0;
record->BorrowedHostProcess = (POSIXSRV_PROCESS_RECORD*)0;
record->BorrowedHostGeneration = 0;
record->BorrowedNativeThreadId = 0;
record->BorrowedToken = 0;
PosixsrvSignalWaitersForChildLocked(record);
POSIXSRV_PROCESS_RECORD* parent =
PosixsrvFindPosixProcessLocked(record->ParentPosixPid);
if (parent && PosixsrvProcessHasCommittedGeneration(parent))
(void)PosixsrvRouteSignalLocked(parent, NTL_POSIX_SIGNAL_CHLD);
PosixsrvOrphanChildrenLocked(record->PosixPid);
completed = 1;
}
}
if (record->BorrowedMonitorActive != 0) --record->BorrowedMonitorActive;
POSIXSRV_PROCESS_RECORD* freeRecord = (POSIXSRV_PROCESS_RECORD*)0;
if (PosixsrvCanFreeProcessLocked(record)) {
PosixsrvUnlinkProcessLocked(record);
freeRecord = record;
}
PosixsrvUnlockRegistry();
if (freeRecord) PosixsrvFreeProcessRecord(freeRecord);
}
if (abortPending != SYS_HANDLE_INVALID)
(void)SysKillProcess(abortPending, (int32_t)NTLSTATUS_PROCESS_KILLED);
if (releaseEvent != SYS_HANDLE_INVALID) (void)SysSignalEvent(releaseEvent);
(void)SysCloseHandle(monitor->ThreadHandle);
free(monitor);
SysExitThread(KSUCCESS(waitStatus) && completed ? 0 : 1);
}
// Starts the server-owned monitor that finalizes a borrowed child only after thread death.
static KSTATUS PosixsrvStartBorrowedMonitorLocked(POSIXSRV_PROCESS_RECORD* record,
SYS_HANDLE threadHandle) {
if (!record || threadHandle == SYS_HANDLE_INVALID || record->BorrowedToken == 0 ||
record->BorrowedMonitorActive)
return NTLSTATUS_INVALID_PARAMETER;
SYS_HANDLE waitHandle = SYS_HANDLE_INVALID;
SYS_DERIVE_HANDLE_REQUEST derive;
memset(&derive, 0, sizeof(derive));
derive.Size = sizeof(derive);
derive.SourceHandle = threadHandle;
derive.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT;
derive.DerivedHandleOut = &waitHandle;
KSTATUS status = SysDeriveHandle(&derive);
if (!KSUCCESS(status)) return status;
POSIXSRV_BORROWED_MONITOR_CONTEXT* monitor =
(POSIXSRV_BORROWED_MONITOR_CONTEXT*)calloc(1u, sizeof(*monitor));
if (!monitor) {
(void)SysCloseHandle(waitHandle);
return NTLSTATUS_NO_MEMORY;
}
monitor->Process = record;
monitor->ThreadHandle = waitHandle;
monitor->BorrowedToken = record->BorrowedToken;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)PosixsrvBorrowedMonitorThread;
create.Argument = (uintptr_t)monitor;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
status = SysCreateThread(&create);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(waitHandle);
free(monitor);
return status;
}
record->BorrowedMonitorActive = 1u;
return NTLSTATUS_SUCCESS;
}
// Registers the connection's native process and assigns or recovers its stable POSIX identity.
static KSTATUS PosixsrvRegisterProcess(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request) {
if (!client || !request)
return NTLSTATUS_INVALID_PARAMETER;
if (!PosixsrvValidateCurrentDirectory(request) || request->NameLength != 0 ||
request->TargetPosixPid != 0 || request->UserId != 0 || request->GroupId != 0 ||
request->ProcessGroupId != 0 || request->LookupKind != NTL_POSIX_LOOKUP_NONE ||
request->OpenDescriptionType != 0 || request->OpenStatusFlags != 0 ||
request->OpenDescriptionId != 0)
return NTLSTATUS_INVALID_PARAMETER;
SYS_HANDLE peerProcessHandle = SYS_HANDLE_INVALID;
KSTATUS status = SysOpenPortPeerProcess(
client->Endpoint,
SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_RIGHT_DERIVE |
SYS_PROCESS_RIGHT_TERMINATE | SYS_PROCESS_RIGHT_SECURITY_CONTEXT,
&peerProcessHandle);
if (!KSUCCESS(status)) return status;
uint32_t nativePid = 0;
status = SysGetProcessPid(peerProcessHandle, &nativePid);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(peerProcessHandle);
return status;
}
if (nativePid != client->Connection.ClientProcessId) {
(void)SysCloseHandle(peerProcessHandle);
return NTLSTATUS_ACCESS_DENIED;
}
if (request->BootstrapToken != 0 || request->BootstrapPosixPid != 0 ||
request->BootstrapGeneration != 0) {
if (request->BootstrapToken == 0 || request->BootstrapPosixPid == 0 ||
request->BootstrapGeneration == 0) {
(void)SysCloseHandle(peerProcessHandle);
return NTLSTATUS_INVALID_PARAMETER;
}
KSTATUS lockStatus = PosixsrvLockRegistry();
if (!KSUCCESS(lockStatus)) {
(void)SysCloseHandle(peerProcessHandle);
return lockStatus;
}
POSIXSRV_PROCESS_RECORD* record = PosixsrvFindPosixProcessLocked(request->BootstrapPosixPid);
int constructing = record && record->State == NTL_POSIX_PROCESS_STATE_CONSTRUCTING &&
record->BootstrapToken == request->BootstrapToken &&
record->Generation == request->BootstrapGeneration &&
record->NativeProcessId == nativePid;
int replacing = record &&
(record->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING ||
record->State == NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING) &&
record->PendingBootstrapToken == request->BootstrapToken &&
record->PendingGeneration == request->BootstrapGeneration &&
record->PendingNativeProcessId == nativePid;
if (!record || (!constructing && !replacing) ||
record->SecurityId != client->Connection.ClientSecurityId ||
strcmp(record->CurrentDirectory, request->Path) != 0) {
PosixsrvUnlockRegistry();
(void)SysCloseHandle(peerProcessHandle);
return record ? NTLSTATUS_ACCESS_DENIED : NTLSTATUS_NOT_FOUND;
}
if (!client->Process) {
++record->ConnectionCount;
client->Process = record;
client->NativeGeneration = request->BootstrapGeneration;
} else if (client->Process != record ||
client->NativeGeneration != request->BootstrapGeneration) {
PosixsrvUnlockRegistry();
(void)SysCloseHandle(peerProcessHandle);
return NTLSTATUS_ACCESS_DENIED;
}
PosixsrvUnlockRegistry();
(void)SysCloseHandle(peerProcessHandle);
return NTLSTATUS_SUCCESS;
}
POSIXSRV_PROCESS_RECORD* candidate =
(POSIXSRV_PROCESS_RECORD*)calloc(1, sizeof(*candidate));
if (!candidate) {
(void)SysCloseHandle(peerProcessHandle);
return NTLSTATUS_NO_MEMORY;
}
candidate->ProcessHandle = SYS_HANDLE_INVALID;
candidate->BootstrapReadyEvent = SYS_HANDLE_INVALID;
candidate->PendingBootstrapReadyEvent = SYS_HANDLE_INVALID;
candidate->PendingBootstrapReleaseEvent = SYS_HANDLE_INVALID;
candidate->PendingProcessHandle = SYS_HANDLE_INVALID;
candidate->AlarmTimer = SYS_HANDLE_INVALID;
candidate->AlarmControlEvent = SYS_HANDLE_INVALID;
candidate->IgnoredSignals = NTL_POSIX_SIGNAL_BIT(NTL_POSIX_SIGNAL_CHLD);
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) {
free(candidate);
(void)SysCloseHandle(peerProcessHandle);
return status;
}
if (client->Process) {
PosixsrvUnlockRegistry();
free(candidate);
(void)SysCloseHandle(peerProcessHandle);
return NTLSTATUS_SUCCESS;
}
POSIXSRV_PROCESS_RECORD* existing =
PosixsrvFindCommittedProcessLocked(nativePid, client->Connection.ClientSecurityId);
if (existing) {
existing->ConnectionCount++;
client->Process = existing;
client->NativeGeneration = existing->Generation;
PosixsrvUnlockRegistry();
free(candidate);
(void)SysCloseHandle(peerProcessHandle);
return NTLSTATUS_SUCCESS;
}
uint32_t posixPid = 0;
status = PosixsrvAllocatePidLocked(&posixPid);
if (!KSUCCESS(status)) {
PosixsrvUnlockRegistry();
free(candidate);
(void)SysCloseHandle(peerProcessHandle);
return status;
}
candidate->PosixPid = posixPid;
candidate->ParentPosixPid = 0;
candidate->NativeProcessId = nativePid;
candidate->SecurityId = client->Connection.ClientSecurityId;
candidate->Generation = 1u;
candidate->State = NTL_POSIX_PROCESS_STATE_RUNNING;
candidate->ConnectionCount = 1u;
candidate->MonitorActive = 0u;
candidate->UserId = 0;
candidate->EffectiveUserId = 0;
candidate->GroupId = 0;
candidate->EffectiveGroupId = 0;
candidate->ProcessGroupId = posixPid;
candidate->SessionId = posixPid;
candidate->SupplementaryGroupCount = 0;
(void)PosixsrvCopyText(candidate->LoginName, sizeof(candidate->LoginName), "root");
(void)PosixsrvCopyText(candidate->CurrentDirectory, sizeof(candidate->CurrentDirectory), request->Path);
candidate->ProcessHandle = peerProcessHandle;
peerProcessHandle = SYS_HANDLE_INVALID;
candidate->Next = gProcessRegistry;
gProcessRegistry = candidate;
++gRegisteredProcesses;
client->Process = candidate;
client->NativeGeneration = candidate->Generation;
status = PosixsrvStartProcessMonitorLocked(candidate, candidate->ProcessHandle,
candidate->Generation);
if (!KSUCCESS(status)) {
client->Process = (POSIXSRV_PROCESS_RECORD*)0;
client->NativeGeneration = 0;
PosixsrvUnlinkProcessLocked(candidate);
SYS_HANDLE failedProcessHandle = candidate->ProcessHandle;
candidate->ProcessHandle = SYS_HANDLE_INVALID;
PosixsrvUnlockRegistry();
if (failedProcessHandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(failedProcessHandle);
free(candidate);
return status;
}
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
// Commits one server-built image after its POSIX runtime imported inherited state.
static KSTATUS PosixsrvBootstrapReady(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request) {
if (!client || !client->Process || !request || request->BootstrapToken == 0 ||
request->BootstrapPosixPid == 0 || request->BootstrapGeneration == 0)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_PROCESS_RECORD* process = client->Process;
if (process->State == NTL_POSIX_PROCESS_STATE_CONSTRUCTING &&
process->BootstrapToken == request->BootstrapToken &&
process->PosixPid == request->BootstrapPosixPid &&
process->Generation == request->BootstrapGeneration &&
client->NativeGeneration == process->Generation &&
process->BootstrapReadyEvent != SYS_HANDLE_INVALID) {
process->State = NTL_POSIX_PROCESS_STATE_RUNNING;
PosixsrvDrainPendingSignalsLocked(process);
SYS_HANDLE readyEvent = process->BootstrapReadyEvent;
PosixsrvUnlockRegistry();
return SysSignalEvent(readyEvent);
}
if ((process->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING ||
process->State == NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING) &&
process->PendingBootstrapToken == request->BootstrapToken &&
process->PosixPid == request->BootstrapPosixPid &&
process->PendingGeneration == request->BootstrapGeneration &&
client->NativeGeneration == process->PendingGeneration &&
process->PendingBootstrapReadyEvent != SYS_HANDLE_INVALID &&
process->PendingBootstrapReleaseEvent != SYS_HANDLE_INVALID) {
SYS_HANDLE readyEvent = process->PendingBootstrapReadyEvent;
SYS_HANDLE releaseEvent = process->PendingBootstrapReleaseEvent;
process->PendingBootstrapReadyObserved = 1u;
PosixsrvUnlockRegistry();
status = SysSignalEvent(readyEvent);
if (KSUCCESS(status)) status = SysWait(releaseEvent, SYS_WAIT_FLAG_NONE);
if (KSUCCESS(PosixsrvLockRegistry())) {
if (process->PendingBootstrapReadyEvent == readyEvent)
process->PendingBootstrapReadyEvent = SYS_HANDLE_INVALID;
if (process->PendingBootstrapReleaseEvent == releaseEvent)
process->PendingBootstrapReleaseEvent = SYS_HANDLE_INVALID;
process->PendingBootstrapReadyObserved = 0;
PosixsrvUnlockRegistry();
}
(void)SysCloseHandle(readyEvent);
(void)SysCloseHandle(releaseEvent);
return status;
}
PosixsrvUnlockRegistry();
return NTLSTATUS_ACCESS_DENIED;
}
// Detaches one Port connection without treating transport loss as process termination.
static void PosixsrvDetachClientProcess(POSIXSRV_CLIENT* client) {
if (!client || !client->Process) return;
POSIXSRV_PROCESS_RECORD* freeRecord = (POSIXSRV_PROCESS_RECORD*)0;
if (!KSUCCESS(PosixsrvLockRegistry())) return;
POSIXSRV_PROCESS_RECORD* record = client->Process;
client->Process = (POSIXSRV_PROCESS_RECORD*)0;
if (record->ConnectionCount != 0) --record->ConnectionCount;
if (PosixsrvCanFreeProcessLocked(record)) {
PosixsrvUnlinkProcessLocked(record);
freeRecord = record;
}
PosixsrvUnlockRegistry();
if (freeRecord) PosixsrvFreeProcessRecord(freeRecord);
}
// Creates one server-owned logical child that temporarily borrows the parent's native process.
static KSTATUS PosixsrvBeginVfork(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request,
uint32_t* childPidOut,
uint64_t* tokenOut) {
if (!client || !client->Process || !request || !childPidOut || !tokenOut ||
request->FdInheritanceToken == 0)
return NTLSTATUS_INVALID_PARAMETER;
*childPidOut = 0;
*tokenOut = 0;
POSIXSRV_PROCESS_RECORD* child =
(POSIXSRV_PROCESS_RECORD*)calloc(1u, sizeof(*child));
if (!child) return NTLSTATUS_NO_MEMORY;
child->ProcessHandle = SYS_HANDLE_INVALID;
child->BootstrapReadyEvent = SYS_HANDLE_INVALID;
child->PendingProcessHandle = SYS_HANDLE_INVALID;
child->PendingBootstrapReadyEvent = SYS_HANDLE_INVALID;
child->PendingBootstrapReleaseEvent = SYS_HANDLE_INVALID;
child->AlarmTimer = SYS_HANDLE_INVALID;
child->AlarmControlEvent = SYS_HANDLE_INVALID;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) { free(child); return status; }
POSIXSRV_PROCESS_RECORD* parent = client->Process;
POSIXSRV_FD_INHERITANCE* snapshot =
PosixsrvFindFdInheritanceLocked(request->FdInheritanceToken);
if (!PosixsrvCommittedGenerationOperational(client) ||
parent->State != NTL_POSIX_PROCESS_STATE_RUNNING || !snapshot ||
snapshot->SourceProcess != parent || snapshot->ClaimProcess) {
status = snapshot ? NTLSTATUS_ACCESS_DENIED : NTLSTATUS_NOT_FOUND;
PosixsrvUnlockRegistry();
free(child);
return status;
}
if (gNextVforkToken == 0) {
PosixsrvUnlockRegistry();
free(child);
return NTLSTATUS_ID_EXHAUSTED;
}
status = PosixsrvAllocatePidLocked(&child->PosixPid);
if (!KSUCCESS(status)) {
PosixsrvUnlockRegistry();
free(child);
return status;
}
child->ParentPosixPid = parent->PosixPid;
child->NativeProcessId = parent->NativeProcessId;
child->SecurityId = parent->SecurityId;
child->Generation = 1u;
child->State = NTL_POSIX_PROCESS_STATE_VFORK_BORROWED;
child->BorrowedHostProcess = parent;
child->BorrowedHostGeneration = parent->Generation;
child->BorrowedToken = gNextVforkToken++;
child->UserId = parent->UserId;
child->EffectiveUserId = parent->EffectiveUserId;
child->GroupId = parent->GroupId;
child->EffectiveGroupId = parent->EffectiveGroupId;
child->ProcessGroupId = parent->ProcessGroupId;
child->SessionId = parent->SessionId;
child->SupplementaryGroupCount = parent->SupplementaryGroupCount;
memcpy(child->SupplementaryGroups, parent->SupplementaryGroups,
sizeof(child->SupplementaryGroups));
child->IgnoredSignals = parent->IgnoredSignals;
child->ExplicitIgnoredSignals = parent->ExplicitIgnoredSignals;
(void)PosixsrvCopyText(child->LoginName, sizeof(child->LoginName), parent->LoginName);
(void)PosixsrvCopyText(child->CurrentDirectory, sizeof(child->CurrentDirectory),
parent->CurrentDirectory);
child->Next = gProcessRegistry;
gProcessRegistry = child;
++gRegisteredProcesses;
status = PosixsrvClaimFdInheritanceLocked(snapshot, child);
if (!KSUCCESS(status)) {
PosixsrvUnlinkProcessLocked(child);
PosixsrvUnlockRegistry();
free(child);
return status;
}
*childPidOut = child->PosixPid;
*tokenOut = child->BorrowedToken;
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
// Binds one native thread in the host process to its borrowed logical child.
static KSTATUS PosixsrvAttachVfork(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request,
const SYS_PORT_MESSAGE_INFO* info,
SYS_HANDLE* transferredThreadHandle) {
if (!client || !client->Process || !request || !info || !transferredThreadHandle ||
*transferredThreadHandle == SYS_HANDLE_INVALID || request->TargetPosixPid == 0 ||
request->VforkToken == 0 || info->SenderThreadId == 0)
return NTLSTATUS_INVALID_PARAMETER;
uint32_t nativeThreadId = 0;
KSTATUS status = SysGetThreadTid(*transferredThreadHandle, &nativeThreadId);
if (!KSUCCESS(status)) return status;
if (nativeThreadId != info->SenderThreadId) return NTLSTATUS_ACCESS_DENIED;
POSIXSRV_THREAD_RECORD* thread =
(POSIXSRV_THREAD_RECORD*)calloc(1u, sizeof(*thread));
if (!thread) return NTLSTATUS_NO_MEMORY;
thread->NativeThreadId = nativeThreadId;
thread->Generation = 1u;
thread->ThreadHandle = *transferredThreadHandle;
thread->BlockedSignals = request->SignalMask & NTL_POSIX_SIGNAL_VALID_MASK &
~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
thread->SignalApcRoutine = request->SignalApcRoutine;
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) { free(thread); return status; }
POSIXSRV_PROCESS_RECORD* host = client->Process;
POSIXSRV_PROCESS_RECORD* child =
PosixsrvFindPosixProcessLocked(request->TargetPosixPid);
if (!PosixsrvCommittedGenerationOperational(client) ||
host->State != NTL_POSIX_PROCESS_STATE_RUNNING || !child ||
child->State != NTL_POSIX_PROCESS_STATE_VFORK_BORROWED ||
child->BorrowedHostProcess != host ||
child->BorrowedHostGeneration != client->NativeGeneration ||
child->BorrowedToken != request->VforkToken || child->Threads ||
child->BorrowedNativeThreadId != 0) {
status = child ? NTLSTATUS_ACCESS_DENIED : NTLSTATUS_NOT_FOUND;
PosixsrvUnlockRegistry();
free(thread);
return status;
}
child->BorrowedNativeThreadId = nativeThreadId;
thread->Generation = child->Generation;
child->Threads = thread;
++gRegisteredThreads;
status = PosixsrvStartBorrowedMonitorLocked(child, thread->ThreadHandle);
if (!KSUCCESS(status)) {
child->Threads = (POSIXSRV_THREAD_RECORD*)0;
child->BorrowedNativeThreadId = 0;
if (gRegisteredThreads != 0) --gRegisteredThreads;
PosixsrvUnlockRegistry();
free(thread);
return status;
}
*transferredThreadHandle = SYS_HANDLE_INVALID;
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
// Cancels one unpublished borrowed child after setup failure or after its native thread is dead.
static KSTATUS PosixsrvCancelVfork(POSIXSRV_CLIENT* client,
uint32_t childPid,
uint64_t token) {
if (!client || !client->Process || childPid == 0 || token == 0)
return NTLSTATUS_INVALID_PARAMETER;
POSIXSRV_PROCESS_RECORD* freeChild = (POSIXSRV_PROCESS_RECORD*)0;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_PROCESS_RECORD* child = PosixsrvFindPosixProcessLocked(childPid);
if (!child) status = NTLSTATUS_NOT_FOUND;
else if (child->State != NTL_POSIX_PROCESS_STATE_VFORK_BORROWED ||
child->BorrowedHostProcess != client->Process ||
child->BorrowedHostGeneration != client->NativeGeneration ||
child->BorrowedToken != token)
status = NTLSTATUS_ACCESS_DENIED;
else if (child->Threads && child->Threads->ThreadHandle != SYS_HANDLE_INVALID) {
KSTATUS waitStatus = SysWaitSingle(child->Threads->ThreadHandle, SYS_WAIT_FLAG_NONE, 0u);
if (waitStatus == NTLSTATUS_TIMEOUT) status = NTLSTATUS_WOULD_BLOCK;
else if (!KSUCCESS(waitStatus)) status = waitStatus;
}
if (KSUCCESS(status) && child) {
PosixsrvReleaseFdInheritanceForProcessLocked(child);
PosixsrvReleaseAllOpenDescriptionsLocked(child);
PosixsrvReleaseThreadsLocked(child);
PosixsrvUnlinkProcessLocked(child);
freeChild = child;
}
PosixsrvUnlockRegistry();
if (freeChild) PosixsrvFreeProcessRecord(freeChild);
return status;
}
// Records terminal exit intent; the borrowed monitor publishes exit only after thread death.
static KSTATUS PosixsrvExitVfork(POSIXSRV_CLIENT* client,
uint32_t childPid,
uint64_t token,
int32_t exitCode) {
if (!client || !client->Process || childPid == 0 || token == 0)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_PROCESS_RECORD* child = client->Process;
if (!PosixsrvClientIsBorrowedProcess(client) || child->PosixPid != childPid ||
child->BorrowedToken != token || child->BorrowedTerminalKind != POSIXSRV_VFORK_TERMINAL_NONE)
status = NTLSTATUS_ACCESS_DENIED;
else {
child->BorrowedTerminalKind = POSIXSRV_VFORK_TERMINAL_EXIT;
child->BorrowedExitCode = exitCode;
status = NTLSTATUS_SUCCESS;
}
PosixsrvUnlockRegistry();
return status;
}
// Waits for and atomically collects one child selected by POSIX waitpid rules.
static KSTATUS PosixsrvWaitProcess(POSIXSRV_CLIENT* client,
int32_t waitPid,
uint32_t options,
int32_t* waitedPidOut,
int32_t* exitCodeOut) {
if (!client || !waitedPidOut || !exitCodeOut ||
(options & ~NTL_POSIX_WAIT_NOHANG) != 0)
return NTLSTATUS_INVALID_PARAMETER;
*waitedPidOut = 0;
*exitCodeOut = 0;
POSIXSRV_WAITER selector;
memset(&selector, 0, sizeof(selector));
selector.WaitPid = waitPid;
selector.Event = SYS_HANDLE_INVALID;
POSIXSRV_WAITER* waiter = (POSIXSRV_WAITER*)0;
if ((options & NTL_POSIX_WAIT_NOHANG) == 0) {
waiter = (POSIXSRV_WAITER*)calloc(1u, sizeof(*waiter));
if (!waiter) return NTLSTATUS_NO_MEMORY;
waiter->WaitPid = waitPid;
KSTATUS status = SysCreateEvent(0u, 0u, &waiter->Event);
if (!KSUCCESS(status)) {
free(waiter);
return status;
}
}
KSTATUS result = NTLSTATUS_SUCCESS;
bool waiterOwned = waiter != (POSIXSRV_WAITER*)0;
for (;;) {
POSIXSRV_PROCESS_RECORD* freeRecord = (POSIXSRV_PROCESS_RECORD*)0;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) { result = status; break; }
POSIXSRV_PROCESS_RECORD* parent = client->Process;
if (!parent || !PosixsrvCommittedGenerationOperational(client)) {
PosixsrvUnlockRegistry();
result = NTLSTATUS_OBJECT_DEAD;
break;
}
POSIXSRV_WAITER* match = waiter ? waiter : &selector;
match->ParentPosixPid = parent->PosixPid;
match->ParentProcessGroupId = parent->ProcessGroupId;
POSIXSRV_PROCESS_RECORD* exited = (POSIXSRV_PROCESS_RECORD*)0;
bool hasMatchingChild = false;
for (POSIXSRV_PROCESS_RECORD* child = gProcessRegistry; child; child = child->Next) {
if (!PosixsrvProcessHasCommittedGeneration(child) &&
child->State != NTL_POSIX_PROCESS_STATE_VFORK_BORROWED &&
child->State != NTL_POSIX_PROCESS_STATE_EXITED)
continue;
if (!PosixsrvWaiterMatchesChild(match, child)) continue;
hasMatchingChild = true;
if (child->State == NTL_POSIX_PROCESS_STATE_EXITED &&
(!exited || child->PosixPid < exited->PosixPid))
exited = child;
}
if (exited) {
*waitedPidOut = (int32_t)exited->PosixPid;
*exitCodeOut = exited->ExitCode;
exited->ParentPosixPid = 0;
if (PosixsrvCanFreeProcessLocked(exited)) {
PosixsrvUnlinkProcessLocked(exited);
freeRecord = exited;
}
PosixsrvUnlockRegistry();
if (freeRecord) PosixsrvFreeProcessRecord(freeRecord);
result = NTLSTATUS_SUCCESS;
break;
}
if (!hasMatchingChild) {
PosixsrvUnlockRegistry();
result = NTLSTATUS_NO_CHILD;
break;
}
if (options & NTL_POSIX_WAIT_NOHANG) {
PosixsrvUnlockRegistry();
result = NTLSTATUS_SUCCESS;
break;
}
waiter->Next = gWaiters;
gWaiters = waiter;
PosixsrvUnlockRegistry();
status = SysWait(waiter->Event, SYS_WAIT_FLAG_NONE);
KSTATUS lockStatus = PosixsrvLockRegistry();
if (!KSUCCESS(lockStatus)) {
// Keep the heap waiter and event alive if registry recovery is impossible.
waiterOwned = 0;
result = lockStatus;
break;
}
POSIXSRV_WAITER** link = &gWaiters;
while (*link && *link != waiter) link = &(*link)->Next;
if (*link == waiter) *link = waiter->Next;
waiter->Next = (POSIXSRV_WAITER*)0;
PosixsrvUnlockRegistry();
if (!KSUCCESS(status)) { result = status; break; }
}
if (waiterOwned) {
if (waiter->Event != SYS_HANDLE_INVALID) (void)SysCloseHandle(waiter->Event);
free(waiter);
}
return result;
}
// Replaces the current process's authoritative POSIX current directory.
static KSTATUS PosixsrvSetCurrentDirectory(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request) {
if (!client || !request || !client->Process)
return NTLSTATUS_INVALID_PARAMETER;
if (!PosixsrvValidateCurrentDirectory(request))
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
if (client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING) {
PosixsrvUnlockRegistry();
return PosixsrvCommittedGenerationOperational(client) ?
NTLSTATUS_WOULD_BLOCK : NTLSTATUS_OBJECT_DEAD;
}
(void)PosixsrvCopyText(client->Process->CurrentDirectory,
sizeof(client->Process->CurrentDirectory), request->Path);
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
// Applies setuid semantics to synthetic POSIX credentials.
static KSTATUS PosixsrvSetUid(POSIXSRV_CLIENT* client, uint32_t userId) {
if (!client || !client->Process) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_PROCESS_RECORD* process = client->Process;
if (process->State != NTL_POSIX_PROCESS_STATE_RUNNING) {
status = PosixsrvCommittedGenerationOperational(client) ?
NTLSTATUS_WOULD_BLOCK : NTLSTATUS_OBJECT_DEAD;
} else if (process->EffectiveUserId == 0) {
process->UserId = userId;
process->EffectiveUserId = userId;
} else if (userId != process->UserId) {
status = NTLSTATUS_ACCESS_DENIED;
}
PosixsrvUnlockRegistry();
return status;
}
// Applies setgid semantics to synthetic POSIX credentials.
static KSTATUS PosixsrvSetGid(POSIXSRV_CLIENT* client, uint32_t groupId) {
if (!client || !client->Process) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_PROCESS_RECORD* process = client->Process;
if (process->State != NTL_POSIX_PROCESS_STATE_RUNNING) {
status = PosixsrvCommittedGenerationOperational(client) ?
NTLSTATUS_WOULD_BLOCK : NTLSTATUS_OBJECT_DEAD;
} else if (process->EffectiveUserId == 0) {
process->GroupId = groupId;
process->EffectiveGroupId = groupId;
} else if (groupId != process->GroupId) {
status = NTLSTATUS_ACCESS_DENIED;
}
PosixsrvUnlockRegistry();
return status;
}
// Changes one process group using the subset whose parent relationship is already authoritative.
static KSTATUS PosixsrvSetProcessGroup(POSIXSRV_CLIENT* client,
uint32_t targetPid,
uint32_t processGroupId) {
if (!client || !client->Process) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_PROCESS_RECORD* caller = client->Process;
if (targetPid == 0) targetPid = caller->PosixPid;
POSIXSRV_PROCESS_RECORD* target = PosixsrvFindPosixProcessLocked(targetPid);
if (!target || !PosixsrvProcessHasCommittedGeneration(target)) {
status = NTLSTATUS_NOT_FOUND;
goto done;
}
if (target->State != NTL_POSIX_PROCESS_STATE_RUNNING) {
status = NTLSTATUS_WOULD_BLOCK;
goto done;
}
if (target != caller && target->ParentPosixPid != caller->PosixPid) {
status = NTLSTATUS_ACCESS_DENIED;
goto done;
}
if (target->SessionId != caller->SessionId || target->SessionId == target->PosixPid) {
status = NTLSTATUS_ACCESS_DENIED;
goto done;
}
if (processGroupId == 0) processGroupId = target->PosixPid;
if (processGroupId != target->PosixPid &&
!PosixsrvProcessGroupExistsLocked(target->SessionId, processGroupId)) {
status = NTLSTATUS_ACCESS_DENIED;
goto done;
}
target->ProcessGroupId = processGroupId;
done:
PosixsrvUnlockRegistry();
return status;
}
// Creates a new session for the caller when it is not currently a process-group leader.
static KSTATUS PosixsrvCreateSession(POSIXSRV_CLIENT* client) {
if (!client || !client->Process) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_PROCESS_RECORD* process = client->Process;
if (process->State != NTL_POSIX_PROCESS_STATE_RUNNING) {
status = PosixsrvCommittedGenerationOperational(client) ?
NTLSTATUS_WOULD_BLOCK : NTLSTATUS_OBJECT_DEAD;
} else if (PosixsrvProcessGroupExistsLocked(process->SessionId, process->PosixPid)) {
status = NTLSTATUS_ACCESS_DENIED;
} else {
process->SessionId = process->PosixPid;
process->ProcessGroupId = process->PosixPid;
}
PosixsrvUnlockRegistry();
return status;
}
// Releases dynamically owned data from one construction transaction.
static void PosixsrvDestroyTransaction(POSIXSRV_TRANSACTION* transaction) {
if (!transaction) return;
POSIXSRV_CONSTRUCTION* construction = transaction->Construction;
if (construction) {
free(construction->Executable);
for (uint32_t i = 0; i < construction->ArgumentCount; ++i)
free(construction->Arguments[i].Data);
for (uint32_t i = 0; i < construction->EnvironmentCount; ++i)
free(construction->Environment[i].Data);
free(construction->Arguments);
free(construction->Environment);
free(construction);
}
POSIXSRV_SPAWN_POLICY* spawn = transaction->Spawn;
if (spawn) {
for (uint32_t i = 0; i < spawn->ActionCount; ++i) free(spawn->Actions[i].Path);
free(spawn);
}
memset(transaction, 0, sizeof(*transaction));
}
// Finds one transaction owned by a POSIX process while the registry mutex is held.
static POSIXSRV_TRANSACTION* PosixsrvFindTransactionLocked(POSIXSRV_PROCESS_RECORD* process,
uint32_t transactionId,
uint32_t kind) {
if (!process || transactionId == 0) return (POSIXSRV_TRANSACTION*)0;
for (uint32_t i = 0; i < NTL_POSIX_TRANSACTION_CAPACITY; ++i) {
POSIXSRV_TRANSACTION* transaction = &process->Transactions[i];
if (transaction->Id == transactionId && (kind == NTL_POSIX_TRANSACTION_NONE ||
transaction->Kind == kind))
return transaction;
}
return (POSIXSRV_TRANSACTION*)0;
}
// Allocates one bounded process-owned construction transaction.
static KSTATUS PosixsrvBeginTransaction(POSIXSRV_CLIENT* client,
uint32_t kind,
uint32_t* transactionIdOut) {
if (!client || !client->Process || !transactionIdOut) return NTLSTATUS_INVALID_PARAMETER;
*transactionIdOut = 0;
if (kind != NTL_POSIX_TRANSACTION_SPAWN && kind != NTL_POSIX_TRANSACTION_EXEC)
return NTLSTATUS_INVALID_PARAMETER;
POSIXSRV_TRANSACTION fresh;
memset(&fresh, 0, sizeof(fresh));
fresh.Kind = kind;
fresh.Construction = (POSIXSRV_CONSTRUCTION*)calloc(1, sizeof(*fresh.Construction));
if (!fresh.Construction) return NTLSTATUS_NO_MEMORY;
if (kind == NTL_POSIX_TRANSACTION_SPAWN) {
fresh.Spawn = (POSIXSRV_SPAWN_POLICY*)calloc(1, sizeof(*fresh.Spawn));
if (!fresh.Spawn) {
PosixsrvDestroyTransaction(&fresh);
return NTLSTATUS_NO_MEMORY;
}
}
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) {
PosixsrvDestroyTransaction(&fresh);
return status;
}
POSIXSRV_PROCESS_RECORD* process = client->Process;
if ((kind == NTL_POSIX_TRANSACTION_EXEC &&
!((PosixsrvCommittedGenerationOperational(client) &&
process->State == NTL_POSIX_PROCESS_STATE_RUNNING) ||
PosixsrvClientIsBorrowedProcess(client))) ||
(kind == NTL_POSIX_TRANSACTION_SPAWN &&
!PosixsrvCommittedGenerationOperational(client))) {
status = NTLSTATUS_OBJECT_DEAD;
goto done;
}
uint32_t slot = NTL_POSIX_TRANSACTION_CAPACITY;
for (uint32_t i = 0; i < NTL_POSIX_TRANSACTION_CAPACITY; ++i) {
if (process->Transactions[i].Id == 0) {
slot = i;
break;
}
}
if (slot == NTL_POSIX_TRANSACTION_CAPACITY) {
status = NTLSTATUS_LIMIT_REACHED;
goto done;
}
uint32_t id = ++process->NextTransactionId;
if (id == 0) id = ++process->NextTransactionId;
fresh.Id = id;
process->Transactions[slot] = fresh;
memset(&fresh, 0, sizeof(fresh));
++process->ActiveTransactions;
*transactionIdOut = id;
status = NTLSTATUS_SUCCESS;
done:
PosixsrvUnlockRegistry();
PosixsrvDestroyTransaction(&fresh);
return status;
}
// Detaches one process-owned transaction so a commit can outlive registry locking.
static KSTATUS PosixsrvTakeTransaction(POSIXSRV_CLIENT* client,
uint32_t transactionId,
uint32_t kind,
POSIXSRV_TRANSACTION* transactionOut) {
if (!client || !client->Process || !transactionOut || transactionId == 0)
return NTLSTATUS_INVALID_PARAMETER;
memset(transactionOut, 0, sizeof(*transactionOut));
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_TRANSACTION* transaction =
PosixsrvFindTransactionLocked(client->Process, transactionId, kind);
if (!transaction) status = NTLSTATUS_NOT_FOUND;
else {
*transactionOut = *transaction;
memset(transaction, 0, sizeof(*transaction));
if (client->Process->ActiveTransactions != 0) --client->Process->ActiveTransactions;
status = NTLSTATUS_SUCCESS;
}
PosixsrvUnlockRegistry();
return status;
}
// Cancels one transaction owned by the caller's stable POSIX process.
static KSTATUS PosixsrvCancelTransaction(POSIXSRV_CLIENT* client, uint32_t transactionId) {
if (!client || !client->Process || transactionId == 0) return NTLSTATUS_INVALID_PARAMETER;
POSIXSRV_TRANSACTION detached;
memset(&detached, 0, sizeof(detached));
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_TRANSACTION* transaction =
PosixsrvFindTransactionLocked(client->Process, transactionId, NTL_POSIX_TRANSACTION_NONE);
if (!transaction) status = NTLSTATUS_NOT_FOUND;
else {
detached = *transaction;
memset(transaction, 0, sizeof(*transaction));
if (client->Process->ActiveTransactions != 0) --client->Process->ActiveTransactions;
status = NTLSTATUS_SUCCESS;
}
PosixsrvUnlockRegistry();
PosixsrvDestroyTransaction(&detached);
return status;
}
// Copies one length-delimited protocol path/text payload into server-owned storage.
static KSTATUS PosixsrvCopyRequestPath(const NTL_POSIX_REQUEST* request, char** textOut) {
if (!request || !textOut || request->PathLength == 0 || request->PathLength > SYS_PATH_MAX ||
request->Path[request->PathLength] != '\0' || strlen(request->Path) != request->PathLength)
return NTLSTATUS_INVALID_PARAMETER;
char* text = (char*)malloc((size_t)request->PathLength + 1u);
if (!text) return NTLSTATUS_NO_MEMORY;
memcpy(text, request->Path, (size_t)request->PathLength + 1u);
*textOut = text;
return NTLSTATUS_SUCCESS;
}
// Records the executable pathname for one spawn/exec construction transaction.
static KSTATUS PosixsrvConstructionSetExecutable(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request) {
if (!client || !client->Process || !request) return NTLSTATUS_INVALID_PARAMETER;
char* executable = (char*)0;
KSTATUS status = PosixsrvCopyRequestPath(request, &executable);
if (!KSUCCESS(status)) return status;
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) {
free(executable);
return status;
}
POSIXSRV_TRANSACTION* transaction = PosixsrvFindTransactionLocked(
client->Process, request->TransactionId, NTL_POSIX_TRANSACTION_NONE);
if (!transaction || !transaction->Construction || transaction->Construction->Executable) {
status = NTLSTATUS_INVALID_PARAMETER;
} else if (request->PathLength > NTL_POSIX_CONSTRUCT_DATA_MAX -
transaction->Construction->TotalDataBytes) {
status = NTLSTATUS_LIMIT_REACHED;
} else {
transaction->Construction->Executable = executable;
transaction->Construction->TotalDataBytes += request->PathLength;
executable = (char*)0;
status = NTLSTATUS_SUCCESS;
}
PosixsrvUnlockRegistry();
free(executable);
return status;
}
// Appends one bounded chunk to an argv/envp entry while preserving index order.
static KSTATUS PosixsrvConstructionWriteVector(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request) {
if (!client || !client->Process || !request || request->PathLength > SYS_PATH_MAX ||
request->Path[request->PathLength] != '\0' || strlen(request->Path) != request->PathLength ||
(request->ConstructionVectorFlags & ~NTL_POSIX_CONSTRUCT_VECTOR_CHUNK_FINAL) != 0)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_TRANSACTION* transaction = PosixsrvFindTransactionLocked(
client->Process, request->TransactionId, NTL_POSIX_TRANSACTION_NONE);
if (!transaction || !transaction->Construction) {
PosixsrvUnlockRegistry();
return NTLSTATUS_INVALID_PARAMETER;
}
POSIXSRV_CONSTRUCTION* construction = transaction->Construction;
POSIXSRV_CONSTRUCTION_STRING** vector = (POSIXSRV_CONSTRUCTION_STRING**)0;
uint32_t* count = (uint32_t*)0;
uint32_t* capacity = (uint32_t*)0;
if (request->ConstructionVectorKind == NTL_POSIX_CONSTRUCT_VECTOR_ARGUMENT) {
vector = &construction->Arguments;
count = &construction->ArgumentCount;
capacity = &construction->ArgumentCapacity;
} else if (request->ConstructionVectorKind == NTL_POSIX_CONSTRUCT_VECTOR_ENVIRONMENT) {
vector = &construction->Environment;
count = &construction->EnvironmentCount;
capacity = &construction->EnvironmentCapacity;
} else {
PosixsrvUnlockRegistry();
return NTLSTATUS_INVALID_PARAMETER;
}
if (request->ConstructionVectorIndex > *count ||
request->ConstructionVectorIndex >= NTL_POSIX_CONSTRUCT_VECTOR_MAX) {
PosixsrvUnlockRegistry();
return NTLSTATUS_INVALID_PARAMETER;
}
if (request->ConstructionVectorIndex == *count) {
if (*count == *capacity) {
uint32_t newCapacity = *capacity ? *capacity * 2u : 8u;
if (newCapacity > NTL_POSIX_CONSTRUCT_VECTOR_MAX)
newCapacity = NTL_POSIX_CONSTRUCT_VECTOR_MAX;
POSIXSRV_CONSTRUCTION_STRING* grown = (POSIXSRV_CONSTRUCTION_STRING*)realloc(
*vector, (size_t)newCapacity * sizeof(**vector));
if (!grown) {
PosixsrvUnlockRegistry();
return NTLSTATUS_NO_MEMORY;
}
memset(grown + *capacity, 0, (size_t)(newCapacity - *capacity) * sizeof(*grown));
*vector = grown;
*capacity = newCapacity;
}
++*count;
}
POSIXSRV_CONSTRUCTION_STRING* string = &(*vector)[request->ConstructionVectorIndex];
if (string->Complete || request->ConstructionVectorOffset != string->Length) {
PosixsrvUnlockRegistry();
return NTLSTATUS_INVALID_PARAMETER;
}
if (request->PathLength > NTL_POSIX_CONSTRUCT_DATA_MAX - construction->TotalDataBytes) {
PosixsrvUnlockRegistry();
return NTLSTATUS_LIMIT_REACHED;
}
uint32_t required = string->Length + request->PathLength + 1u;
if (required < string->Length) {
PosixsrvUnlockRegistry();
return NTLSTATUS_LIMIT_REACHED;
}
if (required > string->Capacity) {
uint32_t newCapacity = string->Capacity ? string->Capacity : 64u;
while (newCapacity < required) {
if (newCapacity > NTL_POSIX_CONSTRUCT_DATA_MAX / 2u) {
newCapacity = required;
break;
}
newCapacity *= 2u;
}
char* grown = (char*)realloc(string->Data, newCapacity);
if (!grown) {
PosixsrvUnlockRegistry();
return NTLSTATUS_NO_MEMORY;
}
string->Data = grown;
string->Capacity = newCapacity;
}
if (request->PathLength)
memcpy(string->Data + string->Length, request->Path, request->PathLength);
string->Length += request->PathLength;
string->Data[string->Length] = '\0';
construction->TotalDataBytes += request->PathLength;
if (request->ConstructionVectorFlags & NTL_POSIX_CONSTRUCT_VECTOR_CHUNK_FINAL)
string->Complete = 1u;
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
// Appends one ordered close/dup2/open action to a spawn transaction.
static KSTATUS PosixsrvSpawnAddFileAction(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request) {
if (!client || !client->Process || !request) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_TRANSACTION* transaction = PosixsrvFindTransactionLocked(
client->Process, request->TransactionId, NTL_POSIX_TRANSACTION_SPAWN);
if (!transaction || !transaction->Construction || !transaction->Spawn) {
PosixsrvUnlockRegistry();
return NTLSTATUS_INVALID_PARAMETER;
}
POSIXSRV_CONSTRUCTION* construction = transaction->Construction;
POSIXSRV_SPAWN_POLICY* spawn = transaction->Spawn;
if (spawn->ActionCount >= NTL_POSIX_SPAWN_ACTION_MAX || request->SpawnFileDescriptor < 0 ||
request->SpawnFileDescriptor >= (int32_t)NTL_POSIX_FD_INHERIT_MAX) {
PosixsrvUnlockRegistry();
return NTLSTATUS_LIMIT_REACHED;
}
POSIXSRV_SPAWN_ACTION action;
memset(&action, 0, sizeof(action));
action.Kind = request->SpawnActionKind;
action.FileDescriptor = request->SpawnFileDescriptor;
action.NewFileDescriptor = request->SpawnNewFileDescriptor;
action.OpenFlags = request->SpawnOpenFlags;
action.OpenMode = request->SpawnOpenMode;
if (action.Kind == NTL_POSIX_SPAWN_ACTION_CLOSE) {
if (request->PathLength != 0 || action.NewFileDescriptor != 0 || action.OpenFlags != 0 ||
action.OpenMode != 0) status = NTLSTATUS_INVALID_PARAMETER;
} else if (action.Kind == NTL_POSIX_SPAWN_ACTION_DUP2) {
if (request->PathLength != 0 || action.NewFileDescriptor < 0 ||
action.NewFileDescriptor >= (int32_t)NTL_POSIX_FD_INHERIT_MAX ||
action.OpenFlags != 0 || action.OpenMode != 0)
status = NTLSTATUS_INVALID_PARAMETER;
} else if (action.Kind == NTL_POSIX_SPAWN_ACTION_OPEN) {
if ((action.OpenFlags & ~NTL_POSIX_SPAWN_OPEN_VALID_MASK) != 0 ||
(action.OpenFlags & NTL_POSIX_OPEN_STATUS_ACCMODE) == NTL_POSIX_OPEN_STATUS_ACCMODE) {
status = NTLSTATUS_INVALID_PARAMETER;
} else if (request->PathLength > NTL_POSIX_CONSTRUCT_DATA_MAX -
construction->TotalDataBytes) {
status = NTLSTATUS_LIMIT_REACHED;
} else {
status = PosixsrvCopyRequestPath(request, &action.Path);
if (KSUCCESS(status)) construction->TotalDataBytes += request->PathLength;
}
} else {
status = NTLSTATUS_INVALID_PARAMETER;
}
if (KSUCCESS(status)) spawn->Actions[spawn->ActionCount++] = action;
PosixsrvUnlockRegistry();
if (!KSUCCESS(status)) free(action.Path);
return status;
}
// Records spawn-only attributes whose semantics are authoritative in P5/P6.
static KSTATUS PosixsrvSpawnSetAttributes(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request) {
if (!client || !client->Process || !request) return NTLSTATUS_INVALID_PARAMETER;
const uint32_t validFlags = NTL_POSIX_SPAWN_ATTR_SETPGROUP |
NTL_POSIX_SPAWN_ATTR_SETSIGMASK |
NTL_POSIX_SPAWN_ATTR_SETSIGDEF;
if ((request->SpawnAttributeFlags & ~validFlags) != 0 ||
(request->SpawnSignalMask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0 ||
(request->SpawnSignalDefaultMask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) return status;
POSIXSRV_TRANSACTION* transaction = PosixsrvFindTransactionLocked(
client->Process, request->TransactionId, NTL_POSIX_TRANSACTION_SPAWN);
if (!transaction || !transaction->Spawn) status = NTLSTATUS_INVALID_PARAMETER;
else {
transaction->Spawn->AttributeFlags = request->SpawnAttributeFlags;
transaction->Spawn->ProcessGroupId = request->ProcessGroupId;
transaction->Spawn->SignalMask = request->SpawnSignalMask &
~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
transaction->Spawn->SignalDefaultMask = request->SpawnSignalDefaultMask;
status = NTLSTATUS_SUCCESS;
}
PosixsrvUnlockRegistry();
return status;
}
// Returns whether one absolute path already names a native global namespace root.
static bool PosixsrvPathUsesNativeRoot(const char* path) {
static const char* roots[] = { "/SystemRoot", "/Device", "/Sessions", "/Services" };
if (!path || path[0] != '/') return false;
for (size_t i = 0; i < sizeof(roots) / sizeof(roots[0]); ++i) {
size_t length = strlen(roots[i]);
if (strncmp(path, roots[i], length) == 0 && (path[length] == '\0' || path[length] == '/'))
return true;
}
return false;
}
// Resolves one POSIX pathname against the server-owned cwd into native global syntax.
static KSTATUS PosixsrvResolveSpawnPath(const POSIXSRV_PROCESS_RECORD* parent,
const char* path,
char output[SYS_PATH_MAX + 1u]) {
if (!parent || !path || !path[0] || !output) return NTLSTATUS_INVALID_PARAMETER;
char combined[SYS_PATH_MAX + 1u];
size_t pathLength = strlen(path);
if (pathLength > SYS_PATH_MAX) return NTLSTATUS_NAME_TOO_LONG;
if (PosixsrvPathUsesNativeRoot(path)) {
memcpy(combined, path, pathLength + 1u);
} else if (path[0] == '/') {
static const char prefix[] = "/SystemRoot";
size_t prefixLength = sizeof(prefix) - 1u;
if (prefixLength + pathLength > SYS_PATH_MAX) return NTLSTATUS_NAME_TOO_LONG;
memcpy(combined, prefix, prefixLength);
memcpy(combined + prefixLength, path, pathLength + 1u);
} else {
size_t cwdLength = strlen(parent->CurrentDirectory);
size_t separator = cwdLength && parent->CurrentDirectory[cwdLength - 1u] != '/' ? 1u : 0u;
if (cwdLength + separator + pathLength > SYS_PATH_MAX) return NTLSTATUS_NAME_TOO_LONG;
memcpy(combined, parent->CurrentDirectory, cwdLength);
if (separator) combined[cwdLength++] = '/';
memcpy(combined + cwdLength, path, pathLength + 1u);
}
size_t required = 0;
return NtlRealPath(SysSvFromCstr(combined), output, SYS_PATH_MAX + 1u, &required);
}
// Allocates one globally registered open description with no process holder yet.
// The caller must hold the registry mutex and immediately attach an inheritance reference.
static KSTATUS PosixsrvCreateUnheldDescriptionLocked(uint32_t type,
uint32_t statusFlags,
SYS_HANDLE masterHandle,
POSIXSRV_OPEN_DESCRIPTION** descriptionOut) {
if (!descriptionOut || !PosixsrvOpenDescriptionTypeValid(type) ||
(statusFlags & ~NTL_POSIX_OPEN_STATUS_VALID_MASK) != 0 ||
(statusFlags & NTL_POSIX_OPEN_STATUS_ACCMODE) == NTL_POSIX_OPEN_STATUS_ACCMODE)
return NTLSTATUS_INVALID_PARAMETER;
*descriptionOut = (POSIXSRV_OPEN_DESCRIPTION*)0;
if (gNextOpenDescriptionId == 0) return NTLSTATUS_ID_EXHAUSTED;
POSIXSRV_OPEN_DESCRIPTION* description =
(POSIXSRV_OPEN_DESCRIPTION*)calloc(1, sizeof(*description));
NTL_POSIX_SHARED_OPEN_DESCRIPTION* shared =
(NTL_POSIX_SHARED_OPEN_DESCRIPTION*)calloc(1, sizeof(*shared));
if (!description || !shared) {
free(description);
free(shared);
return NTLSTATUS_NO_MEMORY;
}
description->DescriptionId = gNextOpenDescriptionId++;
description->Type = type;
description->MasterHandle = masterHandle;
description->Shared = shared;
shared->Magic = NTL_POSIX_SHARED_OPEN_MAGIC;
shared->Version = NTL_POSIX_SHARED_OPEN_VERSION;
shared->Type = type;
shared->DescriptionId = description->DescriptionId;
shared->StatusFlags = statusFlags;
description->Next = gOpenDescriptionRegistry;
gOpenDescriptionRegistry = description;
++gRegisteredOpenDescriptions;
*descriptionOut = description;
return NTLSTATUS_SUCCESS;
}
// Returns the manifest index for one logical descriptor or UINT32_MAX when absent.
static uint32_t PosixsrvFindSnapshotDescriptor(const POSIXSRV_FD_INHERITANCE* snapshot,
int32_t descriptor) {
if (!snapshot) return UINT32_MAX;
for (uint32_t i = 0; i < snapshot->EntryCount; ++i) {
if (snapshot->Entries[i].Manifest.Descriptor == descriptor) return i;
}
return UINT32_MAX;
}
// Removes one logical descriptor from an inheritance manifest while preserving order.
static void PosixsrvRemoveSnapshotEntry(POSIXSRV_FD_INHERITANCE* snapshot, uint32_t index) {
if (!snapshot || index >= snapshot->EntryCount) return;
for (uint32_t i = index + 1u; i < snapshot->EntryCount; ++i)
snapshot->Entries[i - 1u] = snapshot->Entries[i];
if (snapshot->EntryCount) --snapshot->EntryCount;
memset(&snapshot->Entries[snapshot->EntryCount], 0, sizeof(snapshot->Entries[0]));
}
// Rebuilds the snapshot's unique-description pin set after ordered file actions mutate entries.
static void PosixsrvRebuildSnapshotDescriptionsLocked(POSIXSRV_FD_INHERITANCE* snapshot) {
if (!snapshot) return;
POSIXSRV_OPEN_DESCRIPTION* previous[NTL_POSIX_FD_INHERIT_MAX];
uint32_t previousCount = snapshot->DescriptionCount;
for (uint32_t i = 0; i < previousCount; ++i) {
previous[i] = snapshot->Descriptions[i];
if (previous[i] && previous[i]->InheritanceRefCount)
--previous[i]->InheritanceRefCount;
}
memset(snapshot->Descriptions, 0, sizeof(snapshot->Descriptions));
snapshot->DescriptionCount = 0;
for (uint32_t i = 0; i < snapshot->EntryCount; ++i) {
POSIXSRV_OPEN_DESCRIPTION* description = snapshot->Entries[i].Description;
uint32_t found = 0;
for (uint32_t j = 0; j < snapshot->DescriptionCount; ++j) {
if (snapshot->Descriptions[j] == description) { found = 1; break; }
}
if (!found && description) {
snapshot->Descriptions[snapshot->DescriptionCount++] = description;
++description->InheritanceRefCount;
}
}
for (uint32_t i = 0; i < previousCount; ++i)
PosixsrvDestroyOpenDescriptionLocked(previous[i]);
}
// Opens one file action target and creates the shared description inherited by the child.
static KSTATUS PosixsrvOpenSpawnDescriptionLocked(POSIXSRV_PROCESS_RECORD* parent,
const POSIXSRV_SPAWN_ACTION* action,
POSIXSRV_OPEN_DESCRIPTION** descriptionOut) {
if (!parent || !action || !action->Path || !descriptionOut) return NTLSTATUS_INVALID_PARAMETER;
*descriptionOut = (POSIXSRV_OPEN_DESCRIPTION*)0;
uint32_t statusFlags = action->OpenFlags & NTL_POSIX_OPEN_STATUS_VALID_MASK;
if (strcmp(action->Path, "/dev/null") == 0)
return PosixsrvCreateUnheldDescriptionLocked(NTL_POSIX_OPEN_TYPE_DEV_NULL, statusFlags,
SYS_HANDLE_INVALID, descriptionOut);
if (strcmp(action->Path, "/dev/zero") == 0)
return PosixsrvCreateUnheldDescriptionLocked(NTL_POSIX_OPEN_TYPE_DEV_ZERO, statusFlags,
SYS_HANDLE_INVALID, descriptionOut);
char native[SYS_PATH_MAX + 1u];
KSTATUS status = PosixsrvResolveSpawnPath(parent, action->Path, native);
if (!KSUCCESS(status)) return status;
uint32_t access = SYS_FILE_ACCESS_QUERY | SYS_FILE_ACCESS_LOCK;
uint32_t mode = action->OpenFlags & NTL_POSIX_OPEN_STATUS_ACCMODE;
if (mode == NTL_POSIX_OPEN_STATUS_RDWR)
access |= SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE;
else if (mode == NTL_POSIX_OPEN_STATUS_WRONLY)
access |= SYS_FILE_ACCESS_WRITE;
else
access |= SYS_FILE_ACCESS_READ;
if (action->OpenFlags & NTL_POSIX_OPEN_STATUS_APPEND) access |= SYS_FILE_ACCESS_APPEND;
uint32_t disposition = SYS_FILE_OPEN_EXISTING;
if ((action->OpenFlags & NTL_POSIX_OPEN_CREATE) && (action->OpenFlags & NTL_POSIX_OPEN_TRUNCATE))
disposition = SYS_FILE_CREATE_ALWAYS;
else if (action->OpenFlags & NTL_POSIX_OPEN_CREATE)
disposition = SYS_FILE_OPEN_ALWAYS;
else if (action->OpenFlags & NTL_POSIX_OPEN_TRUNCATE)
disposition = SYS_FILE_TRUNCATE_EXISTING;
SYS_HANDLE file = SYS_HANDLE_INVALID;
status = NtlOpenFile(SysSvFromCstr(native), access, disposition, SYS_FILE_FLAG_NONE, &file);
if (!KSUCCESS(status)) return status;
status = PosixsrvCreateUnheldDescriptionLocked(NTL_POSIX_OPEN_TYPE_FILE, statusFlags,
file, descriptionOut);
if (!KSUCCESS(status)) (void)SysCloseHandle(file);
return status;
}
// Applies ordered POSIX spawn file actions and only then closes remaining CLOEXEC descriptors.
static KSTATUS PosixsrvApplySpawnFileActionsLocked(POSIXSRV_PROCESS_RECORD* parent,
POSIXSRV_FD_INHERITANCE* snapshot,
const POSIXSRV_SPAWN_POLICY* spawn) {
if (!parent || !snapshot || !spawn) return NTLSTATUS_INVALID_PARAMETER;
for (uint32_t i = 0; i < spawn->ActionCount; ++i) {
const POSIXSRV_SPAWN_ACTION* action = &spawn->Actions[i];
uint32_t target = PosixsrvFindSnapshotDescriptor(snapshot, action->FileDescriptor);
if (action->Kind == NTL_POSIX_SPAWN_ACTION_CLOSE) {
if (target == UINT32_MAX) return NTLSTATUS_INVALID_HANDLE;
PosixsrvRemoveSnapshotEntry(snapshot, target);
} else if (action->Kind == NTL_POSIX_SPAWN_ACTION_DUP2) {
uint32_t source = target;
if (source == UINT32_MAX) return NTLSTATUS_INVALID_HANDLE;
POSIXSRV_FD_INHERITANCE_ENTRY sourceEntry = snapshot->Entries[source];
if (action->FileDescriptor == action->NewFileDescriptor) continue;
uint32_t replacement = PosixsrvFindSnapshotDescriptor(snapshot, action->NewFileDescriptor);
if (replacement != UINT32_MAX) {
PosixsrvRemoveSnapshotEntry(snapshot, replacement);
source = PosixsrvFindSnapshotDescriptor(snapshot, action->FileDescriptor);
sourceEntry = snapshot->Entries[source];
}
if (snapshot->EntryCount >= NTL_POSIX_FD_INHERIT_MAX) return NTLSTATUS_LIMIT_REACHED;
sourceEntry.Manifest.Descriptor = action->NewFileDescriptor;
sourceEntry.Manifest.DescriptorFlags = 0;
snapshot->Entries[snapshot->EntryCount++] = sourceEntry;
} else if (action->Kind == NTL_POSIX_SPAWN_ACTION_OPEN) {
if (target != UINT32_MAX) PosixsrvRemoveSnapshotEntry(snapshot, target);
if (snapshot->EntryCount >= NTL_POSIX_FD_INHERIT_MAX) return NTLSTATUS_LIMIT_REACHED;
POSIXSRV_OPEN_DESCRIPTION* description = (POSIXSRV_OPEN_DESCRIPTION*)0;
KSTATUS status = PosixsrvOpenSpawnDescriptionLocked(parent, action, &description);
if (!KSUCCESS(status)) return status;
POSIXSRV_FD_INHERITANCE_ENTRY* entry = &snapshot->Entries[snapshot->EntryCount++];
memset(entry, 0, sizeof(*entry));
entry->Manifest.Descriptor = action->FileDescriptor;
entry->Manifest.OpenDescriptionId = description->DescriptionId;
entry->Description = description;
}
PosixsrvRebuildSnapshotDescriptionsLocked(snapshot);
}
for (uint32_t i = 0; i < snapshot->EntryCount;) {
if (snapshot->Entries[i].Manifest.DescriptorFlags & NTL_POSIX_FD_DESCRIPTOR_CLOEXEC)
PosixsrvRemoveSnapshotEntry(snapshot, i);
else
++i;
}
PosixsrvRebuildSnapshotDescriptionsLocked(snapshot);
return NTLSTATUS_SUCCESS;
}
// Verifies that every streamed argv/envp element reached a final chunk.
static bool PosixsrvConstructionVectorsComplete(const POSIXSRV_CONSTRUCTION* construction) {
if (!construction) return false;
for (uint32_t i = 0; i < construction->ArgumentCount; ++i) {
if (!construction->Arguments[i].Complete) return false;
}
for (uint32_t i = 0; i < construction->EnvironmentCount; ++i) {
if (!construction->Environment[i].Complete) return false;
}
return true;
}
// Marks a failed constructing child non-waitable and releases its server-owned state.
static void PosixsrvFailConstructingProcess(POSIXSRV_PROCESS_RECORD* child, int32_t exitCode) {
if (!child) return;
POSIXSRV_PROCESS_RECORD* freeRecord = (POSIXSRV_PROCESS_RECORD*)0;
SYS_HANDLE closeProcess = SYS_HANDLE_INVALID;
SYS_HANDLE closeReady = SYS_HANDLE_INVALID;
if (!KSUCCESS(PosixsrvLockRegistry())) return;
child->ParentPosixPid = 0;
PosixsrvReleaseFdInheritanceForProcessLocked(child);
PosixsrvReleaseAllOpenDescriptionsLocked(child);
child->State = NTL_POSIX_PROCESS_STATE_EXITED;
child->ExitCode = exitCode;
closeProcess = child->ProcessHandle;
child->ProcessHandle = SYS_HANDLE_INVALID;
closeReady = child->BootstrapReadyEvent;
child->BootstrapReadyEvent = SYS_HANDLE_INVALID;
child->BootstrapToken = 0;
if (child->ConnectionCount == 0 && !child->MonitorActive) {
PosixsrvUnlinkProcessLocked(child);
freeRecord = child;
}
PosixsrvUnlockRegistry();
if (closeProcess != SYS_HANDLE_INVALID) (void)SysCloseHandle(closeProcess);
if (closeReady != SYS_HANDLE_INVALID) (void)SysCloseHandle(closeReady);
if (freeRecord) PosixsrvFreeProcessRecord(freeRecord);
}
// Prepares one inert native image from the canonical construction payload.
static KSTATUS PosixsrvPrepareConstruction(POSIXSRV_PROCESS_RECORD* process,
const POSIXSRV_CONSTRUCTION* construction,
SYS_HANDLE securityContext,
const NTL_POSIX_BOOTSTRAP_DATA* bootstrap,
int synthesizeArgumentZero,
NTL_PROCESS_PREPARATION** preparationOut,
uint32_t* nativePidOut) {
if (!process || !construction || !construction->Executable ||
securityContext == SYS_HANDLE_INVALID || !bootstrap || !preparationOut || !nativePidOut)
return NTLSTATUS_INVALID_PARAMETER;
*preparationOut = 0;
*nativePidOut = 0;
char executable[SYS_PATH_MAX + 1u];
KSTATUS status = PosixsrvResolveSpawnPath(process, construction->Executable, executable);
if (!KSUCCESS(status)) return status;
SYS_HANDLE imageFile = SYS_HANDLE_INVALID;
status = NtlOpenFile(SysSvFromCstr(executable), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, &imageFile);
if (!KSUCCESS(status)) return status;
SYS_HANDLE systemRoot = SYS_HANDLE_INVALID;
status = NtlResourceOpenSystemRoot(SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &systemRoot);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(imageFile);
return status;
}
uint32_t argumentCount = construction->ArgumentCount;
if (argumentCount == 0 && synthesizeArgumentZero) argumentCount = 1u;
SYS_STRING_VIEW* arguments = argumentCount
? (SYS_STRING_VIEW*)calloc(argumentCount, sizeof(SYS_STRING_VIEW))
: (SYS_STRING_VIEW*)0;
SYS_STRING_VIEW* environment = construction->EnvironmentCount
? (SYS_STRING_VIEW*)calloc(construction->EnvironmentCount, sizeof(SYS_STRING_VIEW))
: (SYS_STRING_VIEW*)0;
if ((argumentCount && !arguments) || (construction->EnvironmentCount && !environment)) {
free(arguments);
free(environment);
(void)SysCloseHandle(systemRoot);
(void)SysCloseHandle(imageFile);
return NTLSTATUS_NO_MEMORY;
}
if (construction->ArgumentCount) {
for (uint32_t i = 0; i < construction->ArgumentCount; ++i)
arguments[i] = SysSvFromParts(construction->Arguments[i].Data,
construction->Arguments[i].Length);
} else if (synthesizeArgumentZero) {
arguments[0] = SysSvFromCstr(construction->Executable);
}
for (uint32_t i = 0; i < construction->EnvironmentCount; ++i)
environment[i] = SysSvFromParts(construction->Environment[i].Data,
construction->Environment[i].Length);
NTL_PREPARE_PROCESS_REQUEST build;
memset(&build, 0, sizeof(build));
build.Size = sizeof(build);
build.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
build.RootHandle = systemRoot;
build.Path = SysSvFromCstr(executable);
build.CommandLine = argumentCount ? arguments[0] : SysSvFromCstr(construction->Executable);
build.CurrentDirectory = SysSvFromCstr(process->CurrentDirectory);
build.SecurityContext = securityContext;
build.ModuleRootHandle = systemRoot;
build.ModuleDirectory = SYS_SV_LITERAL("system");
build.ResolveModuleName = NtlResolveSystemModuleName;
build.Arguments.Entries = arguments;
build.Arguments.Count = argumentCount;
build.Environment.Entries = environment;
build.Environment.Count = construction->EnvironmentCount;
build.StartupData = bootstrap;
build.StartupDataSize = sizeof(*bootstrap);
status = NtlPrepareProcess(&build, imageFile, preparationOut);
free(arguments);
free(environment);
(void)SysCloseHandle(systemRoot);
(void)SysCloseHandle(imageFile);
if (!KSUCCESS(status)) return status;
SYS_HANDLE preparedProcess = SYS_HANDLE_INVALID;
status = NtlPreparedProcessGetHandle(*preparationOut, &preparedProcess);
if (!KSUCCESS(status)) {
NtlAbortPreparedProcess(*preparationOut);
*preparationOut = 0;
return status;
}
status = SysGetProcessPid(preparedProcess, nativePidOut);
if (!KSUCCESS(status) || *nativePidOut == 0) {
NtlAbortPreparedProcess(*preparationOut);
*preparationOut = 0;
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
}
return NTLSTATUS_SUCCESS;
}
// Builds and starts one POSIX child entirely under posixsrv authority.
static KSTATUS PosixsrvCommitSpawn(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request,
SYS_HANDLE securityContext,
const POSIXSRV_TRANSACTION* transaction,
uint32_t* childPidOut) {
if (!client || !client->Process || !request || securityContext == SYS_HANDLE_INVALID ||
!transaction || !childPidOut || request->FdInheritanceToken == 0)
return NTLSTATUS_INVALID_PARAMETER;
*childPidOut = 0;
if (transaction->Kind != NTL_POSIX_TRANSACTION_SPAWN || !transaction->Construction ||
!transaction->Spawn || !transaction->Construction->Executable ||
!PosixsrvConstructionVectorsComplete(transaction->Construction))
return NTLSTATUS_INVALID_PARAMETER;
const POSIXSRV_CONSTRUCTION* construction = transaction->Construction;
const POSIXSRV_SPAWN_POLICY* spawn = transaction->Spawn;
SYS_SECURITY_CONTEXT_INFO securityInfo;
memset(&securityInfo, 0, sizeof(securityInfo));
securityInfo.Size = sizeof(securityInfo);
KSTATUS status = SysQuerySecurityContext(securityContext, &securityInfo);
if (!KSUCCESS(status)) return status;
POSIXSRV_PROCESS_RECORD* child =
(POSIXSRV_PROCESS_RECORD*)calloc(1, sizeof(*child));
if (!child) return NTLSTATUS_NO_MEMORY;
child->ProcessHandle = SYS_HANDLE_INVALID;
child->BootstrapReadyEvent = SYS_HANDLE_INVALID;
child->PendingProcessHandle = SYS_HANDLE_INVALID;
child->PendingBootstrapReadyEvent = SYS_HANDLE_INVALID;
child->PendingBootstrapReleaseEvent = SYS_HANDLE_INVALID;
child->AlarmTimer = SYS_HANDLE_INVALID;
child->AlarmControlEvent = SYS_HANDLE_INVALID;
status = SysCreateEvent(0u, 0u, &child->BootstrapReadyEvent);
if (!KSUCCESS(status)) { free(child); return status; }
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) { PosixsrvFreeProcessRecord(child); return status; }
POSIXSRV_PROCESS_RECORD* parent = client->Process;
POSIXSRV_FD_INHERITANCE* snapshot =
PosixsrvFindFdInheritanceLocked(request->FdInheritanceToken);
if (!PosixsrvCommittedGenerationOperational(client) || !snapshot ||
snapshot->SourceProcess != parent || snapshot->ClaimProcess) {
status = snapshot ? NTLSTATUS_ACCESS_DENIED : NTLSTATUS_NOT_FOUND;
PosixsrvUnlockRegistry();
PosixsrvFreeProcessRecord(child);
return status;
}
status = PosixsrvAllocatePidLocked(&child->PosixPid);
if (KSUCCESS(status)) {
if (gNextBootstrapToken == 0) status = NTLSTATUS_ID_EXHAUSTED;
else child->BootstrapToken = gNextBootstrapToken++;
}
if (KSUCCESS(status)) status = PosixsrvApplySpawnFileActionsLocked(parent, snapshot, spawn);
if (!KSUCCESS(status)) {
PosixsrvDestroyFdInheritanceLocked(snapshot);
PosixsrvUnlockRegistry();
PosixsrvFreeProcessRecord(child);
return status;
}
child->ParentPosixPid = parent->PosixPid;
child->SecurityId = securityInfo.SecurityId;
child->Generation = 1u;
child->State = NTL_POSIX_PROCESS_STATE_CONSTRUCTING;
child->UserId = parent->UserId;
child->EffectiveUserId = parent->EffectiveUserId;
child->GroupId = parent->GroupId;
child->EffectiveGroupId = parent->EffectiveGroupId;
child->SessionId = parent->SessionId;
child->SupplementaryGroupCount = parent->SupplementaryGroupCount;
memcpy(child->SupplementaryGroups, parent->SupplementaryGroups,
sizeof(child->SupplementaryGroups));
child->ExplicitIgnoredSignals = parent->ExplicitIgnoredSignals & ~spawn->SignalDefaultMask;
child->IgnoredSignals = child->ExplicitIgnoredSignals |
NTL_POSIX_SIGNAL_BIT(NTL_POSIX_SIGNAL_CHLD);
(void)PosixsrvCopyText(child->LoginName, sizeof(child->LoginName), parent->LoginName);
(void)PosixsrvCopyText(child->CurrentDirectory, sizeof(child->CurrentDirectory),
parent->CurrentDirectory);
if (spawn->AttributeFlags & NTL_POSIX_SPAWN_ATTR_SETPGROUP) {
uint32_t requestedGroup = spawn->ProcessGroupId ? spawn->ProcessGroupId : child->PosixPid;
if (requestedGroup != child->PosixPid &&
!PosixsrvProcessGroupExistsLocked(parent->SessionId, requestedGroup))
status = NTLSTATUS_ACCESS_DENIED;
else
child->ProcessGroupId = requestedGroup;
} else {
child->ProcessGroupId = parent->ProcessGroupId;
}
if (!KSUCCESS(status)) {
PosixsrvDestroyFdInheritanceLocked(snapshot);
PosixsrvUnlockRegistry();
PosixsrvFreeProcessRecord(child);
return status;
}
PosixsrvUnlockRegistry();
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.Token = child->BootstrapToken;
bootstrap.PosixPid = child->PosixPid;
bootstrap.Generation = child->Generation;
bootstrap.FdInheritanceToken = request->FdInheritanceToken;
bootstrap.InitialSignalMask = spawn->SignalMask;
bootstrap.IgnoredSignals = child->ExplicitIgnoredSignals;
NTL_PROCESS_PREPARATION* preparation = 0;
status = PosixsrvPrepareConstruction(child, construction, securityContext, &bootstrap, 1,
&preparation, &child->NativeProcessId);
if (!KSUCCESS(status)) goto before_insert_fail;
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) { NtlAbortPreparedProcess(preparation); goto before_insert_fail; }
snapshot = PosixsrvFindFdInheritanceLocked(request->FdInheritanceToken);
if (!snapshot || snapshot->SourceProcess != parent || snapshot->ClaimProcess ||
!PosixsrvCommittedGenerationOperational(client)) {
status = snapshot ? NTLSTATUS_ACCESS_DENIED : NTLSTATUS_NOT_FOUND;
PosixsrvUnlockRegistry();
NtlAbortPreparedProcess(preparation);
goto before_insert_fail;
}
child->Next = gProcessRegistry;
gProcessRegistry = child;
++gRegisteredProcesses;
status = PosixsrvClaimFdInheritanceLocked(snapshot, child);
if (!KSUCCESS(status)) {
PosixsrvUnlinkProcessLocked(child);
PosixsrvUnlockRegistry();
NtlAbortPreparedProcess(preparation);
goto before_insert_fail;
}
PosixsrvUnlockRegistry();
SYS_HANDLE processHandle = SYS_HANDLE_INVALID;
status = NtlStartPreparedProcess(preparation, &processHandle);
if (!KSUCCESS(status)) {
PosixsrvFailConstructingProcess(child, (int32_t)status);
return status;
}
if (KSUCCESS(PosixsrvLockRegistry())) {
child->ProcessHandle = processHandle;
PosixsrvUnlockRegistry();
} else {
(void)SysKillProcess(processHandle, (int32_t)NTLSTATUS_PROCESS_KILLED);
(void)SysCloseHandle(processHandle);
child->ProcessHandle = SYS_HANDLE_INVALID;
PosixsrvFailConstructingProcess(child, (int32_t)NTLSTATUS_FAILURE);
return NTLSTATUS_FAILURE;
}
SYS_HANDLE waits[2] = { child->BootstrapReadyEvent, processHandle };
uint32_t signaled = UINT32_MAX;
SYS_WAIT_MANY_REQUEST wait;
memset(&wait, 0, sizeof(wait));
wait.Size = sizeof(wait);
wait.Flags = SYS_WAIT_FLAG_NONE;
wait.Handles = waits;
wait.Count = 2u;
wait.SignaledIndexOut = &signaled;
wait.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
status = SysWaitMany(&wait);
if (!KSUCCESS(status) || signaled != 0u) {
int32_t exitCode = (int32_t)NTLSTATUS_PROCESS_KILLED;
if (KSUCCESS(status) && signaled == 1u)
(void)SysGetProcessExitCode(processHandle, &exitCode);
else
(void)SysKillProcess(processHandle, exitCode);
PosixsrvFailConstructingProcess(child, exitCode);
return KSUCCESS(status) ? NTLSTATUS_PROCESS_KILLED : status;
}
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) {
(void)SysKillProcess(processHandle, (int32_t)NTLSTATUS_PROCESS_KILLED);
PosixsrvFailConstructingProcess(child, (int32_t)status);
return status;
}
status = PosixsrvStartProcessMonitorLocked(child, processHandle, child->Generation);
if (KSUCCESS(status)) {
if (child->BootstrapReadyEvent != SYS_HANDLE_INVALID) {
(void)SysCloseHandle(child->BootstrapReadyEvent);
child->BootstrapReadyEvent = SYS_HANDLE_INVALID;
}
child->BootstrapToken = 0;
}
PosixsrvUnlockRegistry();
if (!KSUCCESS(status)) {
(void)SysKillProcess(processHandle, (int32_t)NTLSTATUS_PROCESS_KILLED);
PosixsrvFailConstructingProcess(child, (int32_t)status);
return status;
}
*childPidOut = child->PosixPid;
return NTLSTATUS_SUCCESS;
before_insert_fail:
if (KSUCCESS(PosixsrvLockRegistry())) {
POSIXSRV_FD_INHERITANCE* cleanup =
PosixsrvFindFdInheritanceLocked(request->FdInheritanceToken);
if (cleanup && cleanup->SourceProcess == client->Process && !cleanup->ClaimProcess)
PosixsrvDestroyFdInheritanceLocked(cleanup);
PosixsrvUnlockRegistry();
}
PosixsrvFreeProcessRecord(child);
return status;
}
// Rolls an uncommitted exec replacement back to the still-valid committed generation.
static void PosixsrvRollbackExec(POSIXSRV_PROCESS_RECORD* process,
uint64_t fdInheritanceToken,
SYS_HANDLE pendingProcess,
int replacementReachedReady) {
if (!process) return;
SYS_HANDLE readyEvent = SYS_HANDLE_INVALID;
SYS_HANDLE releaseEvent = SYS_HANDLE_INVALID;
if (KSUCCESS(PosixsrvLockRegistry())) {
POSIXSRV_FD_INHERITANCE* snapshot =
PosixsrvFindFdInheritanceLocked(fdInheritanceToken);
if (snapshot && snapshot->SourceProcess == process && !snapshot->ClaimProcess)
PosixsrvDestroyFdInheritanceLocked(snapshot);
if (process->PendingFdInheritanceToken == fdInheritanceToken) {
if (process->State == NTL_POSIX_PROCESS_STATE_EXEC_PREPARING ||
process->State == NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING) {
if (process->BorrowedHostProcess)
process->State = NTL_POSIX_PROCESS_STATE_VFORK_BORROWED;
else if (process->Generation != 0 && process->ProcessHandle != SYS_HANDLE_INVALID)
process->State = NTL_POSIX_PROCESS_STATE_RUNNING;
}
process->BorrowedTerminalKind = POSIXSRV_VFORK_TERMINAL_NONE;
if (process->PendingExecHolderPlan) {
PosixsrvDestroyExecHolderPlan(process->PendingExecHolderPlan);
free(process->PendingExecHolderPlan);
process->PendingExecHolderPlan = (POSIXSRV_EXEC_HOLDER_PLAN*)0;
}
readyEvent = process->PendingBootstrapReadyEvent;
releaseEvent = process->PendingBootstrapReleaseEvent;
process->PendingNativeProcessId = 0;
process->PendingGeneration = 0;
process->PendingBootstrapToken = 0;
process->PendingBootstrapReadyObserved = 0;
process->PendingProcessHandle = SYS_HANDLE_INVALID;
process->PendingFdInheritanceToken = 0;
if (!replacementReachedReady) {
process->PendingBootstrapReadyEvent = SYS_HANDLE_INVALID;
process->PendingBootstrapReleaseEvent = SYS_HANDLE_INVALID;
}
}
PosixsrvUnlockRegistry();
}
if (pendingProcess != SYS_HANDLE_INVALID) {
(void)SysKillProcess(pendingProcess, (int32_t)NTLSTATUS_PROCESS_KILLED);
(void)SysCloseHandle(pendingProcess);
}
if (replacementReachedReady) {
if (releaseEvent != SYS_HANDLE_INVALID) (void)SysSignalEvent(releaseEvent);
} else {
if (readyEvent != SYS_HANDLE_INVALID) (void)SysCloseHandle(readyEvent);
if (releaseEvent != SYS_HANDLE_INVALID) (void)SysCloseHandle(releaseEvent);
}
}
// Replaces one native process generation while preserving the stable POSIX process record.
static KSTATUS PosixsrvCommitExec(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request,
const POSIXSRV_TRANSACTION* transaction) {
if (!client || !client->Process || !request || !transaction ||
request->FdInheritanceToken == 0)
return NTLSTATUS_INVALID_PARAMETER;
if (transaction->Kind != NTL_POSIX_TRANSACTION_EXEC || !transaction->Construction ||
transaction->Spawn || !transaction->Construction->Executable ||
!PosixsrvConstructionVectorsComplete(transaction->Construction))
return NTLSTATUS_INVALID_PARAMETER;
if ((request->SignalMask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = NTLSTATUS_SUCCESS;
SYS_HANDLE securityContext = SYS_HANDLE_INVALID;
SYS_HANDLE readyEvent = SYS_HANDLE_INVALID;
SYS_HANDLE releaseEvent = SYS_HANDLE_INVALID;
status = SysCreateEvent(0u, 0u, &readyEvent);
if (!KSUCCESS(status)) return status;
status = SysCreateEvent(0u, 0u, &releaseEvent);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(readyEvent);
return status;
}
POSIXSRV_PROCESS_RECORD* process = client->Process;
bool borrowedExec = PosixsrvClientIsBorrowedProcess(client);
uint32_t pendingGeneration = 0;
uint64_t bootstrapToken = 0;
uint64_t fdToken = request->FdInheritanceToken;
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) {
(void)SysCloseHandle(readyEvent);
(void)SysCloseHandle(releaseEvent);
return status;
}
POSIXSRV_FD_INHERITANCE* snapshot = PosixsrvFindFdInheritanceLocked(fdToken);
if (!((PosixsrvCommittedGenerationOperational(client) &&
process->State == NTL_POSIX_PROCESS_STATE_RUNNING) || borrowedExec) || !snapshot ||
snapshot->SourceProcess != process || snapshot->ClaimProcess ||
process->Generation == UINT32_MAX || gNextBootstrapToken == 0) {
status = !snapshot ? NTLSTATUS_NOT_FOUND : NTLSTATUS_ACCESS_DENIED;
PosixsrvUnlockRegistry();
(void)SysCloseHandle(readyEvent);
(void)SysCloseHandle(releaseEvent);
return status;
}
SYS_HANDLE securityProcess = borrowedExec && process->BorrowedHostProcess
? process->BorrowedHostProcess->ProcessHandle
: process->ProcessHandle;
status = SysOpenProcessSecurityContext(
securityProcess, SYS_RIGHT_QUERY | SYS_SECURITY_CONTEXT_RIGHT_ASSIGN,
&securityContext);
if (KSUCCESS(status)) {
SYS_SECURITY_CONTEXT_INFO securityInfo;
memset(&securityInfo, 0, sizeof(securityInfo));
securityInfo.Size = sizeof(securityInfo);
status = SysQuerySecurityContext(securityContext, &securityInfo);
if (KSUCCESS(status) && securityInfo.SecurityId != process->SecurityId)
status = NTLSTATUS_ACCESS_DENIED;
}
if (!KSUCCESS(status)) {
if (securityContext != SYS_HANDLE_INVALID) (void)SysCloseHandle(securityContext);
PosixsrvUnlockRegistry();
(void)SysCloseHandle(readyEvent);
(void)SysCloseHandle(releaseEvent);
return status;
}
if (process->PendingBootstrapReadyEvent != SYS_HANDLE_INVALID)
(void)SysCloseHandle(process->PendingBootstrapReadyEvent);
if (process->PendingBootstrapReleaseEvent != SYS_HANDLE_INVALID)
(void)SysCloseHandle(process->PendingBootstrapReleaseEvent);
pendingGeneration = process->Generation + 1u;
bootstrapToken = gNextBootstrapToken++;
process->PendingGeneration = pendingGeneration;
process->PendingBootstrapToken = bootstrapToken;
process->PendingBootstrapReadyEvent = readyEvent;
process->PendingBootstrapReleaseEvent = releaseEvent;
process->PendingBootstrapReadyObserved = 0;
process->PendingProcessHandle = SYS_HANDLE_INVALID;
process->PendingNativeProcessId = 0;
process->PendingFdInheritanceToken = fdToken;
process->State = NTL_POSIX_PROCESS_STATE_EXEC_PREPARING;
PosixsrvUnlockRegistry();
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.Token = bootstrapToken;
bootstrap.PosixPid = process->PosixPid;
bootstrap.Generation = pendingGeneration;
bootstrap.FdInheritanceToken = fdToken;
bootstrap.InitialSignalMask = request->SignalMask & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
bootstrap.IgnoredSignals = process->ExplicitIgnoredSignals;
NTL_PROCESS_PREPARATION* preparation = 0;
uint32_t pendingNativePid = 0;
status = PosixsrvPrepareConstruction(process, transaction->Construction, securityContext,
&bootstrap, 0, &preparation, &pendingNativePid);
(void)SysCloseHandle(securityContext);
securityContext = SYS_HANDLE_INVALID;
if (!KSUCCESS(status)) {
PosixsrvRollbackExec(process, fdToken, SYS_HANDLE_INVALID, 0);
return status;
}
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) {
NtlAbortPreparedProcess(preparation);
PosixsrvRollbackExec(process, fdToken, SYS_HANDLE_INVALID, 0);
return status;
}
if (process->State != NTL_POSIX_PROCESS_STATE_EXEC_PREPARING ||
process->PendingGeneration != pendingGeneration ||
process->PendingBootstrapToken != bootstrapToken ||
process->PendingFdInheritanceToken != fdToken) {
PosixsrvUnlockRegistry();
NtlAbortPreparedProcess(preparation);
PosixsrvRollbackExec(process, fdToken, SYS_HANDLE_INVALID, 0);
return NTLSTATUS_OBJECT_DEAD;
}
process->PendingNativeProcessId = pendingNativePid;
PosixsrvUnlockRegistry();
SYS_HANDLE pendingProcess = SYS_HANDLE_INVALID;
status = NtlStartPreparedProcess(preparation, &pendingProcess);
if (!KSUCCESS(status)) {
PosixsrvRollbackExec(process, fdToken, SYS_HANDLE_INVALID, 0);
return status;
}
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) {
PosixsrvRollbackExec(process, fdToken, pendingProcess, 0);
return status;
}
if (process->State != NTL_POSIX_PROCESS_STATE_EXEC_PREPARING ||
process->PendingGeneration != pendingGeneration) {
PosixsrvUnlockRegistry();
PosixsrvRollbackExec(process, fdToken, pendingProcess, 0);
return NTLSTATUS_OBJECT_DEAD;
}
process->PendingProcessHandle = pendingProcess;
PosixsrvUnlockRegistry();
SYS_HANDLE waits[2] = { readyEvent, pendingProcess };
uint32_t signaled = UINT32_MAX;
SYS_WAIT_MANY_REQUEST wait;
memset(&wait, 0, sizeof(wait));
wait.Size = sizeof(wait);
wait.Flags = SYS_WAIT_FLAG_NONE;
wait.Handles = waits;
wait.Count = 2u;
wait.SignaledIndexOut = &signaled;
wait.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
status = SysWaitMany(&wait);
if (!KSUCCESS(status) || signaled != 0u) {
int32_t exitCode = (int32_t)NTLSTATUS_PROCESS_KILLED;
if (KSUCCESS(status) && signaled == 1u)
(void)SysGetProcessExitCode(pendingProcess, &exitCode);
PosixsrvRollbackExec(process, fdToken, pendingProcess, 0);
return KSUCCESS(status) ? NTLSTATUS_PROCESS_KILLED : status;
}
POSIXSRV_EXEC_HOLDER_PLAN holderPlan;
memset(&holderPlan, 0, sizeof(holderPlan));
POSIXSRV_EXEC_HOLDER_PLAN* borrowedPlan = (POSIXSRV_EXEC_HOLDER_PLAN*)0;
status = PosixsrvLockRegistry();
if (!KSUCCESS(status)) {
PosixsrvRollbackExec(process, fdToken, pendingProcess, 1);
return status;
}
snapshot = PosixsrvFindFdInheritanceLocked(fdToken);
if (process->State != NTL_POSIX_PROCESS_STATE_EXEC_PREPARING ||
process->PendingGeneration != pendingGeneration || !snapshot ||
snapshot->SourceProcess != process || snapshot->ClaimProcess) {
PosixsrvUnlockRegistry();
PosixsrvRollbackExec(process, fdToken, pendingProcess, 1);
return NTLSTATUS_OBJECT_DEAD;
}
status = PosixsrvPrepareExecHolderPlanLocked(process, snapshot, &holderPlan);
if (!KSUCCESS(status)) {
PosixsrvUnlockRegistry();
PosixsrvRollbackExec(process, fdToken, pendingProcess, 1);
return status;
}
if (borrowedExec) {
borrowedPlan = (POSIXSRV_EXEC_HOLDER_PLAN*)calloc(1u, sizeof(*borrowedPlan));
if (!borrowedPlan) {
PosixsrvUnlockRegistry();
PosixsrvDestroyExecHolderPlan(&holderPlan);
PosixsrvRollbackExec(process, fdToken, pendingProcess, 1);
return NTLSTATUS_NO_MEMORY;
}
*borrowedPlan = holderPlan;
memset(&holderPlan, 0, sizeof(holderPlan));
}
status = PosixsrvStartProcessMonitorLocked(process, pendingProcess, pendingGeneration);
if (!KSUCCESS(status)) {
PosixsrvUnlockRegistry();
PosixsrvDestroyExecHolderPlan(&holderPlan);
if (borrowedPlan) {
PosixsrvDestroyExecHolderPlan(borrowedPlan);
free(borrowedPlan);
}
PosixsrvRollbackExec(process, fdToken, pendingProcess, 1);
return status;
}
process->State = NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING;
SYS_HANDLE oldProcess = process->ProcessHandle;
uint32_t oldGeneration = process->Generation;
// A zero-timeout wait reports TIMEOUT while the replacement remains unsignaled.
status = SysWaitSingle(pendingProcess, SYS_WAIT_FLAG_NONE, 0u);
if (status != NTLSTATUS_TIMEOUT) {
process->State = NTL_POSIX_PROCESS_STATE_EXEC_PREPARING;
PosixsrvUnlockRegistry();
PosixsrvDestroyExecHolderPlan(&holderPlan);
if (borrowedPlan) {
PosixsrvDestroyExecHolderPlan(borrowedPlan);
free(borrowedPlan);
}
PosixsrvRollbackExec(process, fdToken, pendingProcess, 1);
return KSUCCESS(status) ? NTLSTATUS_PROCESS_KILLED : status;
}
if (borrowedExec) {
// The replacement is READY but remains behind its barrier until the borrowed thread dies.
process->PendingExecHolderPlan = borrowedPlan;
process->BorrowedTerminalKind = POSIXSRV_VFORK_TERMINAL_EXEC;
PosixsrvUnlockRegistry();
return NTLSTATUS_SUCCESS;
}
status = SysKillProcess(oldProcess, (int32_t)NTLSTATUS_PROCESS_KILLED);
if (!KSUCCESS(status)) {
process->State = NTL_POSIX_PROCESS_STATE_EXEC_PREPARING;
PosixsrvUnlockRegistry();
PosixsrvDestroyExecHolderPlan(&holderPlan);
PosixsrvRollbackExec(process, fdToken, pendingProcess, 1);
return status;
}
// No fallible allocation or lock acquisition remains after the old image is retired.
(void)SysCloseHandle(oldProcess);
PosixsrvApplyExecHolderPlanLocked(process, snapshot, &holderPlan);
PosixsrvReleaseThreadsForGenerationLocked(process, oldGeneration);
process->ProcessHandle = pendingProcess;
process->NativeProcessId = pendingNativePid;
process->Generation = pendingGeneration;
process->State = NTL_POSIX_PROCESS_STATE_RUNNING;
process->PendingProcessHandle = SYS_HANDLE_INVALID;
process->PendingNativeProcessId = 0;
process->PendingGeneration = 0;
process->PendingBootstrapToken = 0;
process->PendingFdInheritanceToken = 0;
process->IgnoredSignals = process->ExplicitIgnoredSignals |
NTL_POSIX_SIGNAL_BIT(NTL_POSIX_SIGNAL_CHLD);
PosixsrvDestroyFdInheritanceLocked(snapshot);
PosixsrvDrainPendingSignalsLocked(process);
SYS_HANDLE release = process->PendingBootstrapReleaseEvent;
PosixsrvUnlockRegistry();
(void)SysSignalEvent(release);
return NTLSTATUS_SUCCESS;
}
// Returns whether operation-specific bootstrap fields are all unused.
static bool PosixsrvHasNoBootstrap(const NTL_POSIX_REQUEST* request) {
return request && request->BootstrapToken == 0 && request->BootstrapPosixPid == 0 &&
request->BootstrapGeneration == 0;
}
// Returns whether all P3 shared-attribute payload fields are unused.
static bool PosixsrvHasNoAttributePayload(const NTL_POSIX_REQUEST* request) {
return request && request->TargetPosixPid == 0 && request->UserId == 0 &&
request->GroupId == 0 && request->ProcessGroupId == 0 &&
request->LookupKind == NTL_POSIX_LOOKUP_NONE &&
request->OpenDescriptionType == 0 && request->OpenStatusFlags == 0 &&
request->OpenDescriptionId == 0 && request->NameLength == 0 &&
request->PathLength == 0 && request->Name[0] == '\0' && request->Path[0] == '\0';
}
// Returns whether one request carries no descriptor-inheritance payload.
static bool PosixsrvHasNoFdInheritancePayload(const NTL_POSIX_REQUEST* request) {
if (!request || request->FdInheritanceToken != 0 || request->FdInheritanceIndex != 0 ||
request->FdInheritanceCount != 0 || request->FdInheritanceFlags != 0)
return false;
for (uint32_t i = 0; i < NTL_POSIX_FD_INHERIT_MAX; ++i) {
if (request->FdManifest[i].Descriptor != 0 ||
request->FdManifest[i].DescriptorFlags != 0 ||
request->FdManifest[i].OpenDescriptionId != 0)
return false;
}
return true;
}
// Returns whether one request carries no P5 spawn transaction payload.
static bool PosixsrvHasNoSpawnPayload(const NTL_POSIX_REQUEST* request) {
return request && request->ConstructionVectorKind == 0 && request->ConstructionVectorIndex == 0 &&
request->ConstructionVectorOffset == 0 && request->ConstructionVectorFlags == 0 &&
request->SpawnActionKind == 0 && request->SpawnFileDescriptor == 0 &&
request->SpawnNewFileDescriptor == 0 && request->SpawnOpenFlags == 0 &&
request->SpawnOpenMode == 0 && request->SpawnAttributeFlags == 0 &&
request->SpawnSignalMask == 0 && request->SpawnSignalDefaultMask == 0;
}
// Returns whether one request carries no child-wait selection payload.
static bool PosixsrvHasNoWaitPayload(const NTL_POSIX_REQUEST* request) {
return request && request->WaitPid == 0 && request->WaitOptions == 0;
}
// Returns whether one request carries no P6 signal payload.
static bool PosixsrvHasNoSignalPayload(const NTL_POSIX_REQUEST* request) {
return request && request->SignalTargetPid == 0 && request->SignalNumber == 0 &&
request->SignalDisposition == 0 && request->SignalReserved == 0 &&
request->SignalMask == 0 && request->SignalApcRoutine == 0;
}
// Returns whether one request carries no alarm payload.
static bool PosixsrvHasNoAlarmPayload(const NTL_POSIX_REQUEST* request) {
return request && request->AlarmSeconds == 0 && request->AlarmReserved == 0;
}
// Returns whether one request carries no borrowed-vfork lifecycle payload.
static bool PosixsrvHasNoVforkPayload(const NTL_POSIX_REQUEST* request) {
return request && request->VforkToken == 0 && request->VforkExitCode == 0 &&
request->VforkReserved == 0;
}
// Returns whether one request carries no transaction state.
static bool PosixsrvHasNoTransaction(const NTL_POSIX_REQUEST* request) {
return request && request->TransactionId == 0 &&
request->TransactionKind == NTL_POSIX_TRANSACTION_NONE;
}
// Returns whether all payload domains other than P6 signals are unused.
static bool PosixsrvHasOnlySignalPayload(const NTL_POSIX_REQUEST* request) {
return request && PosixsrvHasNoBootstrap(request) && PosixsrvHasNoTransaction(request) &&
PosixsrvHasNoAttributePayload(request) && PosixsrvHasNoFdInheritancePayload(request) &&
PosixsrvHasNoSpawnPayload(request) && PosixsrvHasNoWaitPayload(request) &&
PosixsrvHasNoAlarmPayload(request) && PosixsrvHasNoVforkPayload(request);
}
// Dispatches one validated bootstrap/process request and consumes transferred handles explicitly.
static KSTATUS PosixsrvDispatch(POSIXSRV_CLIENT* client,
const NTL_POSIX_REQUEST* request,
const SYS_PORT_MESSAGE_INFO* info,
SYS_HANDLE* handles) {
if (!client || !request || !info) return NTLSTATUS_INVALID_PARAMETER;
uint32_t operation = request->Operation;
if (info->SenderProcessId != client->Connection.ClientProcessId ||
info->SenderSecurityId != client->Connection.ClientSecurityId)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_ACCESS_DENIED, 0);
if (request->Size != sizeof(*request) || request->Flags != NTL_POSIX_REQUEST_FLAG_NONE ||
request->SignalReserved != 0 || request->AlarmReserved != 0 || request->VforkReserved != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
if (request->Version != NTL_POSIX_PROTOCOL_VERSION)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_NOT_SUPPORTED, 0);
if (operation != NTL_POSIX_OP_VFORK_ATTACH && operation != NTL_POSIX_OP_VFORK_CANCEL &&
operation != NTL_POSIX_OP_VFORK_EXIT && !PosixsrvHasNoVforkPayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
int signalOperation = operation == NTL_POSIX_OP_UPDATE_SIGNAL_MASK ||
operation == NTL_POSIX_OP_SET_SIGNAL_DISPOSITION ||
operation == NTL_POSIX_OP_GET_PENDING_SIGNALS ||
operation == NTL_POSIX_OP_SEND_SIGNAL;
if (operation != NTL_POSIX_OP_REGISTER_THREAD && !signalOperation &&
operation != NTL_POSIX_OP_EXEC_COMMIT && operation != NTL_POSIX_OP_VFORK_ATTACH &&
!PosixsrvHasNoSignalPayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
if (operation != NTL_POSIX_OP_SET_ALARM && !PosixsrvHasNoAlarmPayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
if (operation == NTL_POSIX_OP_REGISTER_PROCESS) {
if (!PosixsrvHasNoTransaction(request) || !PosixsrvHasNoFdInheritancePayload(request) ||
!PosixsrvHasNoSpawnPayload(request) || !PosixsrvHasNoWaitPayload(request) ||
info->HandleCount != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvRegisterProcess(client, request);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_BOOTSTRAP_READY) {
if (info->HandleCount != 0 || !PosixsrvHasNoTransaction(request) ||
!PosixsrvHasNoFdInheritancePayload(request) || !PosixsrvHasNoSpawnPayload(request) ||
!PosixsrvHasNoWaitPayload(request) ||
request->TargetPosixPid != 0 || request->UserId != 0 || request->GroupId != 0 ||
request->ProcessGroupId != 0 || request->LookupKind != NTL_POSIX_LOOKUP_NONE ||
request->OpenDescriptionType != 0 || request->OpenStatusFlags != 0 ||
request->OpenDescriptionId != 0 || request->NameLength != 0 || request->PathLength != 0 ||
request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvBootstrapReady(client, request);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (!PosixsrvHasNoBootstrap(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
if (operation == NTL_POSIX_OP_VFORK_BEGIN) {
if (!client->Process || info->HandleCount != 0 || !PosixsrvHasNoTransaction(request) ||
request->FdInheritanceToken == 0 || request->FdInheritanceIndex != 0 ||
request->FdInheritanceCount != 0 || request->FdInheritanceFlags != 0 ||
!PosixsrvHasNoSpawnPayload(request) || !PosixsrvHasNoWaitPayload(request) ||
!PosixsrvHasNoSignalPayload(request) || !PosixsrvHasNoAlarmPayload(request) ||
!PosixsrvHasNoVforkPayload(request) || request->TargetPosixPid != 0 ||
request->UserId != 0 || request->GroupId != 0 || request->ProcessGroupId != 0 ||
request->LookupKind != NTL_POSIX_LOOKUP_NONE || request->OpenDescriptionType != 0 ||
request->OpenStatusFlags != 0 || request->OpenDescriptionId != 0 ||
request->NameLength != 0 || request->PathLength != 0 || request->Name[0] != '\0' ||
request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
uint32_t childPid = 0;
uint64_t token = 0;
KSTATUS status = PosixsrvBeginVfork(client, request, &childPid, &token);
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, operation, status, 0);
if (KSUCCESS(status)) {
reply.SpawnedPosixPid = childPid;
reply.VforkToken = token;
}
return PosixsrvSendReply(client, info->MessageId, &reply);
}
if (operation == NTL_POSIX_OP_VFORK_ATTACH) {
if (!client->Process || info->HandleCount != 1 || !handles ||
!PosixsrvHasNoTransaction(request) || !PosixsrvHasNoFdInheritancePayload(request) ||
!PosixsrvHasNoSpawnPayload(request) || !PosixsrvHasNoWaitPayload(request) ||
!PosixsrvHasNoAlarmPayload(request) || request->TargetPosixPid == 0 ||
request->VforkToken == 0 || request->UserId != 0 || request->GroupId != 0 ||
request->ProcessGroupId != 0 || request->LookupKind != NTL_POSIX_LOOKUP_NONE ||
request->OpenDescriptionType != 0 || request->OpenStatusFlags != 0 ||
request->OpenDescriptionId != 0 || request->NameLength != 0 || request->PathLength != 0 ||
request->Name[0] != '\0' || request->Path[0] != '\0' ||
request->SignalTargetPid != 0 || request->SignalNumber != 0 ||
request->SignalDisposition != 0 ||
(request->SignalMask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0 ||
request->SignalApcRoutine == 0 || request->VforkExitCode != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvAttachVfork(client, request, info, &handles[0]);
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, operation, status, 0);
if (KSUCCESS(status)) {
reply.PosixPid = request->TargetPosixPid;
reply.NativeGeneration = 1u;
reply.ProcessState = NTL_POSIX_PROCESS_STATE_VFORK_BORROWED;
reply.VforkToken = request->VforkToken;
}
return PosixsrvSendReply(client, info->MessageId, &reply);
}
if (operation == NTL_POSIX_OP_VFORK_CANCEL) {
if (!client->Process || info->HandleCount != 0 || !PosixsrvHasNoTransaction(request) ||
!PosixsrvHasNoFdInheritancePayload(request) || !PosixsrvHasNoSpawnPayload(request) ||
!PosixsrvHasNoWaitPayload(request) || !PosixsrvHasNoSignalPayload(request) ||
!PosixsrvHasNoAlarmPayload(request) || request->TargetPosixPid == 0 ||
request->VforkToken == 0 || request->UserId != 0 || request->GroupId != 0 ||
request->ProcessGroupId != 0 || request->LookupKind != NTL_POSIX_LOOKUP_NONE ||
request->OpenDescriptionType != 0 || request->OpenStatusFlags != 0 ||
request->OpenDescriptionId != 0 || request->NameLength != 0 || request->PathLength != 0 ||
request->Name[0] != '\0' || request->Path[0] != '\0' || request->VforkExitCode != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvCancelVfork(client, request->TargetPosixPid, request->VforkToken);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
POSIXSRV_CLIENT routedClient;
if (client->Process && info->SenderThreadId != 0) {
KSTATUS routeStatus = PosixsrvLockRegistry();
if (!KSUCCESS(routeStatus))
return PosixsrvReply(client, info->MessageId, operation, routeStatus, 0);
POSIXSRV_PROCESS_RECORD* borrowed = PosixsrvFindBorrowedChildLocked(
client->Process, client->NativeGeneration, info->SenderThreadId);
if (borrowed) {
routedClient = *client;
routedClient.Process = borrowed;
routedClient.NativeGeneration = borrowed->Generation;
client = &routedClient;
}
PosixsrvUnlockRegistry();
}
if (operation == NTL_POSIX_OP_VFORK_EXIT) {
if (info->HandleCount != 0 || !PosixsrvHasNoTransaction(request) ||
!PosixsrvHasNoFdInheritancePayload(request) || !PosixsrvHasNoSpawnPayload(request) ||
!PosixsrvHasNoWaitPayload(request) || !PosixsrvHasNoSignalPayload(request) ||
!PosixsrvHasNoAlarmPayload(request) || request->TargetPosixPid == 0 ||
request->VforkToken == 0 || request->UserId != 0 || request->GroupId != 0 ||
request->ProcessGroupId != 0 || request->LookupKind != NTL_POSIX_LOOKUP_NONE ||
request->OpenDescriptionType != 0 || request->OpenStatusFlags != 0 ||
request->OpenDescriptionId != 0 || request->NameLength != 0 || request->PathLength != 0 ||
request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvExitVfork(client, request->TargetPosixPid, request->VforkToken,
request->VforkExitCode);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_REGISTER_THREAD) {
if (!client->Process || info->HandleCount != 1 || !handles ||
!PosixsrvHasNoTransaction(request) || !PosixsrvHasNoFdInheritancePayload(request) ||
!PosixsrvHasNoSpawnPayload(request) || !PosixsrvHasNoWaitPayload(request) ||
!PosixsrvHasNoAttributePayload(request) || request->SignalTargetPid != 0 ||
request->SignalNumber != 0 || request->SignalDisposition != 0 ||
request->SignalApcRoutine == 0 ||
(request->SignalMask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvRegisterThread(client, request, info, &handles[0]);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_UNREGISTER_THREAD) {
if (!client->Process || info->HandleCount != 0 ||
!PosixsrvHasNoTransaction(request) || !PosixsrvHasNoFdInheritancePayload(request) ||
!PosixsrvHasNoSpawnPayload(request) || !PosixsrvHasNoWaitPayload(request) ||
!PosixsrvHasNoAttributePayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvUnregisterThread(client, info);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (signalOperation) {
if (!client->Process || info->HandleCount != 0 || !PosixsrvHasOnlySignalPayload(request) ||
request->SignalApcRoutine != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
if (operation == NTL_POSIX_OP_UPDATE_SIGNAL_MASK) {
if (request->SignalTargetPid != 0 || request->SignalNumber != 0 ||
request->SignalDisposition != 0 ||
(request->SignalMask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvUpdateSignalMask(client, info, request->SignalMask);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_SET_SIGNAL_DISPOSITION) {
if (request->SignalTargetPid != 0 || request->SignalMask != 0 ||
!NtlPosixSignalValid(request->SignalNumber))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvSetSignalDisposition(client, request->SignalNumber,
request->SignalDisposition);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_GET_PENDING_SIGNALS) {
if (request->SignalTargetPid != 0 || request->SignalNumber != 0 ||
request->SignalDisposition != 0 || request->SignalMask != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, operation, NTLSTATUS_SUCCESS, 0);
KSTATUS status = PosixsrvLockRegistry();
if (KSUCCESS(status)) {
reply.PendingSignals = client->Process->ProcessPendingSignals;
PosixsrvUnlockRegistry();
} else reply.Status = status;
return PosixsrvSendReply(client, info->MessageId, &reply);
}
if (request->SignalDisposition != 0 || request->SignalMask != 0 ||
request->SignalNumber < 0 || request->SignalNumber > NTL_POSIX_SIGNAL_MAX)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvSendSignal(client, request->SignalTargetPid, request->SignalNumber);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_SET_ALARM) {
if (!client->Process || info->HandleCount != 0 ||
!PosixsrvHasNoTransaction(request) || !PosixsrvHasNoAttributePayload(request) ||
!PosixsrvHasNoFdInheritancePayload(request) || !PosixsrvHasNoSpawnPayload(request) ||
!PosixsrvHasNoWaitPayload(request) || !PosixsrvHasNoSignalPayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
uint32_t previousSeconds = 0;
KSTATUS status = PosixsrvSetAlarm(client, request->AlarmSeconds, &previousSeconds);
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, operation, status, 0);
if (KSUCCESS(status)) reply.AlarmRemainingSeconds = previousSeconds;
return PosixsrvSendReply(client, info->MessageId, &reply);
}
if (operation == NTL_POSIX_OP_CONSTRUCT_SET_EXECUTABLE ||
operation == NTL_POSIX_OP_CONSTRUCT_WRITE_VECTOR ||
operation == NTL_POSIX_OP_SPAWN_ADD_FILE_ACTION ||
operation == NTL_POSIX_OP_SPAWN_SET_ATTRIBUTES ||
operation == NTL_POSIX_OP_SPAWN_COMMIT ||
operation == NTL_POSIX_OP_EXEC_COMMIT) {
if (!client->Process ||
(!PosixsrvCommittedGenerationOperational(client) && !PosixsrvClientIsBorrowedProcess(client)) ||
request->TransactionId == 0 || request->TransactionKind != NTL_POSIX_TRANSACTION_NONE ||
request->TargetPosixPid != 0 || request->UserId != 0 || request->GroupId != 0 ||
request->LookupKind != NTL_POSIX_LOOKUP_NONE || request->OpenDescriptionType != 0 ||
request->OpenStatusFlags != 0 || request->OpenDescriptionId != 0 ||
request->NameLength != 0 || request->Name[0] != '\0' ||
!PosixsrvHasNoWaitPayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = NTLSTATUS_INVALID_PARAMETER;
if (operation == NTL_POSIX_OP_CONSTRUCT_SET_EXECUTABLE) {
if (info->HandleCount != 0 || !PosixsrvHasNoFdInheritancePayload(request) ||
request->ProcessGroupId != 0 || request->ConstructionVectorKind != 0 ||
request->ConstructionVectorIndex != 0 || request->ConstructionVectorOffset != 0 ||
request->ConstructionVectorFlags != 0 || request->SpawnActionKind != 0 ||
request->SpawnFileDescriptor != 0 || request->SpawnNewFileDescriptor != 0 ||
request->SpawnOpenFlags != 0 || request->SpawnOpenMode != 0 ||
request->SpawnAttributeFlags != 0 || request->SpawnSignalMask != 0 ||
request->SpawnSignalDefaultMask != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
status = PosixsrvConstructionSetExecutable(client, request);
} else if (operation == NTL_POSIX_OP_CONSTRUCT_WRITE_VECTOR) {
if (info->HandleCount != 0 || !PosixsrvHasNoFdInheritancePayload(request) ||
request->ProcessGroupId != 0 || request->SpawnActionKind != 0 ||
request->SpawnFileDescriptor != 0 || request->SpawnNewFileDescriptor != 0 ||
request->SpawnOpenFlags != 0 || request->SpawnOpenMode != 0 ||
request->SpawnAttributeFlags != 0 || request->SpawnSignalMask != 0 ||
request->SpawnSignalDefaultMask != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
status = PosixsrvConstructionWriteVector(client, request);
} else if (operation == NTL_POSIX_OP_SPAWN_ADD_FILE_ACTION) {
if (info->HandleCount != 0 || !PosixsrvHasNoFdInheritancePayload(request) ||
request->ProcessGroupId != 0 || request->ConstructionVectorKind != 0 ||
request->ConstructionVectorIndex != 0 || request->ConstructionVectorOffset != 0 ||
request->ConstructionVectorFlags != 0 || request->SpawnAttributeFlags != 0 ||
request->SpawnSignalMask != 0 || request->SpawnSignalDefaultMask != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
status = PosixsrvSpawnAddFileAction(client, request);
} else if (operation == NTL_POSIX_OP_SPAWN_SET_ATTRIBUTES) {
if (info->HandleCount != 0 || !PosixsrvHasNoFdInheritancePayload(request) ||
request->PathLength != 0 || request->Path[0] != '\0' ||
request->ConstructionVectorKind != 0 || request->ConstructionVectorIndex != 0 ||
request->ConstructionVectorOffset != 0 || request->ConstructionVectorFlags != 0 ||
request->SpawnActionKind != 0 || request->SpawnFileDescriptor != 0 ||
request->SpawnNewFileDescriptor != 0 || request->SpawnOpenFlags != 0 ||
request->SpawnOpenMode != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
status = PosixsrvSpawnSetAttributes(client, request);
} else if (operation == NTL_POSIX_OP_SPAWN_COMMIT) {
if (info->HandleCount != 1 || !handles || request->FdInheritanceToken == 0 ||
request->FdInheritanceIndex != 0 || request->FdInheritanceCount != 0 ||
request->FdInheritanceFlags != 0 || request->ProcessGroupId != 0 ||
request->PathLength != 0 || request->Path[0] != '\0' ||
request->ConstructionVectorKind != 0 || request->ConstructionVectorIndex != 0 ||
request->ConstructionVectorOffset != 0 || request->ConstructionVectorFlags != 0 ||
request->SpawnActionKind != 0 || request->SpawnFileDescriptor != 0 ||
request->SpawnNewFileDescriptor != 0 || request->SpawnOpenFlags != 0 ||
request->SpawnOpenMode != 0 || request->SpawnAttributeFlags != 0 ||
request->SpawnSignalMask != 0 || request->SpawnSignalDefaultMask != 0 ||
!PosixsrvHasNoSignalPayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
POSIXSRV_TRANSACTION transaction;
status = PosixsrvTakeTransaction(client, request->TransactionId,
NTL_POSIX_TRANSACTION_SPAWN, &transaction);
if (!KSUCCESS(status))
return PosixsrvReply(client, info->MessageId, operation, status, 0);
uint32_t childPid = 0;
status = PosixsrvCommitSpawn(client, request, handles[0], &transaction, &childPid);
PosixsrvDestroyTransaction(&transaction);
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, operation, status, 0);
if (KSUCCESS(status)) reply.SpawnedPosixPid = childPid;
return PosixsrvSendReply(client, info->MessageId, &reply);
} else {
if (info->HandleCount != 0 || request->FdInheritanceToken == 0 ||
request->FdInheritanceIndex != 0 || request->FdInheritanceCount != 0 ||
request->FdInheritanceFlags != 0 || request->ProcessGroupId != 0 ||
request->PathLength != 0 || request->Path[0] != '\0' ||
request->ConstructionVectorKind != 0 || request->ConstructionVectorIndex != 0 ||
request->ConstructionVectorOffset != 0 || request->ConstructionVectorFlags != 0 ||
request->SpawnActionKind != 0 || request->SpawnFileDescriptor != 0 ||
request->SpawnNewFileDescriptor != 0 || request->SpawnOpenFlags != 0 ||
request->SpawnOpenMode != 0 || request->SpawnAttributeFlags != 0 ||
request->SpawnSignalMask != 0 || request->SpawnSignalDefaultMask != 0 ||
request->SignalTargetPid != 0 || request->SignalNumber != 0 ||
request->SignalDisposition != 0 || request->SignalApcRoutine != 0 ||
(request->SignalMask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
POSIXSRV_TRANSACTION transaction;
status = PosixsrvTakeTransaction(client, request->TransactionId,
NTL_POSIX_TRANSACTION_EXEC, &transaction);
if (!KSUCCESS(status))
return PosixsrvReply(client, info->MessageId, operation, status, 0);
status = PosixsrvCommitExec(client, request, &transaction);
PosixsrvDestroyTransaction(&transaction);
if (!KSUCCESS(status))
return PosixsrvReply(client, info->MessageId, operation, status, 0);
if (client->Process &&
client->Process->BorrowedTerminalKind == POSIXSRV_VFORK_TERMINAL_EXEC)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_SUCCESS, 0);
// Ordinary successful exec kills the requesting native generation; there is no reply.
return NTLSTATUS_SUCCESS;
}
return PosixsrvReply(client, info->MessageId, operation, status, request->TransactionId);
}
if (!PosixsrvHasNoSpawnPayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
if (operation != NTL_POSIX_OP_WAIT_PROCESS && !PosixsrvHasNoWaitPayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
if (operation == NTL_POSIX_OP_CREATE_FD_INHERITANCE) {
if (!client->Process ||
(!PosixsrvCommittedGenerationOperational(client) && !PosixsrvClientIsBorrowedProcess(client)) ||
info->HandleCount != 0 || !PosixsrvHasNoTransaction(request) ||
request->TargetPosixPid != 0 || request->UserId != 0 || request->GroupId != 0 ||
request->ProcessGroupId != 0 || request->LookupKind != NTL_POSIX_LOOKUP_NONE ||
request->OpenDescriptionType != 0 || request->OpenStatusFlags != 0 ||
request->OpenDescriptionId != 0 || request->NameLength != 0 || request->PathLength != 0 ||
request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
uint64_t token = 0;
KSTATUS status = PosixsrvCreateFdInheritance(client, request, &token);
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, operation, status, 0);
if (KSUCCESS(status)) {
reply.FdInheritanceToken = token;
reply.FdInheritanceCount = request->FdInheritanceCount;
}
KSTATUS replyStatus = PosixsrvSendReply(client, info->MessageId, &reply);
if (!KSUCCESS(replyStatus) && KSUCCESS(status))
(void)PosixsrvCancelFdInheritance(client, token);
return replyStatus;
}
if (operation == NTL_POSIX_OP_IMPORT_FD_INHERITANCE) {
if (!client->Process ||
(client->Process->State != NTL_POSIX_PROCESS_STATE_CONSTRUCTING &&
client->Process->State != NTL_POSIX_PROCESS_STATE_VFORK_BORROWED &&
!PosixsrvCommittedGenerationOperational(client) &&
!PosixsrvClientIsPendingExecGeneration(client)) ||
info->HandleCount != 0 || !PosixsrvHasNoTransaction(request) ||
request->FdInheritanceToken == 0 || request->FdInheritanceCount != 0 ||
request->FdInheritanceFlags != 0 || request->TargetPosixPid != 0 ||
request->UserId != 0 || request->GroupId != 0 || request->ProcessGroupId != 0 ||
request->LookupKind != NTL_POSIX_LOOKUP_NONE || request->OpenDescriptionType != 0 ||
request->OpenStatusFlags != 0 || request->OpenDescriptionId != 0 ||
request->NameLength != 0 || request->PathLength != 0 || request->Name[0] != '\0' ||
request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
for (uint32_t i = 0; i < NTL_POSIX_FD_INHERIT_MAX; ++i) {
if (request->FdManifest[i].Descriptor != 0 ||
request->FdManifest[i].DescriptorFlags != 0 ||
request->FdManifest[i].OpenDescriptionId != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
}
return PosixsrvReplyFdInheritance(client, info->MessageId,
request->FdInheritanceToken,
request->FdInheritanceIndex);
}
if (operation == NTL_POSIX_OP_CANCEL_FD_INHERITANCE) {
if (!client->Process || !PosixsrvCommittedGenerationOperational(client) ||
info->HandleCount != 0 || !PosixsrvHasNoTransaction(request) ||
request->FdInheritanceToken == 0 || request->FdInheritanceIndex != 0 ||
request->FdInheritanceCount != 0 || request->FdInheritanceFlags != 0 ||
request->TargetPosixPid != 0 || request->UserId != 0 || request->GroupId != 0 ||
request->ProcessGroupId != 0 || request->LookupKind != NTL_POSIX_LOOKUP_NONE ||
request->OpenDescriptionType != 0 || request->OpenStatusFlags != 0 ||
request->OpenDescriptionId != 0 || request->NameLength != 0 || request->PathLength != 0 ||
request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
for (uint32_t i = 0; i < NTL_POSIX_FD_INHERIT_MAX; ++i) {
if (request->FdManifest[i].Descriptor != 0 ||
request->FdManifest[i].DescriptorFlags != 0 ||
request->FdManifest[i].OpenDescriptionId != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
}
KSTATUS status = PosixsrvCancelFdInheritance(client, request->FdInheritanceToken);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (!PosixsrvHasNoFdInheritancePayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
if (operation == NTL_POSIX_OP_CREATE_OPEN_DESCRIPTION) {
if (!client->Process || !PosixsrvCommittedGenerationOperational(client) ||
!PosixsrvHasNoTransaction(request) || request->TargetPosixPid != 0 ||
request->UserId != 0 || request->GroupId != 0 || request->ProcessGroupId != 0 ||
request->LookupKind != NTL_POSIX_LOOKUP_NONE || request->OpenDescriptionId != 0 ||
request->NameLength != 0 || request->PathLength != 0 ||
request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
uint64_t descriptionId = 0;
NTL_POSIX_SHARED_OPEN_DESCRIPTION* shared = (NTL_POSIX_SHARED_OPEN_DESCRIPTION*)0;
KSTATUS status = PosixsrvCreateOpenDescription(client, request, handles, info->HandleCount,
&descriptionId, &shared);
KSTATUS replyStatus = PosixsrvReplyOpenDescription(
client, info->MessageId, operation, status, descriptionId,
request->OpenDescriptionType, request->OpenStatusFlags, shared);
if (!KSUCCESS(replyStatus) && KSUCCESS(status) && descriptionId != 0)
(void)PosixsrvReleaseOpenDescription(client, descriptionId);
return replyStatus;
}
if (operation == NTL_POSIX_OP_CREATE_PIPE) {
if (!client->Process || !PosixsrvCommittedGenerationOperational(client) ||
info->HandleCount != 0 || !PosixsrvHasNoTransaction(request) ||
!PosixsrvHasNoAttributePayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
uint64_t readId = 0;
uint64_t writeId = 0;
KSTATUS status = PosixsrvCreatePipe(client, &readId, &writeId);
KSTATUS replyStatus = PosixsrvReplyPipe(client, info->MessageId, status,
readId, writeId);
if (!KSUCCESS(replyStatus) && KSUCCESS(status)) {
if (readId != 0) (void)PosixsrvReleaseOpenDescription(client, readId);
if (writeId != 0) (void)PosixsrvReleaseOpenDescription(client, writeId);
}
return replyStatus;
}
if (info->HandleCount != 0)
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
if (operation != NTL_POSIX_OP_RELEASE_OPEN_DESCRIPTION &&
operation != NTL_POSIX_OP_SET_OPEN_STATUS &&
(request->OpenDescriptionType != 0 || request->OpenStatusFlags != 0 ||
request->OpenDescriptionId != 0))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
if (operation == NTL_POSIX_OP_QUERY) {
if (!PosixsrvHasNoTransaction(request) || !PosixsrvHasNoAttributePayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_SUCCESS, 0);
}
if (operation == NTL_POSIX_OP_BEGIN_TRANSACTION) {
if (request->TransactionId != 0 || request->TargetPosixPid != 0 || request->UserId != 0 ||
request->GroupId != 0 || request->ProcessGroupId != 0 ||
request->LookupKind != NTL_POSIX_LOOKUP_NONE || request->NameLength != 0 ||
request->PathLength != 0 || request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
uint32_t transactionId = 0;
KSTATUS status = PosixsrvBeginTransaction(client, request->TransactionKind, &transactionId);
return PosixsrvReply(client, info->MessageId, operation, status, transactionId);
}
if (operation == NTL_POSIX_OP_CANCEL_TRANSACTION) {
if (request->TransactionKind != NTL_POSIX_TRANSACTION_NONE ||
request->TargetPosixPid != 0 || request->UserId != 0 || request->GroupId != 0 ||
request->ProcessGroupId != 0 || request->LookupKind != NTL_POSIX_LOOKUP_NONE ||
request->NameLength != 0 || request->PathLength != 0 ||
request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvCancelTransaction(client, request->TransactionId);
return PosixsrvReply(client, info->MessageId, operation, status,
KSUCCESS(status) ? request->TransactionId : 0);
}
if (!client->Process ||
(!PosixsrvCommittedGenerationOperational(client) &&
!PosixsrvClientIsBorrowedProcess(client) &&
!(operation == NTL_POSIX_OP_RELEASE_OPEN_DESCRIPTION &&
PosixsrvClientIsPendingExecGeneration(client))))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_NOT_FOUND, 0);
if (!PosixsrvHasNoTransaction(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
if (operation == NTL_POSIX_OP_WAIT_PROCESS) {
if (!PosixsrvHasNoAttributePayload(request) ||
!PosixsrvHasNoFdInheritancePayload(request))
return PosixsrvReply(client, info->MessageId, operation,
NTLSTATUS_INVALID_PARAMETER, 0);
int32_t waitedPid = 0;
int32_t exitCode = 0;
KSTATUS status = PosixsrvWaitProcess(client, request->WaitPid, request->WaitOptions,
&waitedPid, &exitCode);
NTL_POSIX_REPLY reply;
PosixsrvInitializeReply(client, &reply, operation, status, 0);
if (KSUCCESS(status)) {
reply.WaitedPosixPid = waitedPid;
reply.ExitCode = exitCode;
}
return PosixsrvSendReply(client, info->MessageId, &reply);
}
if (operation == NTL_POSIX_OP_RELEASE_OPEN_DESCRIPTION) {
if (request->OpenDescriptionId == 0 || request->OpenDescriptionType != 0 ||
request->OpenStatusFlags != 0 || request->TargetPosixPid != 0 ||
request->UserId != 0 || request->GroupId != 0 || request->ProcessGroupId != 0 ||
request->LookupKind != NTL_POSIX_LOOKUP_NONE || request->NameLength != 0 ||
request->PathLength != 0 || request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvReleaseOpenDescription(client, request->OpenDescriptionId);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_SET_OPEN_STATUS) {
if (request->OpenDescriptionId == 0 || request->OpenDescriptionType != 0 ||
request->TargetPosixPid != 0 || request->UserId != 0 || request->GroupId != 0 ||
request->ProcessGroupId != 0 || request->LookupKind != NTL_POSIX_LOOKUP_NONE ||
request->NameLength != 0 || request->PathLength != 0 ||
request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
uint32_t statusFlags = 0;
uint32_t type = 0;
KSTATUS status = PosixsrvSetOpenStatus(client, request->OpenDescriptionId,
request->OpenStatusFlags, &statusFlags, &type);
return PosixsrvReplyOpenDescription(client, info->MessageId, operation, status,
request->OpenDescriptionId, type, statusFlags,
(NTL_POSIX_SHARED_OPEN_DESCRIPTION*)0);
}
if (operation == NTL_POSIX_OP_GET_PROCESS_ATTRIBUTES) {
if (!PosixsrvHasNoAttributePayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_SUCCESS, 0);
}
if (operation == NTL_POSIX_OP_SET_CURRENT_DIRECTORY) {
if (request->TargetPosixPid != 0 || request->UserId != 0 || request->GroupId != 0 ||
request->ProcessGroupId != 0 || request->LookupKind != NTL_POSIX_LOOKUP_NONE ||
request->NameLength != 0 || request->Name[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvSetCurrentDirectory(client, request);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_SET_UID) {
if (request->TargetPosixPid != 0 || request->GroupId != 0 || request->ProcessGroupId != 0 ||
request->LookupKind != NTL_POSIX_LOOKUP_NONE || request->NameLength != 0 ||
request->PathLength != 0 || request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvSetUid(client, request->UserId);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_SET_GID) {
if (request->TargetPosixPid != 0 || request->UserId != 0 || request->ProcessGroupId != 0 ||
request->LookupKind != NTL_POSIX_LOOKUP_NONE || request->NameLength != 0 ||
request->PathLength != 0 || request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvSetGid(client, request->GroupId);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_SET_PROCESS_GROUP) {
if (request->UserId != 0 || request->GroupId != 0 ||
request->LookupKind != NTL_POSIX_LOOKUP_NONE || request->NameLength != 0 ||
request->PathLength != 0 || request->Name[0] != '\0' || request->Path[0] != '\0')
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvSetProcessGroup(client, request->TargetPosixPid,
request->ProcessGroupId);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_CREATE_SESSION) {
if (!PosixsrvHasNoAttributePayload(request))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
KSTATUS status = PosixsrvCreateSession(client);
return PosixsrvReply(client, info->MessageId, operation, status, 0);
}
if (operation == NTL_POSIX_OP_LOOKUP_USER) {
if (request->TargetPosixPid != 0 || request->ProcessGroupId != 0 || request->GroupId != 0 ||
request->PathLength != 0 || request->Path[0] != '\0' ||
(request->LookupKind != NTL_POSIX_LOOKUP_BY_ID &&
request->LookupKind != NTL_POSIX_LOOKUP_BY_NAME) ||
(request->LookupKind == NTL_POSIX_LOOKUP_BY_ID &&
(request->NameLength != 0 || request->Name[0] != '\0')) ||
(request->LookupKind == NTL_POSIX_LOOKUP_BY_NAME && !PosixsrvValidateLookupName(request)))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
const POSIXSRV_USER_RECORD* user =
PosixsrvFindUser(request->LookupKind, request->UserId, request->Name);
return PosixsrvReplyUser(client, info->MessageId, operation,
user ? NTLSTATUS_SUCCESS : NTLSTATUS_NOT_FOUND, user);
}
if (operation == NTL_POSIX_OP_LOOKUP_GROUP) {
if (request->TargetPosixPid != 0 || request->ProcessGroupId != 0 || request->UserId != 0 ||
request->PathLength != 0 || request->Path[0] != '\0' ||
(request->LookupKind != NTL_POSIX_LOOKUP_BY_ID &&
request->LookupKind != NTL_POSIX_LOOKUP_BY_NAME) ||
(request->LookupKind == NTL_POSIX_LOOKUP_BY_ID &&
(request->NameLength != 0 || request->Name[0] != '\0')) ||
(request->LookupKind == NTL_POSIX_LOOKUP_BY_NAME && !PosixsrvValidateLookupName(request)))
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
const POSIXSRV_GROUP_RECORD* group =
PosixsrvFindGroup(request->LookupKind, request->GroupId, request->Name);
return PosixsrvReplyGroup(client, info->MessageId, operation,
group ? NTLSTATUS_SUCCESS : NTLSTATUS_NOT_FOUND, group);
}
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_NOT_SUPPORTED, 0);
}
// Closes every unconsumed capability delivered with one client request.
static void PosixsrvCloseReceivedHandles(SYS_HANDLE* handles, uint32_t count) {
if (!handles) return;
for (uint32_t i = 0; i < count; ++i) {
if (handles[i] != SYS_HANDLE_INVALID)
(void)SysCloseHandle(handles[i]);
}
}
// Serves one client until disconnect; process-owned transactions survive transport loss.
static void PosixsrvClientThread(uintptr_t argument) {
POSIXSRV_CLIENT* client = (POSIXSRV_CLIENT*)argument;
if (!client) {
SysExitThread(1);
return;
}
PosixsrvSetCurrentThreadName("posixsrv.client");
for (;;) {
NTL_POSIX_REQUEST request;
SYS_PORT_MESSAGE_INFO info;
SYS_HANDLE handles[SYS_PORT_MAX_TRANSFER_HANDLES];
memset(&request, 0, sizeof(request));
memset(&info, 0, sizeof(info));
for (uint32_t i = 0; i < SYS_PORT_MAX_TRANSFER_HANDLES; ++i)
handles[i] = SYS_HANDLE_INVALID;
info.Size = sizeof(info);
SYS_RECEIVE_PORT_REQUEST receive;
memset(&receive, 0, sizeof(receive));
receive.Size = sizeof(receive);
receive.EndpointHandle = client->Endpoint;
receive.Flags = SYS_PORT_FLAG_NONE;
receive.Payload = &request;
receive.PayloadCapacity = sizeof(request);
receive.HandlesOut = handles;
receive.HandleCapacity = SYS_PORT_MAX_TRANSFER_HANDLES;
receive.InfoOut = &info;
receive.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysReceivePort(&receive);
if (status == NTLSTATUS_PORT_DISCONNECTED || !KSUCCESS(status)) break;
if (info.Type != SYS_PORT_MESSAGE_REQUEST || info.PayloadLength != sizeof(request)) {
if (info.Type == SYS_PORT_MESSAGE_REQUEST)
(void)PosixsrvReply(client, info.MessageId, request.Operation,
NTLSTATUS_INVALID_PARAMETER, 0);
PosixsrvCloseReceivedHandles(handles, info.HandleCount);
continue;
}
status = PosixsrvDispatch(client, &request, &info, handles);
PosixsrvCloseReceivedHandles(handles, info.HandleCount);
if (!KSUCCESS(status)) break;
}
PosixsrvDetachClientProcess(client);
(void)SysDisconnectPort(client->Endpoint);
(void)SysCloseHandle(client->Endpoint);
free(client);
SysExitThread(0);
}
// Accepts public connections and gives each kernel-stamped client identity its own worker.
static void PosixsrvListenerThread(uintptr_t argument) {
SYS_HANDLE listener = (SYS_HANDLE)argument;
PosixsrvSetCurrentThreadName("posixsrv.listen");
for (;;) {
POSIXSRV_CLIENT* client = (POSIXSRV_CLIENT*)calloc(1, sizeof(*client));
if (!client) {
SysExitThread(1);
return;
}
client->Endpoint = SYS_HANDLE_INVALID;
client->Connection.Size = sizeof(client->Connection);
SYS_ACCEPT_PORT_REQUEST accept;
memset(&accept, 0, sizeof(accept));
accept.Size = sizeof(accept);
accept.ListenerHandle = listener;
accept.Flags = SYS_PORT_ACCEPT_FLAG_NONE;
accept.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
accept.EndpointHandleOut = &client->Endpoint;
accept.ConnectionInfoOut = &client->Connection;
KSTATUS status = SysAcceptPort(&accept);
if (!KSUCCESS(status)) {
free(client);
break;
}
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)PosixsrvClientThread;
create.Argument = (uintptr_t)client;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
status = SysCreateThread(&create);
if (!KSUCCESS(status)) {
(void)SysDisconnectPort(client->Endpoint);
(void)SysCloseHandle(client->Endpoint);
free(client);
}
}
SysExitThread(0);
}
// Reports READY and transfers the public listener for init-owned RSSRV namespace publication.
static KSTATUS PosixsrvSendReady(SYS_HANDLE controlEndpoint, SYS_HANDLE publicListener) {
if (controlEndpoint == SYS_HANDLE_INVALID || publicListener == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
NTL_SERVICE_CONTROL_MESSAGE message;
memset(&message, 0, sizeof(message));
message.Size = sizeof(message);
message.Version = NTL_SERVICE_CONTROL_VERSION;
message.Operation = NTL_SERVICE_CONTROL_READY;
message.ExportCount = 1u;
message.Status = NTLSTATUS_SUCCESS;
if (!NtlServiceControlNameExport(&message, 0u, "public")) return NTLSTATUS_FAILURE;
SYS_PORT_HANDLE_TRANSFER transfer;
memset(&transfer, 0, sizeof(transfer));
transfer.Handle = publicListener;
transfer.DesiredRights = SYS_RIGHT_DUPLICATE | SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
SYS_SEND_PORT_REQUEST send;
memset(&send, 0, sizeof(send));
send.Size = sizeof(send);
send.EndpointHandle = controlEndpoint;
send.Flags = SYS_PORT_FLAG_NONE;
send.Payload = &message;
send.PayloadLength = sizeof(message);
send.Handles = &transfer;
send.HandleCount = 1u;
send.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
return SysSendPort(&send);
}
// Waits for init to stop the service or close the private control connection.
static void PosixsrvControlLoop(SYS_HANDLE endpoint) {
for (;;) {
NTL_SERVICE_CONTROL_MESSAGE message;
SYS_PORT_MESSAGE_INFO info;
memset(&message, 0, sizeof(message));
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_RECEIVE_PORT_REQUEST receive;
memset(&receive, 0, sizeof(receive));
receive.Size = sizeof(receive);
receive.EndpointHandle = endpoint;
receive.Payload = &message;
receive.PayloadCapacity = sizeof(message);
receive.InfoOut = &info;
receive.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysReceivePort(&receive);
if (status == NTLSTATUS_PORT_DISCONNECTED || !KSUCCESS(status)) return;
if (info.Type != SYS_PORT_MESSAGE_NOTIFICATION || info.HandleCount != 0 ||
info.PayloadLength != sizeof(message) || message.Size != sizeof(message) ||
!NtlServiceControlValid(&message, NTL_SERVICE_CONTROL_STOP, 0u))
continue;
if (message.Operation == NTL_SERVICE_CONTROL_STOP) return;
}
}
// Starts the POSIX policy service while keeping stable POSIX process identity out of the kernel.
void _start(void) {
KSTATUS status = NtlInitializeExceptionRuntime();
if (!KSUCCESS(status)) PosixsrvExit(1);
PosixsrvSetCurrentThreadName("posixsrv.main");
status = PosixsrvValidateSecurity();
if (!KSUCCESS(status)) PosixsrvExit(2);
status = SysCreateMutex(&gProcessRegistryMutex);
if (!KSUCCESS(status)) PosixsrvExit(3);
SYS_HANDLE controlListener = SysGetStartupCapability("service.control");
SYS_HANDLE publicationListener = SysGetStartupCapability("rssrv.posix-publisher");
if (controlListener == SYS_HANDLE_INVALID || publicationListener == SYS_HANDLE_INVALID) PosixsrvExit(4);
SYS_HANDLE publicListener = SYS_HANDLE_INVALID;
status = PosixsrvCreateListener(&publicListener);
if (!KSUCCESS(status)) PosixsrvExit(5);
SYS_HANDLE controlEndpoint = SYS_HANDLE_INVALID;
status = PosixsrvAcceptControl(controlListener, &controlEndpoint);
(void)SysCloseHandle(controlListener);
if (!KSUCCESS(status)) PosixsrvExit(6);
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)PosixsrvListenerThread;
create.Argument = (uintptr_t)publicListener;
create.Priority = 1;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
status = SysCreateThread(&create);
if (!KSUCCESS(status)) PosixsrvExit(7);
SYS_HANDLE publication = SYS_HANDLE_INVALID;
status = NtlResourceConnectListener(publicationListener, &publication);
(void)SysCloseHandle(publicationListener);
if (!KSUCCESS(status)) PosixsrvExit(8);
status = NtlResourceBind(publication, SYS_SV_LITERAL(NTL_POSIX_SERVICE_PATH),
NTL_RESOURCE_KIND_CAPABILITY, publicListener,
SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT,
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT);
if (!KSUCCESS(status)) PosixsrvExit(8);
status = PosixsrvSendReady(controlEndpoint, publicListener);
if (!KSUCCESS(status)) PosixsrvExit(8);
PosixsrvControlLoop(controlEndpoint);
(void)NtlResourceUnbind(publication, SYS_SV_LITERAL(NTL_POSIX_SERVICE_PATH));
(void)NtlResourceClose(publication);
(void)SysDisconnectPort(controlEndpoint);
(void)SysCloseHandle(controlEndpoint);
(void)SysCloseHandle(publicListener);
(void)SysCloseHandle(gProcessRegistryMutex);
gProcessRegistryMutex = SYS_HANDLE_INVALID;
PosixsrvExit(0);
}