From 78ce551688a3fedf6843121b90d9fbd55c4e0279 Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Fri, 17 Jul 2026 10:18:58 +0200 Subject: [PATCH] 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 --- kernel/src/Api/BuildNo.hpp | 2 +- kernel/src/Api/Process.hpp | 10 +++++----- kernel/src/Fs/Ramdisk.cpp | 22 +++++++++++++++++----- kernel/src/Fs/Ramdisk.hpp | 3 ++- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/kernel/src/Api/BuildNo.hpp b/kernel/src/Api/BuildNo.hpp index feb58dc..694c0f8 100644 --- a/kernel/src/Api/BuildNo.hpp +++ b/kernel/src/Api/BuildNo.hpp @@ -12,4 +12,4 @@ #pragma once -#define MONTAUK_BUILD_NUMBER 21 +#define MONTAUK_BUILD_NUMBER 22 diff --git a/kernel/src/Api/Process.hpp b/kernel/src/Api/Process.hpp index cbf959e..b06eec9 100644 --- a/kernel/src/Api/Process.hpp +++ b/kernel/src/Api/Process.hpp @@ -194,13 +194,13 @@ namespace montauk::abi { } } - if (isDriveRoot) { + // 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; - } else { - Fs::Vfs::BackendFile file = {-1, -1, 0}; - if (Fs::Vfs::OpenBackendFile(resolved, file) < 0) return -1; - Fs::Vfs::CloseBackendFile(file); } int i = 0; diff --git a/kernel/src/Fs/Ramdisk.cpp b/kernel/src/Fs/Ramdisk.cpp index 6c91998..b39a744 100644 --- a/kernel/src/Fs/Ramdisk.cpp +++ b/kernel/src/Fs/Ramdisk.cpp @@ -107,8 +107,13 @@ namespace Fs::Ramdisk { break; } - // File name at offset 0 (100 bytes) + // File name at offset 0 (100 bytes), long-path directory + // part in the USTAR prefix field at offset 345 (155 bytes). + // Paths over 100 chars (deep C++ header trees) are split + // across the two; joining them is required or the tail of + // the name lands as a bogus root-level entry. const char* name = (const char*)ptr; + const char* namePrefix = (const char*)(ptr + 345); // File size at offset 124 (12 bytes, octal ASCII) uint64_t size = OctalToUint((const char*)(ptr + 124), 12); // Type flag at offset 156 @@ -116,11 +121,18 @@ namespace Fs::Ramdisk { FileEntry& entry = fileTable[fileCount]; - // Copy name + // Copy prefix + '/' + name (prefix may be empty) int nameLen = 0; - while (nameLen < MaxNameLen - 1 && name[nameLen] != '\0') { - entry.name[nameLen] = name[nameLen]; - nameLen++; + if (namePrefix[0] != '\0') { + for (int j = 0; j < 155 && namePrefix[j] != '\0' + && nameLen < MaxNameLen - 2; j++) { + entry.name[nameLen++] = namePrefix[j]; + } + entry.name[nameLen++] = '/'; + } + for (int j = 0; j < 100 && name[j] != '\0' + && nameLen < MaxNameLen - 1; j++) { + entry.name[nameLen++] = name[j]; } entry.name[nameLen] = '\0'; diff --git a/kernel/src/Fs/Ramdisk.hpp b/kernel/src/Fs/Ramdisk.hpp index 8d3c74e..f5f76f9 100644 --- a/kernel/src/Fs/Ramdisk.hpp +++ b/kernel/src/Fs/Ramdisk.hpp @@ -13,7 +13,8 @@ namespace Fs::Ramdisk { // Static file table. The base image ships ~650 files since the // 0:/usr devkit; keep generous headroom for runtime file creation. static constexpr int MaxFiles = 2048; - static constexpr int MaxNameLen = 100; + // USTAR: 155-byte prefix field + '/' + 100-byte name field. + static constexpr int MaxNameLen = 260; struct FileEntry { char name[MaxNameLen];