fix: fix ramdisk readdir inconsistency.

This commit is contained in:
2026-08-12 18:35:14 +02:00
parent 7f2d4b693f
commit a28b8ad0b2
8 changed files with 71 additions and 27 deletions
+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once
#define MONTAUK_BUILD_NUMBER 108
#define MONTAUK_BUILD_NUMBER 109
+13 -5
View File
@@ -262,6 +262,10 @@ namespace Fs::Ramdisk {
}
int ReadDirAt(const char* path, const char** outNames, int maxEntries, int startIndex) {
if (path == nullptr || outNames == nullptr || maxEntries < 0 || startIndex < 0) {
return -1;
}
// Reject outright if path names an existing regular file: without this,
// a file with no matching "path/..." children below falls through to
// the child-scan loop and returns 0 (empty), which callers that use
@@ -269,10 +273,10 @@ namespace Fs::Ramdisk {
int selfIdx = FindEntryByPath(path);
if (selfIdx >= 0 && !fileTable[selfIdx].isDirectory) return -1;
// Normalize path: skip leading '/'
if (path[0] == '/') path++;
int pathLen = StrLen(path);
char normalized[MaxNameLen];
int pathLen = NormalizePath(path, normalized, MaxNameLen);
if (pathLen < 0) return -1;
path = normalized;
int count = 0; // entries written to outNames
int seen = 0; // matching direct children scanned so far (for startIndex skip)
@@ -280,6 +284,7 @@ namespace Fs::Ramdisk {
const char* entryName = fileTable[i].name;
bool isChild = false;
const char* childName = entryName;
if (pathLen == 0) {
// Root directory: find entries without '/' in them (or only trailing '/')
bool hasSlash = false;
@@ -311,11 +316,14 @@ namespace Fs::Ramdisk {
}
}
isChild = (!hasDeepSlash && restLen > 0);
childName = rest;
}
if (!isChild) continue;
if (seen++ < startIndex) continue; // skip entries before the requested page
outNames[count++] = entryName;
// ReadDir follows the same contract as ext2/FAT32: return the
// direct child's name, not its archive path relative to root.
outNames[count++] = childName;
}
// A path with no directory entry and no children does not exist.
@@ -269,8 +269,8 @@ void filemanager_read_dir(FileManagerState* fm) {
fm->virtual_view = FM_VIRTUAL_VIEW_NONE;
fm->entry_count = 0;
// readdir returns full paths from the VFS (e.g. "man/fetch.1" instead
// of just "fetch.1"). Compute the prefix to strip so we get basenames.
// Current drivers return direct-child names. Retain prefix stripping for
// compatibility with images built against the older ramdisk behavior.
const char* after_drive = fm->current_path;
for (int k = 0; after_drive[k]; k++) {
if (after_drive[k] == ':' && after_drive[k + 1] == '/') {
@@ -433,7 +433,7 @@ void filemanager_read_system_configuration(FileManagerState* fm) {
static int filemanager_read_all_children(const char* dir, char (**out)[256]) {
*out = nullptr;
// Compute the prefix to strip (readdir returns drive-root-relative paths).
// Retain support for older ramdisks that returned drive-root-relative paths.
const char* after_drive = dir;
for (int k = 0; after_drive[k]; k++) {
if (after_drive[k] == ':' && after_drive[k + 1] == '/') {
+3 -4
View File
@@ -14,9 +14,8 @@
// App Manifest Scanning
// ============================================================================
// Extract the basename from a readdir entry.
// readdir returns full paths from drive root (e.g. "apps/doom/") —
// strip the directory prefix and any trailing slash to get just "doom".
// Extract the basename from a readdir entry, tolerating both current
// direct-child names and paths returned by older ramdisk kernels.
static void extract_basename(char* out, int outSz, const char* entry) {
// Strip trailing slash
int len = montauk::slen(entry);
@@ -89,7 +88,7 @@ void desktop_scan_apps(DesktopState* ds) {
scanned += got;
for (int i = 0; i < got; i++) {
// readdir returns paths like "apps/doom/" — extract just "doom"
// Strip the directory marker and tolerate old root-relative entries.
char dirname[64];
extract_basename(dirname, sizeof(dirname), entries[i]);
if (dirname[0] == '\0') continue;
+1 -1
View File
@@ -258,7 +258,7 @@ inline void wallpaper_scan_dir(const char* dir_path, WallpaperFileList* list,
int total = montauk::readdir(dir_path, raw_names, 64);
if (total <= 0) return;
// Compute prefix to strip (readdir returns full paths from VFS root)
// Retain support for older ramdisks that returned paths from the VFS root.
const char* after_drive = dir_path;
for (int k = 0; after_drive[k]; k++) {
if (after_drive[k] == ':' && after_drive[k + 1] == '/') {
+44 -7
View File
@@ -124,23 +124,54 @@ bool installer_list_dir(const char* abs_dir, DirList* out) {
int scanned = 0;
int capacity = 0;
// The filesystem API returns direct-child names. Keep this installer's
// historical root-relative representation because component filtering
// and recursive source paths operate on paths such as "sdk/tcc".
const char* dir_rel = abs_dir;
for (int i = 0; dir_rel[i]; i++) {
if (dir_rel[i] == ':') {
dir_rel += i + 1;
if (dir_rel[0] == '/') dir_rel++;
break;
}
}
int dir_rel_len = slen(dir_rel);
while (dir_rel_len > 0 && dir_rel[dir_rel_len - 1] == '/') dir_rel_len--;
int got = montauk::readdir_at(abs_dir, batch, BATCH, 0);
if (got < 0) return false;
while (got > 0) {
for (int i = 0; i < got; i++) {
const char* raw = batch[i];
int len = slen(raw);
int raw_len = slen(raw);
bool is_dir = (len > 0 && raw[len - 1] == '/');
if (is_dir) len--;
if (len <= 0) continue;
bool is_dir = (raw_len > 0 && raw[raw_len - 1] == '/');
if (is_dir) raw_len--;
if (raw_len <= 0) continue;
bool already_root_relative = false;
if (dir_rel_len > 0 && raw_len > dir_rel_len &&
raw[dir_rel_len] == '/') {
already_root_relative = true;
for (int k = 0; k < dir_rel_len; k++) {
if (raw[k] != dir_rel[k]) {
already_root_relative = false;
break;
}
}
}
int len = raw_len;
if (dir_rel_len > 0 && !already_root_relative) {
len += dir_rel_len + 1;
}
// Skip "." and ".."
const char* base = raw;
for (int k = 0; k < len; k++)
for (int k = 0; k < raw_len; k++)
if (raw[k] == '/') base = raw + k + 1;
int base_len = (int)(len - (base - raw));
int base_len = (int)(raw_len - (base - raw));
if (base_len == 1 && base[0] == '.') continue;
if (base_len == 2 && base[0] == '.' && base[1] == '.') continue;
@@ -156,7 +187,13 @@ bool installer_list_dir(const char* abs_dir, DirList* out) {
char* rel = (char*)montauk::malloc(len + 1);
if (!rel) { installer_free_dir_list(out); return false; }
montauk::memcpy(rel, raw, len);
int pos = 0;
if (dir_rel_len > 0 && !already_root_relative) {
montauk::memcpy(rel, dir_rel, dir_rel_len);
pos = dir_rel_len;
rel[pos++] = '/';
}
montauk::memcpy(rel + pos, raw, raw_len);
rel[len] = '\0';
out->entries[out->count].rel = rel;
+4 -4
View File
@@ -284,10 +284,10 @@ void do_install();
void do_update();
bool installer_path_exists(const char* abs_path);
// Heap-backed directory listing. Entries are full paths relative to the
// drive root (e.g. "apps/doom"), with is_dir derived from the trailing '/'
// the VFS appends to directory names. Survives nested listings, unlike the
// kernel's rotating readdir scratch pages.
// Heap-backed directory listing. Direct-child names from readdir are expanded
// to paths relative to the drive root (e.g. "apps/doom"), with is_dir derived
// from the trailing '/' the VFS appends to directory names. Survives nested
// listings, unlike the kernel's rotating readdir scratch pages.
struct DirEntry {
char* rel; // path relative to drive root, no trailing '/'
bool is_dir;
+2 -2
View File
@@ -622,8 +622,8 @@ void wp_load_fonts() {
if (!raw) continue;
int rl = montauk::slen(raw);
if (rl == 0 || raw[rl - 1] == '/') continue; // skip directories
// readdir may return a full internal path (ramdisk) or a bare name;
// take everything after the last '/'.
// Current drivers return a bare name; accepting an older ramdisk's
// internal path here is harmless and keeps mixed images compatible.
const char* base = raw;
for (int k = 0; raw[k]; k++) if (raw[k] == '/') base = raw + k + 1;
wp_register_font_file(base);