move keyboard and mouse out of HAL into a standalone ps2.sys driver, implement usb stack with xhci controller discovery, hid keyboard/mouse, and bulk-only mass storage, add pci msi support with capability chain walking and software irq vector allocation (slots 16-23), rewrite user virtual memory as a USER_VA_REGION descriptor arena with per-process ownership tracking and guard page support, replacing the old bump allocator and top-down image region, add MmCreateUserRegion/MmDestroyAllUserRegions so process teardown reclaims all user va in one pass, allocate a pool-backed shadow framebuffer in hal_screen to fix cursor tearing and scroll corruption, reserve ap trampoline page below 1mb in uefi bootloader before boot services exit, rewrite memset/memcpy/memmove/memcmp with __stosq/__movsq intrinsics, promote driver flag literals to macros and add multi-tu STANDALONE_DRIVERS table to build.c, split rtl8139/rtl816x into header+source, rename KbReadScancode to KbReadKey, improve page fault output with MmDebugDescribeAddress and user region dump, fix %p to %016llX throughout.
This commit is contained in:
1 parent
b970d7f59c
commit
b838f8d83d
130 files changed
+8137
-2932
No files matched your search
+119
-154
@@ -1,54 +1,5 @@
|
||||
// fat32.c - FAT32 filesystem driver
|
||||
// Read + write support: file data, cluster allocation, directory entries.
|
||||
//
|
||||
// Performance improvements over the original:
|
||||
//
|
||||
// 1. FAT sector cache (FAT_CACHE_SLOTS write-back entries).
|
||||
// FatEntry() and WriteFatEntry() go through the cache; the hot FAT
|
||||
// region stays in RAM instead of being read from disk on every chain
|
||||
// traversal step. A 32-slot cache covers 32 x (512/4) = 4096
|
||||
// consecutive cluster entries — enough to keep the entire FAT of a
|
||||
// small partition in memory, and to avoid repeated reads even on large
|
||||
// ones.
|
||||
//
|
||||
// 2. Fat32ReadDir() cluster buffer cached in FAT32_DIR_CTX.
|
||||
// The original re-read the same cluster from disk on every call
|
||||
// (allocate -> read -> return one entry -> free). Now the buffer is
|
||||
// allocated once in Fat32OpenDir, freed in Fat32CloseDir, and only
|
||||
// re-read when the iteration moves to a new cluster.
|
||||
//
|
||||
// 3. AllocCluster() next-free hint.
|
||||
// The search now starts from the cluster after the last allocation
|
||||
// (volume NextFreeHint) and wraps around once, so repeated allocations
|
||||
// on a fragmented volume don't rescan from sector 0 every time.
|
||||
//
|
||||
// 4. Contiguous-cluster run coalescing in Fat32Read() and Fat32Write().
|
||||
// After fetching a cluster's FAT entry the code peeks ahead: if the
|
||||
// next cluster is physically adjacent (cluster+1) it extends the run
|
||||
// rather than issuing a separate ReadSectors/WriteSectors call. A
|
||||
// freshly-written unfragmented file now transfers in one or a handful
|
||||
// of large DMA bursts instead of SectorsPerCluster-sized slices.
|
||||
//
|
||||
// 5. FAT write batching via a dirty-sector cache.
|
||||
// WriteFatEntry() marks modified FAT cache slots dirty instead of
|
||||
// flushing immediately. FlushFatCache() (called from Fat32Close,
|
||||
// Fat32Truncate, and Fat32Delete) writes each dirty slot once for
|
||||
// every FAT copy, collapsing the 2x(read+write) per-entry cost of the
|
||||
// original into a single write per dirty sector per FAT copy.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_irp.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
// fat32.c - FAT32 filesystem core, directory, cache, and I/O implementation.
|
||||
#include "fat32.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// On-disk structures
|
||||
@@ -1042,7 +993,7 @@ static int FindFreeDirentRun(FAT32_VOL *v, uint32_t dirCluster, uint32_t needed,
|
||||
// ---------------------------------------------------------------------------
|
||||
// Open / Close
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||
KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -1059,7 +1010,7 @@ static KSTATUS Fat32Open(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void Fat32Close(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
void Fat32Close(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
(void)fs;
|
||||
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
|
||||
FlushFatCache(f->Vol);
|
||||
@@ -1079,7 +1030,7 @@ static void Fat32Close(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
|
||||
#define MAX_RUN_SECTORS 128u
|
||||
|
||||
static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
uint64_t offset, void *buf,
|
||||
size_t size, size_t *bytesRead) {
|
||||
(void)fs;
|
||||
@@ -1174,7 +1125,7 @@ static KSTATUS Fat32Read(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
// Same strategy as read. Whole-cluster-aligned runs skip the read-modify-
|
||||
// write; partial head/tail clusters still require it.
|
||||
|
||||
static KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
uint64_t offset, const void *buf,
|
||||
size_t size, size_t *bytesWritten) {
|
||||
(void)fs;
|
||||
@@ -1279,9 +1230,9 @@ static KSTATUS Fat32Write(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
// ---------------------------------------------------------------------------
|
||||
// Create
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize);
|
||||
KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize);
|
||||
|
||||
static KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||
KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileCtx) {
|
||||
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -1385,7 +1336,7 @@ static KSTATUS Fat32Create(IO_FILESYSTEM *fs, SYS_STRING_VIEW path, void **fileC
|
||||
// ---------------------------------------------------------------------------
|
||||
// Truncate
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize) {
|
||||
KSTATUS Fat32Truncate(IO_FILESYSTEM *fs, void *fileCtx, uint64_t newSize) {
|
||||
(void)fs;
|
||||
FAT32_FILE *f = (FAT32_FILE *)fileCtx;
|
||||
FAT32_VOL *v = f->Vol;
|
||||
@@ -1445,7 +1396,7 @@ update_size:
|
||||
// ---------------------------------------------------------------------------
|
||||
// Delete
|
||||
// ---------------------------------------------------------------------------
|
||||
static KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
|
||||
KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
|
||||
if (!fs || !SysSvIsValid(path) || path.Length == 0)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -1467,7 +1418,7 @@ static KSTATUS Fat32Delete(IO_FILESYSTEM *fs, SYS_STRING_VIEW path) {
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint64_t Fat32GetSize(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
uint64_t Fat32GetSize(IO_FILESYSTEM *fs, void *fileCtx) {
|
||||
(void)fs; return ((FAT32_FILE *)fileCtx)->FileSize;
|
||||
}
|
||||
|
||||
@@ -1489,7 +1440,7 @@ typedef struct {
|
||||
uint8_t *ClusterBuf; // allocated once in OpenDir, freed in CloseDir
|
||||
} FAT32_DIR_CTX;
|
||||
|
||||
static KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **dirCtx) {
|
||||
KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **dirCtx) {
|
||||
if (!fs || !dirCtx || !SysSvIsValid(dirPath))
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
@@ -1520,7 +1471,7 @@ static KSTATUS Fat32OpenDir(IO_FILESYSTEM *fs, SYS_STRING_VIEW dirPath, void **d
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static KSTATUS Fat32ReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out) {
|
||||
KSTATUS Fat32ReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out) {
|
||||
(void)fs;
|
||||
FAT32_DIR_CTX *ctx = (FAT32_DIR_CTX *)dirCtx;
|
||||
FAT32_VOL *v = ctx->Vol;
|
||||
@@ -1575,7 +1526,7 @@ static KSTATUS Fat32ReadDir(IO_FILESYSTEM *fs, void *dirCtx, KDIR_ENTRY *out) {
|
||||
return STATUS_NO_MORE_FILES;
|
||||
}
|
||||
|
||||
static void Fat32CloseDir(IO_FILESYSTEM *fs, void *dirCtx) {
|
||||
void Fat32CloseDir(IO_FILESYSTEM *fs, void *dirCtx) {
|
||||
(void)fs;
|
||||
FAT32_DIR_CTX *ctx = (FAT32_DIR_CTX *)dirCtx;
|
||||
MmFreePool(ctx->ClusterBuf);
|
||||
@@ -1934,7 +1885,7 @@ static void Fat32AsyncReadSubmitNext(FAT32_ASYNC_RW* ctx) {
|
||||
Fat32AsyncFinish(ctx, s, ctx->Done);
|
||||
}
|
||||
|
||||
static KSTATUS Fat32ReadAsync(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
KSTATUS Fat32ReadAsync(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
uint64_t offset, void *buf, size_t size,
|
||||
IO_FS_ASYNC_COMPLETE complete,
|
||||
void *completeContext) {
|
||||
@@ -2087,7 +2038,7 @@ static void Fat32AsyncWriteSubmitNext(FAT32_ASYNC_RW* ctx) {
|
||||
Fat32AsyncFinish(ctx, s, ctx->Done);
|
||||
}
|
||||
|
||||
static KSTATUS Fat32WriteAsync(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
KSTATUS Fat32WriteAsync(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
uint64_t offset, const void *buf, size_t size,
|
||||
IO_FS_ASYNC_COMPLETE complete,
|
||||
void *completeContext) {
|
||||
@@ -2174,22 +2125,101 @@ static KSTATUS Fat32WriteAsync(IO_FILESYSTEM *fs, void *fileCtx,
|
||||
static FAT32_VOL gVols[FAT32_MAX_VOLUMES];
|
||||
static IO_FILESYSTEM gFs[FAT32_MAX_VOLUMES];
|
||||
|
||||
static void Fat32InitVtable(IO_FILESYSTEM* fs, FAT32_VOL* vol) {
|
||||
memset(fs, 0, sizeof(*fs));
|
||||
fs->Open = Fat32Open;
|
||||
fs->Read = Fat32Read;
|
||||
fs->Write = Fat32Write;
|
||||
fs->ReadAsync = Fat32ReadAsync;
|
||||
fs->WriteAsync = Fat32WriteAsync;
|
||||
fs->Create = Fat32Create;
|
||||
fs->Delete = Fat32Delete;
|
||||
fs->Truncate = Fat32Truncate;
|
||||
fs->GetSize = Fat32GetSize;
|
||||
fs->Close = Fat32Close;
|
||||
fs->DriverCtx = vol;
|
||||
fs->OpenDir = Fat32OpenDir;
|
||||
fs->ReadDir = Fat32ReadDir;
|
||||
fs->CloseDir = Fat32CloseDir;
|
||||
|
||||
static uint32_t gMountedCount;
|
||||
static bool gDiskScanned[FAT32_MAX_VOLUMES];
|
||||
|
||||
static uint32_t Fat32TryMountDisk(uint8_t disk) {
|
||||
if (disk >= FAT32_MAX_VOLUMES)
|
||||
return 0;
|
||||
if (gDiskScanned[disk])
|
||||
return 0;
|
||||
gDiskScanned[disk] = true;
|
||||
|
||||
if (!IoDiskDevicePresent(disk))
|
||||
return 0;
|
||||
|
||||
uint32_t mountedHere = 0;
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (uint32_t part = 0; part < partCount && gMountedCount < FAT32_MAX_VOLUMES; part++) {
|
||||
uint8_t volumeIndex = (uint8_t)gMountedCount;
|
||||
FAT32_VOL* v = &gVols[volumeIndex];
|
||||
memset(v, 0, sizeof(*v));
|
||||
v->DriveIndex = disk;
|
||||
v->PartitionLba = parts[part].StartLba;
|
||||
v->PartitionSize = parts[part].SectorCount;
|
||||
|
||||
uint8_t sector[512];
|
||||
if (!KSUCCESS(ReadSectors(v, v->PartitionLba, 1, sector))) {
|
||||
DbgPrint("FAT32: disk %u part %u: failed to read VBR\n", disk, part);
|
||||
continue;
|
||||
}
|
||||
|
||||
BPB *bpb = (BPB *)sector;
|
||||
if (!IsFat32Vbr(v, v->PartitionLba)) {
|
||||
DbgPrint("FAT32: disk %u part %u: unsupported FAT variant\n", disk, part);
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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)) {
|
||||
gMountedCount++;
|
||||
mountedHere++;
|
||||
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 {
|
||||
DbgPrint("FAT32: disk %u part %u mount as drive %u failed status=%d.\n",
|
||||
disk, part, volumeIndex, status);
|
||||
}
|
||||
}
|
||||
|
||||
if (partCount && !mountedHere && gMountedCount >= FAT32_MAX_VOLUMES) {
|
||||
DbgPrint("FAT32: disk %u: volume table full\n", disk);
|
||||
}
|
||||
|
||||
return mountedHere;
|
||||
}
|
||||
|
||||
static void Fat32DiskArrival(uint8_t driveIndex, DEVICE_OBJECT* device, void* context) {
|
||||
(void)device;
|
||||
(void)context;
|
||||
uint32_t before = gMountedCount;
|
||||
uint32_t mounted = Fat32TryMountDisk(driveIndex);
|
||||
if (mounted) {
|
||||
DbgPrint("FAT32: hotplug scan mounted %u new volume(s); total=%u\n",
|
||||
mounted, gMountedCount);
|
||||
} else if (gMountedCount != before) {
|
||||
DbgPrint("FAT32: hotplug scan total=%u\n", gMountedCount);
|
||||
}
|
||||
}
|
||||
|
||||
KSTATUS DriverEntry(const HAL_API *hal) {
|
||||
@@ -2198,84 +2228,19 @@ KSTATUS DriverEntry(const HAL_API *hal) {
|
||||
|
||||
FatCacheInvalidate();
|
||||
(void)hal;
|
||||
gMountedCount = 0;
|
||||
memset(gDiskScanned, 0, sizeof(gDiskScanned));
|
||||
|
||||
uint32_t mounted = 0;
|
||||
KSTATUS lastStatus = STATUS_NOT_SUPPORTED;
|
||||
|
||||
for (uint8_t disk = 0; disk < FAT32_MAX_VOLUMES && mounted < FAT32_MAX_VOLUMES; disk++) {
|
||||
if (!IoDiskDevicePresent(disk))
|
||||
continue;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
KSTATUS cbStatus = IoRegisterDiskArrivalCallback(Fat32DiskArrival, (void*)0);
|
||||
if (!KSUCCESS(cbStatus)) {
|
||||
DbgPrint("FAT32: disk-arrival callback registration failed st=0x%x\n", cbStatus);
|
||||
}
|
||||
|
||||
if (!mounted) {
|
||||
if (!gMountedCount) {
|
||||
DbgPrint("FAT32: no mountable FAT32 volumes found.\n");
|
||||
return lastStatus;
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
DbgPrint("FAT32: Init complete; mounted %u volume(s).\n", mounted);
|
||||
DbgPrint("FAT32: Init complete; mounted %u volume(s).\n", gMountedCount);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
Reference in new issue
Block a user