move POSIX spawn, exec and child tracking into posixsrv, keep process transactions alive across reconnects and preserve stable PIDs across image replacement, flesh out spawn with ordered file actions, process-group selection and signal inheritance, implement execve with bootstrap validation and rollback before retiring the old process, add vfork children that share the native process while keeping separate POSIX descriptor and signal state, replace local spawn bookkeeping with child reaping, nonblocking and process-group waits and orphan cleanup, add signal dispositions, per-thread masks, pending delivery, process/group signalling and alarms, translate native exceptions into POSIX signals and inherit masks when creating threads, make pipe waits interruptible and raise SIGPIPE for broken pipes, pass arguments and environment into main and add getenv/environ, add direct user APC notifications that can run without an alertable wait, add current-thread capabilities and resume-and-wait, let accepted IPC endpoints open restricted peer process capabilities and preserve caller security during spawn and exec, make process preparation reusable and improve DLL lookup across current, executable and fallback directories, move keyboard repeat into RSSRV so PS/2 and USB share one repeat policy, fix video presentation, report launch failure stages and status codes, mooaaar tests.
This commit is contained in:
1 parent
73abd6f90d
commit
899225f551
57 files changed
+8817
-1216
No files matched your search
@@ -70,8 +70,8 @@ int PosixStatusToErrno(uint32_t s) {
|
||||
}
|
||||
}
|
||||
|
||||
// Locks the process descriptor table.
|
||||
static int PosixFdLock(POSIX_PROCESS_STATE* process) {
|
||||
// Locks one logical process descriptor table.
|
||||
static int PosixFdLock(POSIX_LOGICAL_PROCESS_STATE* process) {
|
||||
if (!process || process->FdLock == SYS_HANDLE_INVALID) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
@@ -84,8 +84,8 @@ static int PosixFdLock(POSIX_PROCESS_STATE* process) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Unlocks the process descriptor table.
|
||||
static void PosixFdUnlock(POSIX_PROCESS_STATE* process) {
|
||||
// Unlocks one logical process descriptor table.
|
||||
static void PosixFdUnlock(POSIX_LOGICAL_PROCESS_STATE* process) {
|
||||
uint32_t status = SysReleaseMutex(process->FdLock);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
errno = PosixStatusToErrno(status);
|
||||
@@ -134,12 +134,12 @@ void PosixFdReleaseServerDescription(uint64_t descriptionId) {
|
||||
(void)PosixRuntimeCallServer(&request, &reply);
|
||||
}
|
||||
|
||||
// Captures process-local descriptor numbers/flags while posixsrv pins unique descriptions.
|
||||
// Captures logical-process descriptor numbers/flags while posixsrv pins unique descriptions.
|
||||
KSTATUS PosixFdCreateInheritanceSnapshot(uint32_t flags, uint64_t* tokenOut) {
|
||||
if (!tokenOut || (flags & ~POSIX_FD_INHERIT_SKIP_CLOEXEC) != 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
*tokenOut = 0;
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
POSIX_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
|
||||
if (!process) return NTLSTATUS_NOT_FOUND;
|
||||
if (PosixFdLock(process) != 0) return NTLSTATUS_FAILURE;
|
||||
|
||||
@@ -177,30 +177,6 @@ KSTATUS PosixFdCreateInheritanceSnapshot(uint32_t flags, uint64_t* tokenOut) {
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Arms one snapshot to a concrete native child lifetime before the parent returns from spawn.
|
||||
KSTATUS PosixFdArmInheritanceSnapshot(uint64_t token, SYS_HANDLE processHandle) {
|
||||
if (token == 0 || processHandle == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_ARM_FD_INHERITANCE;
|
||||
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
|
||||
request.FdInheritanceToken = token;
|
||||
|
||||
SYS_PORT_HANDLE_TRANSFER transfer;
|
||||
memset(&transfer, 0, sizeof(transfer));
|
||||
transfer.Handle = processHandle;
|
||||
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT;
|
||||
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
|
||||
|
||||
NTL_POSIX_REPLY reply;
|
||||
KSTATUS status = PosixRuntimeCallServerWithHandles(&request, &transfer, 1u, &reply);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
return reply.Status;
|
||||
}
|
||||
|
||||
// Cancels one snapshot when native process construction failed before a child could claim it.
|
||||
void PosixFdCancelInheritanceSnapshot(uint64_t token) {
|
||||
if (token == 0) return;
|
||||
@@ -327,13 +303,20 @@ int PosixFdSetStatusFlags(POSIX_OPEN_DESCRIPTION* description, int statusFlags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Releases one stable local wrapper without changing server holder ownership.
|
||||
static void PosixFdReleaseLocal(POSIX_OPEN_DESCRIPTION* description) {
|
||||
if (!description) return;
|
||||
if (_InterlockedDecrement(&description->RefCount) != 0) return;
|
||||
if (PosixOpenDescriptionNeedsHandle(description->Type) &&
|
||||
description->KHandle != SYS_HANDLE_INVALID)
|
||||
(void)SysCloseHandle(description->KHandle);
|
||||
free(description);
|
||||
}
|
||||
|
||||
// Releases one stable local wrapper and its server holder at the final reference.
|
||||
void PosixFdRelease(POSIX_OPEN_DESCRIPTION* description) {
|
||||
if (!description)
|
||||
return;
|
||||
if (_InterlockedDecrement(&description->RefCount) != 0)
|
||||
return;
|
||||
|
||||
if (!description) return;
|
||||
if (_InterlockedDecrement(&description->RefCount) != 0) return;
|
||||
if (PosixOpenDescriptionNeedsHandle(description->Type) &&
|
||||
description->KHandle != SYS_HANDLE_INVALID)
|
||||
(void)SysCloseHandle(description->KHandle);
|
||||
@@ -342,7 +325,7 @@ void PosixFdRelease(POSIX_OPEN_DESCRIPTION* description) {
|
||||
}
|
||||
|
||||
// Installs a description at the lowest free descriptor at or above minFd.
|
||||
static int PosixFdInstall(POSIX_PROCESS_STATE* process,
|
||||
static int PosixFdInstall(POSIX_LOGICAL_PROCESS_STATE* process,
|
||||
POSIX_OPEN_DESCRIPTION* description,
|
||||
int minFd,
|
||||
int descriptorFlags) {
|
||||
@@ -371,7 +354,7 @@ static int PosixFdInstall(POSIX_PROCESS_STATE* process,
|
||||
|
||||
// Creates and installs one new open description.
|
||||
int PosixFdCreate(POSIX_FD_TYPE type, uint32_t khandle, int statusFlags) {
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
POSIX_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
|
||||
if (!process) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
@@ -427,7 +410,7 @@ static POSIX_OPEN_DESCRIPTION* PosixOpenDescriptionImport(POSIX_FD_TYPE type,
|
||||
// Installs one process-local wrapper for a server-created shared open description.
|
||||
int PosixFdImport(POSIX_FD_TYPE type, uint32_t khandle, uint64_t descriptionId,
|
||||
NTL_POSIX_SHARED_OPEN_DESCRIPTION* shared, int descriptorFlags) {
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
POSIX_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
|
||||
if (!process) { errno = EIO; return -1; }
|
||||
POSIX_OPEN_DESCRIPTION* description =
|
||||
PosixOpenDescriptionImport(type, khandle, descriptionId, shared);
|
||||
@@ -439,7 +422,7 @@ int PosixFdImport(POSIX_FD_TYPE type, uint32_t khandle, uint64_t descriptionId,
|
||||
|
||||
// Acquires a stable reference to the open description backing fd.
|
||||
POSIX_OPEN_DESCRIPTION* PosixFdAcquire(int fd) {
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
POSIX_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
|
||||
if (!process || fd < 0 || fd >= POSIX_FD_MAX) {
|
||||
errno = EBADF;
|
||||
return (POSIX_OPEN_DESCRIPTION*)0;
|
||||
@@ -459,7 +442,7 @@ POSIX_OPEN_DESCRIPTION* PosixFdAcquire(int fd) {
|
||||
|
||||
// Removes one descriptor and releases its table-owned reference.
|
||||
int PosixFdClose(int fd) {
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
POSIX_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
|
||||
if (!process || fd < 0 || fd >= POSIX_FD_MAX) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
@@ -481,7 +464,7 @@ int PosixFdClose(int fd) {
|
||||
}
|
||||
|
||||
// Installs one fixed standard descriptor during process initialization.
|
||||
static uint32_t PosixFdInstallStandard(POSIX_PROCESS_STATE* process,
|
||||
static uint32_t PosixFdInstallStandard(POSIX_LOGICAL_PROCESS_STATE* process,
|
||||
int fd,
|
||||
POSIX_FD_TYPE type,
|
||||
int statusFlags) {
|
||||
@@ -498,7 +481,7 @@ static uint32_t PosixFdInstallStandard(POSIX_PROCESS_STATE* process,
|
||||
}
|
||||
|
||||
// Reconstructs one exact descriptor table from a server-owned inheritance snapshot.
|
||||
static uint32_t PosixFdImportInheritance(POSIX_PROCESS_STATE* process, uint64_t token) {
|
||||
static uint32_t PosixFdImportInheritance(POSIX_LOGICAL_PROCESS_STATE* process, uint64_t token) {
|
||||
if (!process || token == 0) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
NTL_POSIX_FD_MANIFEST_ENTRY manifest[NTL_POSIX_FD_INHERIT_MAX];
|
||||
@@ -650,8 +633,8 @@ static uint32_t PosixFdImportInheritance(POSIX_PROCESS_STATE* process, uint64_t
|
||||
return result;
|
||||
}
|
||||
|
||||
// Initializes the process descriptor table and standard descriptors.
|
||||
uint32_t PosixFdInitializeProcess(POSIX_PROCESS_STATE* process, uint64_t inheritanceToken) {
|
||||
// Initializes one logical process descriptor table and standard descriptors.
|
||||
uint32_t PosixFdInitializeLogicalProcess(POSIX_LOGICAL_PROCESS_STATE* process, uint64_t inheritanceToken) {
|
||||
if (!process)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -664,7 +647,7 @@ uint32_t PosixFdInitializeProcess(POSIX_PROCESS_STATE* process, uint64_t inherit
|
||||
|
||||
if (inheritanceToken != 0) {
|
||||
status = PosixFdImportInheritance(process, inheritanceToken);
|
||||
if (status != NTLSTATUS_SUCCESS) PosixFdDestroyProcess(process);
|
||||
if (status != NTLSTATUS_SUCCESS) PosixFdDestroyLogicalProcess(process);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -674,15 +657,13 @@ uint32_t PosixFdInitializeProcess(POSIX_PROCESS_STATE* process, uint64_t inherit
|
||||
if (status == NTLSTATUS_SUCCESS)
|
||||
status = PosixFdInstallStandard(process, 2, PosixFdConsoleWrite, O_WRONLY);
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
PosixFdDestroyProcess(process);
|
||||
PosixFdDestroyLogicalProcess(process);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Releases descriptor-table resources during failed process startup.
|
||||
void PosixFdDestroyProcess(POSIX_PROCESS_STATE* process) {
|
||||
if (!process)
|
||||
return;
|
||||
|
||||
void PosixFdDestroyLogicalProcess(POSIX_LOGICAL_PROCESS_STATE* process) {
|
||||
if (!process) return;
|
||||
for (int fd = 0; fd < POSIX_FD_MAX; fd++) {
|
||||
POSIX_OPEN_DESCRIPTION* description = process->FdTable[fd].Description;
|
||||
process->FdTable[fd].Description = (POSIX_OPEN_DESCRIPTION*)0;
|
||||
@@ -695,6 +676,21 @@ void PosixFdDestroyProcess(POSIX_PROCESS_STATE* process) {
|
||||
}
|
||||
}
|
||||
|
||||
// Drops inherited local aliases after borrowed exec without releasing committed server holders.
|
||||
void PosixFdDestroyLogicalProcessLocal(POSIX_LOGICAL_PROCESS_STATE* process) {
|
||||
if (!process) return;
|
||||
for (int fd = 0; fd < POSIX_FD_MAX; fd++) {
|
||||
POSIX_OPEN_DESCRIPTION* description = process->FdTable[fd].Description;
|
||||
process->FdTable[fd].Description = (POSIX_OPEN_DESCRIPTION*)0;
|
||||
process->FdTable[fd].DescriptorFlags = 0;
|
||||
PosixFdReleaseLocal(description);
|
||||
}
|
||||
if (process->FdLock != SYS_HANDLE_INVALID) {
|
||||
(void)SysCloseHandle(process->FdLock);
|
||||
process->FdLock = SYS_HANDLE_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
// Duplicates oldfd into the lowest free descriptor at or above minFd.
|
||||
static int PosixFdDuplicateFrom(int oldfd, int minFd) {
|
||||
if (minFd < 0 || minFd >= POSIX_FD_MAX) {
|
||||
@@ -704,7 +700,7 @@ static int PosixFdDuplicateFrom(int oldfd, int minFd) {
|
||||
POSIX_OPEN_DESCRIPTION* description = PosixFdAcquire(oldfd);
|
||||
if (!description)
|
||||
return -1;
|
||||
int result = PosixFdInstall(PosixRuntimeProcessState(), description, minFd, 0);
|
||||
int result = PosixFdInstall(PosixRuntimeLogicalProcessState(), description, minFd, 0);
|
||||
PosixFdRelease(description);
|
||||
return result;
|
||||
}
|
||||
@@ -729,7 +725,7 @@ NTL_POSIX_API int dup2(int oldfd, int newfd) {
|
||||
return newfd;
|
||||
}
|
||||
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
POSIX_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
|
||||
if (PosixFdLock(process) != 0) {
|
||||
PosixFdRelease(description);
|
||||
return -1;
|
||||
@@ -903,7 +899,7 @@ static int PosixFdControlLock(POSIX_OPEN_DESCRIPTION* description, int command,
|
||||
|
||||
// Implements descriptor and open-description file-control operations.
|
||||
NTL_POSIX_API int fcntl(int fd, int command, ...) {
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
POSIX_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
|
||||
if (!process || fd < 0 || fd >= POSIX_FD_MAX) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
|
||||
@@ -30,28 +30,28 @@ typedef struct POSIX_OPEN_DESCRIPTION {
|
||||
NTL_POSIX_SHARED_OPEN_DESCRIPTION* Shared;
|
||||
} POSIX_OPEN_DESCRIPTION;
|
||||
|
||||
// One process-local descriptor entry with descriptor-local flags.
|
||||
// One logical-process descriptor entry with descriptor-local flags.
|
||||
typedef struct POSIX_FD {
|
||||
POSIX_OPEN_DESCRIPTION* Description;
|
||||
int DescriptorFlags;
|
||||
} POSIX_FD;
|
||||
|
||||
struct POSIX_PROCESS_STATE;
|
||||
struct POSIX_LOGICAL_PROCESS_STATE;
|
||||
|
||||
// Initializes the process descriptor table from inherited state or fresh standard descriptors.
|
||||
uint32_t PosixFdInitializeProcess(struct POSIX_PROCESS_STATE* process, uint64_t inheritanceToken);
|
||||
// Initializes one logical process descriptor table from inherited state or fresh standard descriptors.
|
||||
uint32_t PosixFdInitializeLogicalProcess(struct POSIX_LOGICAL_PROCESS_STATE* process, uint64_t inheritanceToken);
|
||||
|
||||
// Captures the current descriptor table in one server-owned inheritance snapshot.
|
||||
KSTATUS PosixFdCreateInheritanceSnapshot(uint32_t flags, uint64_t* tokenOut);
|
||||
|
||||
// Arms one inheritance snapshot to the lifetime of the native child process.
|
||||
KSTATUS PosixFdArmInheritanceSnapshot(uint64_t token, SYS_HANDLE processHandle);
|
||||
|
||||
// Cancels one unclaimed inheritance snapshot owned by the current process.
|
||||
void PosixFdCancelInheritanceSnapshot(uint64_t token);
|
||||
|
||||
// Releases descriptor-table resources during failed process startup.
|
||||
void PosixFdDestroyProcess(struct POSIX_PROCESS_STATE* process);
|
||||
void PosixFdDestroyLogicalProcess(struct POSIX_LOGICAL_PROCESS_STATE* process);
|
||||
|
||||
// Releases only process-local wrappers after server-side exec holder ownership has committed.
|
||||
void PosixFdDestroyLogicalProcessLocal(struct POSIX_LOGICAL_PROCESS_STATE* process);
|
||||
|
||||
// Creates and installs one new open description at the lowest free descriptor >= 3.
|
||||
int PosixFdCreate(POSIX_FD_TYPE type, uint32_t khandle, int statusFlags);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <pthread.h>
|
||||
#include <spawn.h>
|
||||
#include <signal.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
@@ -31,25 +32,27 @@
|
||||
#define POSIX_PROCESS_STATE_MAGIC 0x50505243u
|
||||
#define POSIX_THREAD_STATE_MAGIC 0x50544852u
|
||||
#define POSIX_DLERROR_MAX 160u
|
||||
#define POSIX_SPAWN_TABLE_SIZE 32u
|
||||
#define POSIX_PTHREAD_TABLE_SIZE 32u
|
||||
|
||||
typedef struct POSIX_SPAWN_ENTRY {
|
||||
pid_t Pid;
|
||||
SYS_HANDLE Handle;
|
||||
uint64_t FdInheritanceToken;
|
||||
bool Used;
|
||||
} POSIX_SPAWN_ENTRY;
|
||||
|
||||
typedef struct POSIX_PTHREAD_SLOT {
|
||||
uint32_t Tid;
|
||||
void* RetVal;
|
||||
bool Used;
|
||||
} POSIX_PTHREAD_SLOT;
|
||||
|
||||
typedef struct POSIX_SIGNAL_ACTION_STATE {
|
||||
volatile uintptr_t Handler;
|
||||
volatile uint64_t Mask;
|
||||
volatile uint32_t Flags;
|
||||
uint32_t Reserved;
|
||||
} POSIX_SIGNAL_ACTION_STATE;
|
||||
|
||||
typedef struct POSIX_LOGICAL_PROCESS_STATE {
|
||||
uint32_t PosixPid;
|
||||
uint32_t Generation;
|
||||
SYS_HANDLE FdLock;
|
||||
POSIX_FD FdTable[POSIX_FD_MAX];
|
||||
POSIX_SIGNAL_ACTION_STATE SignalActions[NTL_POSIX_SIGNAL_MAX + 1u];
|
||||
} POSIX_LOGICAL_PROCESS_STATE;
|
||||
|
||||
typedef struct POSIX_PROCESS_STATE {
|
||||
@@ -57,20 +60,40 @@ typedef struct POSIX_PROCESS_STATE {
|
||||
SYS_PEB* Peb;
|
||||
SYS_HANDLE ServerEndpoint;
|
||||
POSIX_LOGICAL_PROCESS_STATE DefaultIdentity;
|
||||
SYS_HANDLE FdLock;
|
||||
POSIX_FD FdTable[POSIX_FD_MAX];
|
||||
POSIX_SPAWN_ENTRY SpawnTable[POSIX_SPAWN_TABLE_SIZE];
|
||||
uint64_t BootstrapToken;
|
||||
uint32_t BootstrapPosixPid;
|
||||
uint32_t BootstrapGeneration;
|
||||
void* ExceptionHandlerHandle;
|
||||
int ArgumentCount;
|
||||
char** Arguments;
|
||||
char** Environment;
|
||||
char* CommandLine;
|
||||
POSIX_PTHREAD_SLOT PthreadTable[POSIX_PTHREAD_TABLE_SIZE];
|
||||
} POSIX_PROCESS_STATE;
|
||||
|
||||
typedef struct POSIX_VFORK_CHILD_STATE {
|
||||
POSIX_LOGICAL_PROCESS_STATE LogicalProcess;
|
||||
uint64_t ServerToken;
|
||||
uint64_t FdInheritanceToken;
|
||||
uint64_t InitialSignalMask;
|
||||
} POSIX_VFORK_CHILD_STATE;
|
||||
|
||||
typedef struct POSIX_THREAD_STATE {
|
||||
uint32_t Magic;
|
||||
int ErrnoValue;
|
||||
uint32_t NativeThreadId;
|
||||
uint8_t DlErrorPending;
|
||||
uint8_t Reserved[3];
|
||||
uint8_t ServerRegistered;
|
||||
uint8_t Reserved[2];
|
||||
volatile uint64_t SignalBlocked;
|
||||
volatile uint64_t SignalHandlerBlocked;
|
||||
volatile uint64_t SignalPending;
|
||||
volatile uint64_t SignalDeliveryCount;
|
||||
SYS_HANDLE SignalWaitEvent;
|
||||
char DlError[POSIX_DLERROR_MAX];
|
||||
POSIX_PROCESS_STATE* NativeProcess;
|
||||
POSIX_LOGICAL_PROCESS_STATE* LogicalProcess;
|
||||
POSIX_VFORK_CHILD_STATE* BorrowedChild;
|
||||
struct passwd PasswordResult;
|
||||
char PasswordName[NTL_POSIX_IDENTITY_NAME_MAX + 1u];
|
||||
char PasswordHome[NTL_POSIX_IDENTITY_HOME_MAX + 1u];
|
||||
@@ -90,9 +113,42 @@ POSIX_PROCESS_STATE* PosixRuntimeProcessState(void);
|
||||
// Returns the current thread state, attaching lazily when necessary.
|
||||
POSIX_THREAD_STATE* PosixRuntimeThreadState(void);
|
||||
|
||||
// Returns the logical POSIX process selected by the current native thread.
|
||||
POSIX_LOGICAL_PROCESS_STATE* PosixRuntimeLogicalProcessState(void);
|
||||
|
||||
// Attaches POSIX state to the current native TEB.
|
||||
uint32_t PosixRuntimeAttachThread(void);
|
||||
|
||||
// Attaches a thread while seeding its inherited persistent signal mask.
|
||||
uint32_t PosixRuntimeAttachThreadWithSignalMask(uint64_t signalMask);
|
||||
|
||||
// Allocates one borrowed logical child and pins its inherited descriptor snapshot.
|
||||
KSTATUS PosixVforkBeginBorrowedChild(POSIX_VFORK_CHILD_STATE** childOut);
|
||||
|
||||
// Attaches the calling native thread to one prepared borrowed logical child.
|
||||
KSTATUS PosixVforkAttachBorrowedChild(POSIX_VFORK_CHILD_STATE* child);
|
||||
|
||||
// Tears down the calling borrowed thread's local wrappers before its native thread exits.
|
||||
void PosixVforkDetachBorrowedChildLocal(POSIX_VFORK_CHILD_STATE* child, bool releaseServerHolders);
|
||||
|
||||
// Cancels one borrowed child after setup failure or after its native thread has exited.
|
||||
KSTATUS PosixVforkCancelBorrowedChild(POSIX_VFORK_CHILD_STATE* child);
|
||||
|
||||
// Records a genuine borrowed-child exit before its native thread terminates.
|
||||
KSTATUS PosixVforkRecordExit(POSIX_VFORK_CHILD_STATE* child, int status);
|
||||
|
||||
// Returns whether the calling POSIX thread is one borrowed vfork child.
|
||||
POSIX_VFORK_CHILD_STATE* PosixVforkCurrentBorrowedChild(void);
|
||||
|
||||
// Delivers one POSIX signal from the generic user-APC substrate.
|
||||
void PosixSignalApc(void* signalValue, void* routeValue, void* reserved);
|
||||
|
||||
// Dispatches thread-pending signals made deliverable by a mask change.
|
||||
void PosixSignalDispatchLocalPending(POSIX_THREAD_STATE* thread);
|
||||
|
||||
// Installs the POSIX exception-to-signal personality adapter.
|
||||
KSTATUS PosixSignalInitializeExceptionTranslation(POSIX_PROCESS_STATE* process);
|
||||
|
||||
// Releases POSIX state owned by the current native TEB.
|
||||
void PosixRuntimeDetachThread(void);
|
||||
|
||||
@@ -108,6 +164,15 @@ char* PosixRuntimeTakeDlError(void);
|
||||
// Calls posixsrv over the registered process endpoint and validates one fixed-size reply.
|
||||
KSTATUS PosixRuntimeCallServer(const NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* replyOut);
|
||||
|
||||
// Calls posixsrv through a temporary registered connection for operations that may block.
|
||||
KSTATUS PosixRuntimeCallServerBlocking(const NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* replyOut);
|
||||
|
||||
// Calls posixsrv through a temporary registered connection while transferring capabilities.
|
||||
KSTATUS PosixRuntimeCallServerBlockingWithHandles(const NTL_POSIX_REQUEST* request,
|
||||
const SYS_PORT_HANDLE_TRANSFER* handles,
|
||||
uint32_t handleCount,
|
||||
NTL_POSIX_REPLY* replyOut);
|
||||
|
||||
// Calls posixsrv while transferring a bounded set of native capabilities.
|
||||
KSTATUS PosixRuntimeCallServerWithHandles(const NTL_POSIX_REQUEST* request,
|
||||
const SYS_PORT_HANDLE_TRANSFER* handles,
|
||||
|
||||
@@ -122,7 +122,16 @@ static ssize_t PosixPipeRead(POSIX_OPEN_DESCRIPTION* description, void* buffer,
|
||||
}
|
||||
PosixPipeUnlock(&shared->ReadLock);
|
||||
|
||||
uint32_t status = SysWait(description->KHandle, SYS_WAIT_FLAG_NONE);
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
uint64_t deliveryCount = thread ? thread->SignalDeliveryCount : 0;
|
||||
uint32_t status = SysWait(description->KHandle, SYS_WAIT_FLAG_ALERTABLE);
|
||||
if (status == NTLSTATUS_USER_APC || status == NTLSTATUS_KERNEL_APC ||
|
||||
status == NTLSTATUS_ALERTED) {
|
||||
if (!thread || thread->SignalDeliveryCount == deliveryCount)
|
||||
continue;
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
}
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
errno = PosixStatusToErrno(status);
|
||||
return -1;
|
||||
@@ -139,6 +148,7 @@ static ssize_t PosixPipeWrite(POSIX_OPEN_DESCRIPTION* description, const void* b
|
||||
PosixPipeLock(&shared->WriteLock);
|
||||
if (shared->ReaderOpen == 0) {
|
||||
PosixPipeUnlock(&shared->WriteLock);
|
||||
(void)raise(SIGPIPE);
|
||||
errno = EPIPE;
|
||||
return -1;
|
||||
}
|
||||
@@ -177,7 +187,16 @@ static ssize_t PosixPipeWrite(POSIX_OPEN_DESCRIPTION* description, const void* b
|
||||
}
|
||||
PosixPipeUnlock(&shared->WriteLock);
|
||||
|
||||
uint32_t status = SysWait(description->KHandle, SYS_WAIT_FLAG_NONE);
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
uint64_t deliveryCount = thread ? thread->SignalDeliveryCount : 0;
|
||||
uint32_t status = SysWait(description->KHandle, SYS_WAIT_FLAG_ALERTABLE);
|
||||
if (status == NTLSTATUS_USER_APC || status == NTLSTATUS_KERNEL_APC ||
|
||||
status == NTLSTATUS_ALERTED) {
|
||||
if (!thread || thread->SignalDeliveryCount == deliveryCount)
|
||||
continue;
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
}
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
errno = PosixStatusToErrno(status);
|
||||
return -1;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// POSIX compatibility support for posix proc.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include <ntlite/security.h>
|
||||
#include <ntlite/path.h>
|
||||
#include <ntlite/process_loader.h>
|
||||
#include "posix_internal.h"
|
||||
@@ -14,80 +16,16 @@ static SYS_PEB* PosixPeb(void) {
|
||||
return process ? process->Peb : (SYS_PEB*)0;
|
||||
}
|
||||
|
||||
// Implements the posix get command line operation.
|
||||
// Returns the POSIX argv-derived command-line compatibility view when available.
|
||||
__declspec(dllexport) const char* posix_get_command_line(void) {
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
if (process && process->CommandLine) return process->CommandLine;
|
||||
SYS_PEB* peb = PosixPeb();
|
||||
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return "";
|
||||
return peb->CommandLine.Data;
|
||||
}
|
||||
|
||||
// Records one spawn association in process-local POSIX state.
|
||||
static void ProcTableInsert(pid_t pid, SYS_HANDLE handle, uint64_t inheritanceToken) {
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
if (!process) {
|
||||
SysCloseHandle(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) {
|
||||
if (!process->SpawnTable[i].Used) {
|
||||
process->SpawnTable[i] = (POSIX_SPAWN_ENTRY){ pid, handle, inheritanceToken, true };
|
||||
return;
|
||||
}
|
||||
}
|
||||
SysCloseHandle(handle);
|
||||
if (inheritanceToken != 0) PosixFdCancelInheritanceSnapshot(inheritanceToken);
|
||||
}
|
||||
|
||||
// Finds a spawned process handle retained by the current POSIX process.
|
||||
static SYS_HANDLE ProcTableFind(pid_t pid) {
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
if (!process) return SYS_HANDLE_INVALID;
|
||||
|
||||
for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) {
|
||||
if (process->SpawnTable[i].Used && process->SpawnTable[i].Pid == pid)
|
||||
return process->SpawnTable[i].Handle;
|
||||
}
|
||||
return SYS_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
// Removes one spawn association from process-local POSIX state.
|
||||
static void ProcTableRemove(pid_t pid) {
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
if (!process) return;
|
||||
|
||||
for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) {
|
||||
if (process->SpawnTable[i].Used && process->SpawnTable[i].Pid == pid) {
|
||||
uint64_t inheritanceToken = process->SpawnTable[i].FdInheritanceToken;
|
||||
memset(&process->SpawnTable[i], 0, sizeof(process->SpawnTable[i]));
|
||||
if (inheritanceToken != 0) PosixFdCancelInheritanceSnapshot(inheritanceToken);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build a flat Windows-style cmdline from a NULL-terminated argv array.
|
||||
// Each token that contains a space is wrapped in double-quotes.
|
||||
// argv[0] is included (it becomes the executable token visible to _start).
|
||||
static void BuildCmdline(char* const argv[], char* out, size_t outSize) {
|
||||
size_t pos = 0;
|
||||
for (int i = 0; argv && argv[i]; i++) {
|
||||
if (i > 0 && pos < outSize - 1) out[pos++] = ' ';
|
||||
const char* tok = argv[i];
|
||||
bool needQuote = false;
|
||||
for (const char* c = tok; *c; c++) if (*c == ' ' || *c == '\t') { needQuote = true; break; }
|
||||
if (needQuote && pos < outSize - 1) out[pos++] = '"';
|
||||
for (; *tok && pos < outSize - 2; tok++) {
|
||||
if (*tok == '"') { out[pos++] = '\\'; }
|
||||
out[pos++] = *tok;
|
||||
}
|
||||
if (needQuote && pos < outSize - 1) out[pos++] = '"';
|
||||
}
|
||||
if (pos < outSize) out[pos] = '\0';
|
||||
}
|
||||
|
||||
// POSIX API.
|
||||
|
||||
// Returns the stable POSIX PID selected for the calling logical thread.
|
||||
__declspec(dllexport) pid_t getpid(void) {
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
if (!thread || !thread->LogicalProcess || thread->LogicalProcess->PosixPid == 0)
|
||||
@@ -95,110 +33,622 @@ __declspec(dllexport) pid_t getpid(void) {
|
||||
return (pid_t)thread->LogicalProcess->PosixPid;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int posix_spawn(pid_t* pidOut,
|
||||
const char* path,
|
||||
const posix_spawn_file_actions_t* file_actions, // ignored v1
|
||||
const posix_spawnattr_t* attrp, // ignored v1
|
||||
char* const argv[],
|
||||
char* const envp[]) // ignored v1
|
||||
{
|
||||
(void)file_actions; (void)attrp; (void)envp;
|
||||
#define POSIX_SPAWN_ACTIONS_MAGIC 0x53464131u
|
||||
#define POSIX_SPAWN_ATTRIBUTES_MAGIC 0x53415431u
|
||||
|
||||
char native[512];
|
||||
uint32_t pathStatus = PosixProcessPath(path, native, sizeof(native));
|
||||
if (pathStatus != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(pathStatus); return -1; }
|
||||
|
||||
SYS_HANDLE imageFile = SYS_HANDLE_INVALID;
|
||||
pathStatus = NtlOpenFile(SysSvFromCstr(native), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
|
||||
SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, &imageFile);
|
||||
if (pathStatus != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(pathStatus); return -1; }
|
||||
|
||||
char cmdline[512];
|
||||
BuildCmdline(argv, cmdline, sizeof(cmdline));
|
||||
// If argv was NULL or empty, fall back to just the path so _start still
|
||||
// gets a meaningful argv[0].
|
||||
if (cmdline[0] == '\0') {
|
||||
size_t len = strlen(path);
|
||||
if (len >= sizeof(cmdline)) len = sizeof(cmdline) - 1;
|
||||
memcpy(cmdline, path, len);
|
||||
cmdline[len] = '\0';
|
||||
}
|
||||
|
||||
uint64_t inheritanceToken = 0;
|
||||
uint32_t snapshotStatus =
|
||||
PosixFdCreateInheritanceSnapshot(POSIX_FD_INHERIT_SKIP_CLOEXEC, &inheritanceToken);
|
||||
if (snapshotStatus != NTLSTATUS_SUCCESS) {
|
||||
(void)SysCloseHandle(imageFile);
|
||||
errno = PosixStatusToErrno(snapshotStatus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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.FdInheritanceToken = inheritanceToken;
|
||||
|
||||
uint32_t procHandle = 0;
|
||||
NTL_CREATE_SYSTEM_PROCESS_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Size = sizeof(req);
|
||||
req.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
|
||||
req.ImageFile = imageFile;
|
||||
req.Path = SysSvFromCstr(native);
|
||||
req.CommandLine = SysSvFromCstr(cmdline);
|
||||
req.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT;
|
||||
req.ResolveModuleName = PosixResolveModuleName;
|
||||
req.StartupData = &bootstrap;
|
||||
req.StartupDataSize = sizeof(bootstrap);
|
||||
req.ProcessHandleOut = &procHandle;
|
||||
uint32_t s = NtlCreateSystemProcess(&req);
|
||||
if (imageFile != SYS_HANDLE_INVALID) (void)SysCloseHandle(imageFile);
|
||||
if (s != NTLSTATUS_SUCCESS) {
|
||||
PosixFdCancelInheritanceSnapshot(inheritanceToken);
|
||||
errno = PosixStatusToErrno(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
KSTATUS armStatus = PosixFdArmInheritanceSnapshot(inheritanceToken, procHandle);
|
||||
if (!KSUCCESS(armStatus) && armStatus != NTLSTATUS_NOT_FOUND) {
|
||||
(void)SysKillProcess(procHandle, (int32_t)NTLSTATUS_PROCESS_KILLED);
|
||||
(void)SysCloseHandle(procHandle);
|
||||
PosixFdCancelInheritanceSnapshot(inheritanceToken);
|
||||
errno = PosixStatusToErrno(armStatus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t kpid = 0;
|
||||
SysGetProcessPid(procHandle, &kpid);
|
||||
ProcTableInsert((pid_t)kpid, procHandle, inheritanceToken);
|
||||
if (pidOut) *pidOut = (pid_t)kpid;
|
||||
// Converts one posixsrv transport/result pair into a POSIX construction error.
|
||||
static int PosixConstructionCall(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* reply) {
|
||||
KSTATUS status = PosixRuntimeCallServer(request, reply);
|
||||
if (!KSUCCESS(status)) return PosixStatusToErrno(status);
|
||||
if (!KSUCCESS(reply->Status)) return PosixStatusToErrno(reply->Status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// options: WNOHANG not supported in v1 (always blocks).
|
||||
__declspec(dllexport) pid_t waitpid(pid_t pid, int* status, int options) {
|
||||
(void)options;
|
||||
|
||||
SYS_HANDLE handle = ProcTableFind(pid);
|
||||
if (handle == SYS_HANDLE_INVALID) { errno = ESRCH; return -1; }
|
||||
|
||||
SysWait(handle, SYS_WAIT_FLAG_NONE);
|
||||
|
||||
int32_t exitCode = 0;
|
||||
SysGetProcessExitCode(handle, &exitCode);
|
||||
if (status) *status = exitCode << 8; // WEXITSTATUS convention
|
||||
|
||||
SysCloseHandle(handle);
|
||||
ProcTableRemove(pid);
|
||||
return pid;
|
||||
// Cancels one process-owned construction transaction after a client-side failure.
|
||||
static void PosixConstructionCancelTransaction(uint32_t transactionId) {
|
||||
if (!transactionId) return;
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_CANCEL_TRANSACTION;
|
||||
request.TransactionId = transactionId;
|
||||
NTL_POSIX_REPLY reply;
|
||||
(void)PosixRuntimeCallServer(&request, &reply);
|
||||
}
|
||||
|
||||
// Implements the exit operation.
|
||||
// Begins one server-owned process-construction transaction.
|
||||
static int PosixConstructionBegin(uint32_t kind, uint32_t* transactionIdOut) {
|
||||
if (!transactionIdOut ||
|
||||
(kind != NTL_POSIX_TRANSACTION_SPAWN && kind != NTL_POSIX_TRANSACTION_EXEC))
|
||||
return EINVAL;
|
||||
*transactionIdOut = 0;
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_BEGIN_TRANSACTION;
|
||||
request.TransactionKind = kind;
|
||||
NTL_POSIX_REPLY reply;
|
||||
int error = PosixConstructionCall(&request, &reply);
|
||||
if (error) return error;
|
||||
if (!reply.TransactionId) return EIO;
|
||||
*transactionIdOut = reply.TransactionId;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Adds one bounded string length to the aggregate construction payload size.
|
||||
static int PosixConstructionMeasureString(const char* text, uint32_t* totalBytes) {
|
||||
if (!text || !totalBytes) return EINVAL;
|
||||
size_t length = strlen(text);
|
||||
if (length > NTL_POSIX_CONSTRUCT_DATA_MAX ||
|
||||
(uint32_t)length > NTL_POSIX_CONSTRUCT_DATA_MAX - *totalBytes)
|
||||
return E2BIG;
|
||||
*totalBytes += (uint32_t)length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Validates one argv/envp vector against the bounded server transaction format.
|
||||
static int PosixConstructionMeasureVector(char* const vector[], uint32_t* totalBytes) {
|
||||
if (!vector) return 0;
|
||||
for (uint32_t index = 0; vector[index]; ++index) {
|
||||
if (index >= NTL_POSIX_CONSTRUCT_VECTOR_MAX) return E2BIG;
|
||||
int error = PosixConstructionMeasureString(vector[index], totalBytes);
|
||||
if (error) return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Validates all bounded spawn text before allocating a server transaction.
|
||||
static int PosixSpawnValidateInput(const char* path,
|
||||
const posix_spawn_file_actions_t* actions,
|
||||
char* const argv[],
|
||||
char* const envp[]) {
|
||||
if (!path || !path[0]) return EINVAL;
|
||||
size_t pathLength = strlen(path);
|
||||
if (pathLength > SYS_PATH_MAX) return ENAMETOOLONG;
|
||||
|
||||
uint32_t totalBytes = 0;
|
||||
int error = PosixConstructionMeasureString(path, &totalBytes);
|
||||
if (error) return error;
|
||||
error = PosixConstructionMeasureVector(argv, &totalBytes);
|
||||
if (error) return error;
|
||||
error = PosixConstructionMeasureVector(envp, &totalBytes);
|
||||
if (error) return error;
|
||||
|
||||
if (!actions) return 0;
|
||||
if (actions->Count > POSIX_SPAWN_FILE_ACTION_CAPACITY ||
|
||||
actions->Count > NTL_POSIX_SPAWN_ACTION_MAX)
|
||||
return EINVAL;
|
||||
for (uint32_t i = 0; i < actions->Count; ++i) {
|
||||
const posix_spawn_file_action_entry* action = &actions->Entries[i];
|
||||
if (action->Type != NTL_POSIX_SPAWN_FILE_ACTION_OPEN) continue;
|
||||
if (!action->Path) return EINVAL;
|
||||
size_t length = strlen(action->Path);
|
||||
if (length > SYS_PATH_MAX) return ENAMETOOLONG;
|
||||
error = PosixConstructionMeasureString(action->Path, &totalBytes);
|
||||
if (error) return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sends one construction executable pathname to posixsrv.
|
||||
static int PosixConstructionSendExecutable(uint32_t transactionId, const char* path) {
|
||||
if (!path || !path[0]) return EINVAL;
|
||||
size_t length = strlen(path);
|
||||
if (length > SYS_PATH_MAX) return ENAMETOOLONG;
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_CONSTRUCT_SET_EXECUTABLE;
|
||||
request.TransactionId = transactionId;
|
||||
request.PathLength = (uint32_t)length;
|
||||
memcpy(request.Path, path, length + 1u);
|
||||
NTL_POSIX_REPLY reply;
|
||||
return PosixConstructionCall(&request, &reply);
|
||||
}
|
||||
|
||||
// Streams one argv/envp vector through bounded Port messages.
|
||||
static int PosixConstructionSendVector(uint32_t transactionId, uint32_t kind, char* const vector[]) {
|
||||
if (!vector) return 0;
|
||||
for (uint32_t index = 0; vector[index]; ++index) {
|
||||
if (index >= NTL_POSIX_CONSTRUCT_VECTOR_MAX) return E2BIG;
|
||||
size_t length = strlen(vector[index]);
|
||||
if (length > UINT32_MAX) return E2BIG;
|
||||
uint32_t offset = 0;
|
||||
do {
|
||||
uint32_t remaining = (uint32_t)length - offset;
|
||||
uint32_t chunk = remaining > SYS_PATH_MAX ? SYS_PATH_MAX : remaining;
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_CONSTRUCT_WRITE_VECTOR;
|
||||
request.TransactionId = transactionId;
|
||||
request.ConstructionVectorKind = kind;
|
||||
request.ConstructionVectorIndex = index;
|
||||
request.ConstructionVectorOffset = offset;
|
||||
request.PathLength = chunk;
|
||||
if (chunk) memcpy(request.Path, vector[index] + offset, chunk);
|
||||
request.Path[chunk] = '\0';
|
||||
if (offset + chunk == (uint32_t)length)
|
||||
request.ConstructionVectorFlags = NTL_POSIX_CONSTRUCT_VECTOR_CHUNK_FINAL;
|
||||
NTL_POSIX_REPLY reply;
|
||||
int error = PosixConstructionCall(&request, &reply);
|
||||
if (error) return error;
|
||||
offset += chunk;
|
||||
} while (offset < (uint32_t)length || (length == 0 && offset == 0));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sends ordered file actions to the server-owned logical descriptor manifest.
|
||||
static int PosixSpawnSendFileActions(uint32_t transactionId,
|
||||
const posix_spawn_file_actions_t* actions) {
|
||||
if (!actions) return 0;
|
||||
for (uint32_t i = 0; i < actions->Count; ++i) {
|
||||
const posix_spawn_file_action_entry* action = &actions->Entries[i];
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_SPAWN_ADD_FILE_ACTION;
|
||||
request.TransactionId = transactionId;
|
||||
request.SpawnActionKind = action->Type;
|
||||
request.SpawnFileDescriptor = action->FileDescriptor;
|
||||
request.SpawnNewFileDescriptor = action->NewFileDescriptor;
|
||||
request.SpawnOpenFlags = (uint32_t)action->OpenFlags;
|
||||
request.SpawnOpenMode = (uint32_t)action->Mode;
|
||||
if (action->Type == NTL_POSIX_SPAWN_FILE_ACTION_OPEN) {
|
||||
if (!action->Path) return EINVAL;
|
||||
size_t length = strlen(action->Path);
|
||||
if (length > SYS_PATH_MAX) return ENAMETOOLONG;
|
||||
request.PathLength = (uint32_t)length;
|
||||
memcpy(request.Path, action->Path, length + 1u);
|
||||
}
|
||||
NTL_POSIX_REPLY reply;
|
||||
int error = PosixConstructionCall(&request, &reply);
|
||||
if (error) return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sends process-group and signal inheritance policy for one server-owned spawn.
|
||||
static int PosixSpawnSendAttributes(uint32_t transactionId, const posix_spawnattr_t* attributes) {
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
if (!thread) return EIO;
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_SPAWN_SET_ATTRIBUTES;
|
||||
request.TransactionId = transactionId;
|
||||
request.SpawnSignalMask = thread->SignalBlocked;
|
||||
if (attributes) {
|
||||
request.SpawnAttributeFlags = attributes->Flags;
|
||||
request.ProcessGroupId = (uint32_t)attributes->ProcessGroup;
|
||||
if (attributes->Flags & POSIX_SPAWN_SETSIGMASK)
|
||||
request.SpawnSignalMask = attributes->SignalMask;
|
||||
if (attributes->Flags & POSIX_SPAWN_SETSIGDEF)
|
||||
request.SpawnSignalDefaultMask = attributes->SignalDefault;
|
||||
}
|
||||
NTL_POSIX_REPLY reply;
|
||||
return PosixConstructionCall(&request, &reply);
|
||||
}
|
||||
|
||||
// Delegates the caller's native security authority so posixsrv does not spawn as itself.
|
||||
static KSTATUS PosixProcessDeriveSecurityContext(SYS_HANDLE* contextOut) {
|
||||
if (!contextOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*contextOut = SYS_HANDLE_INVALID;
|
||||
SYS_SECURITY_CONTEXT_INFO info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.Size = sizeof(info);
|
||||
KSTATUS status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &info);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if ((info.EffectivePrivileges & ~info.DelegablePrivileges) != 0 ||
|
||||
(info.ServiceClaims & ~info.DelegableServiceClaims) != 0)
|
||||
return NTLSTATUS_ACCESS_DENIED;
|
||||
|
||||
SYS_DERIVE_SECURITY_CONTEXT_REQUEST derive;
|
||||
memset(&derive, 0, sizeof(derive));
|
||||
derive.Size = sizeof(derive);
|
||||
derive.SourceContext = SYS_SECURITY_CONTEXT_CURRENT;
|
||||
derive.EffectivePrivileges = info.EffectivePrivileges;
|
||||
derive.DelegablePrivileges = info.DelegablePrivileges;
|
||||
derive.ServiceClaims = info.ServiceClaims;
|
||||
derive.DelegableServiceClaims = info.DelegableServiceClaims;
|
||||
derive.Flags = SYS_SECURITY_CONTEXT_FLAG_NONE;
|
||||
derive.ContextHandleOut = contextOut;
|
||||
return SysDeriveSecurityContext(&derive);
|
||||
}
|
||||
|
||||
// Initializes an empty ordered spawn file-action set.
|
||||
__declspec(dllexport) int posix_spawn_file_actions_init(posix_spawn_file_actions_t* actions) {
|
||||
if (!actions) return EINVAL;
|
||||
memset(actions, 0, sizeof(*actions));
|
||||
actions->Initialized = POSIX_SPAWN_ACTIONS_MAGIC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Releases pathname storage owned by one file-action set.
|
||||
__declspec(dllexport) int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t* actions) {
|
||||
if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC) return EINVAL;
|
||||
for (uint32_t i = 0; i < actions->Count; ++i) free(actions->Entries[i].Path);
|
||||
memset(actions, 0, sizeof(*actions));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Appends one descriptor close action.
|
||||
__declspec(dllexport) int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t* actions,
|
||||
int fd) {
|
||||
if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC || fd < 0) return EBADF;
|
||||
if (actions->Count >= POSIX_SPAWN_FILE_ACTION_CAPACITY) return ENOMEM;
|
||||
posix_spawn_file_action_entry* action = &actions->Entries[actions->Count++];
|
||||
memset(action, 0, sizeof(*action));
|
||||
action->Type = NTL_POSIX_SPAWN_FILE_ACTION_CLOSE;
|
||||
action->FileDescriptor = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Appends one dup2 action; the destination loses FD_CLOEXEC during server application.
|
||||
__declspec(dllexport) int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t* actions,
|
||||
int fd, int newFd) {
|
||||
if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC || fd < 0 || newFd < 0)
|
||||
return EBADF;
|
||||
if (actions->Count >= POSIX_SPAWN_FILE_ACTION_CAPACITY) return ENOMEM;
|
||||
posix_spawn_file_action_entry* action = &actions->Entries[actions->Count++];
|
||||
memset(action, 0, sizeof(*action));
|
||||
action->Type = NTL_POSIX_SPAWN_FILE_ACTION_DUP2;
|
||||
action->FileDescriptor = fd;
|
||||
action->NewFileDescriptor = newFd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Appends one open-at-descriptor action and owns a private copy of its pathname.
|
||||
__declspec(dllexport) int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t* actions,
|
||||
int fd, const char* path,
|
||||
int openFlags, mode_t mode) {
|
||||
if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC || fd < 0 || !path)
|
||||
return EINVAL;
|
||||
if (actions->Count >= POSIX_SPAWN_FILE_ACTION_CAPACITY) return ENOMEM;
|
||||
size_t length = strlen(path);
|
||||
char* copy = (char*)malloc(length + 1u);
|
||||
if (!copy) return ENOMEM;
|
||||
memcpy(copy, path, length + 1u);
|
||||
posix_spawn_file_action_entry* action = &actions->Entries[actions->Count++];
|
||||
memset(action, 0, sizeof(*action));
|
||||
action->Type = NTL_POSIX_SPAWN_FILE_ACTION_OPEN;
|
||||
action->FileDescriptor = fd;
|
||||
action->OpenFlags = openFlags;
|
||||
action->Mode = mode;
|
||||
action->Path = copy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initializes one POSIX spawn attribute object.
|
||||
__declspec(dllexport) int posix_spawnattr_init(posix_spawnattr_t* attributes) {
|
||||
if (!attributes) return EINVAL;
|
||||
memset(attributes, 0, sizeof(*attributes));
|
||||
attributes->Initialized = POSIX_SPAWN_ATTRIBUTES_MAGIC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invalidates one spawn attribute object.
|
||||
__declspec(dllexport) int posix_spawnattr_destroy(posix_spawnattr_t* attributes) {
|
||||
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC) return EINVAL;
|
||||
memset(attributes, 0, sizeof(*attributes));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns active spawn attribute flags.
|
||||
__declspec(dllexport) int posix_spawnattr_getflags(const posix_spawnattr_t* attributes,
|
||||
short* flags) {
|
||||
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !flags)
|
||||
return EINVAL;
|
||||
*flags = (short)attributes->Flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Selects supported process-group and signal spawn attributes.
|
||||
__declspec(dllexport) int posix_spawnattr_setflags(posix_spawnattr_t* attributes, short flags) {
|
||||
const uint32_t valid = POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK |
|
||||
POSIX_SPAWN_SETSIGDEF;
|
||||
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC ||
|
||||
((uint32_t)(unsigned short)flags & ~valid) != 0)
|
||||
return EINVAL;
|
||||
attributes->Flags = (uint32_t)(unsigned short)flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns the requested process group for POSIX_SPAWN_SETPGROUP.
|
||||
__declspec(dllexport) int posix_spawnattr_getpgroup(const posix_spawnattr_t* attributes,
|
||||
pid_t* pgroup) {
|
||||
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !pgroup)
|
||||
return EINVAL;
|
||||
*pgroup = attributes->ProcessGroup;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Sets the requested child process group; zero means the child's own stable POSIX PID.
|
||||
__declspec(dllexport) int posix_spawnattr_setpgroup(posix_spawnattr_t* attributes, pid_t pgroup) {
|
||||
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || pgroup < 0)
|
||||
return EINVAL;
|
||||
attributes->ProcessGroup = pgroup;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns the explicit child signal mask stored in one spawn attribute object.
|
||||
__declspec(dllexport) int posix_spawnattr_getsigmask(const posix_spawnattr_t* attributes,
|
||||
sigset_t* mask) {
|
||||
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !mask)
|
||||
return EINVAL;
|
||||
*mask = attributes->SignalMask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Stores the child signal mask used with POSIX_SPAWN_SETSIGMASK.
|
||||
__declspec(dllexport) int posix_spawnattr_setsigmask(posix_spawnattr_t* attributes,
|
||||
const sigset_t* mask) {
|
||||
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !mask ||
|
||||
(*mask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
|
||||
return EINVAL;
|
||||
attributes->SignalMask = *mask & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns the set forced to default disposition in the spawned image.
|
||||
__declspec(dllexport) int posix_spawnattr_getsigdefault(const posix_spawnattr_t* attributes,
|
||||
sigset_t* set) {
|
||||
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !set)
|
||||
return EINVAL;
|
||||
*set = attributes->SignalDefault;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Stores the signals forced to default disposition in the spawned image.
|
||||
__declspec(dllexport) int posix_spawnattr_setsigdefault(posix_spawnattr_t* attributes,
|
||||
const sigset_t* set) {
|
||||
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !set ||
|
||||
(*set & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
|
||||
return EINVAL;
|
||||
attributes->SignalDefault = *set;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Constructs one POSIX child through the authoritative posixsrv transaction.
|
||||
__declspec(dllexport) int posix_spawn(pid_t* pidOut,
|
||||
const char* path,
|
||||
const posix_spawn_file_actions_t* fileActions,
|
||||
const posix_spawnattr_t* attributes,
|
||||
char* const argv[],
|
||||
char* const envp[]) {
|
||||
if (!pidOut || !path || !path[0]) return EINVAL;
|
||||
if (fileActions && fileActions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC) return EINVAL;
|
||||
if (attributes && attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC) return EINVAL;
|
||||
char* const* environment = envp ? envp : environ;
|
||||
int error = PosixSpawnValidateInput(path, fileActions, argv, (char* const*)environment);
|
||||
if (error) return error;
|
||||
*pidOut = (pid_t)-1;
|
||||
|
||||
uint32_t transactionId = 0;
|
||||
error = PosixConstructionBegin(NTL_POSIX_TRANSACTION_SPAWN, &transactionId);
|
||||
if (error) return error;
|
||||
|
||||
error = PosixConstructionSendExecutable(transactionId, path);
|
||||
if (!error) error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ARGUMENT, argv);
|
||||
if (!error) error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ENVIRONMENT,
|
||||
(char* const*)environment);
|
||||
if (!error) error = PosixSpawnSendFileActions(transactionId, fileActions);
|
||||
if (!error) error = PosixSpawnSendAttributes(transactionId, attributes);
|
||||
if (error) {
|
||||
PosixConstructionCancelTransaction(transactionId);
|
||||
return error;
|
||||
}
|
||||
|
||||
uint64_t inheritanceToken = 0;
|
||||
KSTATUS status = PosixFdCreateInheritanceSnapshot(0, &inheritanceToken);
|
||||
if (!KSUCCESS(status)) {
|
||||
PosixConstructionCancelTransaction(transactionId);
|
||||
return PosixStatusToErrno(status);
|
||||
}
|
||||
|
||||
SYS_HANDLE securityContext = SYS_HANDLE_INVALID;
|
||||
status = PosixProcessDeriveSecurityContext(&securityContext);
|
||||
if (!KSUCCESS(status)) {
|
||||
PosixFdCancelInheritanceSnapshot(inheritanceToken);
|
||||
PosixConstructionCancelTransaction(transactionId);
|
||||
return PosixStatusToErrno(status);
|
||||
}
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_SPAWN_COMMIT;
|
||||
request.TransactionId = transactionId;
|
||||
request.FdInheritanceToken = inheritanceToken;
|
||||
SYS_PORT_HANDLE_TRANSFER transfer;
|
||||
memset(&transfer, 0, sizeof(transfer));
|
||||
transfer.Handle = securityContext;
|
||||
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_SECURITY_CONTEXT_RIGHT_ASSIGN;
|
||||
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
|
||||
NTL_POSIX_REPLY reply;
|
||||
status = PosixRuntimeCallServerWithHandles(&request, &transfer, 1u, &reply);
|
||||
(void)SysCloseHandle(securityContext);
|
||||
if (!KSUCCESS(status)) {
|
||||
PosixFdCancelInheritanceSnapshot(inheritanceToken);
|
||||
PosixConstructionCancelTransaction(transactionId);
|
||||
return PosixStatusToErrno(status);
|
||||
}
|
||||
if (!KSUCCESS(reply.Status)) {
|
||||
PosixFdCancelInheritanceSnapshot(inheritanceToken);
|
||||
PosixConstructionCancelTransaction(transactionId);
|
||||
return PosixStatusToErrno(reply.Status);
|
||||
}
|
||||
if (reply.SpawnedPosixPid == 0 || reply.SpawnedPosixPid > INT32_MAX) return EIO;
|
||||
*pidOut = (pid_t)reply.SpawnedPosixPid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Validates one exec image and its replacement argv/envp payload.
|
||||
static int PosixExecValidateInput(const char* path, char* const argv[], char* const envp[]) {
|
||||
if (!path || !path[0]) return EINVAL;
|
||||
size_t pathLength = strlen(path);
|
||||
if (pathLength > SYS_PATH_MAX) return ENAMETOOLONG;
|
||||
uint32_t totalBytes = 0;
|
||||
int error = PosixConstructionMeasureString(path, &totalBytes);
|
||||
if (!error) error = PosixConstructionMeasureVector(argv, &totalBytes);
|
||||
if (!error) error = PosixConstructionMeasureVector(envp, &totalBytes);
|
||||
return error;
|
||||
}
|
||||
|
||||
// Replaces the current native generation while preserving its stable POSIX process identity.
|
||||
__declspec(dllexport) int execve(const char* path, char* const argv[], char* const envp[]) {
|
||||
int error = PosixExecValidateInput(path, argv, envp);
|
||||
if (error) {
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
if (!thread || !thread->LogicalProcess) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
POSIX_VFORK_CHILD_STATE* borrowedChild = PosixVforkCurrentBorrowedChild();
|
||||
|
||||
uint32_t transactionId = 0;
|
||||
error = PosixConstructionBegin(NTL_POSIX_TRANSACTION_EXEC, &transactionId);
|
||||
if (error) {
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = PosixConstructionSendExecutable(transactionId, path);
|
||||
if (!error)
|
||||
error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ARGUMENT,
|
||||
argv);
|
||||
if (!error)
|
||||
error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ENVIRONMENT,
|
||||
envp);
|
||||
if (error) {
|
||||
PosixConstructionCancelTransaction(transactionId);
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t inheritanceToken = 0;
|
||||
KSTATUS status = PosixFdCreateInheritanceSnapshot(POSIX_FD_INHERIT_SKIP_CLOEXEC,
|
||||
&inheritanceToken);
|
||||
if (!KSUCCESS(status)) {
|
||||
PosixConstructionCancelTransaction(transactionId);
|
||||
errno = PosixStatusToErrno(status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_EXEC_COMMIT;
|
||||
request.TransactionId = transactionId;
|
||||
request.FdInheritanceToken = inheritanceToken;
|
||||
request.SignalMask = (thread->SignalBlocked | thread->SignalHandlerBlocked) &
|
||||
~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
|
||||
|
||||
NTL_POSIX_REPLY reply;
|
||||
status = PosixRuntimeCallServerBlockingWithHandles(
|
||||
&request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply);
|
||||
if (!KSUCCESS(status)) {
|
||||
PosixFdCancelInheritanceSnapshot(inheritanceToken);
|
||||
PosixConstructionCancelTransaction(transactionId);
|
||||
errno = PosixStatusToErrno(status);
|
||||
return -1;
|
||||
}
|
||||
if (!KSUCCESS(reply.Status)) {
|
||||
PosixFdCancelInheritanceSnapshot(inheritanceToken);
|
||||
PosixConstructionCancelTransaction(transactionId);
|
||||
errno = PosixStatusToErrno(reply.Status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (borrowedChild) {
|
||||
// The server has a READY replacement but will release it only after this thread is dead.
|
||||
PosixVforkDetachBorrowedChildLocal(borrowedChild, false);
|
||||
for (;;) SysExitThread(0);
|
||||
}
|
||||
|
||||
// Ordinary successful exec has no reply path; reaching here violates that contract.
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Returns the server-owned parent launch association for the current POSIX process.
|
||||
__declspec(dllexport) pid_t getppid(void) {
|
||||
NTL_POSIX_REPLY reply;
|
||||
KSTATUS status = PosixRuntimeGetProcessAttributes(&reply);
|
||||
if (!KSUCCESS(status)) {
|
||||
errno = PosixStatusToErrno(status);
|
||||
return (pid_t)-1;
|
||||
}
|
||||
return (pid_t)reply.ParentPosixPid;
|
||||
}
|
||||
|
||||
// Waits for one matching POSIX child record and collects it exactly once.
|
||||
__declspec(dllexport) pid_t waitpid(pid_t pid, int* statusOut, int options) {
|
||||
if ((options & ~WNOHANG) != 0) {
|
||||
errno = EINVAL;
|
||||
return (pid_t)-1;
|
||||
}
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_WAIT_PROCESS;
|
||||
request.WaitPid = (int32_t)pid;
|
||||
request.WaitOptions = (options & WNOHANG) ? NTL_POSIX_WAIT_NOHANG : 0u;
|
||||
|
||||
NTL_POSIX_REPLY reply;
|
||||
KSTATUS status = PosixRuntimeCallServerBlocking(&request, &reply);
|
||||
if (!KSUCCESS(status)) {
|
||||
errno = PosixStatusToErrno(status);
|
||||
return (pid_t)-1;
|
||||
}
|
||||
if (!KSUCCESS(reply.Status)) {
|
||||
errno = PosixStatusToErrno(reply.Status);
|
||||
return (pid_t)-1;
|
||||
}
|
||||
if (reply.WaitedPosixPid == 0) return 0;
|
||||
if (reply.WaitedPosixPid < 0) {
|
||||
errno = EIO;
|
||||
return (pid_t)-1;
|
||||
}
|
||||
if (statusOut) *statusOut = (reply.ExitCode & 0xff) << 8;
|
||||
return (pid_t)reply.WaitedPosixPid;
|
||||
}
|
||||
|
||||
// Waits for any POSIX child.
|
||||
__declspec(dllexport) pid_t wait(int* status) {
|
||||
return waitpid((pid_t)-1, status, 0);
|
||||
}
|
||||
|
||||
// Terminates either an ordinary POSIX native generation or one borrowed vfork child.
|
||||
__declspec(dllexport) void _exit(int status) {
|
||||
POSIX_VFORK_CHILD_STATE* borrowedChild = PosixVforkCurrentBorrowedChild();
|
||||
if (borrowedChild) {
|
||||
(void)PosixVforkRecordExit(borrowedChild, status);
|
||||
PosixVforkDetachBorrowedChildLocal(borrowedChild, true);
|
||||
for (;;) SysExitThread(status);
|
||||
}
|
||||
for (;;) SysExitProcess(status);
|
||||
}
|
||||
|
||||
// Terminates the process; stdio/atexit policy can layer above this when those facilities exist.
|
||||
__declspec(dllexport) void exit(int status) {
|
||||
SysExitProcess(status);
|
||||
while (true) {}
|
||||
_exit(status);
|
||||
}
|
||||
|
||||
// Initializes one ordinary shared-attribute request.
|
||||
|
||||
@@ -12,6 +12,159 @@
|
||||
|
||||
static POSIX_PROCESS_STATE* gPosixProcessState;
|
||||
static int gEmergencyErrno;
|
||||
NTL_POSIX_API char** environ;
|
||||
|
||||
// Reports one startup failure without depending on the POSIX descriptor layer.
|
||||
static void PosixRuntimeReportStartupFailure(const char* stage, KSTATUS status) {
|
||||
static const char hex[] = "0123456789ABCDEF";
|
||||
char buffer[128];
|
||||
const char* prefix = "posix startup: ";
|
||||
size_t position = 0;
|
||||
while (*prefix && position + 1u < sizeof(buffer)) buffer[position++] = *prefix++;
|
||||
while (stage && *stage && position + 1u < sizeof(buffer)) buffer[position++] = *stage++;
|
||||
const char* middle = " failed status=0x";
|
||||
while (*middle && position + 1u < sizeof(buffer)) buffer[position++] = *middle++;
|
||||
for (int shift = 28; shift >= 0 && position + 1u < sizeof(buffer); shift -= 4)
|
||||
buffer[position++] = hex[(status >> shift) & 0xFu];
|
||||
if (position + 1u < sizeof(buffer)) buffer[position++] = '\n';
|
||||
buffer[position] = '\0';
|
||||
SYS_WRITE_CONSOLE_REQUEST write;
|
||||
memset(&write, 0, sizeof(write));
|
||||
write.Size = sizeof(write);
|
||||
write.Text = SysSvFromParts(buffer, position);
|
||||
(void)SysWriteConsole(&write);
|
||||
}
|
||||
|
||||
static void PosixRuntimeDestroyVectors(POSIX_PROCESS_STATE* process);
|
||||
|
||||
// Copies one immutable loader vector into mutable process-local POSIX strings.
|
||||
static KSTATUS PosixRuntimeCopyVector(int environment, char*** vectorOut, int* countOut) {
|
||||
if (!vectorOut || !countOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*vectorOut = (char**)0;
|
||||
*countOut = 0;
|
||||
|
||||
uint32_t count = 0;
|
||||
KSTATUS status = environment ? NtlGetProcessEnvironmentCount(&count)
|
||||
: NtlGetProcessArgumentCount(&count);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (count > INT32_MAX) return NTLSTATUS_LIMIT_REACHED;
|
||||
|
||||
char** vector = (char**)calloc((size_t)count + 1u, sizeof(char*));
|
||||
if (!vector) return NTLSTATUS_NO_MEMORY;
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
SYS_STRING_VIEW view = SYS_SV_NULL;
|
||||
status = environment ? NtlGetProcessEnvironment(i, &view)
|
||||
: NtlGetProcessArgument(i, &view);
|
||||
if (!KSUCCESS(status)) {
|
||||
for (uint32_t j = 0; j < i; ++j) free(vector[j]);
|
||||
free(vector);
|
||||
return status;
|
||||
}
|
||||
vector[i] = (char*)malloc(view.Length + 1u);
|
||||
if (!vector[i]) {
|
||||
for (uint32_t j = 0; j < i; ++j) free(vector[j]);
|
||||
free(vector);
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
}
|
||||
if (view.Length) memcpy(vector[i], view.Data, view.Length);
|
||||
vector[i][view.Length] = '\0';
|
||||
}
|
||||
*vectorOut = vector;
|
||||
*countOut = (int)count;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Builds the compatibility command-line view from the loader-owned argv vector.
|
||||
static KSTATUS PosixRuntimeBuildCommandLine(POSIX_PROCESS_STATE* process) {
|
||||
if (!process) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (process->ArgumentCount <= 0 || !process->Arguments) return NTLSTATUS_SUCCESS;
|
||||
|
||||
size_t required = 1u;
|
||||
for (int i = 0; i < process->ArgumentCount; ++i) {
|
||||
const char* argument = process->Arguments[i] ? process->Arguments[i] : "";
|
||||
int quote = 0;
|
||||
for (const char* cursor = argument; *cursor; ++cursor) {
|
||||
if (*cursor == ' ' || *cursor == '\t') quote = 1;
|
||||
if (*cursor == '"') {
|
||||
if (required == (size_t)-1) return NTLSTATUS_LIMIT_REACHED;
|
||||
++required;
|
||||
}
|
||||
if (required == (size_t)-1) return NTLSTATUS_LIMIT_REACHED;
|
||||
++required;
|
||||
}
|
||||
if (quote) {
|
||||
if (required > (size_t)-1 - 2u) return NTLSTATUS_LIMIT_REACHED;
|
||||
required += 2u;
|
||||
}
|
||||
if (i != 0) {
|
||||
if (required == (size_t)-1) return NTLSTATUS_LIMIT_REACHED;
|
||||
++required;
|
||||
}
|
||||
}
|
||||
|
||||
char* commandLine = (char*)malloc(required);
|
||||
if (!commandLine) return NTLSTATUS_NO_MEMORY;
|
||||
size_t position = 0;
|
||||
for (int i = 0; i < process->ArgumentCount; ++i) {
|
||||
const char* argument = process->Arguments[i] ? process->Arguments[i] : "";
|
||||
int quote = 0;
|
||||
for (const char* cursor = argument; *cursor; ++cursor) {
|
||||
if (*cursor == ' ' || *cursor == '\t') { quote = 1; break; }
|
||||
}
|
||||
if (i != 0) commandLine[position++] = ' ';
|
||||
if (quote) commandLine[position++] = '"';
|
||||
for (const char* cursor = argument; *cursor; ++cursor) {
|
||||
if (*cursor == '"') commandLine[position++] = '\\';
|
||||
commandLine[position++] = *cursor;
|
||||
}
|
||||
if (quote) commandLine[position++] = '"';
|
||||
}
|
||||
commandLine[position] = '\0';
|
||||
process->CommandLine = commandLine;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Materializes argv and environ before application entry executes.
|
||||
static KSTATUS PosixRuntimeInitializeVectors(POSIX_PROCESS_STATE* process) {
|
||||
if (!process) return NTLSTATUS_INVALID_PARAMETER;
|
||||
int environmentCount = 0;
|
||||
KSTATUS status = PosixRuntimeCopyVector(0, &process->Arguments, &process->ArgumentCount);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
status = PosixRuntimeCopyVector(1, &process->Environment, &environmentCount);
|
||||
if (!KSUCCESS(status)) {
|
||||
for (int i = 0; i < process->ArgumentCount; ++i) free(process->Arguments[i]);
|
||||
free(process->Arguments);
|
||||
process->Arguments = (char**)0;
|
||||
process->ArgumentCount = 0;
|
||||
return status;
|
||||
}
|
||||
environ = process->Environment;
|
||||
status = PosixRuntimeBuildCommandLine(process);
|
||||
if (!KSUCCESS(status)) {
|
||||
PosixRuntimeDestroyVectors(process);
|
||||
return status;
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Releases process-local argv/environ storage during initialization rollback.
|
||||
static void PosixRuntimeDestroyVectors(POSIX_PROCESS_STATE* process) {
|
||||
if (!process) return;
|
||||
if (process->Arguments) {
|
||||
for (int i = 0; i < process->ArgumentCount; ++i) free(process->Arguments[i]);
|
||||
free(process->Arguments);
|
||||
}
|
||||
if (process->Environment) {
|
||||
for (char** entry = process->Environment; *entry; ++entry) free(*entry);
|
||||
free(process->Environment);
|
||||
}
|
||||
free(process->CommandLine);
|
||||
process->Arguments = (char**)0;
|
||||
process->Environment = (char**)0;
|
||||
process->CommandLine = (char*)0;
|
||||
process->ArgumentCount = 0;
|
||||
environ = (char**)0;
|
||||
}
|
||||
|
||||
// Resolves the published POSIX service and opens one private client endpoint.
|
||||
static KSTATUS PosixRuntimeConnectServer(SYS_HANDLE* endpointOut) {
|
||||
@@ -154,6 +307,65 @@ KSTATUS PosixRuntimeCallServer(const NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY
|
||||
replyOut);
|
||||
}
|
||||
|
||||
// Registers a temporary endpoint to the current native/POSIX process identity.
|
||||
static KSTATUS PosixRuntimeRegisterAuxiliaryEndpoint(SYS_HANDLE endpoint) {
|
||||
POSIX_PROCESS_STATE* process = gPosixProcessState;
|
||||
if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC || endpoint == SYS_HANDLE_INVALID)
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_REGISTER_PROCESS;
|
||||
|
||||
size_t currentDirectorySize = 0;
|
||||
KSTATUS status = NtlGetCurrentDirectory(request.Path, sizeof(request.Path), ¤tDirectorySize);
|
||||
if (!KSUCCESS(status) || currentDirectorySize == 0 || currentDirectorySize > sizeof(request.Path))
|
||||
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
||||
request.PathLength = (uint32_t)strlen(request.Path);
|
||||
|
||||
NTL_POSIX_REPLY reply;
|
||||
status = PosixRuntimeCallEndpoint(endpoint, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
|
||||
(SYS_HANDLE*)0, 0, 0, (uint32_t*)0, &reply);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (!KSUCCESS(reply.Status)) return reply.Status;
|
||||
if (reply.PosixPid != process->DefaultIdentity.PosixPid ||
|
||||
reply.NativeGeneration != process->DefaultIdentity.Generation ||
|
||||
(reply.ProcessState != NTL_POSIX_PROCESS_STATE_RUNNING &&
|
||||
reply.ProcessState != NTL_POSIX_PROCESS_STATE_EXEC_PREPARING))
|
||||
return NTLSTATUS_ACCESS_DENIED;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Uses an auxiliary registered connection so one blocking request cannot stall ordinary POSIX calls.
|
||||
KSTATUS PosixRuntimeCallServerBlockingWithHandles(const NTL_POSIX_REQUEST* request,
|
||||
const SYS_PORT_HANDLE_TRANSFER* handles,
|
||||
uint32_t handleCount,
|
||||
NTL_POSIX_REPLY* replyOut) {
|
||||
if (!request || !replyOut || (handleCount != 0 && !handles) ||
|
||||
handleCount > SYS_PORT_MAX_TRANSFER_HANDLES)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
|
||||
KSTATUS status = PosixRuntimeConnectServer(&endpoint);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
status = PosixRuntimeRegisterAuxiliaryEndpoint(endpoint);
|
||||
if (KSUCCESS(status))
|
||||
status = PosixRuntimeCallEndpoint(endpoint, request, handles, handleCount,
|
||||
(SYS_HANDLE*)0, 0, 0, (uint32_t*)0, replyOut);
|
||||
(void)SysDisconnectPort(endpoint);
|
||||
(void)SysCloseHandle(endpoint);
|
||||
return status;
|
||||
}
|
||||
|
||||
// Uses an auxiliary registered connection without transferring capabilities.
|
||||
KSTATUS PosixRuntimeCallServerBlocking(const NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* replyOut) {
|
||||
return PosixRuntimeCallServerBlockingWithHandles(
|
||||
request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, replyOut);
|
||||
}
|
||||
|
||||
// Queries the current server-owned POSIX process attributes.
|
||||
KSTATUS PosixRuntimeGetProcessAttributes(NTL_POSIX_REPLY* replyOut) {
|
||||
if (!replyOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
@@ -180,8 +392,12 @@ static KSTATUS PosixRuntimeReadBootstrap(NTL_POSIX_BOOTSTRAP_DATA* bootstrapOut)
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (size != sizeof(*bootstrapOut) || bootstrapOut->Magic != NTL_POSIX_BOOTSTRAP_MAGIC ||
|
||||
bootstrapOut->Version != NTL_POSIX_BOOTSTRAP_VERSION ||
|
||||
bootstrapOut->Size != sizeof(*bootstrapOut) || bootstrapOut->Reserved != 0)
|
||||
bootstrapOut->Size != sizeof(*bootstrapOut) || bootstrapOut->Reserved != 0 ||
|
||||
(bootstrapOut->InitialSignalMask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0 ||
|
||||
(bootstrapOut->IgnoredSignals & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
bootstrapOut->InitialSignalMask &= ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
|
||||
bootstrapOut->IgnoredSignals &= ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -195,16 +411,6 @@ static KSTATUS PosixRuntimeRegisterProcess(POSIX_PROCESS_STATE* process,
|
||||
status = PosixRuntimeConnectServer(&endpoint);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
SYS_HANDLE selfProcess = SYS_HANDLE_INVALID;
|
||||
status = SysOpenProcess(SysGetCurrentPid(),
|
||||
SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_RIGHT_TRANSFER,
|
||||
&selfProcess);
|
||||
if (!KSUCCESS(status)) {
|
||||
(void)SysDisconnectPort(endpoint);
|
||||
(void)SysCloseHandle(endpoint);
|
||||
return status;
|
||||
}
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
@@ -221,21 +427,13 @@ static KSTATUS PosixRuntimeRegisterProcess(POSIX_PROCESS_STATE* process,
|
||||
if (!KSUCCESS(status) || currentDirectorySize == 0 || currentDirectorySize > sizeof(request.Path)) {
|
||||
(void)SysDisconnectPort(endpoint);
|
||||
(void)SysCloseHandle(endpoint);
|
||||
(void)SysCloseHandle(selfProcess);
|
||||
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
||||
}
|
||||
request.PathLength = (uint32_t)strlen(request.Path);
|
||||
|
||||
SYS_PORT_HANDLE_TRANSFER transfer;
|
||||
memset(&transfer, 0, sizeof(transfer));
|
||||
transfer.Handle = selfProcess;
|
||||
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT;
|
||||
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
|
||||
|
||||
NTL_POSIX_REPLY reply;
|
||||
status = PosixRuntimeCallEndpoint(endpoint, &request, &transfer, 1u,
|
||||
status = PosixRuntimeCallEndpoint(endpoint, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
|
||||
(SYS_HANDLE*)0, 0, 0, (uint32_t*)0, &reply);
|
||||
(void)SysCloseHandle(selfProcess);
|
||||
if (!KSUCCESS(status)) {
|
||||
(void)SysDisconnectPort(endpoint);
|
||||
(void)SysCloseHandle(endpoint);
|
||||
@@ -249,7 +447,10 @@ static KSTATUS PosixRuntimeRegisterProcess(POSIX_PROCESS_STATE* process,
|
||||
}
|
||||
if (reply.ClientProcessId != SysGetCurrentPid() || reply.PosixPid == 0 ||
|
||||
reply.PosixPid > 0x7FFFFFFFu || reply.NativeGeneration == 0 ||
|
||||
reply.ProcessState != NTL_POSIX_PROCESS_STATE_RUNNING) {
|
||||
(reply.ProcessState != NTL_POSIX_PROCESS_STATE_RUNNING &&
|
||||
reply.ProcessState != NTL_POSIX_PROCESS_STATE_CONSTRUCTING &&
|
||||
reply.ProcessState != NTL_POSIX_PROCESS_STATE_EXEC_PREPARING &&
|
||||
reply.ProcessState != NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING)) {
|
||||
(void)SysDisconnectPort(endpoint);
|
||||
(void)SysCloseHandle(endpoint);
|
||||
return NTLSTATUS_FAILURE;
|
||||
@@ -277,7 +478,7 @@ uint32_t PosixRuntimeInitializeProcess(void) {
|
||||
process->Magic = POSIX_PROCESS_STATE_MAGIC;
|
||||
process->Peb = peb;
|
||||
process->ServerEndpoint = SYS_HANDLE_INVALID;
|
||||
process->FdLock = SYS_HANDLE_INVALID;
|
||||
process->DefaultIdentity.FdLock = SYS_HANDLE_INVALID;
|
||||
gPosixProcessState = process;
|
||||
|
||||
NTL_POSIX_BOOTSTRAP_DATA bootstrap;
|
||||
@@ -290,13 +491,18 @@ uint32_t PosixRuntimeInitializeProcess(void) {
|
||||
|
||||
status = PosixRuntimeRegisterProcess(process, &bootstrap);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
PosixRuntimeReportStartupFailure("register-process", status);
|
||||
gPosixProcessState = (POSIX_PROCESS_STATE*)0;
|
||||
free(process);
|
||||
return status;
|
||||
}
|
||||
process->BootstrapToken = bootstrap.Token;
|
||||
process->BootstrapPosixPid = bootstrap.PosixPid;
|
||||
process->BootstrapGeneration = bootstrap.Generation;
|
||||
|
||||
status = PosixFdInitializeProcess(process, bootstrap.FdInheritanceToken);
|
||||
status = PosixRuntimeInitializeVectors(process);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
PosixRuntimeReportStartupFailure("vectors", status);
|
||||
(void)SysDisconnectPort(process->ServerEndpoint);
|
||||
(void)SysCloseHandle(process->ServerEndpoint);
|
||||
process->ServerEndpoint = SYS_HANDLE_INVALID;
|
||||
@@ -305,9 +511,30 @@ uint32_t PosixRuntimeInitializeProcess(void) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = PosixRuntimeAttachThread();
|
||||
for (int signalNumber = NTL_POSIX_SIGNAL_HUP;
|
||||
signalNumber <= NTL_POSIX_SIGNAL_MAX; ++signalNumber) {
|
||||
uint64_t bit = NTL_POSIX_SIGNAL_BIT(signalNumber);
|
||||
if (bootstrap.IgnoredSignals & bit)
|
||||
process->DefaultIdentity.SignalActions[signalNumber].Handler = (uintptr_t)SIG_IGN;
|
||||
}
|
||||
|
||||
status = PosixFdInitializeLogicalProcess(&process->DefaultIdentity, bootstrap.FdInheritanceToken);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
PosixFdDestroyProcess(process);
|
||||
PosixRuntimeReportStartupFailure("fd-init", status);
|
||||
PosixRuntimeDestroyVectors(process);
|
||||
(void)SysDisconnectPort(process->ServerEndpoint);
|
||||
(void)SysCloseHandle(process->ServerEndpoint);
|
||||
process->ServerEndpoint = SYS_HANDLE_INVALID;
|
||||
gPosixProcessState = (POSIX_PROCESS_STATE*)0;
|
||||
free(process);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = PosixRuntimeAttachThreadWithSignalMask(bootstrap.InitialSignalMask);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
PosixRuntimeReportStartupFailure("thread-attach", status);
|
||||
PosixFdDestroyLogicalProcess(&process->DefaultIdentity);
|
||||
PosixRuntimeDestroyVectors(process);
|
||||
(void)SysDisconnectPort(process->ServerEndpoint);
|
||||
(void)SysCloseHandle(process->ServerEndpoint);
|
||||
process->ServerEndpoint = SYS_HANDLE_INVALID;
|
||||
@@ -327,8 +554,61 @@ POSIX_PROCESS_STATE* PosixRuntimeProcessState(void) {
|
||||
return process;
|
||||
}
|
||||
|
||||
// Attaches the native thread to the process's default logical POSIX identity.
|
||||
uint32_t PosixRuntimeAttachThread(void) {
|
||||
// Registers the current native thread capability with posixsrv.
|
||||
static KSTATUS PosixRuntimeRegisterThread(POSIX_PROCESS_STATE* process, POSIX_THREAD_STATE* thread) {
|
||||
if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC || !thread)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
KSTATUS status = SysSetUserApcDispatcher((void*)SysUserApcDispatcher);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
SYS_HANDLE selfThread = SYS_HANDLE_INVALID;
|
||||
status = SysOpenCurrentThread(SYS_RIGHT_QUERY | SYS_RIGHT_TRANSFER | SYS_THREAD_RIGHT_NOTIFY,
|
||||
&selfThread);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
|
||||
SYS_PORT_HANDLE_TRANSFER transfer;
|
||||
memset(&transfer, 0, sizeof(transfer));
|
||||
transfer.Handle = selfThread;
|
||||
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_THREAD_RIGHT_NOTIFY;
|
||||
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_REGISTER_THREAD;
|
||||
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
|
||||
request.SignalMask = thread->SignalBlocked;
|
||||
request.SignalApcRoutine = (uint64_t)(uintptr_t)PosixSignalApc;
|
||||
|
||||
NTL_POSIX_REPLY reply;
|
||||
status = PosixRuntimeCallServerWithHandles(&request, &transfer, 1u, &reply);
|
||||
(void)SysCloseHandle(selfThread);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
return reply.Status;
|
||||
}
|
||||
|
||||
// Removes the current native thread from posixsrv's target registry.
|
||||
static void PosixRuntimeUnregisterThread(POSIX_THREAD_STATE* thread) {
|
||||
if (!thread || !thread->ServerRegistered ||
|
||||
!thread->NativeProcess || thread->NativeProcess->Magic != POSIX_PROCESS_STATE_MAGIC)
|
||||
return;
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_UNREGISTER_THREAD;
|
||||
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
|
||||
|
||||
NTL_POSIX_REPLY reply;
|
||||
(void)PosixRuntimeCallServer(&request, &reply);
|
||||
thread->ServerRegistered = 0;
|
||||
}
|
||||
|
||||
// Attaches the native thread with one inherited persistent POSIX signal mask.
|
||||
uint32_t PosixRuntimeAttachThreadWithSignalMask(uint64_t signalMask) {
|
||||
SYS_TEB* teb = SysGetTeb();
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
if (!teb || teb->Magic != SYS_TEB_MAGIC || !process)
|
||||
@@ -349,10 +629,30 @@ uint32_t PosixRuntimeAttachThread(void) {
|
||||
thread->Magic = POSIX_THREAD_STATE_MAGIC;
|
||||
thread->NativeProcess = process;
|
||||
thread->LogicalProcess = &process->DefaultIdentity;
|
||||
thread->NativeThreadId = SysGetCurrentTid();
|
||||
thread->SignalBlocked = signalMask & NTL_POSIX_SIGNAL_VALID_MASK &
|
||||
~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
|
||||
if (thread->NativeThreadId == 0) {
|
||||
free(thread);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
KSTATUS status = PosixRuntimeRegisterThread(process, thread);
|
||||
if (!KSUCCESS(status)) {
|
||||
thread->Magic = 0;
|
||||
free(thread);
|
||||
return status;
|
||||
}
|
||||
thread->ServerRegistered = 1u;
|
||||
teb->SubsystemData = thread;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Attaches a native thread with the default empty signal mask.
|
||||
uint32_t PosixRuntimeAttachThread(void) {
|
||||
return PosixRuntimeAttachThreadWithSignalMask(0);
|
||||
}
|
||||
|
||||
// Returns the current thread state, attaching lazily when necessary.
|
||||
POSIX_THREAD_STATE* PosixRuntimeThreadState(void) {
|
||||
if (!PosixRuntimeProcessState()) {
|
||||
@@ -374,6 +674,13 @@ POSIX_THREAD_STATE* PosixRuntimeThreadState(void) {
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
||||
// Returns the logical POSIX process selected by the current native thread.
|
||||
POSIX_LOGICAL_PROCESS_STATE* PosixRuntimeLogicalProcessState(void) {
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
return thread ? thread->LogicalProcess : (POSIX_LOGICAL_PROCESS_STATE*)0;
|
||||
}
|
||||
|
||||
// Releases POSIX state owned by the current native TEB.
|
||||
void PosixRuntimeDetachThread(void) {
|
||||
SYS_TEB* teb = SysGetTeb();
|
||||
@@ -385,6 +692,7 @@ void PosixRuntimeDetachThread(void) {
|
||||
thread->NativeProcess != PosixRuntimeProcessState())
|
||||
return;
|
||||
|
||||
PosixRuntimeUnregisterThread(thread);
|
||||
teb->SubsystemData = (void*)0;
|
||||
thread->Magic = 0;
|
||||
free(thread);
|
||||
@@ -428,11 +736,72 @@ char* PosixRuntimeTakeDlError(void) {
|
||||
return thread->DlError;
|
||||
}
|
||||
|
||||
// Commits a server-built child only after every current personality bootstrap step succeeds.
|
||||
static KSTATUS PosixRuntimeReportBootstrapReady(POSIX_PROCESS_STATE* process) {
|
||||
if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (process->BootstrapToken == 0) return NTLSTATUS_SUCCESS;
|
||||
|
||||
NTL_POSIX_REQUEST ready;
|
||||
memset(&ready, 0, sizeof(ready));
|
||||
ready.Size = sizeof(ready);
|
||||
ready.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
ready.Operation = NTL_POSIX_OP_BOOTSTRAP_READY;
|
||||
ready.BootstrapToken = process->BootstrapToken;
|
||||
ready.BootstrapPosixPid = process->BootstrapPosixPid;
|
||||
ready.BootstrapGeneration = process->BootstrapGeneration;
|
||||
NTL_POSIX_REPLY reply;
|
||||
KSTATUS status = PosixRuntimeCallServer(&ready, &reply);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (!KSUCCESS(reply.Status)) return reply.Status;
|
||||
|
||||
process->BootstrapToken = 0;
|
||||
process->BootstrapPosixPid = 0;
|
||||
process->BootstrapGeneration = 0;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Initializes all POSIX process state required before application main.
|
||||
NTL_POSIX_API uint32_t __ntl_posix_process_start(void) {
|
||||
uint32_t status = PosixRuntimeInitializeProcess();
|
||||
if (status != NTLSTATUS_SUCCESS)
|
||||
return status;
|
||||
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
status = PosixSignalInitializeExceptionTranslation(process);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
PosixRuntimeReportStartupFailure("signal-init", status);
|
||||
return status;
|
||||
}
|
||||
status = PosixRuntimeReportBootstrapReady(process);
|
||||
if (status != NTLSTATUS_SUCCESS) {
|
||||
PosixRuntimeReportStartupFailure("bootstrap-ready", status);
|
||||
return status;
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns process-local vectors to the POSIX startup shim after initialization.
|
||||
NTL_POSIX_API uint32_t __ntl_posix_process_vectors(int* argcOut,
|
||||
char*** argvOut,
|
||||
char*** envpOut) {
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
if (!process || !argcOut || !argvOut || !envpOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*argcOut = process->ArgumentCount;
|
||||
*argvOut = process->Arguments;
|
||||
*envpOut = process->Environment;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Finds one process-local environment entry by exact variable name.
|
||||
NTL_POSIX_API char* getenv(const char* name) {
|
||||
if (!name || !name[0] || strchr(name, '=')) return (char*)0;
|
||||
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
|
||||
if (!process || !process->Environment) return (char*)0;
|
||||
size_t nameLength = strlen(name);
|
||||
for (char** entry = process->Environment; *entry; ++entry) {
|
||||
if (strncmp(*entry, name, nameLength) == 0 && (*entry)[nameLength] == '=')
|
||||
return *entry + nameLength + 1u;
|
||||
}
|
||||
return (char*)0;
|
||||
}
|
||||
@@ -0,0 +1,476 @@
|
||||
// POSIX signal policy and user-mode dispatch over generic thread APC delivery.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <ntlite/exception.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix_internal.h"
|
||||
|
||||
// Returns one signal bit after validating its public signal number.
|
||||
static uint64_t PosixSignalBit(int signalNumber) {
|
||||
return NtlPosixSignalValid(signalNumber) ? NTL_POSIX_SIGNAL_BIT(signalNumber) : 0;
|
||||
}
|
||||
|
||||
// Converts the process-local action snapshot into the server's routing view.
|
||||
static uint32_t PosixSignalDisposition(const POSIX_SIGNAL_ACTION_STATE* action) {
|
||||
uintptr_t handler = action ? action->Handler : 0;
|
||||
if (handler == (uintptr_t)SIG_IGN) return NTL_POSIX_SIGNAL_DISPOSITION_IGNORE;
|
||||
if (handler == (uintptr_t)SIG_DFL) return NTL_POSIX_SIGNAL_DISPOSITION_DEFAULT;
|
||||
return NTL_POSIX_SIGNAL_DISPOSITION_CAUGHT;
|
||||
}
|
||||
|
||||
// Sends one signal-control request through the authoritative POSIX server.
|
||||
static KSTATUS PosixSignalCallServer(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* reply) {
|
||||
if (!request || !reply) return NTLSTATUS_INVALID_PARAMETER;
|
||||
request->Size = sizeof(*request);
|
||||
request->Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request->Flags = NTL_POSIX_REQUEST_FLAG_NONE;
|
||||
KSTATUS status = PosixRuntimeCallServer(request, reply);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
return reply->Status;
|
||||
}
|
||||
|
||||
// Applies one caught/default/ignored signal on the current POSIX thread.
|
||||
static int PosixSignalDeliver(POSIX_THREAD_STATE* thread, int signalNumber) {
|
||||
if (!thread || !thread->NativeProcess || !NtlPosixSignalValid(signalNumber)) return 0;
|
||||
uint64_t bit = PosixSignalBit(signalNumber);
|
||||
uint64_t effectiveMask = thread->SignalBlocked | thread->SignalHandlerBlocked;
|
||||
if (signalNumber != SIGKILL && (effectiveMask & bit) != 0) {
|
||||
thread->SignalPending |= bit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
thread->SignalPending &= ~bit;
|
||||
POSIX_SIGNAL_ACTION_STATE* state = &thread->LogicalProcess->SignalActions[signalNumber];
|
||||
uintptr_t handlerValue = state->Handler;
|
||||
if (signalNumber == SIGKILL) handlerValue = (uintptr_t)SIG_DFL;
|
||||
|
||||
if (handlerValue == (uintptr_t)SIG_IGN ||
|
||||
(handlerValue == (uintptr_t)SIG_DFL && signalNumber == SIGCHLD))
|
||||
return 0;
|
||||
if (handlerValue == (uintptr_t)SIG_DFL) {
|
||||
_exit(128 + signalNumber);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t previousHandlerMask = thread->SignalHandlerBlocked;
|
||||
thread->SignalHandlerBlocked = (previousHandlerMask | state->Mask | bit) &
|
||||
NTL_POSIX_SIGNAL_VALID_MASK &
|
||||
~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
|
||||
++thread->SignalDeliveryCount;
|
||||
if (thread->SignalWaitEvent != SYS_HANDLE_INVALID)
|
||||
(void)SysSignalEvent(thread->SignalWaitEvent);
|
||||
((sighandler_t)handlerValue)(signalNumber);
|
||||
thread->SignalHandlerBlocked = previousHandlerMask;
|
||||
PosixSignalDispatchLocalPending(thread);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Maps one personality-neutral native exception class to a POSIX signal.
|
||||
static int PosixSignalForNativeException(uint32_t code) {
|
||||
switch (code) {
|
||||
case SYS_EXCEPTION_CODE_ACCESS_VIOLATION:
|
||||
case SYS_EXCEPTION_CODE_DATATYPE_MISALIGNMENT:
|
||||
case SYS_EXCEPTION_CODE_STACK_OVERFLOW:
|
||||
case SYS_EXCEPTION_CODE_GUARD_PAGE_VIOLATION:
|
||||
return SIGSEGV;
|
||||
case SYS_EXCEPTION_CODE_ILLEGAL_INSTRUCTION:
|
||||
case SYS_EXCEPTION_CODE_PRIVILEGED_INSTRUCTION:
|
||||
return SIGILL;
|
||||
case SYS_EXCEPTION_CODE_INT_DIVIDE_BY_ZERO:
|
||||
case SYS_EXCEPTION_CODE_INT_OVERFLOW:
|
||||
case SYS_EXCEPTION_CODE_FLT_DENORMAL_OPERAND:
|
||||
case SYS_EXCEPTION_CODE_FLT_DIVIDE_BY_ZERO:
|
||||
case SYS_EXCEPTION_CODE_FLT_INEXACT_RESULT:
|
||||
case SYS_EXCEPTION_CODE_FLT_INVALID_OPERATION:
|
||||
case SYS_EXCEPTION_CODE_FLT_OVERFLOW:
|
||||
case SYS_EXCEPTION_CODE_FLT_STACK_CHECK:
|
||||
case SYS_EXCEPTION_CODE_FLT_UNDERFLOW:
|
||||
return SIGFPE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Translates selected native faults at the POSIX personality boundary.
|
||||
static int PosixSignalNativeException(SYS_EXCEPTION_RECORD* record,
|
||||
SYS_USER_CONTEXT* context,
|
||||
void* userContext) {
|
||||
(void)context;
|
||||
(void)userContext;
|
||||
if (!record) return NTL_EXCEPTION_CONTINUE_SEARCH;
|
||||
int signalNumber = PosixSignalForNativeException(record->Code);
|
||||
if (!signalNumber) return NTL_EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
if (!thread) return NTL_EXCEPTION_CONTINUE_SEARCH;
|
||||
uint64_t bit = PosixSignalBit(signalNumber);
|
||||
if ((thread->SignalBlocked | thread->SignalHandlerBlocked) & bit)
|
||||
return NTL_EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
POSIX_SIGNAL_ACTION_STATE* state = &thread->LogicalProcess->SignalActions[signalNumber];
|
||||
if (state->Handler == (uintptr_t)SIG_IGN)
|
||||
return NTL_EXCEPTION_CONTINUE_SEARCH;
|
||||
return PosixSignalDeliver(thread, signalNumber)
|
||||
? NTL_EXCEPTION_CONTINUE_EXECUTION
|
||||
: NTL_EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
// Installs the native-exception adapter exactly once for one POSIX image.
|
||||
KSTATUS PosixSignalInitializeExceptionTranslation(POSIX_PROCESS_STATE* process) {
|
||||
if (!process) return NTLSTATUS_INVALID_PARAMETER;
|
||||
if (process->ExceptionHandlerHandle) return NTLSTATUS_SUCCESS;
|
||||
void* handle = NtlAddExceptionHandler(1, PosixSignalNativeException, (void*)0);
|
||||
if (!handle) return NTLSTATUS_LIMIT_REACHED;
|
||||
process->ExceptionHandlerHandle = handle;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Delivers one POSIX signal from the generic user-APC substrate.
|
||||
void PosixSignalApc(void* signalValue, void* routeValue, void* reserved) {
|
||||
(void)routeValue;
|
||||
(void)reserved;
|
||||
int signalNumber = (int)(uintptr_t)signalValue;
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
if (!thread || !NtlPosixSignalValid(signalNumber)) return;
|
||||
PosixSignalDeliver(thread, signalNumber);
|
||||
}
|
||||
|
||||
// Dispatches thread-pending signals made deliverable by a mask change.
|
||||
void PosixSignalDispatchLocalPending(POSIX_THREAD_STATE* thread) {
|
||||
if (!thread) return;
|
||||
for (;;) {
|
||||
uint64_t deliverable = thread->SignalPending &
|
||||
~(thread->SignalBlocked | thread->SignalHandlerBlocked) &
|
||||
NTL_POSIX_SIGNAL_VALID_MASK;
|
||||
deliverable |= thread->SignalPending & NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
|
||||
if (deliverable == 0) return;
|
||||
int signalNumber = NTL_POSIX_SIGNAL_HUP;
|
||||
while (signalNumber <= NTL_POSIX_SIGNAL_MAX &&
|
||||
(deliverable & PosixSignalBit(signalNumber)) == 0)
|
||||
++signalNumber;
|
||||
if (signalNumber > NTL_POSIX_SIGNAL_MAX) return;
|
||||
PosixSignalDeliver(thread, signalNumber);
|
||||
}
|
||||
}
|
||||
|
||||
// Initializes an empty signal set.
|
||||
__declspec(dllexport) int sigemptyset(sigset_t* set) {
|
||||
if (!set) { errno = EINVAL; return -1; }
|
||||
*set = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initializes a signal set containing every supported signal.
|
||||
__declspec(dllexport) int sigfillset(sigset_t* set) {
|
||||
if (!set) { errno = EINVAL; return -1; }
|
||||
*set = NTL_POSIX_SIGNAL_VALID_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Adds one signal to a signal set.
|
||||
__declspec(dllexport) int sigaddset(sigset_t* set, int signalNumber) {
|
||||
uint64_t bit = PosixSignalBit(signalNumber);
|
||||
if (!set || bit == 0) { errno = EINVAL; return -1; }
|
||||
*set |= bit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Removes one signal from a signal set.
|
||||
__declspec(dllexport) int sigdelset(sigset_t* set, int signalNumber) {
|
||||
uint64_t bit = PosixSignalBit(signalNumber);
|
||||
if (!set || bit == 0) { errno = EINVAL; return -1; }
|
||||
*set &= ~bit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Tests whether one signal belongs to a signal set.
|
||||
__declspec(dllexport) int sigismember(const sigset_t* set, int signalNumber) {
|
||||
uint64_t bit = PosixSignalBit(signalNumber);
|
||||
if (!set || bit == 0) { errno = EINVAL; return -1; }
|
||||
return (*set & bit) != 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
// Installs or queries one logical-process-wide POSIX signal action.
|
||||
__declspec(dllexport) int sigaction(int signalNumber,
|
||||
const struct sigaction* action,
|
||||
struct sigaction* oldAction) {
|
||||
if (!NtlPosixSignalValid(signalNumber)) { errno = EINVAL; return -1; }
|
||||
POSIX_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
|
||||
if (!process) { errno = EIO; return -1; }
|
||||
POSIX_SIGNAL_ACTION_STATE* state = &process->SignalActions[signalNumber];
|
||||
|
||||
if (oldAction) {
|
||||
oldAction->sa_handler = (sighandler_t)state->Handler;
|
||||
oldAction->sa_mask = state->Mask;
|
||||
oldAction->sa_flags = (int)state->Flags;
|
||||
}
|
||||
if (!action) return 0;
|
||||
if (action->sa_flags != 0 || (action->sa_mask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0 ||
|
||||
action->sa_handler == SIG_ERR ||
|
||||
(signalNumber == SIGKILL && action->sa_handler != SIG_DFL)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
POSIX_SIGNAL_ACTION_STATE previous = *state;
|
||||
state->Handler = (uintptr_t)action->sa_handler;
|
||||
state->Mask = action->sa_mask & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
|
||||
state->Flags = (uint32_t)action->sa_flags;
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Operation = NTL_POSIX_OP_SET_SIGNAL_DISPOSITION;
|
||||
request.SignalNumber = signalNumber;
|
||||
request.SignalDisposition = PosixSignalDisposition(state);
|
||||
NTL_POSIX_REPLY reply;
|
||||
KSTATUS status = PosixSignalCallServer(&request, &reply);
|
||||
if (!KSUCCESS(status)) {
|
||||
*state = previous;
|
||||
errno = PosixStatusToErrno(status);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Changes the calling thread's persistent POSIX signal mask.
|
||||
__declspec(dllexport) int sigprocmask(int how, const sigset_t* set, sigset_t* oldSet) {
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
if (!thread) { errno = EIO; return -1; }
|
||||
uint64_t previous = thread->SignalBlocked;
|
||||
if (oldSet) *oldSet = previous;
|
||||
if (!set) return 0;
|
||||
if ((*set & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0 ||
|
||||
(how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t next = previous;
|
||||
if (how == SIG_BLOCK) next |= *set;
|
||||
else if (how == SIG_UNBLOCK) next &= ~*set;
|
||||
else next = *set;
|
||||
next &= NTL_POSIX_SIGNAL_VALID_MASK & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
|
||||
thread->SignalBlocked = next;
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Operation = NTL_POSIX_OP_UPDATE_SIGNAL_MASK;
|
||||
request.SignalMask = next;
|
||||
NTL_POSIX_REPLY reply;
|
||||
KSTATUS status = PosixSignalCallServer(&request, &reply);
|
||||
if (!KSUCCESS(status)) {
|
||||
thread->SignalBlocked = previous;
|
||||
errno = PosixStatusToErrno(status);
|
||||
return -1;
|
||||
}
|
||||
PosixSignalDispatchLocalPending(thread);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns the union of calling-thread and process-pending signals.
|
||||
__declspec(dllexport) int sigpending(sigset_t* set) {
|
||||
if (!set) { errno = EINVAL; return -1; }
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
if (!thread) { errno = EIO; return -1; }
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Operation = NTL_POSIX_OP_GET_PENDING_SIGNALS;
|
||||
NTL_POSIX_REPLY reply;
|
||||
KSTATUS status = PosixSignalCallServer(&request, &reply);
|
||||
if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return -1; }
|
||||
*set = (thread->SignalPending | reply.PendingSignals) & NTL_POSIX_SIGNAL_VALID_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Atomically substitutes a temporary mask for the duration of one signal wait.
|
||||
__declspec(dllexport) int sigsuspend(const sigset_t* set) {
|
||||
if (!set || (*set & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
if (!thread) { errno = EIO; return -1; }
|
||||
|
||||
SYS_HANDLE event = SYS_HANDLE_INVALID;
|
||||
KSTATUS status = SysCreateEvent(0u, 0u, &event);
|
||||
if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return -1; }
|
||||
|
||||
SYS_HANDLE previousEvent = thread->SignalWaitEvent;
|
||||
uint64_t deliveryCount = thread->SignalDeliveryCount;
|
||||
thread->SignalWaitEvent = event;
|
||||
sigset_t previousMask = 0;
|
||||
if (sigprocmask(SIG_SETMASK, set, &previousMask) != 0) {
|
||||
thread->SignalWaitEvent = previousEvent;
|
||||
(void)SysCloseHandle(event);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (thread->SignalDeliveryCount != deliveryCount) {
|
||||
status = NTLSTATUS_SUCCESS;
|
||||
break;
|
||||
}
|
||||
status = SysWait(event, SYS_WAIT_FLAG_ALERTABLE);
|
||||
if (status == NTLSTATUS_SUCCESS && thread->SignalDeliveryCount != deliveryCount)
|
||||
break;
|
||||
if (status == NTLSTATUS_USER_APC || status == NTLSTATUS_KERNEL_APC ||
|
||||
status == NTLSTATUS_ALERTED)
|
||||
continue;
|
||||
if (!KSUCCESS(status)) break;
|
||||
}
|
||||
|
||||
thread->SignalWaitEvent = previousEvent;
|
||||
int restore = sigprocmask(SIG_SETMASK, &previousMask, (sigset_t*)0);
|
||||
(void)SysCloseHandle(event);
|
||||
if (restore != 0) return -1;
|
||||
if (!KSUCCESS(status)) {
|
||||
errno = PosixStatusToErrno(status);
|
||||
return -1;
|
||||
}
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Sends one signal through stable POSIX process or process-group identity.
|
||||
__declspec(dllexport) int kill(pid_t pid, int signalNumber) {
|
||||
if (signalNumber < 0 || signalNumber > NTL_POSIX_SIGNAL_MAX) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Operation = NTL_POSIX_OP_SEND_SIGNAL;
|
||||
request.SignalTargetPid = (int32_t)pid;
|
||||
request.SignalNumber = signalNumber;
|
||||
NTL_POSIX_REPLY reply;
|
||||
KSTATUS status = PosixSignalCallServer(&request, &reply);
|
||||
if (!KSUCCESS(status)) {
|
||||
if (status == NTLSTATUS_NOT_FOUND) errno = ESRCH;
|
||||
else if (status == NTLSTATUS_ACCESS_DENIED) errno = EPERM;
|
||||
else errno = PosixStatusToErrno(status);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Replaces the process alarm and returns the rounded-up previous remaining interval.
|
||||
__declspec(dllexport) unsigned int alarm(unsigned int seconds) {
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Operation = NTL_POSIX_OP_SET_ALARM;
|
||||
request.AlarmSeconds = seconds;
|
||||
NTL_POSIX_REPLY reply;
|
||||
KSTATUS status = PosixSignalCallServer(&request, &reply);
|
||||
if (!KSUCCESS(status)) {
|
||||
errno = PosixStatusToErrno(status);
|
||||
return 0;
|
||||
}
|
||||
return reply.AlarmRemainingSeconds;
|
||||
}
|
||||
|
||||
// Waits until one caught signal runs.
|
||||
__declspec(dllexport) int pause(void) {
|
||||
sigset_t mask = 0;
|
||||
if (sigprocmask(SIG_SETMASK, (const sigset_t*)0, &mask) != 0) return -1;
|
||||
return sigsuspend(&mask);
|
||||
}
|
||||
|
||||
// Sleeps on a native waitable timer and reports whole seconds left after a caught signal.
|
||||
__declspec(dllexport) unsigned int sleep(unsigned int seconds) {
|
||||
if (seconds == 0) {
|
||||
(void)SysYield();
|
||||
return 0;
|
||||
}
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
if (!thread) return seconds;
|
||||
|
||||
SYS_HANDLE timer = SYS_HANDLE_INVALID;
|
||||
SYS_HANDLE signalEvent = SYS_HANDLE_INVALID;
|
||||
KSTATUS status = SysCreateTimer(&timer);
|
||||
if (!KSUCCESS(status)) return seconds;
|
||||
status = SysCreateEvent(0u, 0u, &signalEvent);
|
||||
if (!KSUCCESS(status)) { (void)SysCloseHandle(timer); return seconds; }
|
||||
|
||||
uint64_t frequency = 0;
|
||||
status = SysGetTickFrequency(&frequency);
|
||||
if (!KSUCCESS(status) || frequency == 0) {
|
||||
(void)SysCloseHandle(signalEvent);
|
||||
(void)SysCloseHandle(timer);
|
||||
return seconds;
|
||||
}
|
||||
uint64_t start = SysGetTick();
|
||||
|
||||
SYS_SET_TIMER_REQUEST setTimer;
|
||||
memset(&setTimer, 0, sizeof(setTimer));
|
||||
setTimer.Size = sizeof(setTimer);
|
||||
setTimer.TimerHandle = timer;
|
||||
setTimer.Flags = SYS_TIMER_FLAG_NONE;
|
||||
setTimer.DueTimeMilliseconds = (uint64_t)seconds * 1000u;
|
||||
status = SysSetTimer(&setTimer);
|
||||
if (!KSUCCESS(status)) {
|
||||
(void)SysCloseHandle(signalEvent);
|
||||
(void)SysCloseHandle(timer);
|
||||
return seconds;
|
||||
}
|
||||
|
||||
SYS_HANDLE previousEvent = thread->SignalWaitEvent;
|
||||
uint64_t deliveryCount = thread->SignalDeliveryCount;
|
||||
thread->SignalWaitEvent = signalEvent;
|
||||
SYS_HANDLE waits[2] = { timer, signalEvent };
|
||||
uint32_t selected = UINT32_MAX;
|
||||
SYS_WAIT_MANY_REQUEST wait;
|
||||
memset(&wait, 0, sizeof(wait));
|
||||
wait.Size = sizeof(wait);
|
||||
wait.Flags = SYS_WAIT_FLAG_ALERTABLE;
|
||||
wait.Handles = waits;
|
||||
wait.Count = 2u;
|
||||
wait.SignaledIndexOut = &selected;
|
||||
wait.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
|
||||
for (;;) {
|
||||
selected = UINT32_MAX;
|
||||
status = SysWaitMany(&wait);
|
||||
if (status == NTLSTATUS_SUCCESS && selected == 0u) break;
|
||||
if (thread->SignalDeliveryCount != deliveryCount) break;
|
||||
if (status == NTLSTATUS_SUCCESS || status == NTLSTATUS_USER_APC ||
|
||||
status == NTLSTATUS_KERNEL_APC || status == NTLSTATUS_ALERTED)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
thread->SignalWaitEvent = previousEvent;
|
||||
|
||||
uint64_t elapsedTicks = SysGetTick() - start;
|
||||
uint64_t elapsedMs = SysTicksToMilliseconds(elapsedTicks, frequency);
|
||||
uint64_t totalMs = (uint64_t)seconds * 1000u;
|
||||
uint64_t remainingMs = elapsedMs < totalMs ? totalMs - elapsedMs : 0;
|
||||
unsigned int remaining = (unsigned int)((remainingMs + 999u) / 1000u);
|
||||
|
||||
(void)SysCancelTimer(timer);
|
||||
(void)SysCloseHandle(signalEvent);
|
||||
(void)SysCloseHandle(timer);
|
||||
if (status == NTLSTATUS_SUCCESS && selected == 0u) return 0;
|
||||
if (thread->SignalDeliveryCount != deliveryCount) return remaining;
|
||||
return remaining ? remaining : seconds;
|
||||
}
|
||||
|
||||
// Installs the historical one-argument signal disposition API.
|
||||
__declspec(dllexport) sighandler_t signal(int signalNumber, sighandler_t handler) {
|
||||
struct sigaction action;
|
||||
struct sigaction previous;
|
||||
action.sa_handler = handler;
|
||||
action.sa_mask = 0;
|
||||
action.sa_flags = 0;
|
||||
if (sigaction(signalNumber, &action, &previous) != 0) return SIG_ERR;
|
||||
return previous.sa_handler;
|
||||
}
|
||||
|
||||
// Raises one signal on the calling POSIX thread.
|
||||
__declspec(dllexport) int raise(int signalNumber) {
|
||||
if (!NtlPosixSignalValid(signalNumber)) { errno = EINVAL; return -1; }
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
if (!thread) { errno = EIO; return -1; }
|
||||
PosixSignalDeliver(thread, signalNumber);
|
||||
return 0;
|
||||
}
|
||||
@@ -35,6 +35,7 @@ static POSIX_PTHREAD_SLOT* PthreadSlotFind(uint32_t tid) {
|
||||
typedef struct {
|
||||
void* (*Fn)(void*);
|
||||
void* Arg;
|
||||
uint64_t SignalMask;
|
||||
} PTHREAD_TRAMP;
|
||||
|
||||
// Implements the pthread Trampoline operation.
|
||||
@@ -44,7 +45,7 @@ static void PthreadTrampoline(uintptr_t raw) {
|
||||
void* arg = tr->Arg;
|
||||
uint32_t tid = SysGetCurrentTid();
|
||||
|
||||
if (PosixRuntimeAttachThread() != NTLSTATUS_SUCCESS) {
|
||||
if (PosixRuntimeAttachThreadWithSignalMask(tr->SignalMask) != NTLSTATUS_SUCCESS) {
|
||||
free(tr);
|
||||
SysExitThread(ENOMEM);
|
||||
}
|
||||
@@ -76,6 +77,12 @@ __declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* att
|
||||
if (!tr) return ENOMEM;
|
||||
tr->Fn = fn;
|
||||
tr->Arg = arg;
|
||||
POSIX_THREAD_STATE* parentThread = PosixRuntimeThreadState();
|
||||
if (!parentThread) {
|
||||
free(tr);
|
||||
return EIO;
|
||||
}
|
||||
tr->SignalMask = parentThread->SignalBlocked;
|
||||
|
||||
uint32_t tid = 0;
|
||||
uint32_t handle = 0;
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
; x64 POSIX vfork continuation. The parent enters the resume/wait syscall without
|
||||
; a nested userspace return frame while the child owns the caller's stack.
|
||||
|
||||
INCLUDE syscall_nr.inc
|
||||
|
||||
VF_ReturnAddress EQU 0
|
||||
VF_ParentRsp EQU 8
|
||||
VF_Rbx EQU 16
|
||||
VF_Rbp EQU 24
|
||||
VF_Rsi EQU 32
|
||||
VF_Rdi EQU 40
|
||||
VF_R12 EQU 48
|
||||
VF_R13 EQU 56
|
||||
VF_R14 EQU 64
|
||||
VF_R15 EQU 72
|
||||
VF_Xmm6 EQU 80
|
||||
VF_Xmm7 EQU 96
|
||||
VF_Xmm8 EQU 112
|
||||
VF_Xmm9 EQU 128
|
||||
VF_Xmm10 EQU 144
|
||||
VF_Xmm11 EQU 160
|
||||
VF_Xmm12 EQU 176
|
||||
VF_Xmm13 EQU 192
|
||||
VF_Xmm14 EQU 208
|
||||
VF_Xmm15 EQU 224
|
||||
VF_Mxcsr EQU 240
|
||||
VF_X87Control EQU 244
|
||||
VF_ThreadHandle EQU 256
|
||||
VF_PreviousSuspendCount EQU 264
|
||||
VF_ContextSize EQU 280
|
||||
VF_LocalSize EQU 312
|
||||
|
||||
EXTERN PosixVforkPrepare:PROC
|
||||
EXTERN PosixVforkParentFinish:PROC
|
||||
|
||||
.CODE
|
||||
|
||||
PUBLIC vfork
|
||||
vfork PROC EXPORT
|
||||
sub rsp, VF_LocalSize
|
||||
lea r10, [rsp + 32]
|
||||
|
||||
mov rax, [rsp + VF_LocalSize]
|
||||
mov [r10 + VF_ReturnAddress], rax
|
||||
lea rax, [rsp + VF_LocalSize + 8]
|
||||
mov [r10 + VF_ParentRsp], rax
|
||||
mov [r10 + VF_Rbx], rbx
|
||||
mov [r10 + VF_Rbp], rbp
|
||||
mov [r10 + VF_Rsi], rsi
|
||||
mov [r10 + VF_Rdi], rdi
|
||||
mov [r10 + VF_R12], r12
|
||||
mov [r10 + VF_R13], r13
|
||||
mov [r10 + VF_R14], r14
|
||||
mov [r10 + VF_R15], r15
|
||||
movdqu xmmword ptr [r10 + VF_Xmm6], xmm6
|
||||
movdqu xmmword ptr [r10 + VF_Xmm7], xmm7
|
||||
movdqu xmmword ptr [r10 + VF_Xmm8], xmm8
|
||||
movdqu xmmword ptr [r10 + VF_Xmm9], xmm9
|
||||
movdqu xmmword ptr [r10 + VF_Xmm10], xmm10
|
||||
movdqu xmmword ptr [r10 + VF_Xmm11], xmm11
|
||||
movdqu xmmword ptr [r10 + VF_Xmm12], xmm12
|
||||
movdqu xmmword ptr [r10 + VF_Xmm13], xmm13
|
||||
movdqu xmmword ptr [r10 + VF_Xmm14], xmm14
|
||||
movdqu xmmword ptr [r10 + VF_Xmm15], xmm15
|
||||
stmxcsr dword ptr [r10 + VF_Mxcsr]
|
||||
fnstcw word ptr [r10 + VF_X87Control]
|
||||
|
||||
mov rcx, r10
|
||||
call PosixVforkPrepare
|
||||
test rax, rax
|
||||
jnz vfork_wait
|
||||
add rsp, VF_LocalSize
|
||||
mov eax, -1
|
||||
ret
|
||||
|
||||
vfork_wait:
|
||||
; R12 deliberately holds the heap context across the kernel wait. The child
|
||||
; restores the caller's original R12 in its independent native context.
|
||||
mov r12, rax
|
||||
mov ecx, dword ptr [r12 + VF_ThreadHandle]
|
||||
lea rdx, [r12 + VF_PreviousSuspendCount]
|
||||
mov eax, SYS_RESUME_THREAD_AND_WAIT
|
||||
int 40h
|
||||
mov r9d, eax
|
||||
|
||||
; The child may have overwritten every byte of the old vfork/helper frames.
|
||||
; Rebuild only a synthetic return frame from the heap continuation.
|
||||
mov r10, r12
|
||||
mov r11, [r10 + VF_ParentRsp]
|
||||
mov rax, [r10 + VF_ReturnAddress]
|
||||
mov rsp, r11
|
||||
sub rsp, 8
|
||||
mov [rsp], rax
|
||||
sub rsp, 40
|
||||
|
||||
ldmxcsr dword ptr [r10 + VF_Mxcsr]
|
||||
fldcw word ptr [r10 + VF_X87Control]
|
||||
movdqu xmm6, xmmword ptr [r10 + VF_Xmm6]
|
||||
movdqu xmm7, xmmword ptr [r10 + VF_Xmm7]
|
||||
movdqu xmm8, xmmword ptr [r10 + VF_Xmm8]
|
||||
movdqu xmm9, xmmword ptr [r10 + VF_Xmm9]
|
||||
movdqu xmm10, xmmword ptr [r10 + VF_Xmm10]
|
||||
movdqu xmm11, xmmword ptr [r10 + VF_Xmm11]
|
||||
movdqu xmm12, xmmword ptr [r10 + VF_Xmm12]
|
||||
movdqu xmm13, xmmword ptr [r10 + VF_Xmm13]
|
||||
movdqu xmm14, xmmword ptr [r10 + VF_Xmm14]
|
||||
movdqu xmm15, xmmword ptr [r10 + VF_Xmm15]
|
||||
mov rbx, [r10 + VF_Rbx]
|
||||
mov rbp, [r10 + VF_Rbp]
|
||||
mov rsi, [r10 + VF_Rsi]
|
||||
mov rdi, [r10 + VF_Rdi]
|
||||
mov r12, [r10 + VF_R12]
|
||||
mov r13, [r10 + VF_R13]
|
||||
mov r14, [r10 + VF_R14]
|
||||
mov r15, [r10 + VF_R15]
|
||||
|
||||
mov rcx, r10
|
||||
mov edx, r9d
|
||||
call PosixVforkParentFinish
|
||||
add rsp, 40
|
||||
ret
|
||||
vfork ENDP
|
||||
|
||||
PUBLIC PosixVforkEnterChild
|
||||
PosixVforkEnterChild PROC
|
||||
mov r10, rcx
|
||||
mov r11, [r10 + VF_ParentRsp]
|
||||
mov rax, [r10 + VF_ReturnAddress]
|
||||
mov rsp, r11
|
||||
sub rsp, 8
|
||||
mov [rsp], rax
|
||||
|
||||
ldmxcsr dword ptr [r10 + VF_Mxcsr]
|
||||
fldcw word ptr [r10 + VF_X87Control]
|
||||
movdqu xmm6, xmmword ptr [r10 + VF_Xmm6]
|
||||
movdqu xmm7, xmmword ptr [r10 + VF_Xmm7]
|
||||
movdqu xmm8, xmmword ptr [r10 + VF_Xmm8]
|
||||
movdqu xmm9, xmmword ptr [r10 + VF_Xmm9]
|
||||
movdqu xmm10, xmmword ptr [r10 + VF_Xmm10]
|
||||
movdqu xmm11, xmmword ptr [r10 + VF_Xmm11]
|
||||
movdqu xmm12, xmmword ptr [r10 + VF_Xmm12]
|
||||
movdqu xmm13, xmmword ptr [r10 + VF_Xmm13]
|
||||
movdqu xmm14, xmmword ptr [r10 + VF_Xmm14]
|
||||
movdqu xmm15, xmmword ptr [r10 + VF_Xmm15]
|
||||
mov rbx, [r10 + VF_Rbx]
|
||||
mov rbp, [r10 + VF_Rbp]
|
||||
mov rsi, [r10 + VF_Rsi]
|
||||
mov rdi, [r10 + VF_Rdi]
|
||||
mov r12, [r10 + VF_R12]
|
||||
mov r13, [r10 + VF_R13]
|
||||
mov r14, [r10 + VF_R14]
|
||||
mov r15, [r10 + VF_R15]
|
||||
xor eax, eax
|
||||
ret
|
||||
PosixVforkEnterChild ENDP
|
||||
|
||||
END
|
||||
@@ -0,0 +1,331 @@
|
||||
// POSIX borrowed-child substrate used by the later public vfork continuation layer.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/status.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix_internal.h"
|
||||
#include "posix_fd.h"
|
||||
|
||||
// Allocates one borrowed logical child and pins its inherited descriptor snapshot.
|
||||
KSTATUS PosixVforkBeginBorrowedChild(POSIX_VFORK_CHILD_STATE** childOut) {
|
||||
if (!childOut) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*childOut = (POSIX_VFORK_CHILD_STATE*)0;
|
||||
|
||||
POSIX_THREAD_STATE* parentThread = PosixRuntimeThreadState();
|
||||
POSIX_PROCESS_STATE* nativeProcess = PosixRuntimeProcessState();
|
||||
if (!parentThread || !nativeProcess ||
|
||||
parentThread->LogicalProcess != &nativeProcess->DefaultIdentity)
|
||||
return NTLSTATUS_NOT_SUPPORTED;
|
||||
|
||||
POSIX_VFORK_CHILD_STATE* child =
|
||||
(POSIX_VFORK_CHILD_STATE*)calloc(1u, sizeof(*child));
|
||||
if (!child) return NTLSTATUS_NO_MEMORY;
|
||||
child->LogicalProcess.FdLock = SYS_HANDLE_INVALID;
|
||||
child->InitialSignalMask = parentThread->SignalBlocked &
|
||||
NTL_POSIX_SIGNAL_VALID_MASK &
|
||||
~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
|
||||
memcpy(child->LogicalProcess.SignalActions,
|
||||
parentThread->LogicalProcess->SignalActions,
|
||||
sizeof(child->LogicalProcess.SignalActions));
|
||||
|
||||
KSTATUS status = PosixFdCreateInheritanceSnapshot(0u, &child->FdInheritanceToken);
|
||||
if (!KSUCCESS(status)) {
|
||||
free(child);
|
||||
return status;
|
||||
}
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_VFORK_BEGIN;
|
||||
request.FdInheritanceToken = child->FdInheritanceToken;
|
||||
|
||||
NTL_POSIX_REPLY reply;
|
||||
status = PosixRuntimeCallServer(&request, &reply);
|
||||
if (!KSUCCESS(status) || !KSUCCESS(reply.Status)) {
|
||||
KSTATUS failure = KSUCCESS(status) ? reply.Status : status;
|
||||
PosixFdCancelInheritanceSnapshot(child->FdInheritanceToken);
|
||||
free(child);
|
||||
return failure;
|
||||
}
|
||||
if (reply.SpawnedPosixPid == 0 || reply.VforkToken == 0 ||
|
||||
reply.SpawnedPosixPid > 0x7FFFFFFFu) {
|
||||
if (reply.SpawnedPosixPid != 0 && reply.VforkToken != 0) {
|
||||
child->LogicalProcess.PosixPid = reply.SpawnedPosixPid;
|
||||
child->LogicalProcess.Generation = 1u;
|
||||
child->ServerToken = reply.VforkToken;
|
||||
if (KSUCCESS(PosixVforkCancelBorrowedChild(child)))
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
free(child);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
child->LogicalProcess.PosixPid = reply.SpawnedPosixPid;
|
||||
child->LogicalProcess.Generation = 1u;
|
||||
child->ServerToken = reply.VforkToken;
|
||||
*childOut = child;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Attaches the calling native thread to one prepared borrowed logical child.
|
||||
KSTATUS PosixVforkAttachBorrowedChild(POSIX_VFORK_CHILD_STATE* child) {
|
||||
if (!child || child->LogicalProcess.PosixPid == 0 || child->ServerToken == 0 ||
|
||||
child->FdInheritanceToken == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
SYS_TEB* teb = SysGetTeb();
|
||||
POSIX_PROCESS_STATE* nativeProcess = PosixRuntimeProcessState();
|
||||
if (!teb || teb->Magic != SYS_TEB_MAGIC || !nativeProcess || teb->SubsystemData)
|
||||
return NTLSTATUS_ACCESS_DENIED;
|
||||
|
||||
POSIX_THREAD_STATE* thread = (POSIX_THREAD_STATE*)calloc(1u, sizeof(*thread));
|
||||
if (!thread) return NTLSTATUS_NO_MEMORY;
|
||||
thread->Magic = POSIX_THREAD_STATE_MAGIC;
|
||||
thread->NativeProcess = nativeProcess;
|
||||
thread->LogicalProcess = &child->LogicalProcess;
|
||||
thread->BorrowedChild = child;
|
||||
thread->NativeThreadId = SysGetCurrentTid();
|
||||
thread->SignalBlocked = child->InitialSignalMask;
|
||||
if (thread->NativeThreadId == 0) {
|
||||
free(thread);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
KSTATUS status = SysSetUserApcDispatcher((void*)SysUserApcDispatcher);
|
||||
if (!KSUCCESS(status)) {
|
||||
free(thread);
|
||||
return status;
|
||||
}
|
||||
|
||||
SYS_HANDLE selfThread = SYS_HANDLE_INVALID;
|
||||
status = SysOpenCurrentThread(SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_RIGHT_DERIVE |
|
||||
SYS_RIGHT_TRANSFER | SYS_THREAD_RIGHT_NOTIFY, &selfThread);
|
||||
if (!KSUCCESS(status)) {
|
||||
free(thread);
|
||||
return status;
|
||||
}
|
||||
|
||||
SYS_PORT_HANDLE_TRANSFER transfer;
|
||||
memset(&transfer, 0, sizeof(transfer));
|
||||
transfer.Handle = selfThread;
|
||||
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT | SYS_RIGHT_DERIVE |
|
||||
SYS_THREAD_RIGHT_NOTIFY;
|
||||
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_VFORK_ATTACH;
|
||||
request.TargetPosixPid = child->LogicalProcess.PosixPid;
|
||||
request.VforkToken = child->ServerToken;
|
||||
request.SignalMask = thread->SignalBlocked;
|
||||
request.SignalApcRoutine = (uint64_t)(uintptr_t)PosixSignalApc;
|
||||
|
||||
NTL_POSIX_REPLY reply;
|
||||
status = PosixRuntimeCallServerWithHandles(&request, &transfer, 1u, &reply);
|
||||
(void)SysCloseHandle(selfThread);
|
||||
if (!KSUCCESS(status) || !KSUCCESS(reply.Status)) {
|
||||
KSTATUS failure = KSUCCESS(status) ? reply.Status : status;
|
||||
thread->Magic = 0;
|
||||
free(thread);
|
||||
return failure;
|
||||
}
|
||||
if (reply.PosixPid != child->LogicalProcess.PosixPid ||
|
||||
reply.NativeGeneration != child->LogicalProcess.Generation ||
|
||||
reply.ProcessState != NTL_POSIX_PROCESS_STATE_VFORK_BORROWED ||
|
||||
reply.VforkToken != child->ServerToken) {
|
||||
thread->Magic = 0;
|
||||
free(thread);
|
||||
return NTLSTATUS_FAILURE;
|
||||
}
|
||||
|
||||
thread->ServerRegistered = 1u;
|
||||
teb->SubsystemData = thread;
|
||||
status = PosixFdInitializeLogicalProcess(&child->LogicalProcess,
|
||||
child->FdInheritanceToken);
|
||||
if (!KSUCCESS(status)) {
|
||||
teb->SubsystemData = (void*)0;
|
||||
thread->ServerRegistered = 0;
|
||||
thread->BorrowedChild = (POSIX_VFORK_CHILD_STATE*)0;
|
||||
thread->Magic = 0;
|
||||
free(thread);
|
||||
return status;
|
||||
}
|
||||
child->FdInheritanceToken = 0;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Tears down the calling borrowed thread's local wrappers before its native thread exits.
|
||||
void PosixVforkDetachBorrowedChildLocal(POSIX_VFORK_CHILD_STATE* child, bool releaseServerHolders) {
|
||||
if (!child) return;
|
||||
SYS_TEB* teb = SysGetTeb();
|
||||
if (!teb || teb->Magic != SYS_TEB_MAGIC || !teb->SubsystemData) return;
|
||||
POSIX_THREAD_STATE* thread = (POSIX_THREAD_STATE*)teb->SubsystemData;
|
||||
if (thread->Magic != POSIX_THREAD_STATE_MAGIC ||
|
||||
thread->LogicalProcess != &child->LogicalProcess)
|
||||
return;
|
||||
|
||||
if (releaseServerHolders) PosixFdDestroyLogicalProcess(&child->LogicalProcess);
|
||||
else PosixFdDestroyLogicalProcessLocal(&child->LogicalProcess);
|
||||
teb->SubsystemData = (void*)0;
|
||||
thread->ServerRegistered = 0;
|
||||
thread->BorrowedChild = (POSIX_VFORK_CHILD_STATE*)0;
|
||||
thread->Magic = 0;
|
||||
free(thread);
|
||||
}
|
||||
|
||||
// Cancels one borrowed child after setup failure or after its native thread has exited.
|
||||
KSTATUS PosixVforkCancelBorrowedChild(POSIX_VFORK_CHILD_STATE* child) {
|
||||
if (!child || child->LogicalProcess.PosixPid == 0 || child->ServerToken == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_VFORK_CANCEL;
|
||||
request.TargetPosixPid = child->LogicalProcess.PosixPid;
|
||||
request.VforkToken = child->ServerToken;
|
||||
|
||||
NTL_POSIX_REPLY reply;
|
||||
KSTATUS status = PosixRuntimeCallServer(&request, &reply);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
if (!KSUCCESS(reply.Status)) return reply.Status;
|
||||
free(child);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns the borrowed-child state attached to the calling thread, if any.
|
||||
POSIX_VFORK_CHILD_STATE* PosixVforkCurrentBorrowedChild(void) {
|
||||
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
|
||||
return thread ? thread->BorrowedChild : (POSIX_VFORK_CHILD_STATE*)0;
|
||||
}
|
||||
|
||||
// Records a genuine POSIX exit before the borrowed native thread self-terminates.
|
||||
KSTATUS PosixVforkRecordExit(POSIX_VFORK_CHILD_STATE* child, int statusCode) {
|
||||
if (!child || child->LogicalProcess.PosixPid == 0 || child->ServerToken == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
NTL_POSIX_REQUEST request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.Size = sizeof(request);
|
||||
request.Version = NTL_POSIX_PROTOCOL_VERSION;
|
||||
request.Operation = NTL_POSIX_OP_VFORK_EXIT;
|
||||
request.TargetPosixPid = child->LogicalProcess.PosixPid;
|
||||
request.VforkToken = child->ServerToken;
|
||||
request.VforkExitCode = statusCode;
|
||||
NTL_POSIX_REPLY reply;
|
||||
KSTATUS status = PosixRuntimeCallServer(&request, &reply);
|
||||
if (!KSUCCESS(status)) return status;
|
||||
return reply.Status;
|
||||
}
|
||||
|
||||
#include "posix_vfork_asm.h"
|
||||
|
||||
_Static_assert(offsetof(POSIX_VFORK_CONTEXT, ReturnAddress) == 0, "vfork return offset");
|
||||
_Static_assert(offsetof(POSIX_VFORK_CONTEXT, ParentRsp) == 8, "vfork rsp offset");
|
||||
_Static_assert(offsetof(POSIX_VFORK_CONTEXT, Xmm) == 80, "vfork xmm offset");
|
||||
_Static_assert(offsetof(POSIX_VFORK_CONTEXT, Mxcsr) == 240, "vfork mxcsr offset");
|
||||
_Static_assert(offsetof(POSIX_VFORK_CONTEXT, Child) == 248, "vfork child offset");
|
||||
_Static_assert(offsetof(POSIX_VFORK_CONTEXT, ThreadHandle) == 256, "vfork handle offset");
|
||||
_Static_assert(offsetof(POSIX_VFORK_CONTEXT, PreviousSuspendCount) == 264, "vfork suspend offset");
|
||||
_Static_assert(offsetof(POSIX_VFORK_CONTEXT, ChildAttached) == 268, "vfork attached offset");
|
||||
_Static_assert(offsetof(POSIX_VFORK_CONTEXT, ChildAttachStatus) == 272, "vfork status offset");
|
||||
_Static_assert(sizeof(POSIX_VFORK_CONTEXT) == 280, "vfork context size");
|
||||
|
||||
// Allocates the persistent continuation and creates its borrowed native thread suspended.
|
||||
POSIX_VFORK_CONTEXT* PosixVforkPrepare(const POSIX_VFORK_CONTEXT* captured) {
|
||||
if (!captured) {
|
||||
errno = EINVAL;
|
||||
return (POSIX_VFORK_CONTEXT*)0;
|
||||
}
|
||||
POSIX_VFORK_CONTEXT* context = (POSIX_VFORK_CONTEXT*)calloc(1u, sizeof(*context));
|
||||
if (!context) {
|
||||
errno = ENOMEM;
|
||||
return (POSIX_VFORK_CONTEXT*)0;
|
||||
}
|
||||
memcpy(context, captured, offsetof(POSIX_VFORK_CONTEXT, Child));
|
||||
|
||||
KSTATUS status = PosixVforkBeginBorrowedChild(&context->Child);
|
||||
if (!KSUCCESS(status)) {
|
||||
free(context);
|
||||
errno = PosixStatusToErrno(status);
|
||||
return (POSIX_VFORK_CONTEXT*)0;
|
||||
}
|
||||
context->ChildPid = context->Child->LogicalProcess.PosixPid;
|
||||
context->ThreadHandle = SYS_HANDLE_INVALID;
|
||||
context->ChildAttachStatus = NTLSTATUS_FAILURE;
|
||||
|
||||
SYS_CREATE_THREAD_REQUEST create;
|
||||
memset(&create, 0, sizeof(create));
|
||||
create.Size = sizeof(create);
|
||||
create.Entry = (uintptr_t)PosixVforkChildBootstrap;
|
||||
create.Argument = (uintptr_t)context;
|
||||
create.Priority = 1u;
|
||||
create.Flags = SYS_CREATE_THREAD_FLAG_SUSPENDED;
|
||||
create.ThreadHandleOut = &context->ThreadHandle;
|
||||
status = SysCreateThread(&create);
|
||||
if (!KSUCCESS(status)) {
|
||||
(void)PosixVforkCancelBorrowedChild(context->Child);
|
||||
context->Child = (POSIX_VFORK_CHILD_STATE*)0;
|
||||
free(context);
|
||||
errno = PosixStatusToErrno(status);
|
||||
return (POSIX_VFORK_CONTEXT*)0;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
// Attaches on the private initial stack before entering the borrowed parent continuation.
|
||||
void PosixVforkChildBootstrap(uintptr_t argument) {
|
||||
POSIX_VFORK_CONTEXT* context = (POSIX_VFORK_CONTEXT*)argument;
|
||||
if (!context || !context->Child) {
|
||||
SysExitThread((int32_t)NTLSTATUS_INVALID_PARAMETER);
|
||||
return;
|
||||
}
|
||||
KSTATUS status = PosixVforkAttachBorrowedChild(context->Child);
|
||||
context->ChildAttachStatus = status;
|
||||
if (!KSUCCESS(status)) {
|
||||
SysExitThread((int32_t)status);
|
||||
return;
|
||||
}
|
||||
_ReadWriteBarrier();
|
||||
context->ChildAttached = 1u;
|
||||
_ReadWriteBarrier();
|
||||
PosixVforkEnterChild(context);
|
||||
SysExitThread((int32_t)NTLSTATUS_FAILURE);
|
||||
}
|
||||
|
||||
// Closes parent-side construction state after the atomic resume/wait has made the stack safe.
|
||||
int PosixVforkParentFinish(POSIX_VFORK_CONTEXT* context, KSTATUS waitStatus) {
|
||||
if (!context) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
if (context->ThreadHandle != SYS_HANDLE_INVALID) {
|
||||
(void)SysCloseHandle(context->ThreadHandle);
|
||||
context->ThreadHandle = SYS_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
int result = -1;
|
||||
if (!KSUCCESS(waitStatus)) {
|
||||
errno = PosixStatusToErrno(waitStatus);
|
||||
} else if (context->PreviousSuspendCount != 1u) {
|
||||
errno = EIO;
|
||||
} else if (!context->ChildAttached) {
|
||||
KSTATUS attachStatus = context->ChildAttachStatus;
|
||||
if (context->Child) {
|
||||
KSTATUS cancelStatus = PosixVforkCancelBorrowedChild(context->Child);
|
||||
if (KSUCCESS(cancelStatus)) context->Child = (POSIX_VFORK_CHILD_STATE*)0;
|
||||
}
|
||||
errno = PosixStatusToErrno(KSUCCESS(attachStatus) ? NTLSTATUS_FAILURE : attachStatus);
|
||||
} else {
|
||||
result = (int)context->ChildPid;
|
||||
}
|
||||
|
||||
if (context->Child) free(context->Child);
|
||||
free(context);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Shared layout for the x64 vfork continuation context.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix_internal.h"
|
||||
|
||||
typedef struct POSIX_VFORK_CONTEXT {
|
||||
uint64_t ReturnAddress;
|
||||
uint64_t ParentRsp;
|
||||
uint64_t Rbx;
|
||||
uint64_t Rbp;
|
||||
uint64_t Rsi;
|
||||
uint64_t Rdi;
|
||||
uint64_t R12;
|
||||
uint64_t R13;
|
||||
uint64_t R14;
|
||||
uint64_t R15;
|
||||
uint8_t Xmm[10][16];
|
||||
uint32_t Mxcsr;
|
||||
uint16_t X87Control;
|
||||
uint16_t Reserved;
|
||||
POSIX_VFORK_CHILD_STATE* Child;
|
||||
SYS_HANDLE ThreadHandle;
|
||||
uint32_t ChildPid;
|
||||
uint32_t PreviousSuspendCount;
|
||||
volatile uint32_t ChildAttached;
|
||||
volatile KSTATUS ChildAttachStatus;
|
||||
uint32_t Reserved2;
|
||||
} POSIX_VFORK_CONTEXT;
|
||||
|
||||
POSIX_VFORK_CONTEXT* PosixVforkPrepare(const POSIX_VFORK_CONTEXT* captured);
|
||||
void PosixVforkChildBootstrap(uintptr_t argument);
|
||||
int PosixVforkParentFinish(POSIX_VFORK_CONTEXT* context, KSTATUS waitStatus);
|
||||
void PosixVforkEnterChild(POSIX_VFORK_CONTEXT* context);
|
||||
Reference in new issue
Block a user