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:
@@ -12,4 +12,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MONTAUK_BUILD_NUMBER 66
|
||||
#define MONTAUK_BUILD_NUMBER 68
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace Fs {
|
||||
Ramdisk::Mkdir,
|
||||
Ramdisk::Rename,
|
||||
Ramdisk::GetLabel,
|
||||
Ramdisk::ReadDirAt,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+14
-3
@@ -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
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user