Files
nt-lite/code/user/personalities/posix/posix_proc.c
T

904 lines
36 KiB
C

// 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"
#include "posix_path.h"
// Returns the native PEB retained by the process-local POSIX runtime state.
static SYS_PEB* PosixPeb(void) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
return process ? process->Peb : (SYS_PEB*)0;
}
// 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;
}
// 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)
return (pid_t)-1;
return (pid_t)thread->LogicalProcess->PosixPid;
}
#define POSIX_SPAWN_ACTIONS_MAGIC 0x53464131u
#define POSIX_SPAWN_ATTRIBUTES_MAGIC 0x53415431u
// 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;
}
// 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);
}
// 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)
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.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) {
_exit(status);
}
// Initializes one ordinary shared-attribute request.
static void PosixProcessRequest(NTL_POSIX_REQUEST* request, uint32_t operation) {
memset(request, 0, sizeof(*request));
request->Size = sizeof(*request);
request->Version = NTL_POSIX_PROTOCOL_VERSION;
request->Operation = operation;
request->Flags = NTL_POSIX_REQUEST_FLAG_NONE;
}
// Executes one shared-attribute request and converts transport/server failure to errno.
static int PosixProcessCall(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* reply) {
KSTATUS status = PosixRuntimeCallServer(request, reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return -1;
}
if (!KSUCCESS(reply->Status)) {
errno = PosixStatusToErrno(reply->Status);
return -1;
}
return 0;
}
// Executes one operation whose permission failures are POSIX EPERM rather than EACCES.
static int PosixProcessCallPermission(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* reply) {
KSTATUS status = PosixRuntimeCallServer(request, reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return -1;
}
if (!KSUCCESS(reply->Status)) {
errno = reply->Status == NTLSTATUS_ACCESS_DENIED ? EPERM : PosixStatusToErrno(reply->Status);
return -1;
}
return 0;
}
// Returns the real POSIX user id owned by posixsrv.
__declspec(dllexport) uid_t getuid(void) {
NTL_POSIX_REPLY reply;
if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (uid_t)-1;
return (uid_t)reply.UserId;
}
// Returns the effective POSIX user id owned by posixsrv.
__declspec(dllexport) uid_t geteuid(void) {
NTL_POSIX_REPLY reply;
if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (uid_t)-1;
return (uid_t)reply.EffectiveUserId;
}
// Returns the real POSIX group id owned by posixsrv.
__declspec(dllexport) gid_t getgid(void) {
NTL_POSIX_REPLY reply;
if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (gid_t)-1;
return (gid_t)reply.GroupId;
}
// Returns the effective POSIX group id owned by posixsrv.
__declspec(dllexport) gid_t getegid(void) {
NTL_POSIX_REPLY reply;
if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (gid_t)-1;
return (gid_t)reply.EffectiveGroupId;
}
// Changes synthetic user credentials; uid zero is the only credential-changing authority.
__declspec(dllexport) int setuid(uid_t uid) {
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_SET_UID);
request.UserId = (uint32_t)uid;
return PosixProcessCallPermission(&request, &reply);
}
// Changes synthetic group credentials; effective uid zero is the authority.
__declspec(dllexport) int setgid(gid_t gid) {
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_SET_GID);
request.GroupId = (uint32_t)gid;
return PosixProcessCallPermission(&request, &reply);
}
// Returns the supplementary group list attached to the current POSIX process.
__declspec(dllexport) int getgroups(int size, gid_t list[]) {
if (size < 0 || (size != 0 && !list)) {
errno = EINVAL;
return -1;
}
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeGetProcessAttributes(&reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return -1;
}
if (reply.SupplementaryGroupCount > NTL_POSIX_SUPPLEMENTARY_GROUP_MAX) {
errno = EIO;
return -1;
}
if (size == 0) return (int)reply.SupplementaryGroupCount;
if ((uint32_t)size < reply.SupplementaryGroupCount) {
errno = EINVAL;
return -1;
}
for (uint32_t i = 0; i < reply.SupplementaryGroupCount; ++i)
list[i] = (gid_t)reply.SupplementaryGroups[i];
return (int)reply.SupplementaryGroupCount;
}
// Returns the login identity fixed when the POSIX process record is created.
__declspec(dllexport) char* getlogin(void) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread) return (char*)0;
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeGetProcessAttributes(&reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return (char*)0;
}
if (reply.NameLength == 0 || reply.NameLength > NTL_POSIX_IDENTITY_NAME_MAX ||
reply.Name[reply.NameLength] != '\0') {
errno = ENOENT;
return (char*)0;
}
memcpy(thread->LoginName, reply.Name, reply.NameLength + 1u);
return thread->LoginName;
}
// Returns the current POSIX process-group id.
__declspec(dllexport) pid_t getpgrp(void) {
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeGetProcessAttributes(&reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return (pid_t)-1;
}
return (pid_t)reply.ProcessGroupId;
}
// Changes process-group membership under posixsrv's parent/session invariants.
__declspec(dllexport) int setpgid(pid_t pid, pid_t pgid) {
if (pid < 0 || pgid < 0) {
errno = EINVAL;
return -1;
}
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_SET_PROCESS_GROUP);
request.TargetPosixPid = (uint32_t)pid;
request.ProcessGroupId = (uint32_t)pgid;
return PosixProcessCallPermission(&request, &reply);
}
// Creates a new POSIX session and process group when the caller is not a group leader.
__declspec(dllexport) pid_t setsid(void) {
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_CREATE_SESSION);
if (PosixProcessCallPermission(&request, &reply) != 0) return (pid_t)-1;
return (pid_t)reply.SessionId;
}
// Performs one passwd lookup and builds a thread-local POSIX result view.
static struct passwd* PosixLookupUser(uint32_t lookupKind, uid_t uid, const char* name) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread) return (struct passwd*)0;
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_LOOKUP_USER);
request.LookupKind = lookupKind;
request.UserId = (uint32_t)uid;
if (lookupKind == NTL_POSIX_LOOKUP_BY_NAME) {
if (!name) { errno = EINVAL; return (struct passwd*)0; }
size_t length = strlen(name);
if (length == 0 || length > NTL_POSIX_IDENTITY_NAME_MAX) {
errno = ENOENT;
return (struct passwd*)0;
}
memcpy(request.Name, name, length + 1u);
request.NameLength = (uint32_t)length;
}
if (PosixProcessCall(&request, &reply) != 0) return (struct passwd*)0;
if (reply.NameLength == 0 || reply.NameLength > NTL_POSIX_IDENTITY_NAME_MAX ||
reply.HomeLength > NTL_POSIX_IDENTITY_HOME_MAX ||
reply.ShellLength > NTL_POSIX_IDENTITY_SHELL_MAX ||
reply.Name[reply.NameLength] != '\0' || reply.Home[reply.HomeLength] != '\0' ||
reply.Shell[reply.ShellLength] != '\0') {
errno = EIO;
return (struct passwd*)0;
}
memcpy(thread->PasswordName, reply.Name, reply.NameLength + 1u);
memcpy(thread->PasswordHome, reply.Home, reply.HomeLength + 1u);
memcpy(thread->PasswordShell, reply.Shell, reply.ShellLength + 1u);
thread->PasswordResult.pw_name = thread->PasswordName;
thread->PasswordResult.pw_uid = (uid_t)reply.UserId;
thread->PasswordResult.pw_gid = (gid_t)reply.GroupId;
thread->PasswordResult.pw_dir = thread->PasswordHome;
thread->PasswordResult.pw_shell = thread->PasswordShell;
return &thread->PasswordResult;
}
// Looks up one synthesized passwd entry by name.
__declspec(dllexport) struct passwd* getpwnam(const char* name) {
return PosixLookupUser(NTL_POSIX_LOOKUP_BY_NAME, 0, name);
}
// Looks up one synthesized passwd entry by uid.
__declspec(dllexport) struct passwd* getpwuid(uid_t uid) {
return PosixLookupUser(NTL_POSIX_LOOKUP_BY_ID, uid, (const char*)0);
}
// Performs one group lookup and builds a thread-local POSIX result view.
static struct group* PosixLookupGroup(uint32_t lookupKind, gid_t gid, const char* name) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread) return (struct group*)0;
NTL_POSIX_REQUEST request;
NTL_POSIX_REPLY reply;
PosixProcessRequest(&request, NTL_POSIX_OP_LOOKUP_GROUP);
request.LookupKind = lookupKind;
request.GroupId = (uint32_t)gid;
if (lookupKind == NTL_POSIX_LOOKUP_BY_NAME) {
if (!name) { errno = EINVAL; return (struct group*)0; }
size_t length = strlen(name);
if (length == 0 || length > NTL_POSIX_IDENTITY_NAME_MAX) {
errno = ENOENT;
return (struct group*)0;
}
memcpy(request.Name, name, length + 1u);
request.NameLength = (uint32_t)length;
}
if (PosixProcessCall(&request, &reply) != 0) return (struct group*)0;
if (reply.NameLength == 0 || reply.NameLength > NTL_POSIX_IDENTITY_NAME_MAX ||
reply.Name[reply.NameLength] != '\0') {
errno = EIO;
return (struct group*)0;
}
memcpy(thread->GroupName, reply.Name, reply.NameLength + 1u);
thread->GroupMembers[0] = (char*)0;
thread->GroupResult.gr_name = thread->GroupName;
thread->GroupResult.gr_gid = (gid_t)reply.GroupId;
thread->GroupResult.gr_mem = thread->GroupMembers;
return &thread->GroupResult;
}
// Looks up one synthesized group entry by name.
__declspec(dllexport) struct group* getgrnam(const char* name) {
return PosixLookupGroup(NTL_POSIX_LOOKUP_BY_NAME, 0, name);
}
// Looks up one synthesized group entry by gid.
__declspec(dllexport) struct group* getgrgid(gid_t gid) {
return PosixLookupGroup(NTL_POSIX_LOOKUP_BY_ID, gid, (const char*)0);
}