904 lines
36 KiB
C
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);
|
|
}
|