feat: filesystem and Files app support for >64 file entries, 64-bit sizes, async ops, GUI toolkit improvements to devexplorer app

This commit is contained in:
2026-06-07 09:33:46 +02:00
parent 6554ef7e15
commit 3d620673c0
27 changed files with 1110 additions and 293 deletions
+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once
#define MONTAUK_BUILD_NUMBER 66
#define MONTAUK_BUILD_NUMBER 68
+10 -1
View File
@@ -36,15 +36,24 @@ namespace Montauk {
Ipc::CloseHandle(handle);
}
static int Sys_ReadDirAt(const char* path, const char** outNames, int maxEntries,
int startIndex);
static int Sys_ReadDir(const char* path, const char** outNames, int maxEntries) {
return Sys_ReadDirAt(path, outNames, maxEntries, 0);
}
static int Sys_ReadDirAt(const char* path, const char** outNames, int maxEntries,
int startIndex) {
char resolved[256];
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
if (startIndex < 0) return -1;
// Get entries from VFS into a kernel-local array
const char* kernelNames[256];
int max = maxEntries;
if (max > 256) max = 256;
int count = Fs::Vfs::VfsReadDir(resolved, kernelNames, max);
int count = Fs::Vfs::VfsReadDirAt(resolved, kernelNames, max, startIndex);
if (count <= 0) return count;
// Allocate a user-accessible page for string data via process heap
+13
View File
@@ -97,6 +97,19 @@ namespace Montauk {
return (int64_t)Sys_ReadDir((const char*)frame->arg1,
(const char**)frame->arg2,
(int)frame->arg3);
case SYS_READDIR_AT:
if ((int64_t)frame->arg3 < 0) return -1;
if ((int64_t)frame->arg4 < 0) return -1;
if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1;
if (!UserMemory::Range(frame->arg2,
(uint64_t)((frame->arg3 > 256) ? 256 : frame->arg3) * sizeof(const char*),
true)) {
return -1;
}
return (int64_t)Sys_ReadDirAt((const char*)frame->arg1,
(const char**)frame->arg2,
(int)frame->arg3,
(int)frame->arg4);
case SYS_ALLOC:
return (int64_t)Sys_Alloc(frame->arg1);
case SYS_FREE:
+3
View File
@@ -255,6 +255,9 @@ namespace Montauk {
/* Power.hpp -- cross-process graceful power-off request channel */
static constexpr uint64_t SYS_POWER_REQUEST = 135;
/* Filesystem.hpp -- paginated directory read (path, names, max, startIndex) */
static constexpr uint64_t SYS_READDIR_AT = 136;
// Graceful power-off request actions (SYS_POWER_REQUEST). The desktop posts
// a pending action and exits; login.elf reads it, runs the shutdown stages,
// then issues the matching SYS_SHUTDOWN / SYS_RESET.
+1
View File
@@ -66,6 +66,7 @@ namespace Fs {
Ramdisk::Mkdir,
Ramdisk::Rename,
Ramdisk::GetLabel,
Ramdisk::ReadDirAt,
};
}
+14 -3
View File
@@ -806,11 +806,12 @@ namespace Fs::Ext2 {
static int ReadDirectoryNames(Ext2Instance& inst, const Inode& dirInode,
char outNames[MaxDirEntries][MaxNameLen],
int maxEntries) {
int maxEntries, int startOffset = 0) {
uint32_t dirSize = dirInode.i_size;
uint32_t blockSize = inst.blockSize;
uint32_t numBlocks = (dirSize + blockSize - 1) / blockSize;
int count = 0;
int skipped = 0; // valid entries skipped to reach startOffset
uint8_t* dirBuf = (uint8_t*)Memory::g_pfa->AllocateZeroed();
if (!dirBuf) return 0;
@@ -846,6 +847,14 @@ namespace Fs::Ext2 {
outNames[count][nameLen] = '\0';
}
// Skip entries before the requested page. The name above was
// written into outNames[count] as scratch and will be reused.
if (skipped < startOffset) {
skipped++;
pos += de->rec_len;
continue;
}
count++;
}
@@ -1146,7 +1155,7 @@ namespace Fs::Ext2 {
}
static int ReadDirImpl(int inst, const char* path,
const char** outNames, int maxEntries) {
const char** outNames, int maxEntries, int startIndex = 0) {
if (inst < 0 || inst >= g_instanceCount) return -1;
auto& self = g_instances[inst];
@@ -1156,7 +1165,7 @@ namespace Fs::Ext2 {
if ((inode.i_mode & IMODE_TYPE_MASK) != IMODE_DIR) return -1;
int limit = maxEntries < MaxDirEntries ? maxEntries : MaxDirEntries;
int count = ReadDirectoryNames(self, inode, self.dirNames, limit);
int count = ReadDirectoryNames(self, inode, self.dirNames, limit, startIndex);
self.dirNameCount = count;
for (int i = 0; i < count; i++) {
@@ -1603,6 +1612,7 @@ namespace Fs::Ext2 {
static uint64_t GetSize(int h) { return GetSizeImpl(N, h); }
static void Close(int h) { CloseImpl(N, h); }
static int ReadDir(const char* p, const char** o, int m) { return ReadDirImpl(N, p, o, m); }
static int ReadDirAt(const char* p, const char** o, int m, int s) { return ReadDirImpl(N, p, o, m, s); }
static int Write(int h, const uint8_t* b, uint64_t o, uint64_t s) { return WriteImpl(N, h, b, o, s); }
static int Create(const char* p) { return CreateImpl(N, p); }
static int Delete(const char* p) { return DeleteImpl(N, p); }
@@ -1625,6 +1635,7 @@ namespace Fs::Ext2 {
Thunks<N>::Mkdir,
Thunks<N>::Rename,
Thunks<N>::GetLabel,
Thunks<N>::ReadDirAt,
};
}
+10 -3
View File
@@ -674,11 +674,12 @@ namespace Fs::Fat32 {
static int ReadDirectoryNames(int inst, uint32_t dirCluster,
char outNames[MaxDirEntries][MaxNameLen],
int maxEntries) {
int maxEntries, int startOffset = 0) {
auto& self = g_instances[inst];
uint16_t lfnBuf[MaxNameLen];
bool hasLfn = false;
int count = 0;
int skipped = 0; // valid entries skipped to reach startOffset
uint32_t cluster = dirCluster;
while (!IsEndOfChain(cluster) && count < maxEntries) {
@@ -736,6 +737,10 @@ namespace Fs::Fat32 {
outNames[count][len] = '\0';
}
// Skip entries before the requested page. The name above was
// written into outNames[count] as scratch and will be reused.
if (skipped < startOffset) { skipped++; continue; }
count++;
}
@@ -997,7 +1002,7 @@ namespace Fs::Fat32 {
}
static int ReadDirImpl(int inst, const char* path,
const char** outNames, int maxEntries) {
const char** outNames, int maxEntries, int startIndex = 0) {
if (inst < 0 || inst >= g_instanceCount) return -1;
auto& self = g_instances[inst];
@@ -1006,7 +1011,7 @@ namespace Fs::Fat32 {
if (!(dirEntry.attributes & ATTR_DIRECTORY)) return -1;
int limit = maxEntries < MaxDirEntries ? maxEntries : MaxDirEntries;
int count = ReadDirectoryNames(inst, dirEntry.firstCluster, self.dirNames, limit);
int count = ReadDirectoryNames(inst, dirEntry.firstCluster, self.dirNames, limit, startIndex);
self.dirNameCount = count;
for (int i = 0; i < count; i++) {
@@ -1762,6 +1767,7 @@ namespace Fs::Fat32 {
static uint64_t GetSize(int h) { return GetSizeImpl(N, h); }
static void Close(int h) { CloseImpl(N, h); }
static int ReadDir(const char* p, const char** o, int m) { return ReadDirImpl(N, p, o, m); }
static int ReadDirAt(const char* p, const char** o, int m, int s) { return ReadDirImpl(N, p, o, m, s); }
static int Write(int h, const uint8_t* b, uint64_t o, uint64_t s) { return WriteImpl(N, h, b, o, s); }
static int Create(const char* p) { return CreateImpl(N, p); }
static int Delete(const char* p) { return DeleteImpl(N, p); }
@@ -1784,6 +1790,7 @@ namespace Fs::Fat32 {
Thunks<N>::Mkdir,
Thunks<N>::Rename,
Thunks<N>::GetLabel,
Thunks<N>::ReadDirAt,
};
}
+14 -8
View File
@@ -217,15 +217,17 @@ namespace Fs::Ramdisk {
(void)handle;
}
int ReadDir(const char* path, const char** outNames, int maxEntries) {
int ReadDirAt(const char* path, const char** outNames, int maxEntries, int startIndex) {
// Normalize path: skip leading '/'
if (path[0] == '/') path++;
int pathLen = StrLen(path);
int count = 0;
int count = 0; // entries written to outNames
int seen = 0; // matching direct children scanned so far (for startIndex skip)
for (int i = 0; i < fileCount && count < maxEntries; i++) {
const char* entryName = fileTable[i].name;
bool isChild = false;
if (pathLen == 0) {
// Root directory: find entries without '/' in them (or only trailing '/')
@@ -237,9 +239,7 @@ namespace Fs::Ramdisk {
break;
}
}
if (!hasSlash) {
outNames[count++] = entryName;
}
isChild = !hasSlash;
} else {
// Subdirectory: match entries starting with "path/"
// and that are direct children (no additional '/' beyond the prefix)
@@ -259,15 +259,21 @@ namespace Fs::Ramdisk {
break;
}
}
if (!hasDeepSlash && restLen > 0) {
outNames[count++] = entryName;
}
isChild = (!hasDeepSlash && restLen > 0);
}
if (!isChild) continue;
if (seen++ < startIndex) continue; // skip entries before the requested page
outNames[count++] = entryName;
}
return count;
}
int ReadDir(const char* path, const char** outNames, int maxEntries) {
return ReadDirAt(path, outNames, maxEntries, 0);
}
int Write(int handle, const uint8_t* buffer, uint64_t offset, uint64_t size) {
if (handle < 0 || handle >= fileCount) return -1;
if (buffer == nullptr || size == 0) return 0;
+1
View File
@@ -32,6 +32,7 @@ namespace Fs::Ramdisk {
void Close(int handle);
int ReadDir(const char* path, const char** outNames, int maxEntries);
int ReadDirAt(const char* path, const char** outNames, int maxEntries, int startIndex);
int Delete(const char* path);
int Mkdir(const char* path);
int Rename(const char* oldPath, const char* newPath);
+16 -3
View File
@@ -374,17 +374,30 @@ namespace Fs::Vfs {
}
int VfsReadDir(const char* path, const char** outNames, int maxEntries) {
return VfsReadDirAt(path, outNames, maxEntries, 0);
}
int VfsReadDirAt(const char* path, const char** outNames, int maxEntries, int startIndex) {
int drive;
const char* localPath;
if (startIndex < 0) return -1;
if (!ParsePath(path, drive, localPath)) return -1;
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
vfsLock.Acquire();
FsDriver* driver = driveTable[drive];
int result = (driveActive[drive] && driver && driver->ReadDir)
? driver->ReadDir(localPath, outNames, maxEntries)
: -1;
int result;
if (!driveActive[drive] || !driver) {
result = -1;
} else if (driver->ReadDirAt) {
result = driver->ReadDirAt(localPath, outNames, maxEntries, startIndex);
} else if (driver->ReadDir) {
// Driver without pagination support: only the first page is reachable.
result = (startIndex == 0) ? driver->ReadDir(localPath, outNames, maxEntries) : 0;
} else {
result = -1;
}
vfsLock.Release();
return result;
}
+4
View File
@@ -30,6 +30,9 @@ namespace Fs::Vfs {
int (*Mkdir)(const char* path);
int (*Rename)(const char* oldPath, const char* newPath);
const char* (*GetLabel)();
// Optional: paginated directory read returning entries [startIndex, startIndex+maxEntries).
// Drivers that leave this null are read via ReadDir at startIndex 0 only.
int (*ReadDirAt)(const char* path, const char** outNames, int maxEntries, int startIndex);
};
void Initialize();
@@ -49,6 +52,7 @@ namespace Fs::Vfs {
int VfsDelete(const char* path);
int VfsReadDir(const char* path, const char** outNames, int maxEntries);
int VfsReadDirAt(const char* path, const char** outNames, int maxEntries, int startIndex);
int VfsMkdir(const char* path);
int VfsRename(const char* oldPath, const char* newPath);