Files
MontaukOS/kernel/src/Api/Process.hpp
T
danielandClaude Fable 5 78ce551688 fix: USTAR prefix-field parsing and directory-aware chdir
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]>
2026-07-17 10:18:58 +02:00

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;
}
};