5188 lines
240 KiB
C
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);
|
|
}
|