From 60a9b9e97d3707591f317ce86593473d902d85a5 Mon Sep 17 00:00:00 2001 From: pyao12 Date: Fri, 5 Jun 2026 20:14:21 +0800 Subject: [PATCH] [feat] FASTER READ!!! --- AGENTS.md | 62 +++++++--- include/fs.h | 1 + kernel/fs.cpp | 313 ++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 334 insertions(+), 42 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 2bd274d..cb49691 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,28 +7,52 @@ x86_64 UEFI hobby OS. C17 boot → C++17 kernel, ELF64 → PE32+ via objcopy. | Command | What | |---------|------| | `make` | Build `build/BOOTX64.EFI` + `build/Kernel.elf` | -| `make run` | QEMU + OVMF, serial 1 → `serial.log`, serial 2 → stdio, paused for GDB (`:1234`) | -| `make vdir` | Stage both binaries into `vdir/EFI/BOOT/` for manual boot | -| `make clean` | **Always before commit** — untracked build artifacts leak outside `build/` | +| `make disk` | Build FAT32 disk image (128 MiB) only — no QEMU | +| `make run` | `make disk` + QEMU/OVMF. Serial 1 → `serial.log` (gitignored), paused for GDB (`:1234`) | +| `make vdir` | Stage `BOOTX64.EFI` + `Kernel.elf` + `resources/` into `vdir/EFI/BOOT/` for manual boot | +| `make clean` | wipes `build/`, `vdir/`, and `serial.log` | + +**Prereqs for `make run`:** `qemu-system-x86_64`, OVMF at `/usr/share/ovmf/OVMF.fd`, `mtools` (`mformat`/`mmd`/`mcopy`). QEMU starts paused — connect GDB to `:1234` to proceed. Kernel debug output goes to `serial.log`. ## Architecture -- Two-stage boot: `boot.c` (UEFI app) loads `Kernel.elf` from FAT volume, parses ELF PHDRs, jumps to entry -- `kernel/entry.cpp` — `_start` saves `SystemTable`, calls `kernel_main()` -- `kernel/main.cpp` — `extern "C" kernel_main()`: init GOP → serial → PMM → heap → idle (`hlt`) -- `kernel/serial.cpp` — UEFI serial protocol wrapper (write/read/hex) -- `kernel/memory/pmm.cpp` — bitmap + free-list physical page allocator -- `kernel/memory/heap.cpp` — kmalloc/kfree/kcalloc/krealloc (first-fit w/ coalescing) -- `graphics/` — GOP framebuffer, `fonts/` — Hankaku 8×16 font via `pf_print()` -- `efi/` — bundled gnu-efi sources: `gnuefi/` (crt0, lds, reloc), `lib/`, `inc/` -- Kernel linked at `0x100000` (`kernel/kernel.ld`) +`boot.c` is legacy UEFI glue — loads `Kernel.elf` from the FAT volume, parses ELF PHDRs, jumps to entry. Don't refactor unless touching the boot stage. Bundled gnu-efi sources live in `efi/`. + +**Kernel** lives in `kernel/`. Boot calls `_start` (`kernel/entry.cpp:8`), which saves `EFI_SYSTEM_TABLE*` in global `ST`, then `extern "C" kernel_main()` (`kernel/main.cpp:73`) runs: + +1. `init_gop()` → `gfx_init(GOP)` framebuffer (`graphics/context.cpp`) +2. `init_serial()` → locate SERIAL_IO protocol +3. `pmm_init()` — bitmap + free-list page allocator (`kernel/memory/pmm.cpp`) +4. `init_heap()` — kmalloc/kfree/kcalloc/krealloc, first-fit w/ coalescing (`kernel/memory/heap.cpp`) +5. `fs_init()` / `fs_list()` — reads FAT volume via UEFI (`kernel/fs.cpp`) +6. `gdt_init()` → `idt_init()` → `pic_init()` → `pit_init()` (`kernel/interrupt/`) +7. `ASM("sti")` — interrupts on +8. `layer_init()` + `layer_create(...)` for desktop + windows (`kernel/graphics/layer.cpp`) +9. `task_create("compositor", layer_compositor_task)` +10. `scheduler_run()` — never returns. Preemptive, 100 Hz PIT, 5-tick time slice (`kernel/scheduler/`) + +PIT IRQ 0 dispatches to `pit_irq_handler` → `scheduler_tick` via the IRQ handler in `kernel/main.cpp:56`. + +## `include/common.h` — use these, not raw equivalents + +Always `#include ` (it pulls in `` + `` and defines the `ASM` macro): + +| Provided | Use instead of | +|----------|----------------| +| `SSINT8/16/32/64`, `SUINT8/16/32/64` | raw `int*_t` / `uint*_t` | +| `String` (`const char*`), `WString` (`const uint16_t*`) | raw `char*` / `CHAR16*` | +| `str_len`, `str_cmp`, `str_eq`, `str_copy` | hand-rolled loops on `char*` | +| `wstr_len`, `wstr_cmp`, `wstr_eq`, `wstr_copy`, `wstr_to_ascii` | hand-rolled loops on `CHAR16*` | +| `mem_set`, `mem_copy` | hand-rolled byte loops | +| `ASM(...)` | raw `asm volatile(...)` — e.g. `ASM("cli")`, `ASM("hlt")`, `ASM("sti")` | + +All `string_utils.h` helpers are `static inline` — header-only, no link issues. New kernel code should reach for these before adding anything new to `string_utils.h`. ## Conventions -- Commits: `[type] message` with types `feat`, `fix`, `chore`, `docs` (git log) -- `#define ASM asm volatile` in `include/common.h` -- C17 (`boot.c`), C++17 (`kernel/`) — `-ffreestanding -fno-stack-protector -fshort-wchar -mno-red-zone` -- `uefi_call_wrapper` required for all UEFI protocol calls (omission → page-fault) -- `kernel_main` must be `extern "C"` (C→C++ linkage) -- QEMU starts paused: connect GDB to `:1234` to proceed -- No tests, no CI, no lint config +- **Commits:** `[type] message` — types: `feat`, `fix`, `chore`, `docs`, `refactor` (see `git log`). +- **Languages:** C17 (`boot.c`), C++17 (`kernel/`) — `-ffreestanding -fno-stack-protector -fshort-wchar -mno-red-zone -fcf-protection=none`. Kernel adds `-DGNU_EFI_USE_MS_ABI -fno-pic`. +- **`kernel_main` must be `extern "C"`** — `boot.c` (C) calls into C++ (`kernel/entry.cpp:6`). +- **All UEFI protocol calls must use `uefi_call_wrapper`** — calling the member fn directly page-faults in long mode. See `kernel/main.cpp:23-24` and throughout. +- **Kernel linked at `0x100000`** (`kernel/kernel.ld`); `.bss` start/end symbols exposed as `__bss_start` / `__bss_end`. +- **No tests, no CI, no lint config** — `make` is the only verification step. Boot by hand in QEMU to confirm kernel output in `serial.log`. diff --git a/include/fs.h b/include/fs.h index 59a1657..0e0645b 100644 --- a/include/fs.h +++ b/include/fs.h @@ -5,3 +5,4 @@ EFI_STATUS fs_init(); void fs_list(); EFI_STATUS fs_read(WString Path, void **Buffer, UINTN *Size); +EFI_STATUS fs_write(WString Path, const void *Data, UINTN Size, UINTN Offset); diff --git a/kernel/fs.cpp b/kernel/fs.cpp index 3d7ec82..ac45b23 100644 --- a/kernel/fs.cpp +++ b/kernel/fs.cpp @@ -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); +}