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