Files
nt-lite/code/user/system/posixsrv/posixsrv.c
T
maksw 73abd6f90d isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
2026-09-16 10:33:20 +02:00

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);
}