fix: fix filesystem stack regression

This commit is contained in:
2026-03-28 17:44:15 +01:00
parent 23bb57d026
commit 608625709c
2 changed files with 138 additions and 29 deletions
+64 -16
View File
@@ -695,6 +695,14 @@ namespace Fs::Ext2 {
uint8_t fileType; uint8_t fileType;
}; };
static bool IsValidDirEntry(const DirEntry* de, uint32_t pos, uint32_t remaining) {
if (de->rec_len == 0) return false;
if (de->rec_len < sizeof(DirEntry)) return false;
if (pos + de->rec_len > remaining) return false;
if (de->name_len > de->rec_len - sizeof(DirEntry)) return false;
return true;
}
// Find a single entry by name in a directory inode. // Find a single entry by name in a directory inode.
static bool FindInDirectory(Ext2Instance& inst, const Inode& dirInode, static bool FindInDirectory(Ext2Instance& inst, const Inode& dirInode,
const char* name, ParsedEntry* out) { const char* name, ParsedEntry* out) {
@@ -718,8 +726,7 @@ namespace Fs::Ext2 {
while (pos + 8 <= remaining) { while (pos + 8 <= remaining) {
DirEntry* de = (DirEntry*)(dirBuf + pos); DirEntry* de = (DirEntry*)(dirBuf + pos);
if (de->rec_len == 0) break; if (!IsValidDirEntry(de, pos, remaining)) break;
if (de->rec_len < 8 || pos + de->rec_len > blockSize) break;
if (de->inode != 0 && de->name_len > 0) { if (de->inode != 0 && de->name_len > 0) {
char entryName[MaxNameLen]; char entryName[MaxNameLen];
@@ -767,8 +774,7 @@ namespace Fs::Ext2 {
while (pos + 8 <= remaining && count < maxEntries) { while (pos + 8 <= remaining && count < maxEntries) {
DirEntry* de = (DirEntry*)(dirBuf + pos); DirEntry* de = (DirEntry*)(dirBuf + pos);
if (de->rec_len == 0) break; if (!IsValidDirEntry(de, pos, remaining)) break;
if (de->rec_len < 8 || pos + de->rec_len > blockSize) break;
if (de->inode != 0 && de->name_len > 0) { if (de->inode != 0 && de->name_len > 0) {
int nameLen = de->name_len; int nameLen = de->name_len;
@@ -798,6 +804,59 @@ namespace Fs::Ext2 {
return count; return count;
} }
static int ReadDirectoryNames(Ext2Instance& inst, const Inode& dirInode,
char outNames[MaxDirEntries][MaxNameLen],
int maxEntries) {
uint32_t dirSize = dirInode.i_size;
uint32_t blockSize = inst.blockSize;
uint32_t numBlocks = (dirSize + blockSize - 1) / blockSize;
int count = 0;
uint8_t* dirBuf = (uint8_t*)Memory::g_pfa->AllocateZeroed();
if (!dirBuf) return 0;
for (uint32_t bi = 0; bi < numBlocks && count < maxEntries; bi++) {
uint32_t physBlock = GetPhysicalBlock(inst, dirInode, bi);
if (physBlock == 0) continue;
if (!ReadBlock(inst, physBlock, dirBuf)) continue;
uint32_t pos = 0;
uint32_t remaining = dirSize - bi * blockSize;
if (remaining > blockSize) remaining = blockSize;
while (pos + 8 <= remaining && count < maxEntries) {
DirEntry* de = (DirEntry*)(dirBuf + pos);
if (!IsValidDirEntry(de, pos, remaining)) break;
if (de->inode != 0 && de->name_len > 0) {
int nameLen = de->name_len;
if (nameLen >= MaxNameLen) nameLen = MaxNameLen - 1;
memcpy(outNames[count], (uint8_t*)de + sizeof(DirEntry), nameLen);
outNames[count][nameLen] = '\0';
if (outNames[count][0] == '.' &&
(outNames[count][1] == '\0' ||
(outNames[count][1] == '.' && outNames[count][2] == '\0'))) {
pos += de->rec_len;
continue;
}
if (de->file_type == EXT2_FT_DIR && nameLen < MaxNameLen - 1) {
outNames[count][nameLen++] = '/';
outNames[count][nameLen] = '\0';
}
count++;
}
pos += de->rec_len;
}
}
Memory::g_pfa->Free(dirBuf);
return count;
}
// Add a directory entry to a directory inode // Add a directory entry to a directory inode
static bool AddDirEntry(Ext2Instance& inst, uint32_t dirInodeNum, Inode& dirInode, static bool AddDirEntry(Ext2Instance& inst, uint32_t dirInodeNum, Inode& dirInode,
uint32_t childInodeNum, const char* name, uint8_t fileType) { uint32_t childInodeNum, const char* name, uint8_t fileType) {
@@ -1096,22 +1155,11 @@ namespace Fs::Ext2 {
if (!TraversePath(self, path, &inodeNum, &inode)) return -1; if (!TraversePath(self, path, &inodeNum, &inode)) return -1;
if ((inode.i_mode & IMODE_TYPE_MASK) != IMODE_DIR) return -1; if ((inode.i_mode & IMODE_TYPE_MASK) != IMODE_DIR) return -1;
ParsedEntry entries[MaxDirEntries];
int limit = maxEntries < MaxDirEntries ? maxEntries : MaxDirEntries; int limit = maxEntries < MaxDirEntries ? maxEntries : MaxDirEntries;
int count = ReadDirectoryEntries(self, inode, entries, limit); int count = ReadDirectoryNames(self, inode, self.dirNames, limit);
self.dirNameCount = count; self.dirNameCount = count;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int j = 0;
while (entries[i].name[j] && j < MaxNameLen - 2) {
self.dirNames[i][j] = entries[i].name[j];
j++;
}
// Append trailing '/' for directories so userspace can distinguish them
if (entries[i].fileType == EXT2_FT_DIR && j < MaxNameLen - 1) {
self.dirNames[i][j++] = '/';
}
self.dirNames[i][j] = '\0';
outNames[i] = self.dirNames[i]; outNames[i] = self.dirNames[i];
} }
+74 -13
View File
@@ -672,6 +672,79 @@ namespace Fs::Fat32 {
return count; return count;
} }
static int ReadDirectoryNames(int inst, uint32_t dirCluster,
char outNames[MaxDirEntries][MaxNameLen],
int maxEntries) {
auto& self = g_instances[inst];
uint16_t lfnBuf[MaxNameLen];
bool hasLfn = false;
int count = 0;
uint32_t cluster = dirCluster;
while (!IsEndOfChain(cluster) && count < maxEntries) {
if (!ReadCluster(self, cluster)) break;
int perCluster = (int)(self.clusterSize / 32);
for (int i = 0; i < perCluster && count < maxEntries; i++) {
uint8_t* e = self.clusterBuf + i * 32;
if (e[0] == 0x00) return count;
if (e[0] == 0xE5) { hasLfn = false; continue; }
uint8_t attr = e[11];
if (attr == ATTR_LFN) {
uint8_t seq = e[0];
int seqNum = seq & 0x1F;
if (seq & 0x40) {
for (int k = 0; k < MaxNameLen; k++) lfnBuf[k] = 0;
hasLfn = true;
}
if (hasLfn && seqNum >= 1 && seqNum <= 20) {
uint16_t chars[13];
ExtractLfnChars(e, chars);
int offset = (seqNum - 1) * 13;
for (int k = 0; k < 13 && offset + k < MaxNameLen; k++) {
lfnBuf[offset + k] = chars[k];
}
}
continue;
}
if (attr & ATTR_VOLUME_ID) { hasLfn = false; continue; }
if (hasLfn) {
Utf16ToAscii(lfnBuf, MaxNameLen, outNames[count]);
} else {
ParseShortName(e, outNames[count]);
}
hasLfn = false;
if (outNames[count][0] == '.' &&
(outNames[count][1] == '\0' ||
(outNames[count][1] == '.' && outNames[count][2] == '\0'))) {
continue;
}
int len = 0;
while (outNames[count][len] && len < MaxNameLen - 2) len++;
if ((attr & ATTR_DIRECTORY) && len < MaxNameLen - 1) {
outNames[count][len++] = '/';
outNames[count][len] = '\0';
}
count++;
}
cluster = GetNextCluster(self, cluster);
}
return count;
}
// ========================================================================= // =========================================================================
// Case-insensitive string comparison // Case-insensitive string comparison
// ========================================================================= // =========================================================================
@@ -932,23 +1005,11 @@ namespace Fs::Fat32 {
if (!TraversePath(inst, path, &dirEntry)) return -1; if (!TraversePath(inst, path, &dirEntry)) return -1;
if (!(dirEntry.attributes & ATTR_DIRECTORY)) return -1; if (!(dirEntry.attributes & ATTR_DIRECTORY)) return -1;
ParsedEntry entries[MaxDirEntries];
int limit = maxEntries < MaxDirEntries ? maxEntries : MaxDirEntries; int limit = maxEntries < MaxDirEntries ? maxEntries : MaxDirEntries;
int count = ReadDirectory(inst, dirEntry.firstCluster, entries, limit); int count = ReadDirectoryNames(inst, dirEntry.firstCluster, self.dirNames, limit);
// Copy names into persistent cache
self.dirNameCount = count; self.dirNameCount = count;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int j = 0;
while (entries[i].name[j] && j < MaxNameLen - 2) {
self.dirNames[i][j] = entries[i].name[j];
j++;
}
// Append trailing '/' for directories so userspace can distinguish them
if ((entries[i].attributes & ATTR_DIRECTORY) && j < MaxNameLen - 1) {
self.dirNames[i][j++] = '/';
}
self.dirNames[i][j] = '\0';
outNames[i] = self.dirNames[i]; outNames[i] = self.dirNames[i];
} }