2154 lines
96 KiB
C
2154 lines
96 KiB
C
// POSIX personality-server bootstrap, stable process identity, shared attributes, and transaction plumbing.
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <intrin.h>
|
|
#include <ntlite/exception.h>
|
|
#include <ntlite/posix_service.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_TRANSACTION {
|
|
uint32_t Id;
|
|
uint32_t Kind;
|
|
} 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_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_FD_INHERITANCE_ENTRY {
|
|
NTL_POSIX_FD_MANIFEST_ENTRY Manifest;
|
|
POSIXSRV_OPEN_DESCRIPTION* Description;
|
|
} POSIXSRV_FD_INHERITANCE_ENTRY;
|
|
|
|
typedef struct POSIXSRV_FD_INHERITANCE_MONITOR {
|
|
uint64_t Token;
|
|
uint32_t NativeProcessId;
|
|
SYS_HANDLE ProcessHandle;
|
|
} POSIXSRV_FD_INHERITANCE_MONITOR;
|
|
|
|
struct POSIXSRV_FD_INHERITANCE {
|
|
POSIXSRV_FD_INHERITANCE* Next;
|
|
uint64_t Token;
|
|
POSIXSRV_PROCESS_RECORD* SourceProcess;
|
|
POSIXSRV_PROCESS_RECORD* ClaimProcess;
|
|
uint32_t SecurityId;
|
|
uint32_t Armed;
|
|
uint32_t TargetNativeProcessId;
|
|
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_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;
|
|
uint32_t MonitorActive;
|
|
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;
|
|
SYS_HANDLE ProcessHandle;
|
|
POSIXSRV_OPEN_HOLDER* OpenDescriptions;
|
|
};
|
|
|
|
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 NextTransactionId;
|
|
uint32_t ActiveTransactions;
|
|
POSIXSRV_TRANSACTION Transactions[NTL_POSIX_TRANSACTION_CAPACITY];
|
|
} POSIXSRV_CLIENT;
|
|
|
|
static SYS_HANDLE gProcessRegistryMutex = SYS_HANDLE_INVALID;
|
|
static POSIXSRV_PROCESS_RECORD* gProcessRegistry;
|
|
static POSIXSRV_OPEN_DESCRIPTION* gOpenDescriptionRegistry;
|
|
static POSIXSRV_FD_INHERITANCE* gFdInheritanceRegistry;
|
|
static uint32_t gNextPosixPid = POSIXSRV_FIRST_PID;
|
|
static uint64_t gNextOpenDescriptionId = 1u;
|
|
static uint64_t gNextFdInheritanceToken = 1u;
|
|
static uint32_t gRegisteredProcesses;
|
|
static uint32_t gRegisteredOpenDescriptions;
|
|
|
|
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 one live or retained record by kernel-stamped native identity.
|
|
static POSIXSRV_PROCESS_RECORD* PosixsrvFindProcessLocked(uint32_t nativePid, uint32_t securityId) {
|
|
for (POSIXSRV_PROCESS_RECORD* record = gProcessRegistry; record; record = record->Next) {
|
|
if (record->NativeProcessId == nativePid && record->SecurityId == securityId)
|
|
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 int PosixsrvProcessGroupExistsLocked(uint32_t sessionId, uint32_t processGroupId) {
|
|
if (processGroupId == 0) return 0;
|
|
for (POSIXSRV_PROCESS_RECORD* record = gProcessRegistry; record; record = record->Next) {
|
|
if (record->State == NTL_POSIX_PROCESS_STATE_RUNNING && record->SessionId == sessionId &&
|
|
record->ProcessGroupId == processGroupId)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Returns whether one protocol open-description type requires a retained native handle.
|
|
static int 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 int 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 && !snapshot->Armed)) {
|
|
PosixsrvDestroyFdInheritanceLocked(snapshot);
|
|
} else if (snapshot->SourceProcess == process) {
|
|
snapshot->SourceProcess = (POSIXSRV_PROCESS_RECORD*)0;
|
|
}
|
|
snapshot = next;
|
|
}
|
|
}
|
|
|
|
// Reclaims one armed snapshot if its designated native child dies before import completes.
|
|
static void PosixsrvFdInheritanceMonitorThread(uintptr_t argument) {
|
|
POSIXSRV_FD_INHERITANCE_MONITOR* monitor =
|
|
(POSIXSRV_FD_INHERITANCE_MONITOR*)argument;
|
|
if (!monitor) {
|
|
SysExitThread(1);
|
|
return;
|
|
}
|
|
PosixsrvSetCurrentThreadName("posixsrv.fd-inherit");
|
|
|
|
KSTATUS waitStatus = SysWait(monitor->ProcessHandle, SYS_WAIT_FLAG_NONE);
|
|
if (KSUCCESS(PosixsrvLockRegistry())) {
|
|
POSIXSRV_FD_INHERITANCE* snapshot = PosixsrvFindFdInheritanceLocked(monitor->Token);
|
|
if (snapshot && snapshot->Armed &&
|
|
snapshot->TargetNativeProcessId == monitor->NativeProcessId)
|
|
PosixsrvDestroyFdInheritanceLocked(snapshot);
|
|
PosixsrvUnlockRegistry();
|
|
}
|
|
|
|
(void)SysCloseHandle(monitor->ProcessHandle);
|
|
free(monitor);
|
|
SysExitThread(KSUCCESS(waitStatus) ? 0 : 1);
|
|
}
|
|
|
|
// Pins one inheritance snapshot to a concrete native child until import or child exit.
|
|
static KSTATUS PosixsrvArmFdInheritance(POSIXSRV_CLIENT* client, uint64_t token,
|
|
SYS_HANDLE* targetProcessHandle) {
|
|
if (!client || !client->Process || token == 0 || !targetProcessHandle ||
|
|
*targetProcessHandle == SYS_HANDLE_INVALID)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t nativePid = 0;
|
|
KSTATUS status = SysGetProcessPid(*targetProcessHandle, &nativePid);
|
|
if (!KSUCCESS(status)) return status;
|
|
if (nativePid == 0 || nativePid == client->Process->NativeProcessId)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
POSIXSRV_FD_INHERITANCE_MONITOR* monitor =
|
|
(POSIXSRV_FD_INHERITANCE_MONITOR*)calloc(1, sizeof(*monitor));
|
|
if (!monitor) return NTLSTATUS_NO_MEMORY;
|
|
monitor->Token = token;
|
|
monitor->NativeProcessId = nativePid;
|
|
monitor->ProcessHandle = *targetProcessHandle;
|
|
|
|
status = PosixsrvLockRegistry();
|
|
if (!KSUCCESS(status)) {
|
|
free(monitor);
|
|
return status;
|
|
}
|
|
|
|
POSIXSRV_FD_INHERITANCE* snapshot = PosixsrvFindFdInheritanceLocked(token);
|
|
if (!snapshot) {
|
|
PosixsrvUnlockRegistry();
|
|
free(monitor);
|
|
return NTLSTATUS_NOT_FOUND;
|
|
}
|
|
if (snapshot->SecurityId != client->Process->SecurityId ||
|
|
snapshot->SourceProcess != client->Process) {
|
|
PosixsrvUnlockRegistry();
|
|
free(monitor);
|
|
return NTLSTATUS_ACCESS_DENIED;
|
|
}
|
|
if (snapshot->ClaimProcess) {
|
|
status = snapshot->ClaimProcess->NativeProcessId == nativePid
|
|
? NTLSTATUS_SUCCESS
|
|
: NTLSTATUS_ACCESS_DENIED;
|
|
PosixsrvUnlockRegistry();
|
|
free(monitor);
|
|
return status;
|
|
}
|
|
if (snapshot->Armed) {
|
|
status = snapshot->TargetNativeProcessId == nativePid
|
|
? NTLSTATUS_SUCCESS
|
|
: NTLSTATUS_ACCESS_DENIED;
|
|
PosixsrvUnlockRegistry();
|
|
free(monitor);
|
|
return status;
|
|
}
|
|
|
|
snapshot->Armed = 1u;
|
|
snapshot->TargetNativeProcessId = nativePid;
|
|
SYS_CREATE_THREAD_REQUEST create;
|
|
memset(&create, 0, sizeof(create));
|
|
create.Size = sizeof(create);
|
|
create.Entry = (uintptr_t)PosixsrvFdInheritanceMonitorThread;
|
|
create.Argument = (uintptr_t)monitor;
|
|
create.Priority = 1;
|
|
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
|
|
status = SysCreateThread(&create);
|
|
if (!KSUCCESS(status)) {
|
|
snapshot->Armed = 0;
|
|
snapshot->TargetNativeProcessId = 0;
|
|
PosixsrvUnlockRegistry();
|
|
free(monitor);
|
|
return status;
|
|
}
|
|
|
|
*targetProcessHandle = SYS_HANDLE_INVALID;
|
|
PosixsrvUnlockRegistry();
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// 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 = (uint32_t)_InterlockedCompareExchange(
|
|
&description->Shared->StatusFlags, 0, 0);
|
|
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 (client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING) {
|
|
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->Armed && snapshot->TargetNativeProcessId != process->NativeProcessId)
|
|
return NTLSTATUS_ACCESS_DENIED;
|
|
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;
|
|
}
|
|
|
|
// 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 (client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING)
|
|
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 (client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING) {
|
|
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 = (long)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 (client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING) {
|
|
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;
|
|
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 = (uint32_t)_InterlockedCompareExchange(
|
|
&description->Shared->StatusFlags, 0, 0);
|
|
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))
|
|
_InterlockedExchange(&description->Shared->StatusFlags, (long)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 int PosixsrvValidateCurrentDirectory(const NTL_POSIX_REQUEST* request) {
|
|
if (!request || request->PathLength == 0 || request->PathLength > SYS_PATH_MAX)
|
|
return 0;
|
|
if (request->Path[0] != '/' || request->Path[request->PathLength] != '\0')
|
|
return 0;
|
|
return strlen(request->Path) == request->PathLength;
|
|
}
|
|
|
|
// Validates one optional length-delimited identity lookup name.
|
|
static int PosixsrvValidateLookupName(const NTL_POSIX_REQUEST* request) {
|
|
if (!request || request->NameLength == 0 || request->NameLength > NTL_POSIX_IDENTITY_NAME_MAX)
|
|
return 0;
|
|
if (request->Name[request->NameLength] != '\0')
|
|
return 0;
|
|
return strlen(request->Name) == request->NameLength;
|
|
}
|
|
|
|
// 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;
|
|
if (record->ProcessHandle != SYS_HANDLE_INVALID)
|
|
(void)SysCloseHandle(record->ProcessHandle);
|
|
free(record);
|
|
}
|
|
|
|
// 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 ||
|
|
security.ServiceClaims != 0 || security.DelegableServiceClaims != 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.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 = 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->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;
|
|
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->ActiveTransactions = client->ActiveTransactions;
|
|
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 = (uint32_t)_InterlockedCompareExchange(
|
|
&readDescription->Shared->StatusFlags, 0, 0);
|
|
reply.SharedOpenDescription = (uint64_t)(uintptr_t)readDescription->Shared;
|
|
reply.OpenDescriptionId2 = writeDescription->DescriptionId;
|
|
reply.OpenDescriptionType2 = writeDescription->Type;
|
|
reply.OpenStatusFlags2 = (uint32_t)_InterlockedCompareExchange(
|
|
&writeDescription->Shared->StatusFlags, 0, 0);
|
|
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);
|
|
if (!snapshot || snapshot->SecurityId != client->Process->SecurityId ||
|
|
client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING) {
|
|
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);
|
|
}
|
|
|
|
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 = (uint32_t)_InterlockedCompareExchange(
|
|
&description->Shared->StatusFlags, 0, 0);
|
|
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)
|
|
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_RECORD* record = (POSIXSRV_PROCESS_RECORD*)argument;
|
|
if (!record) {
|
|
SysExitThread(1);
|
|
return;
|
|
}
|
|
PosixsrvSetCurrentThreadName("posixsrv.process");
|
|
|
|
KSTATUS waitStatus = SysWait(record->ProcessHandle, SYS_WAIT_FLAG_NONE);
|
|
int32_t exitCode = (int32_t)NTLSTATUS_PROCESS_UNKNOWN_FAULT;
|
|
if (KSUCCESS(waitStatus))
|
|
(void)SysGetProcessExitCode(record->ProcessHandle, &exitCode);
|
|
|
|
POSIXSRV_PROCESS_RECORD* freeRecord = (POSIXSRV_PROCESS_RECORD*)0;
|
|
SYS_HANDLE closeHandle = SYS_HANDLE_INVALID;
|
|
if (KSUCCESS(PosixsrvLockRegistry())) {
|
|
if (KSUCCESS(waitStatus) && record->State == NTL_POSIX_PROCESS_STATE_RUNNING) {
|
|
PosixsrvReleaseFdInheritanceForProcessLocked(record);
|
|
PosixsrvReleaseAllOpenDescriptionsLocked(record);
|
|
record->State = NTL_POSIX_PROCESS_STATE_EXITED;
|
|
record->ExitCode = exitCode;
|
|
closeHandle = record->ProcessHandle;
|
|
record->ProcessHandle = SYS_HANDLE_INVALID;
|
|
}
|
|
record->MonitorActive = 0;
|
|
if (record->State == NTL_POSIX_PROCESS_STATE_EXITED && record->ConnectionCount == 0) {
|
|
PosixsrvUnlinkProcessLocked(record);
|
|
freeRecord = record;
|
|
}
|
|
PosixsrvUnlockRegistry();
|
|
}
|
|
|
|
if (closeHandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(closeHandle);
|
|
if (freeRecord) PosixsrvFreeProcessRecord(freeRecord);
|
|
SysExitThread(KSUCCESS(waitStatus) ? 0 : 1);
|
|
}
|
|
|
|
// 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,
|
|
SYS_HANDLE* transferredProcessHandle) {
|
|
if (!client || !request || !transferredProcessHandle ||
|
|
*transferredProcessHandle == SYS_HANDLE_INVALID)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
// Reserved bootstrap identities become active when server-owned process construction lands.
|
|
if (request->BootstrapToken != 0 || request->BootstrapPosixPid != 0 ||
|
|
request->BootstrapGeneration != 0)
|
|
return NTLSTATUS_NOT_SUPPORTED;
|
|
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;
|
|
|
|
uint32_t nativePid = 0;
|
|
KSTATUS status = SysGetProcessPid(*transferredProcessHandle, &nativePid);
|
|
if (!KSUCCESS(status)) return status;
|
|
if (nativePid != client->Connection.ClientProcessId)
|
|
return NTLSTATUS_ACCESS_DENIED;
|
|
|
|
POSIXSRV_PROCESS_RECORD* candidate =
|
|
(POSIXSRV_PROCESS_RECORD*)calloc(1, sizeof(*candidate));
|
|
if (!candidate) return NTLSTATUS_NO_MEMORY;
|
|
candidate->ProcessHandle = SYS_HANDLE_INVALID;
|
|
|
|
status = PosixsrvLockRegistry();
|
|
if (!KSUCCESS(status)) {
|
|
free(candidate);
|
|
return status;
|
|
}
|
|
|
|
if (client->Process) {
|
|
PosixsrvUnlockRegistry();
|
|
free(candidate);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
POSIXSRV_PROCESS_RECORD* existing =
|
|
PosixsrvFindProcessLocked(nativePid, client->Connection.ClientSecurityId);
|
|
if (existing && existing->State == NTL_POSIX_PROCESS_STATE_RUNNING) {
|
|
existing->ConnectionCount++;
|
|
client->Process = existing;
|
|
PosixsrvUnlockRegistry();
|
|
free(candidate);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
uint32_t posixPid = 0;
|
|
status = PosixsrvAllocatePidLocked(&posixPid);
|
|
if (!KSUCCESS(status)) {
|
|
PosixsrvUnlockRegistry();
|
|
free(candidate);
|
|
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 = 1u;
|
|
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 = *transferredProcessHandle;
|
|
candidate->Next = gProcessRegistry;
|
|
gProcessRegistry = candidate;
|
|
++gRegisteredProcesses;
|
|
client->Process = candidate;
|
|
|
|
SYS_CREATE_THREAD_REQUEST create;
|
|
memset(&create, 0, sizeof(create));
|
|
create.Size = sizeof(create);
|
|
create.Entry = (uintptr_t)PosixsrvProcessMonitorThread;
|
|
create.Argument = (uintptr_t)candidate;
|
|
create.Priority = 1;
|
|
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
|
|
status = SysCreateThread(&create);
|
|
if (!KSUCCESS(status)) {
|
|
client->Process = (POSIXSRV_PROCESS_RECORD*)0;
|
|
PosixsrvUnlinkProcessLocked(candidate);
|
|
candidate->ProcessHandle = SYS_HANDLE_INVALID;
|
|
PosixsrvUnlockRegistry();
|
|
free(candidate);
|
|
return status;
|
|
}
|
|
|
|
*transferredProcessHandle = SYS_HANDLE_INVALID;
|
|
PosixsrvUnlockRegistry();
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// 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 (record->State == NTL_POSIX_PROCESS_STATE_EXITED && record->ConnectionCount == 0 &&
|
|
!record->MonitorActive) {
|
|
PosixsrvUnlinkProcessLocked(record);
|
|
freeRecord = record;
|
|
}
|
|
PosixsrvUnlockRegistry();
|
|
if (freeRecord) PosixsrvFreeProcessRecord(freeRecord);
|
|
}
|
|
|
|
// 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 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 = 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 = 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 || target->State != NTL_POSIX_PROCESS_STATE_RUNNING) {
|
|
status = NTLSTATUS_NOT_FOUND;
|
|
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 = 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;
|
|
}
|
|
|
|
// Allocates one bounded connection-local transaction identifier for future spawn/exec work.
|
|
static KSTATUS PosixsrvBeginTransaction(POSIXSRV_CLIENT* client,
|
|
uint32_t kind,
|
|
uint32_t* transactionIdOut) {
|
|
if (!client || !transactionIdOut) return NTLSTATUS_INVALID_PARAMETER;
|
|
*transactionIdOut = 0;
|
|
if (kind != NTL_POSIX_TRANSACTION_SPAWN && kind != NTL_POSIX_TRANSACTION_EXEC)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
uint32_t slot = NTL_POSIX_TRANSACTION_CAPACITY;
|
|
for (uint32_t i = 0; i < NTL_POSIX_TRANSACTION_CAPACITY; ++i) {
|
|
if (client->Transactions[i].Id == 0) {
|
|
slot = i;
|
|
break;
|
|
}
|
|
}
|
|
if (slot == NTL_POSIX_TRANSACTION_CAPACITY) return NTLSTATUS_LIMIT_REACHED;
|
|
|
|
uint32_t id = ++client->NextTransactionId;
|
|
if (id == 0) id = ++client->NextTransactionId;
|
|
client->Transactions[slot].Id = id;
|
|
client->Transactions[slot].Kind = kind;
|
|
++client->ActiveTransactions;
|
|
*transactionIdOut = id;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Cancels one transaction owned by the current connection.
|
|
static KSTATUS PosixsrvCancelTransaction(POSIXSRV_CLIENT* client, uint32_t transactionId) {
|
|
if (!client || transactionId == 0) return NTLSTATUS_INVALID_PARAMETER;
|
|
for (uint32_t i = 0; i < NTL_POSIX_TRANSACTION_CAPACITY; ++i) {
|
|
if (client->Transactions[i].Id != transactionId) continue;
|
|
memset(&client->Transactions[i], 0, sizeof(client->Transactions[i]));
|
|
if (client->ActiveTransactions != 0) --client->ActiveTransactions;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
return NTLSTATUS_NOT_FOUND;
|
|
}
|
|
|
|
// Returns whether operation-specific bootstrap fields are all unused.
|
|
static int 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 int 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 int PosixsrvHasNoFdInheritancePayload(const NTL_POSIX_REQUEST* request) {
|
|
if (!request || request->FdInheritanceToken != 0 || request->FdInheritanceIndex != 0 ||
|
|
request->FdInheritanceCount != 0 || request->FdInheritanceFlags != 0)
|
|
return 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 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// Returns whether one request carries no transaction state.
|
|
static int PosixsrvHasNoTransaction(const NTL_POSIX_REQUEST* request) {
|
|
return request && request->TransactionId == 0 &&
|
|
request->TransactionKind == NTL_POSIX_TRANSACTION_NONE;
|
|
}
|
|
|
|
// 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->Reserved[0] != 0 || request->Reserved[1] != 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_REGISTER_PROCESS) {
|
|
if (!PosixsrvHasNoTransaction(request) || !PosixsrvHasNoFdInheritancePayload(request) ||
|
|
info->HandleCount != 1 || !handles)
|
|
return PosixsrvReply(client, info->MessageId, operation, NTLSTATUS_INVALID_PARAMETER, 0);
|
|
KSTATUS status = PosixsrvRegisterProcess(client, request, &handles[0]);
|
|
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_CREATE_FD_INHERITANCE) {
|
|
if (!client->Process || client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING ||
|
|
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_RUNNING ||
|
|
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 || client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING ||
|
|
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 (operation == NTL_POSIX_OP_ARM_FD_INHERITANCE) {
|
|
if (!client->Process || client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING ||
|
|
info->HandleCount != 1 || !handles || !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 = PosixsrvArmFdInheritance(client, request->FdInheritanceToken, &handles[0]);
|
|
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 || client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING ||
|
|
!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 || client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING ||
|
|
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 || client->Process->State != NTL_POSIX_PROCESS_STATE_RUNNING)
|
|
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_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 and drops connection-owned transactions on exit.
|
|
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;
|
|
|
|
SYS_PORT_HANDLE_TRANSFER transfer;
|
|
memset(&transfer, 0, sizeof(transfer));
|
|
transfer.Handle = publicListener;
|
|
transfer.DesiredRights = 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) ||
|
|
message.Version != NTL_SERVICE_CONTROL_VERSION || message.ExportCount != 0 ||
|
|
message.Reserved[0] != 0 || message.Reserved[1] != 0 || message.Reserved[2] != 0)
|
|
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(SYS_STARTUP_CAP_SERVICE_CONTROL);
|
|
if (controlListener == 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);
|
|
|
|
status = PosixsrvSendReady(controlEndpoint, publicListener);
|
|
if (!KSUCCESS(status)) PosixsrvExit(8);
|
|
|
|
PosixsrvControlLoop(controlEndpoint);
|
|
(void)SysDisconnectPort(controlEndpoint);
|
|
(void)SysCloseHandle(controlEndpoint);
|
|
(void)SysCloseHandle(publicListener);
|
|
(void)SysCloseHandle(gProcessRegistryMutex);
|
|
gProcessRegistryMutex = SYS_HANDLE_INVALID;
|
|
PosixsrvExit(0);
|
|
}
|