Two ramdisk fixes exposed by shipping the C++ header tree: Paths longer than 100 characters (libstdc++'s pb_ds detail headers) are split by USTAR across the name field and the 155-byte prefix field at offset 345. The parser only read the name field, so the tails of 7 deep header paths appeared as bogus root-level entries while the real paths were missing. The parser now joins prefix + '/' + name, and MaxNameLen grows to 260 to hold the full combined path. SYS_CHDIR validated non-root targets by opening them as files, which only ever worked because directories used to be openable. It now uses the ReadDir probe, which fails for nonexistent paths and regular files on all backends (ext2/fat32 already validate the inode type). Co-Authored-By: Claude Fable 5 <[email protected]>
212 lines
7.0 KiB
C++
212 lines
7.0 KiB
C++
/*
|
|
* Process.hpp
|
|
* SYS_EXIT, SYS_YIELD, SYS_SLEEP_MS, SYS_GETPID,
|
|
* SYS_WAITPID, SYS_SPAWN, SYS_GETARGS, SYS_PROCLIST, SYS_KILL syscalls
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#pragma once
|
|
#include <Sched/Scheduler.hpp>
|
|
#include <Timekeeping/ApicTimer.hpp>
|
|
#include <Memory/Paging.hpp>
|
|
#include <Memory/PageFrameAllocator.hpp>
|
|
#include <Memory/HHDM.hpp>
|
|
#include <Fs/Vfs.hpp>
|
|
|
|
#include "Syscall.hpp"
|
|
#include "Common.hpp"
|
|
#include "WinServer.hpp"
|
|
#include "Path.hpp"
|
|
|
|
namespace montauk::abi {
|
|
static void Sys_Exit(int exitCode) {
|
|
Sched::SetProcessExitCode(Sched::GetCurrentPid(), exitCode & 0xFF);
|
|
Sched::ExitProcess();
|
|
}
|
|
|
|
static void Sys_Yield() {
|
|
Sched::Schedule();
|
|
}
|
|
|
|
static void Sys_SleepMs(uint64_t ms) {
|
|
Sched::BlockForSleep(ms);
|
|
}
|
|
|
|
static int Sys_GetPid() {
|
|
return Sched::GetCurrentPid();
|
|
}
|
|
|
|
// Returns 0..255 for a normal exit, 256+signal for killed/crashed,
|
|
// 0 when the pid is unknown or its ledger entry has been evicted.
|
|
static int Sys_WaitPid(int pid) {
|
|
Sched::BlockOnPid(pid);
|
|
return Sched::LookupExitCode(pid);
|
|
}
|
|
|
|
static int Sys_Spawn(const char* path, const char* args) {
|
|
char resolved[256];
|
|
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
|
|
|
auto* parent = Sched::GetCurrentProcessPtr();
|
|
int parentSlot = Ipc::CurrentSlot();
|
|
bool inheritRedirection = parent && parent->redirected;
|
|
int childPid = Sched::Spawn(resolved, args, !inheritRedirection);
|
|
if (childPid < 0) return childPid;
|
|
|
|
if (inheritRedirection) {
|
|
auto* child = Sched::GetProcessByPid(childPid);
|
|
int childSlot = Ipc::SlotForPid(childPid);
|
|
if (child == nullptr || childSlot < 0 || parentSlot < 0) {
|
|
Sched::KillProcess(childPid);
|
|
return -1;
|
|
}
|
|
|
|
child->ioOutHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioOutHandle, childSlot);
|
|
child->ioInHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioInHandle, childSlot);
|
|
child->ioKeyHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioKeyHandle, childSlot);
|
|
|
|
if (child->ioOutHandle < 0 || child->ioInHandle < 0 || child->ioKeyHandle < 0 ||
|
|
!ConfigureRedirWaitsetForSlot(childSlot, child)) {
|
|
Sched::KillProcess(childPid);
|
|
return -1;
|
|
}
|
|
|
|
child->redirected = true;
|
|
child->parentPid = parent->pid;
|
|
child->termCols = parent->termCols;
|
|
child->termRows = parent->termRows;
|
|
if (Sched::StartProcess(childPid) < 0) {
|
|
Sched::KillProcess(childPid);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return childPid;
|
|
}
|
|
|
|
static int Sys_GetArgs(char* buf, uint64_t maxLen) {
|
|
auto* proc = Sched::GetCurrentProcessPtr();
|
|
if (proc == nullptr || buf == nullptr || maxLen == 0) return -1;
|
|
int i = 0;
|
|
for (; i < (int)maxLen - 1 && proc->args[i]; i++) {
|
|
buf[i] = proc->args[i];
|
|
}
|
|
buf[i] = '\0';
|
|
return i;
|
|
}
|
|
|
|
static int Sys_ProcList(ProcInfo* buf, int maxCount) {
|
|
if (buf == nullptr || maxCount <= 0) return 0;
|
|
int count = 0;
|
|
for (int i = 0; i < Sched::MaxProcesses && count < maxCount; i++) {
|
|
auto* proc = Sched::GetProcessSlot(i);
|
|
if (!proc || proc->state == Sched::ProcessState::Free) continue;
|
|
// Skip sibling-thread slots; only show one entry per process.
|
|
if (proc->primarySlot != i) continue;
|
|
|
|
buf[count].pid = (int32_t)proc->pid;
|
|
buf[count].parentPid = (int32_t)proc->parentPid;
|
|
buf[count].state = (uint8_t)proc->state;
|
|
buf[count]._pad[0] = 0;
|
|
buf[count]._pad[1] = 0;
|
|
buf[count]._pad[2] = 0;
|
|
{
|
|
int j = 0;
|
|
for (; j < 63 && proc->name[j]; j++)
|
|
buf[count].name[j] = proc->name[j];
|
|
buf[count].name[j] = '\0';
|
|
}
|
|
buf[count].heapUsed = Sched::g_allocatedPages[i] * 0x1000;
|
|
buf[count].cpuTimeMs = proc->cpuTimeMs;
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int Sys_Kill(int pid) {
|
|
return Sched::KillProcess(pid);
|
|
}
|
|
|
|
static int Sys_SetUser(int pid, const char* name) {
|
|
if (name == nullptr) return -1;
|
|
auto* target = Sched::GetProcessByPid(pid);
|
|
if (target == nullptr) return -1;
|
|
int i = 0;
|
|
for (; i < 31 && name[i]; i++)
|
|
target->user[i] = name[i];
|
|
target->user[i] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
static int Sys_GetUser(char* buf, uint64_t maxLen) {
|
|
auto* proc = Sched::GetCurrentProcessPtr();
|
|
if (proc == nullptr || buf == nullptr || maxLen == 0) return -1;
|
|
int i = 0;
|
|
for (; i < (int)maxLen - 1 && proc->user[i]; i++)
|
|
buf[i] = proc->user[i];
|
|
buf[i] = '\0';
|
|
return i;
|
|
}
|
|
|
|
static int Sys_GetCwd(char* buf, uint64_t maxLen) {
|
|
auto* proc = Sched::GetCurrentProcessPtr();
|
|
if (proc == nullptr || buf == nullptr || maxLen == 0) return -1;
|
|
|
|
const char* cwd = proc->cwd[0] ? proc->cwd : "0:/";
|
|
int i = 0;
|
|
for (; i < (int)maxLen - 1 && cwd[i]; i++) buf[i] = cwd[i];
|
|
buf[i] = '\0';
|
|
return i;
|
|
}
|
|
|
|
// ====================================================================
|
|
// Threading
|
|
// ====================================================================
|
|
static int Sys_ThreadSpawn(uint64_t entry, uint64_t arg, uint64_t userStackTop) {
|
|
return Sched::SpawnThread(entry, arg, userStackTop);
|
|
}
|
|
|
|
[[noreturn]] static void Sys_ThreadExit(int exitCode) {
|
|
Sched::ExitCurrentThread(exitCode);
|
|
}
|
|
|
|
static int Sys_ThreadJoin(int tid, int* outExitCode) {
|
|
return Sched::JoinThread(tid, outExitCode);
|
|
}
|
|
|
|
static int Sys_ThreadSelf() {
|
|
return Sched::GetCurrentTid();
|
|
}
|
|
|
|
static int Sys_Chdir(const char* path) {
|
|
auto* proc = Sched::GetCurrentProcessPtr();
|
|
if (proc == nullptr || path == nullptr) return -1;
|
|
|
|
char resolved[256];
|
|
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
|
|
|
bool isDriveRoot = false;
|
|
{
|
|
int prefixLen = 0;
|
|
if (ParseDrivePrefix(resolved, &prefixLen) >= 0 &&
|
|
resolved[prefixLen] == '/' && resolved[prefixLen + 1] == '\0') {
|
|
isDriveRoot = true;
|
|
}
|
|
}
|
|
|
|
// ReadDir doubles as the directory-existence probe: it fails
|
|
// for nonexistent paths and for regular files (directories are
|
|
// not openable as files anymore, so the old open-based check
|
|
// would reject every valid target).
|
|
{
|
|
const char* entries[1];
|
|
if (Fs::Vfs::VfsReadDir(resolved, entries, 1) < 0) return -1;
|
|
}
|
|
|
|
int i = 0;
|
|
for (; i < 255 && resolved[i]; i++) proc->cwd[i] = resolved[i];
|
|
proc->cwd[i] = '\0';
|
|
return 0;
|
|
}
|
|
};
|