diff --git a/kernel/src/Fs/Ext2.cpp b/kernel/src/Fs/Ext2.cpp index 2e6d1a6..3a30dda 100644 --- a/kernel/src/Fs/Ext2.cpp +++ b/kernel/src/Fs/Ext2.cpp @@ -695,6 +695,14 @@ namespace Fs::Ext2 { 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. static bool FindInDirectory(Ext2Instance& inst, const Inode& dirInode, const char* name, ParsedEntry* out) { @@ -718,8 +726,7 @@ namespace Fs::Ext2 { while (pos + 8 <= remaining) { DirEntry* de = (DirEntry*)(dirBuf + pos); - if (de->rec_len == 0) break; - if (de->rec_len < 8 || pos + de->rec_len > blockSize) break; + if (!IsValidDirEntry(de, pos, remaining)) break; if (de->inode != 0 && de->name_len > 0) { char entryName[MaxNameLen]; @@ -767,8 +774,7 @@ namespace Fs::Ext2 { while (pos + 8 <= remaining && count < maxEntries) { DirEntry* de = (DirEntry*)(dirBuf + pos); - if (de->rec_len == 0) break; - if (de->rec_len < 8 || pos + de->rec_len > blockSize) break; + if (!IsValidDirEntry(de, pos, remaining)) break; if (de->inode != 0 && de->name_len > 0) { int nameLen = de->name_len; @@ -798,6 +804,59 @@ namespace Fs::Ext2 { 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 static bool AddDirEntry(Ext2Instance& inst, uint32_t dirInodeNum, Inode& dirInode, 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 ((inode.i_mode & IMODE_TYPE_MASK) != IMODE_DIR) return -1; - ParsedEntry entries[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; 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]; } diff --git a/kernel/src/Fs/Fat32.cpp b/kernel/src/Fs/Fat32.cpp index 793d86a..e5d535f 100644 --- a/kernel/src/Fs/Fat32.cpp +++ b/kernel/src/Fs/Fat32.cpp @@ -672,6 +672,79 @@ namespace Fs::Fat32 { 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 // ========================================================================= @@ -932,23 +1005,11 @@ namespace Fs::Fat32 { if (!TraversePath(inst, path, &dirEntry)) return -1; if (!(dirEntry.attributes & ATTR_DIRECTORY)) return -1; - ParsedEntry entries[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; 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]; }