feat: GPT, FAT32 driver, disks app, userspace adapted for multiple drives, and more

This commit is contained in:
2026-03-07 15:58:27 +01:00
parent 8c5c259f5d
commit 4d0177d55e
48 changed files with 5777 additions and 873 deletions
+4
View File
@@ -72,4 +72,8 @@ namespace Montauk {
static int Sys_FCreate(const char* path) {
return Fs::Vfs::VfsCreate(path);
}
static int Sys_FDelete(const char* path) {
return Fs::Vfs::VfsDelete(path);
}
};
+123
View File
@@ -0,0 +1,123 @@
/*
* Storage.hpp
* Storage, GPT, format, and mount syscall implementations
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
#include <Drivers/Storage/BlockDevice.hpp>
#include <Drivers/Storage/Gpt.hpp>
#include <Fs/FsProbe.hpp>
#include <Fs/Fat32.hpp>
#include "Syscall.hpp"
namespace Montauk {
static void dl_strcpy_s(char* dst, const char* src, int max) {
int i = 0;
for (; i < max - 1 && src[i]; i++) dst[i] = src[i];
dst[i] = '\0';
}
// Fill a user buffer with partition info. Returns the number of entries written.
static int Sys_PartList(PartInfo* buf, int maxCount) {
if (buf == nullptr || maxCount <= 0) return 0;
int total = Drivers::Storage::Gpt::GetPartitionCount();
int count = total < maxCount ? total : maxCount;
for (int i = 0; i < count; i++) {
auto* p = Drivers::Storage::Gpt::GetPartition(i);
if (!p) break;
buf[i].blockDev = p->BlockDevIndex;
buf[i]._pad0 = 0;
buf[i].startLba = p->StartLba;
buf[i].endLba = p->EndLba;
buf[i].sectorCount = p->SectorCount;
// Copy GUIDs (layout-compatible)
__builtin_memcpy(&buf[i].typeGuid, &p->TypeGuid, sizeof(PartGuid));
__builtin_memcpy(&buf[i].uniqueGuid, &p->UniqueGuid, sizeof(PartGuid));
buf[i].attributes = p->Attributes;
dl_strcpy_s(buf[i].name, p->Name, 72);
dl_strcpy_s(buf[i].typeName,
Drivers::Storage::Gpt::GetTypeName(p->TypeGuid), 24);
}
return count;
}
// Read sectors from a block device. Returns bytes read, or -1 on error.
static int64_t Sys_DiskRead(int blockDev, uint64_t lba, uint32_t count, void* buffer) {
if (buffer == nullptr || count == 0) return -1;
if (count > 128) return -1; // same limit as AHCI
auto* dev = Drivers::Storage::GetBlockDevice(blockDev);
if (!dev) return -1;
if (lba + count > dev->SectorCount) return -1;
if (!dev->ReadSectors(dev->Ctx, lba, count, buffer)) return -1;
return (int64_t)(count * dev->SectorSize);
}
// Write sectors to a block device. Returns bytes written, or -1 on error.
static int64_t Sys_DiskWrite(int blockDev, uint64_t lba, uint32_t count, const void* buffer) {
if (buffer == nullptr || count == 0) return -1;
if (count > 128) return -1;
auto* dev = Drivers::Storage::GetBlockDevice(blockDev);
if (!dev) return -1;
if (lba + count > dev->SectorCount) return -1;
if (!dev->WriteSectors(dev->Ctx, lba, count, buffer)) return -1;
return (int64_t)(count * dev->SectorSize);
}
// Initialize a new GPT on a block device. Returns 0 on success, -1 on error.
static int64_t Sys_GptInit(int blockDev) {
return (int64_t)Drivers::Storage::Gpt::InitializeGpt(blockDev);
}
// Add a partition to an existing GPT. Returns partition index on success, -1 on error.
static int64_t Sys_GptAdd(const GptAddParams* params) {
if (params == nullptr) return -1;
Drivers::Storage::Gpt::Guid typeGuid;
__builtin_memcpy(&typeGuid, &params->typeGuid, sizeof(typeGuid));
return (int64_t)Drivers::Storage::Gpt::AddPartition(
params->blockDev, params->startLba, params->endLba,
typeGuid, params->name);
}
// Mount a partition as a VFS drive. Returns 0 on success, -1 on error.
static int64_t Sys_FsMount(int partIndex, int driveNum) {
return (int64_t)Fs::FsProbe::MountPartition(partIndex, driveNum);
}
// Format a partition with a filesystem. Returns 0 on success, -1 on error.
static int64_t Sys_FsFormat(const FsFormatParams* params) {
if (params == nullptr) return -1;
auto* part = Drivers::Storage::Gpt::GetPartition(params->partIndex);
if (!part) return -1;
switch (params->fsType) {
case FS_TYPE_FAT32:
return (int64_t)Fs::Fat32::Format(
part->BlockDevIndex, part->StartLba, part->SectorCount,
params->label[0] ? params->label : nullptr);
default:
return -1; // unknown filesystem type
}
}
};
+20 -1
View File
@@ -27,7 +27,8 @@
#include "IoRedir.hpp" // SYS_SPAWN_REDIR, SYS_CHILDIO_READ, SYS_CHILDIO_WRITE, SYS_CHILDIO_WRITEKEY, SYS_CHILDIO_SETTERMSZ
#include "Random.hpp" // SYS_GETRANDOM
#include "MemInfo.hpp" // SYS_MEMSTATS
#include "Device.hpp" // SYS_DEVLIST
#include "Device.hpp" // SYS_DEVLIST, SYS_DISKINFO
#include "Storage.hpp" // SYS_PARTLIST, SYS_DISKREAD, SYS_DISKWRITE
#include "Window.hpp" // SYS_WINCREATE, SYS_WINDESTROY, SYS_WINPRESENT, SYS_WINPOLL, SYS_WINENUM, SYS_WINMAP, SYS_WINSENDEVENT, SYS_WINRESIZE, SYS_WINSETSCALE, SYS_WINGETSCALE
// Assembly entry point
@@ -154,6 +155,8 @@ namespace Montauk {
frame->arg3, frame->arg4);
case SYS_FCREATE:
return (int64_t)Sys_FCreate((const char*)frame->arg1);
case SYS_FDELETE:
return (int64_t)Sys_FDelete((const char*)frame->arg1);
case SYS_TERMSCALE:
return Sys_TermScale(frame->arg1, frame->arg2);
case SYS_RESOLVE:
@@ -220,6 +223,22 @@ namespace Montauk {
case SYS_MEMSTATS:
Sys_MemStats((MemStats*)frame->arg1);
return 0;
case SYS_PARTLIST:
return (int64_t)Sys_PartList((PartInfo*)frame->arg1, (int)frame->arg2);
case SYS_DISKREAD:
return (int64_t)Sys_DiskRead((int)frame->arg1, frame->arg2,
(uint32_t)frame->arg3, (void*)frame->arg4);
case SYS_DISKWRITE:
return (int64_t)Sys_DiskWrite((int)frame->arg1, frame->arg2,
(uint32_t)frame->arg3, (const void*)frame->arg4);
case SYS_GPTINIT:
return (int64_t)Sys_GptInit((int)frame->arg1);
case SYS_GPTADD:
return (int64_t)Sys_GptAdd((const GptAddParams*)frame->arg1);
case SYS_FSMOUNT:
return (int64_t)Sys_FsMount((int)frame->arg1, (int)frame->arg2);
case SYS_FSFORMAT:
return (int64_t)Sys_FsFormat((const FsFormatParams*)frame->arg1);
default:
return -1;
}
+48
View File
@@ -90,6 +90,7 @@ namespace Montauk {
/* Filesystem.hpp */
static constexpr uint64_t SYS_FWRITE = 41;
static constexpr uint64_t SYS_FCREATE = 42;
static constexpr uint64_t SYS_FDELETE = 77;
/* Graphics.hpp */
static constexpr uint64_t SYS_TERMSCALE = 43;
@@ -137,6 +138,15 @@ namespace Montauk {
/* MemInfo.hpp */
static constexpr uint64_t SYS_MEMSTATS = 67;
/* Storage.hpp */
static constexpr uint64_t SYS_PARTLIST = 70;
static constexpr uint64_t SYS_DISKREAD = 71;
static constexpr uint64_t SYS_DISKWRITE = 72;
static constexpr uint64_t SYS_GPTINIT = 73;
static constexpr uint64_t SYS_GPTADD = 74;
static constexpr uint64_t SYS_FSMOUNT = 75;
static constexpr uint64_t SYS_FSFORMAT = 76;
static constexpr int SOCK_TCP = 1;
static constexpr int SOCK_UDP = 2;
@@ -254,6 +264,44 @@ namespace Montauk {
char _pad2[1];
};
struct PartGuid {
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
};
struct PartInfo {
int32_t blockDev; // block device index
uint32_t _pad0;
uint64_t startLba;
uint64_t endLba;
uint64_t sectorCount;
PartGuid typeGuid;
PartGuid uniqueGuid;
uint64_t attributes;
char name[72]; // ASCII partition name
char typeName[24]; // human-readable type name
};
struct GptAddParams {
int32_t blockDev;
uint32_t _pad0;
uint64_t startLba; // 0 = auto (fill largest free region)
uint64_t endLba; // 0 = auto
PartGuid typeGuid;
char name[72];
};
// Filesystem type IDs for SYS_FSFORMAT
static constexpr int FS_TYPE_FAT32 = 1;
struct FsFormatParams {
int32_t partIndex; // global partition index
int32_t fsType; // FS_TYPE_FAT32, etc.
char label[32]; // volume label
};
struct ProcInfo {
int32_t pid;
int32_t parentPid;
+4 -3
View File
@@ -11,6 +11,7 @@
#include <Drivers/Net/E1000E.hpp>
#include <Drivers/USB/Xhci.hpp>
#include <Drivers/Storage/Ahci.hpp>
#include <Drivers/Storage/Gpt.hpp>
#include <Graphics/Cursor.hpp>
#include <Net/Net.hpp>
#include <Terminal/Terminal.hpp>
@@ -157,9 +158,9 @@ namespace Drivers {
}
void InitializeStorage() {
// AHCI driver registered SATA devices during ProbeNormal().
// Nothing else to do here for now — the VFS registration
// is handled in Main.cpp after this call.
// AHCI driver registered SATA devices as block devices during
// ProbeNormal(). Now probe all block devices for GPT partitions.
Storage::Gpt::ProbeAll();
}
}
+26 -1
View File
@@ -5,6 +5,7 @@
*/
#include "Ahci.hpp"
#include "BlockDevice.hpp"
#include <Pci/Pci.hpp>
#include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>
@@ -164,13 +165,23 @@ namespace Drivers::Storage::Ahci {
return PortType::None;
}
// Wait for device to become ready (BSY clear) so the signature is valid.
// After HBA/port reset, the device sends a D2H Register FIS with its
// signature once it finishes COMRESET. Until that FIS arrives PORT_SIG
// may contain stale data.
for (int i = 0; i < 1000000; i++) {
uint32_t tfd = ReadPortReg(port, PORT_TFD);
if (!(tfd & PORT_TFD_BSY)) break;
asm volatile("" ::: "memory");
}
uint32_t sig = ReadPortReg(port, PORT_SIG);
switch (sig) {
case SIG_ATA: return PortType::Sata;
case SIG_ATAPI: return PortType::Satapi;
case SIG_SEMB: return PortType::Semb;
case SIG_PM: return PortType::PortMultiplier;
default: return PortType::Sata; // Default to SATA for unknown signatures
default: return PortType::None;
}
}
@@ -725,6 +736,20 @@ namespace Drivers::Storage::Ahci {
if (IdentifyDevice(i)) {
g_ports[i].Active = true;
g_activePortCount++;
// Register as a block device
Storage::BlockDevice bdev = {};
bdev.ReadSectors = [](void* ctx, uint64_t lba, uint32_t count, void* buffer) -> bool {
return ReadSectors((int)(uintptr_t)ctx, lba, count, buffer);
};
bdev.WriteSectors = [](void* ctx, uint64_t lba, uint32_t count, const void* buffer) -> bool {
return WriteSectors((int)(uintptr_t)ctx, lba, count, buffer);
};
bdev.Ctx = (void*)(uintptr_t)i;
bdev.SectorCount = g_ports[i].SectorCount;
bdev.SectorSize = g_ports[i].SectorSizeLog;
memcpy(bdev.Model, g_ports[i].Model, 41);
Storage::RegisterBlockDevice(bdev);
}
}
}
@@ -0,0 +1,29 @@
/*
* BlockDevice.cpp
* Driver-agnostic block device registry
* Copyright (c) 2026 Daniel Hammer
*/
#include "BlockDevice.hpp"
namespace Drivers::Storage {
static BlockDevice g_devices[MaxBlockDevices] = {};
static int g_deviceCount = 0;
int RegisterBlockDevice(const BlockDevice& dev) {
if (g_deviceCount >= MaxBlockDevices) return -1;
g_devices[g_deviceCount] = dev;
return g_deviceCount++;
}
const BlockDevice* GetBlockDevice(int index) {
if (index < 0 || index >= g_deviceCount) return nullptr;
return &g_devices[index];
}
int GetBlockDeviceCount() {
return g_deviceCount;
}
};
@@ -0,0 +1,32 @@
/*
* BlockDevice.hpp
* Driver-agnostic block device registry
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
namespace Drivers::Storage {
static constexpr int MaxBlockDevices = 32;
struct BlockDevice {
bool (*ReadSectors)(void* ctx, uint64_t lba, uint32_t count, void* buffer);
bool (*WriteSectors)(void* ctx, uint64_t lba, uint32_t count, const void* buffer);
void* Ctx;
uint64_t SectorCount;
uint16_t SectorSize;
char Model[41];
};
// Register a block device. Returns the assigned index, or -1 on failure.
int RegisterBlockDevice(const BlockDevice& dev);
// Get a registered block device by index. Returns nullptr if invalid.
const BlockDevice* GetBlockDevice(int index);
// Get the number of registered block devices.
int GetBlockDeviceCount();
};
+710
View File
@@ -0,0 +1,710 @@
/*
* Gpt.cpp
* GUID Partition Table (GPT) parser
* Copyright (c) 2026 Daniel Hammer
*/
#include "Gpt.hpp"
#include "BlockDevice.hpp"
#include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>
#include <Libraries/Memory.hpp>
#include <Memory/PageFrameAllocator.hpp>
using namespace Kt;
namespace Drivers::Storage::Gpt {
// -------------------------------------------------------------------------
// State
// -------------------------------------------------------------------------
static PartitionInfo g_partitions[MaxPartitions] = {};
static int g_partitionCount = 0;
// -------------------------------------------------------------------------
// CRC32 (ISO 3309 / UEFI spec, polynomial 0xEDB88320)
// -------------------------------------------------------------------------
static uint32_t Crc32(const void* data, uint32_t length) {
const uint8_t* buf = (const uint8_t*)data;
uint32_t crc = 0xFFFFFFFF;
for (uint32_t i = 0; i < length; i++) {
crc ^= buf[i];
for (int bit = 0; bit < 8; bit++) {
if (crc & 1)
crc = (crc >> 1) ^ 0xEDB88320;
else
crc >>= 1;
}
}
return ~crc;
}
// -------------------------------------------------------------------------
// GUID helpers
// -------------------------------------------------------------------------
static bool GuidIsZero(const Guid& g) {
return g.Data1 == 0 && g.Data2 == 0 && g.Data3 == 0 &&
g.Data4[0] == 0 && g.Data4[1] == 0 && g.Data4[2] == 0 &&
g.Data4[3] == 0 && g.Data4[4] == 0 && g.Data4[5] == 0 &&
g.Data4[6] == 0 && g.Data4[7] == 0;
}
static bool GuidEquals(const Guid& a, const Guid& b) {
return a.Data1 == b.Data1 && a.Data2 == b.Data2 && a.Data3 == b.Data3 &&
a.Data4[0] == b.Data4[0] && a.Data4[1] == b.Data4[1] &&
a.Data4[2] == b.Data4[2] && a.Data4[3] == b.Data4[3] &&
a.Data4[4] == b.Data4[4] && a.Data4[5] == b.Data4[5] &&
a.Data4[6] == b.Data4[6] && a.Data4[7] == b.Data4[7];
}
// -------------------------------------------------------------------------
// UTF-16LE to ASCII narrowing
// -------------------------------------------------------------------------
static void Utf16ToAscii(const uint16_t* src, int srcLen, char* dst, int dstMax) {
int j = 0;
for (int i = 0; i < srcLen && j < dstMax - 1; i++) {
uint16_t c = src[i];
if (c == 0) break;
dst[j++] = (c < 128) ? (char)c : '?';
}
dst[j] = '\0';
}
// -------------------------------------------------------------------------
// Validate protective MBR
// -------------------------------------------------------------------------
static bool ValidateProtectiveMbr(const uint8_t* sector0) {
const ProtectiveMbr* mbr = (const ProtectiveMbr*)sector0;
if (mbr->Signature != 0xAA55) return false;
// At least one partition entry should have type 0xEE (GPT protective)
for (int i = 0; i < 4; i++) {
if (mbr->Partitions[i].Type == 0xEE) return true;
}
return false;
}
// -------------------------------------------------------------------------
// Validate GPT header
// -------------------------------------------------------------------------
static bool ValidateHeader(GptHeader* hdr, uint64_t expectedLba) {
if (hdr->Signature != GPT_HEADER_SIGNATURE) {
KernelLogStream(ERROR, "GPT") << "Invalid signature";
return false;
}
if (hdr->Revision < GPT_HEADER_REVISION) {
KernelLogStream(ERROR, "GPT") << "Unsupported revision";
return false;
}
if (hdr->HeaderSize < 92 || hdr->HeaderSize > 512) {
KernelLogStream(ERROR, "GPT") << "Invalid header size: " << (uint64_t)hdr->HeaderSize;
return false;
}
if (hdr->MyLba != expectedLba) {
KernelLogStream(ERROR, "GPT") << "MyLBA mismatch: expected "
<< expectedLba << ", got " << hdr->MyLba;
return false;
}
// Verify header CRC32
uint32_t savedCrc = hdr->HeaderCrc32;
hdr->HeaderCrc32 = 0;
uint32_t computed = Crc32(hdr, hdr->HeaderSize);
hdr->HeaderCrc32 = savedCrc;
if (computed != savedCrc) {
KernelLogStream(ERROR, "GPT") << "Header CRC32 mismatch: expected "
<< base::hex << (uint64_t)savedCrc << ", computed " << (uint64_t)computed;
return false;
}
if (hdr->SizeOfPartitionEntry < 128) {
KernelLogStream(ERROR, "GPT") << "Partition entry size too small: "
<< (uint64_t)hdr->SizeOfPartitionEntry;
return false;
}
return true;
}
// -------------------------------------------------------------------------
// Validate partition entry array CRC32
// -------------------------------------------------------------------------
static bool ValidatePartitionArrayCrc(const BlockDevice* dev, const GptHeader* hdr) {
uint32_t totalBytes = hdr->NumberOfPartitionEntries * hdr->SizeOfPartitionEntry;
uint32_t totalSectors = (totalBytes + 511) / 512;
// Read all partition entry sectors into a temporary buffer
// Max 128 entries * 128 bytes = 16384 bytes = 32 sectors = 4 pages
int pagesNeeded = (totalBytes + 0xFFF) / 0x1000;
if (pagesNeeded > 8) {
KernelLogStream(ERROR, "GPT") << "Partition array too large";
return false;
}
void* buf;
if (pagesNeeded == 1) {
buf = Memory::g_pfa->AllocateZeroed();
} else {
buf = Memory::g_pfa->ReallocConsecutive(nullptr, pagesNeeded);
memset(buf, 0, pagesNeeded * 0x1000);
}
// Read in chunks of 128 sectors max
uint8_t* dst = (uint8_t*)buf;
uint64_t lba = hdr->PartitionEntryLba;
uint32_t remaining = totalSectors;
while (remaining > 0) {
uint32_t chunk = remaining > 128 ? 128 : remaining;
if (!dev->ReadSectors(dev->Ctx, lba, chunk, dst)) {
KernelLogStream(ERROR, "GPT") << "Failed to read partition entries at LBA " << lba;
Memory::g_pfa->Free(buf, pagesNeeded);
return false;
}
dst += chunk * 512;
lba += chunk;
remaining -= chunk;
}
uint32_t computed = Crc32(buf, totalBytes);
Memory::g_pfa->Free(buf, pagesNeeded);
if (computed != hdr->PartitionEntryArrayCrc32) {
KernelLogStream(ERROR, "GPT") << "Partition array CRC32 mismatch: expected "
<< base::hex << (uint64_t)hdr->PartitionEntryArrayCrc32
<< ", computed " << (uint64_t)computed;
return false;
}
return true;
}
// -------------------------------------------------------------------------
// Parse partition entries
// -------------------------------------------------------------------------
static int ParsePartitions(const BlockDevice* dev, const GptHeader* hdr, int blockDevIndex) {
uint32_t totalBytes = hdr->NumberOfPartitionEntries * hdr->SizeOfPartitionEntry;
uint32_t totalSectors = (totalBytes + 511) / 512;
int pagesNeeded = (totalBytes + 0xFFF) / 0x1000;
if (pagesNeeded > 8) return 0;
void* buf;
if (pagesNeeded == 1) {
buf = Memory::g_pfa->AllocateZeroed();
} else {
buf = Memory::g_pfa->ReallocConsecutive(nullptr, pagesNeeded);
memset(buf, 0, pagesNeeded * 0x1000);
}
uint8_t* dst = (uint8_t*)buf;
uint64_t lba = hdr->PartitionEntryLba;
uint32_t remaining = totalSectors;
while (remaining > 0) {
uint32_t chunk = remaining > 128 ? 128 : remaining;
if (!dev->ReadSectors(dev->Ctx, lba, chunk, dst)) {
Memory::g_pfa->Free(buf, pagesNeeded);
return 0;
}
dst += chunk * 512;
lba += chunk;
remaining -= chunk;
}
int found = 0;
for (uint32_t i = 0; i < hdr->NumberOfPartitionEntries && g_partitionCount < MaxPartitions; i++) {
const GptPartitionEntry* entry = (const GptPartitionEntry*)
((uint8_t*)buf + i * hdr->SizeOfPartitionEntry);
if (GuidIsZero(entry->TypeGuid)) continue;
if (entry->StartingLba == 0 || entry->EndingLba == 0) continue;
if (entry->StartingLba > entry->EndingLba) continue;
PartitionInfo& part = g_partitions[g_partitionCount];
part.BlockDevIndex = blockDevIndex;
part.StartLba = entry->StartingLba;
part.EndLba = entry->EndingLba;
part.SectorCount = entry->EndingLba - entry->StartingLba + 1;
part.TypeGuid = entry->TypeGuid;
part.UniqueGuid = entry->UniqueGuid;
part.Attributes = entry->Attributes;
uint16_t nameCopy[36];
memcpy(nameCopy, entry->Name, sizeof(nameCopy));
Utf16ToAscii(nameCopy, 36, part.Name, 72);
g_partitionCount++;
found++;
}
Memory::g_pfa->Free(buf, pagesNeeded);
return found;
}
// -------------------------------------------------------------------------
// Public API
// -------------------------------------------------------------------------
const char* GetTypeName(const Guid& typeGuid) {
if (GuidEquals(typeGuid, GUID_EFI_SYSTEM)) return "EFI System";
if (GuidEquals(typeGuid, GUID_BASIC_DATA)) return "Basic Data";
if (GuidEquals(typeGuid, GUID_LINUX_FS)) return "Linux Filesystem";
if (GuidEquals(typeGuid, GUID_LINUX_SWAP)) return "Linux Swap";
return "Unknown";
}
int ProbeDevice(int blockDevIndex) {
const BlockDevice* dev = GetBlockDevice(blockDevIndex);
if (!dev) return 0;
// Need at least 34 sectors (MBR + GPT header + 32 sectors of entries)
if (dev->SectorCount < 34) return 0;
// Read LBA 0 (protective MBR) and LBA 1 (GPT header) — 2 sectors
uint8_t sectorBuf[1024];
if (!dev->ReadSectors(dev->Ctx, 0, 2, sectorBuf)) {
return 0;
}
// Validate protective MBR
if (!ValidateProtectiveMbr(sectorBuf)) {
return 0;
}
// Validate primary GPT header (LBA 1)
GptHeader* hdr = (GptHeader*)(sectorBuf + 512);
if (!ValidateHeader(hdr, 1)) {
KernelLogStream(WARNING, "GPT") << "Primary header invalid on device " << blockDevIndex
<< ", trying backup...";
// Try backup header at last LBA
uint64_t lastLba = dev->SectorCount - 1;
if (!dev->ReadSectors(dev->Ctx, lastLba, 1, sectorBuf + 512)) {
KernelLogStream(ERROR, "GPT") << "Failed to read backup header";
return 0;
}
if (!ValidateHeader(hdr, lastLba)) {
KernelLogStream(ERROR, "GPT") << "Backup header also invalid";
return 0;
}
KernelLogStream(OK, "GPT") << "Using backup GPT header";
}
// Validate partition entry array CRC
if (!ValidatePartitionArrayCrc(dev, hdr)) {
return 0;
}
KernelLogStream(OK, "GPT") << "Valid GPT on device " << blockDevIndex
<< " (" << dev->Model << "): "
<< (uint64_t)hdr->NumberOfPartitionEntries << " entry slots, "
<< (uint64_t)hdr->SizeOfPartitionEntry << " bytes each";
// Parse partition entries
int found = ParsePartitions(dev, hdr, blockDevIndex);
// Log discovered partitions
for (int i = g_partitionCount - found; i < g_partitionCount; i++) {
const PartitionInfo& p = g_partitions[i];
uint64_t sizeMB = (p.SectorCount * dev->SectorSize) / (1024 * 1024);
uint64_t sizeGB = sizeMB / 1024;
if (sizeGB > 0) {
KernelLogStream(OK, "GPT") << " Partition " << (i - (g_partitionCount - found))
<< ": " << (p.Name[0] ? p.Name : "(unnamed)")
<< " [" << GetTypeName(p.TypeGuid) << "] "
<< sizeGB << " GiB"
<< " (LBA " << p.StartLba << "-" << p.EndLba << ")";
} else {
KernelLogStream(OK, "GPT") << " Partition " << (i - (g_partitionCount - found))
<< ": " << (p.Name[0] ? p.Name : "(unnamed)")
<< " [" << GetTypeName(p.TypeGuid) << "] "
<< sizeMB << " MiB"
<< " (LBA " << p.StartLba << "-" << p.EndLba << ")";
}
}
return found;
}
void ProbeAll() {
int devCount = GetBlockDeviceCount();
if (devCount == 0) return;
KernelLogStream(INFO, "GPT") << "Probing " << devCount << " block device(s) for GPT...";
int totalPartitions = 0;
for (int i = 0; i < devCount; i++) {
totalPartitions += ProbeDevice(i);
}
if (totalPartitions > 0) {
KernelLogStream(OK, "GPT") << "Found " << totalPartitions << " partition(s) total";
} else {
KernelLogStream(INFO, "GPT") << "No GPT partitions found";
}
}
int GetPartitionCount() {
return g_partitionCount;
}
const PartitionInfo* GetPartition(int index) {
if (index < 0 || index >= g_partitionCount) return nullptr;
return &g_partitions[index];
}
// -------------------------------------------------------------------------
// ASCII to UTF-16LE for partition names
// -------------------------------------------------------------------------
static void AsciiToUtf16(const char* src, uint16_t* dst, int maxChars) {
int i = 0;
for (; i < maxChars - 1 && src[i]; i++) {
dst[i] = (uint16_t)(uint8_t)src[i];
}
for (; i < maxChars; i++) {
dst[i] = 0;
}
}
// -------------------------------------------------------------------------
// Simple GUID generation from RDTSC + mixing
// -------------------------------------------------------------------------
static uint64_t SimpleRand64() {
uint32_t lo, hi;
asm volatile ("rdtsc" : "=a"(lo), "=d"(hi));
uint64_t val = ((uint64_t)hi << 32) | lo;
// xorshift64
val ^= val << 13;
val ^= val >> 7;
val ^= val << 17;
return val;
}
static Guid GenerateGuid() {
uint64_t a = SimpleRand64();
uint64_t b = SimpleRand64();
Guid g;
memcpy(&g, &a, 8);
memcpy(((uint8_t*)&g) + 8, &b, 8);
// Set version 4 (random) and variant 1
g.Data3 = (g.Data3 & 0x0FFF) | 0x4000;
g.Data4[0] = (g.Data4[0] & 0x3F) | 0x80;
return g;
}
// -------------------------------------------------------------------------
// Write helpers
// -------------------------------------------------------------------------
static bool WriteSector(const BlockDevice* dev, uint64_t lba, const void* buf) {
return dev->WriteSectors(dev->Ctx, lba, 1, buf);
}
// Rebuild and write the partition entry array + both GPT headers.
// entries is the full 128-entry array in memory.
static bool WriteGptStructures(const BlockDevice* dev, GptHeader* primary,
uint8_t* entryArray, uint32_t entryArrayBytes) {
uint32_t entryArraySectors = (entryArrayBytes + 511) / 512;
// Compute partition entry array CRC
uint32_t entryCrc = Crc32(entryArray, entryArrayBytes);
primary->PartitionEntryArrayCrc32 = entryCrc;
// Write primary entry array (starts at LBA 2)
for (uint32_t s = 0; s < entryArraySectors; s++) {
if (!dev->WriteSectors(dev->Ctx, primary->PartitionEntryLba + s, 1,
entryArray + s * 512))
return false;
}
// Write primary header (LBA 1)
primary->HeaderCrc32 = 0;
primary->HeaderCrc32 = Crc32(primary, primary->HeaderSize);
uint8_t hdrSector[512];
memset(hdrSector, 0, 512);
memcpy(hdrSector, primary, primary->HeaderSize);
if (!WriteSector(dev, 1, hdrSector)) return false;
// Build and write backup header at last LBA
GptHeader backup = *primary;
backup.MyLba = primary->AlternateLba;
backup.AlternateLba = primary->MyLba;
// Backup partition entries are right before the backup header
backup.PartitionEntryLba = backup.MyLba - entryArraySectors;
// Write backup entry array
for (uint32_t s = 0; s < entryArraySectors; s++) {
if (!dev->WriteSectors(dev->Ctx, backup.PartitionEntryLba + s, 1,
entryArray + s * 512))
return false;
}
// Write backup header CRC
backup.HeaderCrc32 = 0;
backup.HeaderCrc32 = Crc32(&backup, backup.HeaderSize);
memset(hdrSector, 0, 512);
memcpy(hdrSector, &backup, backup.HeaderSize);
if (!WriteSector(dev, backup.MyLba, hdrSector)) return false;
return true;
}
// -------------------------------------------------------------------------
// InitializeGpt
// -------------------------------------------------------------------------
int InitializeGpt(int blockDevIndex) {
const BlockDevice* dev = GetBlockDevice(blockDevIndex);
if (!dev) return -1;
if (dev->SectorCount < 68) return -1; // minimum for GPT
// Write protective MBR
ProtectiveMbr mbr;
memset(&mbr, 0, sizeof(mbr));
mbr.Signature = 0xAA55;
mbr.Partitions[0].Status = 0x00;
mbr.Partitions[0].Type = 0xEE;
mbr.Partitions[0].LbaFirst = 1;
uint64_t mbrSectors = dev->SectorCount - 1;
mbr.Partitions[0].SectorCount = (mbrSectors > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)mbrSectors;
if (!WriteSector(dev, 0, &mbr)) {
KernelLogStream(ERROR, "GPT") << "Failed to write protective MBR";
return -1;
}
// Build primary GPT header
uint32_t numEntries = 128;
uint32_t entrySize = 128;
uint32_t entryArrayBytes = numEntries * entrySize;
uint32_t entryArraySectors = (entryArrayBytes + 511) / 512; // 32 sectors
GptHeader hdr;
memset(&hdr, 0, sizeof(hdr));
hdr.Signature = GPT_HEADER_SIGNATURE;
hdr.Revision = GPT_HEADER_REVISION;
hdr.HeaderSize = 92;
hdr.MyLba = 1;
hdr.AlternateLba = dev->SectorCount - 1;
hdr.FirstUsableLba = 2 + entryArraySectors; // after primary entries
hdr.LastUsableLba = dev->SectorCount - 2 - entryArraySectors; // before backup entries
hdr.DiskGuid = GenerateGuid();
hdr.PartitionEntryLba = 2;
hdr.NumberOfPartitionEntries = numEntries;
hdr.SizeOfPartitionEntry = entrySize;
// Empty partition entry array
uint8_t* entryArray = (uint8_t*)Memory::g_pfa->ReallocConsecutive(
nullptr, (entryArrayBytes + 0xFFF) / 0x1000);
memset(entryArray, 0, entryArrayBytes);
bool ok = WriteGptStructures(dev, &hdr, entryArray, entryArrayBytes);
Memory::g_pfa->Free(entryArray, (entryArrayBytes + 0xFFF) / 0x1000);
if (!ok) {
KernelLogStream(ERROR, "GPT") << "Failed to write GPT structures";
return -1;
}
KernelLogStream(OK, "GPT") << "Initialized GPT on device " << blockDevIndex
<< " (usable LBA " << hdr.FirstUsableLba << "-" << hdr.LastUsableLba << ")";
return 0;
}
// -------------------------------------------------------------------------
// AddPartition
// -------------------------------------------------------------------------
int AddPartition(int blockDevIndex, uint64_t startLba, uint64_t endLba,
const Guid& typeGuid, const char* name) {
const BlockDevice* dev = GetBlockDevice(blockDevIndex);
if (!dev) return -1;
// Read existing primary GPT header
uint8_t hdrBuf[512];
if (!dev->ReadSectors(dev->Ctx, 1, 1, hdrBuf)) return -1;
GptHeader* hdr = (GptHeader*)hdrBuf;
if (!ValidateHeader(hdr, 1)) {
KernelLogStream(ERROR, "GPT") << "No valid GPT on device " << blockDevIndex;
return -1;
}
// Read partition entry array
uint32_t entryArrayBytes = hdr->NumberOfPartitionEntries * hdr->SizeOfPartitionEntry;
uint32_t pages = (entryArrayBytes + 0xFFF) / 0x1000;
uint8_t* entryArray = (uint8_t*)Memory::g_pfa->ReallocConsecutive(nullptr, pages);
memset(entryArray, 0, pages * 0x1000);
uint32_t entryArraySectors = (entryArrayBytes + 511) / 512;
for (uint32_t s = 0; s < entryArraySectors; s++) {
if (!dev->ReadSectors(dev->Ctx, hdr->PartitionEntryLba + s, 1, entryArray + s * 512)) {
Memory::g_pfa->Free(entryArray, pages);
return -1;
}
}
// Find a free entry slot
int freeSlot = -1;
for (uint32_t i = 0; i < hdr->NumberOfPartitionEntries; i++) {
GptPartitionEntry* e = (GptPartitionEntry*)(entryArray + i * hdr->SizeOfPartitionEntry);
if (GuidIsZero(e->TypeGuid)) {
freeSlot = (int)i;
break;
}
}
if (freeSlot < 0) {
KernelLogStream(ERROR, "GPT") << "No free partition entry slots";
Memory::g_pfa->Free(entryArray, pages);
return -1;
}
// Auto-fill: find largest free region if startLba/endLba are both 0
if (startLba == 0 && endLba == 0) {
// Collect used ranges
struct Range { uint64_t start; uint64_t end; };
Range used[MaxPartitions];
int usedCount = 0;
for (uint32_t i = 0; i < hdr->NumberOfPartitionEntries && usedCount < MaxPartitions; i++) {
GptPartitionEntry* e = (GptPartitionEntry*)(entryArray + i * hdr->SizeOfPartitionEntry);
if (!GuidIsZero(e->TypeGuid)) {
used[usedCount].start = e->StartingLba;
used[usedCount].end = e->EndingLba;
usedCount++;
}
}
// Simple bubble sort by start LBA
for (int i = 0; i < usedCount - 1; i++) {
for (int j = i + 1; j < usedCount; j++) {
if (used[j].start < used[i].start) {
Range tmp = used[i]; used[i] = used[j]; used[j] = tmp;
}
}
}
// Find largest gap
uint64_t bestStart = 0, bestEnd = 0, bestSize = 0;
// Gap before first partition
uint64_t gapStart = hdr->FirstUsableLba;
uint64_t gapEnd = (usedCount > 0) ? used[0].start - 1 : hdr->LastUsableLba;
if (gapEnd >= gapStart && gapEnd - gapStart + 1 > bestSize) {
bestStart = gapStart; bestEnd = gapEnd;
bestSize = gapEnd - gapStart + 1;
}
// Gaps between partitions
for (int i = 0; i < usedCount - 1; i++) {
gapStart = used[i].end + 1;
gapEnd = used[i + 1].start - 1;
if (gapEnd >= gapStart && gapEnd - gapStart + 1 > bestSize) {
bestStart = gapStart; bestEnd = gapEnd;
bestSize = gapEnd - gapStart + 1;
}
}
// Gap after last partition
if (usedCount > 0) {
gapStart = used[usedCount - 1].end + 1;
gapEnd = hdr->LastUsableLba;
if (gapEnd >= gapStart && gapEnd - gapStart + 1 > bestSize) {
bestStart = gapStart; bestEnd = gapEnd;
bestSize = gapEnd - gapStart + 1;
}
}
if (bestSize == 0) {
KernelLogStream(ERROR, "GPT") << "No free space for new partition";
Memory::g_pfa->Free(entryArray, pages);
return -1;
}
startLba = bestStart;
endLba = bestEnd;
}
// Validate range
if (startLba < hdr->FirstUsableLba || endLba > hdr->LastUsableLba || startLba > endLba) {
KernelLogStream(ERROR, "GPT") << "Invalid partition range";
Memory::g_pfa->Free(entryArray, pages);
return -1;
}
// Fill the entry
GptPartitionEntry* newEntry = (GptPartitionEntry*)(entryArray + freeSlot * hdr->SizeOfPartitionEntry);
newEntry->TypeGuid = typeGuid;
newEntry->UniqueGuid = GenerateGuid();
newEntry->StartingLba = startLba;
newEntry->EndingLba = endLba;
newEntry->Attributes = 0;
AsciiToUtf16(name ? name : "", newEntry->Name, 36);
// Write updated GPT structures
bool ok = WriteGptStructures(dev, hdr, entryArray, entryArrayBytes);
Memory::g_pfa->Free(entryArray, pages);
if (!ok) {
KernelLogStream(ERROR, "GPT") << "Failed to write updated GPT";
return -1;
}
// Add to in-memory partition table
if (g_partitionCount < MaxPartitions) {
PartitionInfo& part = g_partitions[g_partitionCount];
part.BlockDevIndex = blockDevIndex;
part.StartLba = startLba;
part.EndLba = endLba;
part.SectorCount = endLba - startLba + 1;
part.TypeGuid = typeGuid;
part.UniqueGuid = newEntry->UniqueGuid;
part.Attributes = 0;
if (name) {
int i = 0;
for (; i < 71 && name[i]; i++) part.Name[i] = name[i];
part.Name[i] = '\0';
} else {
part.Name[0] = '\0';
}
int idx = g_partitionCount++;
KernelLogStream(OK, "GPT") << "Added partition " << freeSlot
<< " [" << GetTypeName(typeGuid) << "] LBA "
<< startLba << "-" << endLba;
return idx;
}
return -1;
}
};
+147
View File
@@ -0,0 +1,147 @@
/*
* Gpt.hpp
* GUID Partition Table (GPT) parser
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
namespace Drivers::Storage::Gpt {
// =========================================================================
// On-disk structures (UEFI Specification §5)
// =========================================================================
struct Guid {
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
} __attribute__((packed));
// Protective MBR partition entry (at LBA 0)
struct MbrPartitionEntry {
uint8_t Status;
uint8_t ChsFirst[3];
uint8_t Type;
uint8_t ChsLast[3];
uint32_t LbaFirst;
uint32_t SectorCount;
} __attribute__((packed));
struct ProtectiveMbr {
uint8_t Bootstrap[446];
MbrPartitionEntry Partitions[4];
uint16_t Signature; // 0xAA55
} __attribute__((packed));
static_assert(sizeof(ProtectiveMbr) == 512);
// GPT Header (at LBA 1, backup at last LBA)
static constexpr uint64_t GPT_HEADER_SIGNATURE = 0x5452415020494645ULL; // "EFI PART"
static constexpr uint32_t GPT_HEADER_REVISION = 0x00010000;
struct GptHeader {
uint64_t Signature;
uint32_t Revision;
uint32_t HeaderSize;
uint32_t HeaderCrc32;
uint32_t Reserved;
uint64_t MyLba;
uint64_t AlternateLba;
uint64_t FirstUsableLba;
uint64_t LastUsableLba;
Guid DiskGuid;
uint64_t PartitionEntryLba;
uint32_t NumberOfPartitionEntries;
uint32_t SizeOfPartitionEntry;
uint32_t PartitionEntryArrayCrc32;
} __attribute__((packed));
static_assert(sizeof(GptHeader) == 92);
// GPT Partition Entry (typically 128 bytes each)
struct GptPartitionEntry {
Guid TypeGuid;
Guid UniqueGuid;
uint64_t StartingLba;
uint64_t EndingLba;
uint64_t Attributes;
uint16_t Name[36]; // UTF-16LE, 36 code units = 72 bytes
} __attribute__((packed));
static_assert(sizeof(GptPartitionEntry) == 128);
// Partition attribute bits
static constexpr uint64_t ATTR_PLATFORM_REQUIRED = (1ULL << 0);
static constexpr uint64_t ATTR_EFI_IGNORE = (1ULL << 1);
static constexpr uint64_t ATTR_LEGACY_BIOS_BOOT = (1ULL << 2);
// Well-known partition type GUIDs
static constexpr Guid GUID_UNUSED = {0, 0, 0, {0,0,0,0,0,0,0,0}};
// EFI System Partition: C12A7328-F81F-11D2-BA4B-00A0C93EC93B
static constexpr Guid GUID_EFI_SYSTEM = {0xC12A7328, 0xF81F, 0x11D2, {0xBA,0x4B,0x00,0xA0,0xC9,0x3E,0xC9,0x3B}};
// Microsoft Basic Data: EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
static constexpr Guid GUID_BASIC_DATA = {0xEBD0A0A2, 0xB9E5, 0x4433, {0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7}};
// Linux Filesystem: 0FC63DAF-8483-4772-8E79-3D69D8477DE4
static constexpr Guid GUID_LINUX_FS = {0x0FC63DAF, 0x8483, 0x4772, {0x8E,0x79,0x3D,0x69,0xD8,0x47,0x7D,0xE4}};
// Linux Swap: 0657FD6D-A4AB-43C4-84E5-0933C84B4F4F
static constexpr Guid GUID_LINUX_SWAP = {0x0657FD6D, 0xA4AB, 0x43C4, {0x84,0xE5,0x09,0x33,0xC8,0x4B,0x4F,0x4F}};
// =========================================================================
// Parsed partition info (kernel-side)
// =========================================================================
static constexpr int MaxPartitions = 128;
struct PartitionInfo {
int BlockDevIndex; // which block device this partition lives on
uint64_t StartLba;
uint64_t EndLba;
uint64_t SectorCount;
Guid TypeGuid;
Guid UniqueGuid;
uint64_t Attributes;
char Name[72]; // ASCII-narrowed from UTF-16LE
};
// =========================================================================
// Public API
// =========================================================================
// Probe a block device for a GPT. Returns the number of partitions found,
// or 0 if no valid GPT was detected.
int ProbeDevice(int blockDevIndex);
// Probe all registered block devices for GPT.
void ProbeAll();
// Get total number of discovered partitions (across all devices).
int GetPartitionCount();
// Get partition info by global index.
const PartitionInfo* GetPartition(int index);
// Look up the human-readable name for a partition type GUID.
const char* GetTypeName(const Guid& typeGuid);
// =========================================================================
// Write support — create GPT and add partitions
// =========================================================================
// Initialize a fresh GPT on a block device (destroys all existing data).
// Creates protective MBR, primary + backup GPT headers, empty partition array.
// Returns 0 on success, -1 on error.
int InitializeGpt(int blockDevIndex);
// Add a partition to an existing GPT.
// startLba/endLba define the range (inclusive). Pass 0/0 to auto-fill
// the largest contiguous free region.
// typeGuid selects the partition type.
// name is an ASCII label (up to 36 chars).
// Returns the global partition index on success, -1 on error.
int AddPartition(int blockDevIndex, uint64_t startLba, uint64_t endLba,
const Guid& typeGuid, const char* name);
};
File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
/*
* Fat32.hpp
* FAT32 filesystem driver
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <cstdint>
#include <Fs/Vfs.hpp>
namespace Fs::Fat32 {
// Try to mount a FAT32 filesystem at the given partition range.
// Returns a FsDriver* on success, nullptr if not a valid FAT32 volume.
Vfs::FsDriver* Mount(int blockDevIndex, uint64_t startLba, uint64_t sectorCount);
// Register Fat32::Mount as a filesystem probe with FsProbe.
void RegisterProbe();
// Format a partition as FAT32.
// blockDevIndex: which block device the partition is on
// startLba: first LBA of the partition
// sectorCount: number of sectors in the partition
// volumeLabel: up to 11-char volume label (null-terminated)
// Returns 0 on success, -1 on error.
int Format(int blockDevIndex, uint64_t startLba, uint64_t sectorCount,
const char* volumeLabel);
};
+70
View File
@@ -0,0 +1,70 @@
/*
* FsProbe.cpp
* Filesystem probe registry
* Copyright (c) 2026 Daniel Hammer
*/
#include "FsProbe.hpp"
#include <Drivers/Storage/Gpt.hpp>
#include <Terminal/Terminal.hpp>
namespace Fs::FsProbe {
static ProbeFn g_probes[MaxProbes] = {};
static int g_probeCount = 0;
void Register(ProbeFn fn) {
if (g_probeCount < MaxProbes && fn) {
g_probes[g_probeCount++] = fn;
}
}
void MountPartitions(int firstDrive) {
int partCount = Drivers::Storage::Gpt::GetPartitionCount();
if (partCount == 0 || g_probeCount == 0) return;
int driveNum = firstDrive;
for (int i = 0; i < partCount && driveNum < Vfs::MaxDrives; i++) {
auto* part = Drivers::Storage::Gpt::GetPartition(i);
if (!part) continue;
for (int p = 0; p < g_probeCount; p++) {
Vfs::FsDriver* driver = g_probes[p](
part->BlockDevIndex, part->StartLba, part->SectorCount);
if (driver) {
Vfs::RegisterDrive(driveNum, driver);
Kt::KernelLogStream(Kt::OK, "FsProbe") << "Mounted partition "
<< i << " as drive " << driveNum;
driveNum++;
break;
}
}
}
}
int MountPartition(int partIndex, int driveNum) {
if (driveNum < 0 || driveNum >= Vfs::MaxDrives) return -1;
if (g_probeCount == 0) return -1;
auto* part = Drivers::Storage::Gpt::GetPartition(partIndex);
if (!part) return -1;
for (int p = 0; p < g_probeCount; p++) {
Vfs::FsDriver* driver = g_probes[p](
part->BlockDevIndex, part->StartLba, part->SectorCount);
if (driver) {
Vfs::RegisterDrive(driveNum, driver);
Kt::KernelLogStream(Kt::OK, "FsProbe") << "Mounted partition "
<< partIndex << " as drive " << driveNum;
return 0;
}
}
Kt::KernelLogStream(Kt::WARNING, "FsProbe") << "No filesystem recognized on partition " << partIndex;
return -1;
}
};
+29
View File
@@ -0,0 +1,29 @@
/*
* FsProbe.hpp
* Filesystem probe registry
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include <Fs/Vfs.hpp>
namespace Fs::FsProbe {
static constexpr int MaxProbes = 8;
// A probe function attempts to mount a filesystem on the given partition.
// Returns a FsDriver* on success, nullptr if the filesystem is not recognized.
using ProbeFn = Vfs::FsDriver* (*)(int blockDevIndex, uint64_t startLba, uint64_t sectorCount);
// Register a filesystem probe function (called once per FS driver at init).
void Register(ProbeFn fn);
// Probe all discovered GPT partitions and auto-mount recognized filesystems.
// Assigns VFS drive numbers starting from firstDrive.
void MountPartitions(int firstDrive = 1);
// Try to mount a single partition (by global partition index) as the given VFS drive.
// Returns 0 on success, -1 if no probe recognized the filesystem.
int MountPartition(int partIndex, int driveNum);
};
+13
View File
@@ -163,6 +163,19 @@ namespace Fs::Vfs {
return globalHandle;
}
int VfsDelete(const char* path) {
int drive;
const char* localPath;
if (!ParsePath(path, drive, localPath)) return -1;
if (drive < 0 || drive >= MaxDrives || driveTable[drive] == nullptr) return -1;
if (driveTable[drive]->Delete == nullptr) return -1;
return driveTable[drive]->Delete(localPath);
}
int VfsReadDir(const char* path, const char** outNames, int maxEntries) {
int drive;
const char* localPath;
+2
View File
@@ -21,6 +21,7 @@ namespace Fs::Vfs {
int (*ReadDir)(const char* path, const char** outNames, int maxEntries);
int (*Write)(int handle, const uint8_t* buffer, uint64_t offset, uint64_t size);
int (*Create)(const char* path);
int (*Delete)(const char* path);
};
void Initialize();
@@ -30,6 +31,7 @@ namespace Fs::Vfs {
int VfsRead(int handle, uint8_t* buffer, uint64_t offset, uint64_t size);
int VfsWrite(int handle, const uint8_t* buffer, uint64_t offset, uint64_t size);
int VfsCreate(const char* path);
int VfsDelete(const char* path);
uint64_t VfsGetSize(int handle);
void VfsClose(int handle);
int VfsReadDir(const char* path, const char** outNames, int maxEntries);
+8 -1
View File
@@ -33,6 +33,8 @@
#include <Hal/Cpu.hpp>
#include <Fs/Ramdisk.hpp>
#include <Fs/Vfs.hpp>
#include <Fs/Fat32.hpp>
#include <Fs/FsProbe.hpp>
#include <Sched/Scheduler.hpp>
#include <Api/Syscall.hpp>
using namespace Kt;
@@ -191,10 +193,15 @@ extern "C" void kmain() {
Fs::Ramdisk::Close,
Fs::Ramdisk::ReadDir,
Fs::Ramdisk::Write,
Fs::Ramdisk::Create
Fs::Ramdisk::Create,
nullptr
};
Fs::Vfs::RegisterDrive(0, &ramdiskDriver);
// Register filesystem probes and auto-mount partitions
Fs::Fat32::RegisterProbe();
Fs::FsProbe::MountPartitions();
Hal::LoadTSS();
Montauk::InitializeSyscalls();