[feat] FASTER READ!!!

This commit is contained in:
2026-06-05 20:14:21 +08:00
Unverified
parent 0f344b8c3e
commit 60a9b9e97d
3 changed files with 334 additions and 42 deletions
+290 -23
View File
@@ -144,6 +144,13 @@ static EFI_STATUS blk_read(struct block_dev *dev, UINT64 LBA,
Sectors * dev->BlockSize, Buf);
}
static EFI_STATUS blk_write(struct block_dev *dev, UINT64 LBA,
UINTN Sectors, const void *Buf) {
return uefi_call_wrapper(dev->Bio->WriteBlocks, 5,
dev->Bio, dev->Bio->Media->MediaId, LBA,
Sectors * dev->BlockSize, (void*)Buf);
}
// ---- Partition detection ----
static EFI_STATUS find_gpt_partition(struct block_dev *dev, UINT64 *StartLBA) {
@@ -435,10 +442,10 @@ static void sfn_to_name(const UINT8 *E, CHAR16 *out, UINTN out_size) {
// ---- Directory reading ----
typedef void (*dir_cb)(void *Ctx, const CHAR16 *Name, UINT8 Attr,
UINT32 Size, UINT32 FirstClus);
UINT32 Size, UINT32 FirstClus, UINTN EntryOff);
// Returns TRUE when end-of-directory reached
static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf,
static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf, UINTN SectBase,
struct lfn_state *lfn,
dir_cb Callback, void *Ctx) {
for (UINTN off = 0; off < fs->BytsPerSec; off += 32) {
@@ -475,7 +482,7 @@ static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf,
else
FirstClus = *(const UINT16*)(E + 26);
Callback(Ctx, Name, Attr, Size, FirstClus);
Callback(Ctx, Name, Attr, Size, FirstClus, SectBase + off);
lfn_reset(lfn);
}
return FALSE;
@@ -483,7 +490,8 @@ static BOOLEAN process_sector(struct fat_fs *fs, UINT8 *Buf,
static void read_directory(struct fat_fs *fs, UINT32 Cluster,
dir_cb Callback, void *Ctx) {
UINT8 *Buf = (UINT8*)kmalloc(fs->BytsPerSec);
UINTN ClusBytes = fs->ClusSize;
UINT8 *Buf = (UINT8*)kmalloc(ClusBytes);
if (!Buf) return;
struct lfn_state lfn;
@@ -493,10 +501,12 @@ static void read_directory(struct fat_fs *fs, UINT32 Cluster,
UINT32 Clus = Cluster;
while (Clus >= 2 && Clus < 0x0FFFFFF8) {
UINT64 BaseLBA = clus_to_lba(fs, Clus);
if (EFI_ERROR(blk_read(fs->Dev, BaseLBA, fs->SecPerClus, Buf)))
goto done;
for (UINTN s = 0; s < fs->SecPerClus; s++) {
if (EFI_ERROR(blk_read(fs->Dev, BaseLBA + s, 1, Buf)))
goto done;
if (process_sector(fs, Buf, &lfn, Callback, Ctx))
if (process_sector(fs, Buf + s * fs->BytsPerSec,
s * fs->BytsPerSec,
&lfn, Callback, Ctx))
goto done;
}
Clus = fat_next(fs, Clus);
@@ -505,19 +515,25 @@ static void read_directory(struct fat_fs *fs, UINT32 Cluster,
UINT32 RootSecs = ((fs->RootEntCnt * 32) + fs->BytsPerSec - 1) / fs->BytsPerSec;
UINT64 RootLBA = fs->PartLBA + fs->RsvdSecCnt + fs->NumFATs * fs->FATSz;
for (UINTN s = 0; s < RootSecs; s++) {
if (EFI_ERROR(blk_read(fs->Dev, RootLBA + s, 1, Buf)))
if (EFI_ERROR(blk_read(fs->Dev, RootLBA + s, 1, Buf + s * fs->BytsPerSec)))
goto done;
if (process_sector(fs, Buf, &lfn, Callback, Ctx))
}
for (UINTN s = 0; s < RootSecs; s++) {
if (process_sector(fs, Buf + s * fs->BytsPerSec,
s * fs->BytsPerSec,
&lfn, Callback, Ctx))
goto done;
}
} else {
UINT32 Clus = Cluster;
while (Clus >= 2 && Clus < 0x0FFFFFF8) {
UINT64 BaseLBA = clus_to_lba(fs, Clus);
if (EFI_ERROR(blk_read(fs->Dev, BaseLBA, fs->SecPerClus, Buf)))
goto done;
for (UINTN s = 0; s < fs->SecPerClus; s++) {
if (EFI_ERROR(blk_read(fs->Dev, BaseLBA + s, 1, Buf)))
goto done;
if (process_sector(fs, Buf, &lfn, Callback, Ctx))
if (process_sector(fs, Buf + s * fs->BytsPerSec,
s * fs->BytsPerSec,
&lfn, Callback, Ctx))
goto done;
}
Clus = fat_next(fs, Clus);
@@ -545,7 +561,7 @@ static void name_to_ascii(const CHAR16 *Name, char *Ascii, UINTN ascii_sz) {
}
static void list_callback(void *ctx, const CHAR16 *Name, UINT8 Attr,
UINT32 Size, UINT32 FirstClus) {
UINT32 Size, UINT32 FirstClus, UINTN EntryOff) {
struct list_ctx *lc = (struct list_ctx*)ctx;
for (SSINT32 i = 0; i < lc->depth; i++) serial_write(" ");
@@ -641,7 +657,7 @@ static BOOLEAN name_match(const CHAR16 *a, const CHAR16 *b) {
}
static void find_callback(void *ctx, const CHAR16 *Name, UINT8 Attr,
UINT32 Size, UINT32 FirstClus) {
UINT32 Size, UINT32 FirstClus, UINTN EntryOff) {
struct find_ctx *fc = (struct find_ctx*)ctx;
if (fc->Found) return;
if (name_match(fc->Target, Name)) {
@@ -684,16 +700,24 @@ EFI_STATUS fs_read(WString Path, void **Buffer, UINTN *Size) {
UINTN ClusBytes = g_fs.ClusSize;
while (Clus >= 2 && Clus < 0x0FFFFFF8 && Offset < FileSz) {
UINT64 LBA = clus_to_lba(&g_fs, Clus);
for (UINTN s = 0; s < g_fs.SecPerClus && Offset < FileSz; s++) {
UINTN Part = g_fs.BytsPerSec;
if (Part > FileSz - Offset) Part = FileSz - Offset;
EFI_STATUS st = blk_read(g_fs.Dev, LBA + s, 1,
(UINT8*)Buf + Offset);
if (EFI_ERROR(st)) { kfree(Buf); return st; }
Offset += Part;
// Find longest contiguous run starting at Clus
UINT32 RunStart = Clus;
UINT32 RunEnd = Clus;
UINT32 Next = fat_next(&g_fs, Clus);
while (Next == RunEnd + 1 && Next >= 2 && Next < 0x0FFFFFF8) {
RunEnd = Next;
Next = fat_next(&g_fs, RunEnd);
}
Clus = fat_next(&g_fs, Clus);
UINTN RunClus = (UINTN)(RunEnd - RunStart + 1);
UINT64 LBA = clus_to_lba(&g_fs, RunStart);
UINTN Want = RunClus * ClusBytes;
if (Want > FileSz - Offset) Want = FileSz - Offset;
UINTN NSecs = (Want + g_fs.BytsPerSec - 1) / g_fs.BytsPerSec;
EFI_STATUS st = blk_read(g_fs.Dev, LBA, NSecs,
(UINT8*)Buf + Offset);
if (EFI_ERROR(st)) { kfree(Buf); return st; }
Offset += Want;
Clus = Next;
}
}
@@ -708,3 +732,246 @@ EFI_STATUS fs_read(WString Path, void **Buffer, UINTN *Size) {
return EFI_NOT_FOUND;
}
// ---- FAT allocation ----
static BOOLEAN fat_entry_free(struct fat_fs *fs, UINT32 Clus) {
if (Clus >= fs->FATEntries) return FALSE;
if (fs->IsFAT32) return (fs->FAT32[Clus] & 0x0FFFFFFF) == 0;
return fs->FAT16[Clus] == 0;
}
static void fat_entry_set(struct fat_fs *fs, UINT32 Clus, UINT32 Val) {
if (Clus >= fs->FATEntries) return;
if (fs->IsFAT32) fs->FAT32[Clus] = Val;
else fs->FAT16[Clus] = (UINT16)Val;
}
// Allocate `Count` free clusters and link them into a chain.
// Returns the first cluster of the new chain; the last entry is marked EOF.
static EFI_STATUS fat_alloc_chain(struct fat_fs *fs, UINT32 Count, UINT32 *OutFirst) {
UINT32 Found = 0;
UINT32 First = 0, Prev = 0;
UINT32 EOC = fs->IsFAT32 ? 0x0FFFFFFF : 0xFFFF;
for (UINT32 i = 2; i < fs->FATEntries && Found < Count; i++) {
if (!fat_entry_free(fs, i)) continue;
if (Found == 0) First = i;
else fat_entry_set(fs, Prev, i);
Found++;
Prev = i;
}
if (Found < Count) return EFI_OUT_OF_RESOURCES;
fat_entry_set(fs, Prev, EOC);
*OutFirst = First;
return EFI_SUCCESS;
}
// Write the in-memory FAT cache back to all FAT copies on disk.
static EFI_STATUS fat_flush(struct fat_fs *fs) {
for (UINTN f = 0; f < fs->NumFATs; f++) {
EFI_STATUS st = blk_write(fs->Dev,
fs->PartLBA + fs->RsvdSecCnt + f * fs->FATSz,
fs->FATSz, fs->FatBuf);
if (EFI_ERROR(st)) return st;
}
return EFI_SUCCESS;
}
// ---- File writing ----
struct find_write_ctx {
const CHAR16 *Target;
BOOLEAN Found;
UINT32 Cluster;
UINT32 Size;
UINT8 Attr;
UINTN DirOffset;
};
static void find_write_callback(void *ctx, const CHAR16 *Name, UINT8 Attr,
UINT32 Size, UINT32 FirstClus, UINTN EntryOff) {
struct find_write_ctx *fc = (struct find_write_ctx*)ctx;
if (fc->Found) return;
if (name_match(fc->Target, Name)) {
fc->Found = TRUE;
fc->Cluster = FirstClus;
fc->Size = Size;
fc->Attr = Attr;
fc->DirOffset = EntryOff;
}
}
// Update a dirent's size + first-cluster fields in-place inside `DirBuf`
// at the given byte offset. For FAT32 we also patch the high cluster word.
static void dirent_update_size(void *DirBuf, UINTN EntryOff,
UINT32 NewSize, UINT32 FirstClus,
BOOLEAN PatchFirstClus, BOOLEAN IsFAT32) {
UINT8 *E = (UINT8*)DirBuf + EntryOff;
*(UINT32*)(E + 28) = NewSize;
if (PatchFirstClus) {
if (IsFAT32) {
*(UINT16*)(E + 20) = (UINT16)(FirstClus >> 16);
*(UINT16*)(E + 26) = (UINT16)(FirstClus & 0xFFFF);
} else {
*(UINT16*)(E + 26) = (UINT16)(FirstClus & 0xFFFF);
}
}
}
// Write [Data, Data+Size) into the file at Path, starting at byte Offset.
// Semantics: existing file only (no create); Offset must be <= current size.
// New file size = Offset + Size. Trailing clusters beyond the new size are
// left allocated (lost) — no truncate support.
EFI_STATUS fs_write(WString Path, const void *Data, UINTN Size, UINTN Offset) {
if (!g_fs_inited) return EFI_NOT_READY;
if (Data == NULL && Size > 0) return EFI_INVALID_PARAMETER;
const CHAR16 *p = Path;
while (*p == L'\\') p++;
UINT32 CurClus = g_fs.IsFAT32 ? g_fs.RootClus : 0;
CHAR16 Comp[256];
BOOLEAN Resolved = FALSE;
UINT32 FileFirst = 0, FileSize = 0, FileAttr = 0;
UINTN FileDirOff = 0;
while (*p) {
UINTN ci = 0;
while (*p && *p != L'\\' && ci < 255) Comp[ci++] = *p++;
Comp[ci] = 0;
while (*p == L'\\') p++;
struct find_write_ctx fc;
fc.Target = Comp;
fc.Found = FALSE;
read_directory(&g_fs, CurClus, find_write_callback, &fc);
if (!fc.Found) return EFI_NOT_FOUND;
if (*p == 0) {
if (fc.Attr & 0x10) return EFI_INVALID_PARAMETER;
Resolved = TRUE;
FileFirst = fc.Cluster;
FileSize = fc.Size;
FileAttr = fc.Attr;
FileDirOff = fc.DirOffset;
} else {
if (!(fc.Attr & 0x10)) return EFI_NOT_FOUND;
CurClus = fc.Cluster;
}
}
if (!Resolved) return EFI_NOT_FOUND;
if (Offset > FileSize) return EFI_INVALID_PARAMETER;
UINT32 NewSize = (UINT32)(Offset + Size);
UINT32 OldClusCount = (FileSize + g_fs.ClusSize - 1) / g_fs.ClusSize;
UINT32 NewClusCount = (NewSize + g_fs.ClusSize - 1) / g_fs.ClusSize;
UINT32 AddClusCount = NewClusCount - OldClusCount;
BOOLEAN FileWasEmpty = (FileSize == 0);
// --- Extend cluster chain if we need more clusters ---
if (AddClusCount > 0) {
UINT32 NewFirst;
EFI_STATUS st = fat_alloc_chain(&g_fs, AddClusCount, &NewFirst);
if (EFI_ERROR(st)) return st;
if (FileWasEmpty) {
FileFirst = NewFirst;
} else {
// Walk to the last cluster of the existing chain
UINT32 Cur = FileFirst;
UINT32 Next;
while (TRUE) {
Next = fat_next(&g_fs, Cur);
if (Next >= 0x0FFFFFF8) break;
Cur = Next;
}
// Cur is the last cluster; link to new chain
fat_entry_set(&g_fs, Cur, NewFirst);
}
}
// --- Write data clusters ---
if (Size > 0) {
// Walk to the cluster containing byte Offset
UINT32 Clus = FileFirst;
UINTN SkipBytes = Offset;
while (SkipBytes >= g_fs.ClusSize) {
Clus = fat_next(&g_fs, Clus);
SkipBytes -= g_fs.ClusSize;
}
void *ClusBuf = kmalloc(g_fs.ClusSize);
if (!ClusBuf) return EFI_OUT_OF_RESOURCES;
const UINT8 *Src = (const UINT8*)Data;
UINTN Remaining = Size;
UINTN InClusOff = SkipBytes;
while (Remaining > 0) {
UINT64 LBA = clus_to_lba(&g_fs, Clus);
UINTN SpaceInClus = g_fs.ClusSize - InClusOff;
UINTN Chunk = Remaining < SpaceInClus ? Remaining : SpaceInClus;
if (InClusOff > 0 || Chunk < g_fs.ClusSize) {
EFI_STATUS rst = blk_read(g_fs.Dev, LBA, g_fs.SecPerClus, ClusBuf);
if (EFI_ERROR(rst)) { kfree(ClusBuf); return rst; }
mem_copy((UINT8*)ClusBuf + InClusOff, Src, Chunk);
EFI_STATUS wst = blk_write(g_fs.Dev, LBA, g_fs.SecPerClus, ClusBuf);
if (EFI_ERROR(wst)) { kfree(ClusBuf); return wst; }
} else {
EFI_STATUS wst = blk_write(g_fs.Dev, LBA, g_fs.SecPerClus, Src);
if (EFI_ERROR(wst)) { kfree(ClusBuf); return wst; }
}
Src += Chunk;
Remaining -= Chunk;
InClusOff = 0;
if (Remaining > 0) {
Clus = fat_next(&g_fs, Clus);
}
}
kfree(ClusBuf);
}
// --- Update dirent (size, and first cluster if file was empty) ---
{
void *DirBuf;
UINT64 DirLBA;
UINTN DirNSecs;
UINTN DirBufBytes;
if (g_fs.IsFAT32) {
DirLBA = clus_to_lba(&g_fs, CurClus);
DirNSecs = g_fs.SecPerClus;
DirBufBytes = g_fs.ClusSize;
} else if (CurClus == 0) {
UINT32 RootSecs = ((g_fs.RootEntCnt * 32) + g_fs.BytsPerSec - 1) / g_fs.BytsPerSec;
DirLBA = g_fs.PartLBA + g_fs.RsvdSecCnt + g_fs.NumFATs * g_fs.FATSz;
DirNSecs = RootSecs;
DirBufBytes = RootSecs * g_fs.BytsPerSec;
} else {
DirLBA = clus_to_lba(&g_fs, CurClus);
DirNSecs = g_fs.SecPerClus;
DirBufBytes = g_fs.ClusSize;
}
DirBuf = kmalloc(DirBufBytes);
if (!DirBuf) return EFI_OUT_OF_RESOURCES;
EFI_STATUS rst = blk_read(g_fs.Dev, DirLBA, DirNSecs, DirBuf);
if (EFI_ERROR(rst)) { kfree(DirBuf); return rst; }
dirent_update_size(DirBuf, FileDirOff, NewSize, FileFirst,
FileWasEmpty, g_fs.IsFAT32);
EFI_STATUS wst = blk_write(g_fs.Dev, DirLBA, DirNSecs, DirBuf);
kfree(DirBuf);
if (EFI_ERROR(wst)) return wst;
}
// --- Flush FAT ---
return fat_flush(&g_fs);
}