457 lines
17 KiB
C
457 lines
17 KiB
C
// POSIX compatibility support for posix proc.
|
|
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <ntlite/syscall.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;
|
|
}
|
|
|
|
// Implements the posix get command line operation.
|
|
__declspec(dllexport) const char* posix_get_command_line(void) {
|
|
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.
|
|
|
|
__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;
|
|
}
|
|
|
|
__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;
|
|
|
|
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;
|
|
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;
|
|
}
|
|
|
|
// Implements the exit operation.
|
|
__declspec(dllexport) void exit(int status) {
|
|
SysExitProcess(status);
|
|
while (true) {}
|
|
}
|
|
|
|
// 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);
|
|
}
|