add support for more than one volume

This commit is contained in:
maksw committed 2026-05-19 21:52:07 +02:00
1 parent 529f816166
commit 09c1420a32
5 files changed
+230 -106

No files matched your search

+213 -96
View File
@@ -233,8 +233,9 @@ static void FlushFatCache(FAT32_VOL *v);
// Driver state
// ---------------------------------------------------------------------------
struct FAT32_VOL_S {
uint8_t DriveIndex;
uint32_t PartitionLba;
uint8_t DriveIndex; // physical disk DEVICE_OBJECT index
uint64_t PartitionLba; // absolute LBA of the mounted volume
uint64_t PartitionSize; // sectors, best-effort bounds/documentation
uint32_t BytesPerSector;
uint32_t SectorsPerCluster;
uint32_t BytesPerCluster;
@@ -242,11 +243,10 @@ struct FAT32_VOL_S {
uint32_t NumFats;
uint32_t FatSize; // sectors per FAT copy
uint32_t RootCluster;
uint32_t DataStart; // absolute LBA of cluster 2
uint64_t DataStart; // absolute LBA of cluster 2
uint32_t TotalClusters; // usable data clusters
uint32_t NextFreeHint; // fix #3: start cluster for next free-scan
};
typedef struct {
uint32_t StartCluster;
uint32_t FileSize;
@@ -1479,66 +1479,174 @@ static void Fat32CloseDir(IO_FILESYSTEM *fs, void *dirCtx) {
}
// ---------------------------------------------------------------------------
// Partition scan (MBR + GPT) — unchanged
// Volume scan (MBR + GPT + superfloppy)
// ---------------------------------------------------------------------------
static const EFI_GUID GUID_EFI_SYSTEM = { 0xC12A7328, 0xF81F, 0x11D2, {0xBA,0x4B,0x00,0xA0,0xC9,0x3E,0xC9,0x3B} };
static const EFI_GUID GUID_MS_BASIC_DATA = { 0xEBD0A0A2, 0xB9E5, 0x4433, {0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7} };
static const EFI_GUID GUID_LINUX_BASIC_DATA = { 0x0FC63DAF, 0x8483, 0x4772, {0x8E,0x79,0x3D,0x69,0xD8,0x47,0x7D,0xE4} };
static int IsFat32Vbr(FAT32_VOL *v, uint32_t lba) {
#define FAT32_MAX_PARTITIONS_PER_DISK 32
typedef struct FAT32_PARTITION_CANDIDATE {
uint64_t StartLba;
uint64_t SectorCount;
} FAT32_PARTITION_CANDIDATE;
static uint32_t ReadLe32(const void* p) {
const uint8_t* b = (const uint8_t*)p;
return (uint32_t)b[0]
| ((uint32_t)b[1] << 8)
| ((uint32_t)b[2] << 16)
| ((uint32_t)b[3] << 24);
}
static int IsFat32Vbr(FAT32_VOL *v, uint64_t lba) {
uint8_t sector[512];
if (!KSUCCESS(ReadSectors(v, lba, 1, sector))) return 0;
if (sector[510] != 0x55 || sector[511] != 0xAA) return 0;
BPB *bpb = (BPB *)sector;
return bpb->FatSize16 == 0 && bpb->RootEntryCount == 0 && bpb->FatSize32 != 0;
if (bpb->BytesPerSector != 512) return 0; // disk I/O layer is 512-byte sector based
if (bpb->SectorsPerCluster == 0) return 0;
if (bpb->NumFats == 0) return 0;
if (bpb->FatSize16 != 0) return 0;
if (bpb->RootEntryCount != 0) return 0;
if (bpb->FatSize32 == 0) return 0;
if (bpb->RootCluster < 2) return 0;
uint32_t totalSectors = bpb->TotalSectors16 ? bpb->TotalSectors16 : bpb->TotalSectors32;
if (totalSectors == 0) return 0;
if (totalSectors <= (uint32_t)bpb->ReservedSectors + (uint32_t)bpb->NumFats * bpb->FatSize32)
return 0;
return 1;
}
static uint32_t FindFat32Partition(FAT32_VOL *v) {
uint8_t mbr[512];
if (!KSUCCESS(ReadSectors(v, 0, 1, mbr))) return 0;
if (mbr[510] != 0x55 || mbr[511] != 0xAA) return 0;
int isGpt = 0;
for (int i = 0; i < 4; i++) if (mbr[0x1BE + i*16 + 4] == 0xEE) { isGpt = 1; break; }
if (isGpt) {
uint8_t gptSector[512];
if (!KSUCCESS(ReadSectors(v, 1, 1, gptSector))) return 0;
GPT_HEADER *gpt = (GPT_HEADER *)gptSector;
if (memcmp(gpt->Signature, "EFI PART", 8) != 0) return 0;
uint64_t entryLBA = gpt->PartitionEntryLBA;
uint32_t numEntries = gpt->NumberOfPartitionEntries;
uint32_t entrySize = gpt->SizeOfPartitionEntry;
if (entrySize < sizeof(GPT_ENTRY)) return 0;
uint32_t eps = 512 / entrySize; uint8_t entrySector[512];
for (uint32_t i = 0; i < numEntries; i++) {
if (i % eps == 0)
if (!KSUCCESS(ReadSectors(v, entryLBA + i/eps, 1, entrySector))) return 0;
GPT_ENTRY *entry = (GPT_ENTRY *)(entrySector + (i % eps) * entrySize);
static const EFI_GUID emptyGuid = {0};
if (memcmp(&entry->PartitionTypeGUID, &emptyGuid, sizeof(EFI_GUID)) == 0) continue;
if (memcmp(&entry->PartitionTypeGUID, &GUID_EFI_SYSTEM, sizeof(EFI_GUID)) == 0 ||
memcmp(&entry->PartitionTypeGUID, &GUID_MS_BASIC_DATA, sizeof(EFI_GUID)) == 0 ||
memcmp(&entry->PartitionTypeGUID, &GUID_LINUX_BASIC_DATA, sizeof(EFI_GUID)) == 0) {
uint32_t lba = (uint32_t)entry->StartingLBA;
if (IsFat32Vbr(v, lba)) return lba;
}
}
return 0;
}
for (int i = 0; i < 4; i++) {
uint8_t *entry = mbr + 0x1BE + i*16;
if (entry[4] == 0x0B || entry[4] == 0x0C) {
uint32_t lba = *(uint32_t *)(entry + 8);
if (IsFat32Vbr(v, lba)) return lba;
}
static int Fat32CandidateExists(FAT32_PARTITION_CANDIDATE* out,
uint32_t count,
uint64_t startLba) {
for (uint32_t i = 0; i < count; i++) {
if (out[i].StartLba == startLba)
return 1;
}
return 0;
}
static void Fat32AddCandidate(FAT32_VOL* probe,
FAT32_PARTITION_CANDIDATE* out,
uint32_t* count,
uint32_t maxCount,
uint64_t startLba,
uint64_t sectorCount) {
if (!out || !count || *count >= maxCount) return;
if (Fat32CandidateExists(out, *count, startLba)) return;
uint64_t diskSectors = IoDiskSectorCount(probe->DriveIndex);
if (diskSectors) {
if (startLba >= diskSectors) return;
if (sectorCount == 0 || startLba + sectorCount > diskSectors)
sectorCount = diskSectors - startLba;
}
if (!IsFat32Vbr(probe, startLba)) return;
out[*count].StartLba = startLba;
out[*count].SectorCount = sectorCount;
(*count)++;
}
static uint32_t Fat32EnumerateDiskVolumes(uint8_t diskIndex,
FAT32_PARTITION_CANDIDATE* out,
uint32_t maxCount) {
if (!out || maxCount == 0) return 0;
FAT32_VOL probe;
memset(&probe, 0, sizeof(probe));
probe.DriveIndex = diskIndex;
uint32_t count = 0;
uint8_t mbr[512];
if (!KSUCCESS(ReadSectors(&probe, 0, 1, mbr)))
return 0;
// A disk image may be a raw FAT32 volume with no partition table.
Fat32AddCandidate(&probe, out, &count, maxCount, 0, IoDiskSectorCount(diskIndex));
if (mbr[510] != 0x55 || mbr[511] != 0xAA)
return count;
int isGpt = 0;
for (int i = 0; i < 4; i++) {
if (mbr[0x1BE + i * 16 + 4] == 0xEE) {
isGpt = 1;
break;
}
}
if (isGpt) {
uint8_t gptSector[512];
if (!KSUCCESS(ReadSectors(&probe, 1, 1, gptSector)))
return count;
GPT_HEADER* gpt = (GPT_HEADER*)gptSector;
if (memcmp(gpt->Signature, "EFI PART", 8) != 0)
return count;
uint64_t entryLBA = gpt->PartitionEntryLBA;
uint32_t numEntries = gpt->NumberOfPartitionEntries;
uint32_t entrySize = gpt->SizeOfPartitionEntry;
if (entrySize < sizeof(GPT_ENTRY) || entrySize > 512)
return count;
uint32_t entriesPerSector = 512 / entrySize;
if (!entriesPerSector)
return count;
uint8_t entrySector[512];
uint64_t loadedSector = UINT64_MAX;
static const EFI_GUID emptyGuid = {0};
for (uint32_t i = 0; i < numEntries && count < maxCount; i++) {
uint64_t sectorLba = entryLBA + i / entriesPerSector;
if (sectorLba != loadedSector) {
if (!KSUCCESS(ReadSectors(&probe, sectorLba, 1, entrySector)))
return count;
loadedSector = sectorLba;
}
GPT_ENTRY* entry = (GPT_ENTRY*)(entrySector + (i % entriesPerSector) * entrySize);
if (memcmp(&entry->PartitionTypeGUID, &emptyGuid, sizeof(EFI_GUID)) == 0)
continue;
if (memcmp(&entry->PartitionTypeGUID, &GUID_EFI_SYSTEM, sizeof(EFI_GUID)) != 0 &&
memcmp(&entry->PartitionTypeGUID, &GUID_MS_BASIC_DATA, sizeof(EFI_GUID)) != 0 &&
memcmp(&entry->PartitionTypeGUID, &GUID_LINUX_BASIC_DATA, sizeof(EFI_GUID)) != 0)
continue;
uint64_t start = entry->StartingLBA;
uint64_t end = entry->EndingLBA;
if (end < start) continue;
Fat32AddCandidate(&probe, out, &count, maxCount, start, end - start + 1);
}
return count;
}
for (int i = 0; i < 4 && count < maxCount; i++) {
uint8_t* entry = mbr + 0x1BE + i * 16;
uint8_t type = entry[4];
uint32_t start = ReadLe32(entry + 8);
uint32_t size = ReadLe32(entry + 12);
if (!start || !size) continue;
// FAT32 CHS/LBA plus EFI System Partition. The VBR probe is the
// authority, so accepting 0xEF here lets FAT32 ESPs mount too.
if (type == 0x0B || type == 0x0C || type == 0xEF)
Fat32AddCandidate(&probe, out, &count, maxCount, start, size);
}
return count;
}
// ---------------------------------------------------------------------------
// Native async read/write
@@ -1990,63 +2098,72 @@ KSTATUS DriverEntry(const HAL_API *hal) {
uint32_t mounted = 0;
KSTATUS lastStatus = STATUS_NOT_SUPPORTED;
for (uint8_t drive = 0; drive < FAT32_MAX_VOLUMES; drive++) {
if (!IoDiskDevicePresent(drive))
for (uint8_t disk = 0; disk < FAT32_MAX_VOLUMES && mounted < FAT32_MAX_VOLUMES; disk++) {
if (!IoDiskDevicePresent(disk))
continue;
FAT32_VOL* v = &gVols[drive];
memset(v, 0, sizeof(*v));
v->DriveIndex = drive;
uint32_t partLba = FindFat32Partition(v);
if (!partLba) {
DbgPrint("FAT32: drive %u: no FAT32 volume\n", drive);
continue;
}
v->PartitionLba = partLba;
uint8_t sector[512];
if (!KSUCCESS(ReadSectors(v, partLba, 1, sector))) {
lastStatus = STATUS_DEVICE_ERROR;
DbgPrint("FAT32: drive %u: failed to read VBR\n", drive);
FAT32_PARTITION_CANDIDATE parts[FAT32_MAX_PARTITIONS_PER_DISK];
uint32_t partCount = Fat32EnumerateDiskVolumes(disk, parts, FAT32_MAX_PARTITIONS_PER_DISK);
if (!partCount) {
DbgPrint("FAT32: disk %u: no FAT32 volumes\n", disk);
continue;
}
BPB *bpb = (BPB *)sector;
if (bpb->FatSize16 != 0 || bpb->RootEntryCount != 0) {
lastStatus = STATUS_NOT_SUPPORTED;
DbgPrint("FAT32: drive %u: unsupported FAT variant\n", drive);
continue;
}
for (uint32_t part = 0; part < partCount && mounted < FAT32_MAX_VOLUMES; part++) {
uint8_t volumeIndex = (uint8_t)mounted;
FAT32_VOL* v = &gVols[volumeIndex];
memset(v, 0, sizeof(*v));
v->DriveIndex = disk;
v->PartitionLba = parts[part].StartLba;
v->PartitionSize = parts[part].SectorCount;
v->BytesPerSector = bpb->BytesPerSector;
v->SectorsPerCluster = bpb->SectorsPerCluster;
v->BytesPerCluster = bpb->BytesPerSector * bpb->SectorsPerCluster;
v->ReservedSectors = bpb->ReservedSectors;
v->NumFats = bpb->NumFats;
v->FatSize = bpb->FatSize32;
v->RootCluster = bpb->RootCluster;
v->DataStart = partLba + bpb->ReservedSectors + bpb->NumFats * bpb->FatSize32;
v->NextFreeHint = 2;
uint8_t sector[512];
if (!KSUCCESS(ReadSectors(v, v->PartitionLba, 1, sector))) {
lastStatus = STATUS_DEVICE_ERROR;
DbgPrint("FAT32: disk %u part %u: failed to read VBR\n", disk, part);
continue;
}
uint32_t totalSectors = bpb->TotalSectors16 ? bpb->TotalSectors16 : bpb->TotalSectors32;
uint32_t dataSectors = totalSectors - bpb->ReservedSectors - bpb->NumFats * bpb->FatSize32;
v->TotalClusters = dataSectors / bpb->SectorsPerCluster;
BPB *bpb = (BPB *)sector;
if (!IsFat32Vbr(v, v->PartitionLba)) {
lastStatus = STATUS_NOT_SUPPORTED;
DbgPrint("FAT32: disk %u part %u: unsupported FAT variant\n", disk, part);
continue;
}
Fat32InitVtable(&gFs[drive], v);
v->BytesPerSector = bpb->BytesPerSector;
v->SectorsPerCluster = bpb->SectorsPerCluster;
v->BytesPerCluster = bpb->BytesPerSector * bpb->SectorsPerCluster;
v->ReservedSectors = bpb->ReservedSectors;
v->NumFats = bpb->NumFats;
v->FatSize = bpb->FatSize32;
v->RootCluster = bpb->RootCluster;
v->DataStart = v->PartitionLba + bpb->ReservedSectors + (uint64_t)bpb->NumFats * bpb->FatSize32;
v->NextFreeHint = 2;
IO_VOLUME_PARAMS volParams;
volParams.DiskIndex = v->DriveIndex;
volParams.PartitionLba = partLba;
volParams.PartitionSize = totalSectors;
uint32_t totalSectors = bpb->TotalSectors16 ? bpb->TotalSectors16 : bpb->TotalSectors32;
uint32_t dataSectors = totalSectors - bpb->ReservedSectors - bpb->NumFats * bpb->FatSize32;
v->TotalClusters = dataSectors / bpb->SectorsPerCluster;
if (v->PartitionSize == 0 || v->PartitionSize > totalSectors)
v->PartitionSize = totalSectors;
KSTATUS status = IoRegisterFilesystem(drive, &gFs[drive], &volParams);
if (KSUCCESS(status)) {
mounted++;
DbgPrint("FAT32: drive %u mounted (clusters=%u).\n", drive, v->TotalClusters);
} else {
lastStatus = status;
DbgPrint("FAT32: drive %u mount failed status=%d.\n", drive, status);
Fat32InitVtable(&gFs[volumeIndex], v);
IO_VOLUME_PARAMS volParams;
volParams.DiskIndex = v->DriveIndex;
volParams.PartitionLba = v->PartitionLba;
volParams.PartitionSize = v->PartitionSize;
KSTATUS status = IoRegisterFilesystem(volumeIndex, &gFs[volumeIndex], &volParams);
if (KSUCCESS(status)) {
mounted++;
DbgPrint("FAT32: disk %u part %u mounted as drive %u (lba=%llu clusters=%u).\n",
disk, part, volumeIndex, (unsigned long long)v->PartitionLba, v->TotalClusters);
} else {
lastStatus = status;
DbgPrint("FAT32: disk %u part %u mount as drive %u failed status=%d.\n",
disk, part, volumeIndex, status);
}
}
}