feat: support for USB mass storage devices, hotplugging

This commit is contained in:
2026-05-18 17:47:12 +02:00
parent d340e01e56
commit c20579afd3
36 changed files with 1537 additions and 130 deletions
+146 -29
View File
@@ -11,10 +11,12 @@
namespace Fs::Vfs {
static FsDriver* driveTable[MaxDrives];
static bool driveActive[MaxDrives];
static uint32_t driveGeneration[MaxDrives];
static bool initialized = false;
// Protects handle table and driver dispatch from concurrent CPU access.
// Uses Mutex (not Spinlock) so interrupts stay enabled while held --
// VFS is never called from interrupt context.
// Protects driver dispatch from concurrent CPU access. Hot-unplug only
// flips driveActive so an interrupted dispatch never sees a null driver.
static kcp::Mutex vfsLock;
// Parse "N:/path" into drive number and local path.
@@ -42,106 +44,202 @@ namespace Fs::Vfs {
return true;
}
static void BumpDriveGeneration(int driveNumber) {
driveGeneration[driveNumber]++;
if (driveGeneration[driveNumber] == 0) {
driveGeneration[driveNumber] = 1;
}
}
void Initialize() {
for (int i = 0; i < MaxDrives; i++) {
driveTable[i] = nullptr;
driveActive[i] = false;
driveGeneration[i] = 1;
}
initialized = true;
Kt::KernelLogStream(Kt::OK, "VFS") << "Initialized (" << MaxDrives << " drives)";
}
bool IsInitialized() {
return initialized;
}
int RegisterDrive(int driveNumber, FsDriver* driver) {
if (driveNumber < 0 || driveNumber >= MaxDrives) return -1;
if (driver == nullptr) return -1;
if (!initialized) return -1;
vfsLock.Acquire();
if (driveActive[driveNumber]) {
vfsLock.Release();
return -1;
}
driveTable[driveNumber] = driver;
BumpDriveGeneration(driveNumber);
driveActive[driveNumber] = true;
vfsLock.Release();
Kt::KernelLogStream(Kt::OK, "VFS") << "Registered drive " << driveNumber;
return 0;
}
int UnregisterDrive(int driveNumber) {
if (!initialized) return -1;
if (driveNumber < 0 || driveNumber >= MaxDrives) return -1;
vfsLock.Acquire();
if (!driveActive[driveNumber]) {
vfsLock.Release();
return -1;
}
driveActive[driveNumber] = false;
BumpDriveGeneration(driveNumber);
vfsLock.Release();
Kt::KernelLogStream(Kt::OK, "VFS") << "Unregistered drive " << driveNumber;
return 0;
}
bool IsDriveRegistered(int driveNumber) {
if (driveNumber < 0 || driveNumber >= MaxDrives) return false;
vfsLock.Acquire();
bool registered = driveActive[driveNumber];
vfsLock.Release();
return registered;
}
int FindLowestAvailableDrive(int firstDrive) {
if (!initialized) return -1;
if (firstDrive < 0) firstDrive = 0;
vfsLock.Acquire();
for (int i = firstDrive; i < MaxDrives; i++) {
if (!driveActive[i]) {
vfsLock.Release();
return i;
}
}
vfsLock.Release();
return -1;
}
int OpenBackendFile(const char* path, BackendFile& outFile) {
outFile.driveNumber = -1;
outFile.localHandle = -1;
outFile.generation = 0;
int drive;
const char* localPath;
if (!ParsePath(path, drive, localPath)) return -1;
if (drive < 0 || drive >= MaxDrives || driveTable[drive] == nullptr) return -1;
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
vfsLock.Acquire();
int localHandle = driveTable[drive]->Open(localPath);
FsDriver* driver = driveTable[drive];
uint32_t generation = driveGeneration[drive];
int localHandle = (driveActive[drive] && driver) ? driver->Open(localPath) : -1;
vfsLock.Release();
if (localHandle < 0) return -1;
outFile.driveNumber = drive;
outFile.localHandle = localHandle;
outFile.generation = generation;
return 0;
}
int ReadBackendFile(const BackendFile& file, uint8_t* buffer, uint64_t offset, uint64_t size) {
vfsLock.Acquire();
if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || driveTable[file.driveNumber] == nullptr ||
FsDriver* driver = (file.driveNumber >= 0 && file.driveNumber < MaxDrives)
? driveTable[file.driveNumber]
: nullptr;
if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || !driveActive[file.driveNumber] ||
driver == nullptr ||
file.generation != driveGeneration[file.driveNumber] ||
file.localHandle < 0) {
vfsLock.Release();
return -1;
}
int result = driveTable[file.driveNumber]->Read(file.localHandle, buffer, offset, size);
int result = driver->Read(file.localHandle, buffer, offset, size);
vfsLock.Release();
return result;
}
uint64_t GetBackendFileSize(const BackendFile& file) {
vfsLock.Acquire();
if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || driveTable[file.driveNumber] == nullptr ||
FsDriver* driver = (file.driveNumber >= 0 && file.driveNumber < MaxDrives)
? driveTable[file.driveNumber]
: nullptr;
if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || !driveActive[file.driveNumber] ||
driver == nullptr ||
file.generation != driveGeneration[file.driveNumber] ||
file.localHandle < 0) {
vfsLock.Release();
return 0;
}
uint64_t result = driveTable[file.driveNumber]->GetSize(file.localHandle);
uint64_t result = driver->GetSize(file.localHandle);
vfsLock.Release();
return result;
}
bool BackendFileCanWrite(const BackendFile& file) {
vfsLock.Acquire();
FsDriver* driver = (file.driveNumber >= 0 && file.driveNumber < MaxDrives)
? driveTable[file.driveNumber]
: nullptr;
bool canWrite = file.driveNumber >= 0 && file.driveNumber < MaxDrives &&
driveTable[file.driveNumber] != nullptr &&
driveActive[file.driveNumber] &&
driver != nullptr &&
file.generation == driveGeneration[file.driveNumber] &&
file.localHandle >= 0 &&
driveTable[file.driveNumber]->Write != nullptr;
driver->Write != nullptr;
vfsLock.Release();
return canWrite;
}
void CloseBackendFile(BackendFile& file) {
vfsLock.Acquire();
if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || driveTable[file.driveNumber] == nullptr ||
FsDriver* driver = (file.driveNumber >= 0 && file.driveNumber < MaxDrives)
? driveTable[file.driveNumber]
: nullptr;
if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || !driveActive[file.driveNumber] ||
driver == nullptr ||
file.generation != driveGeneration[file.driveNumber] ||
file.localHandle < 0) {
vfsLock.Release();
file.driveNumber = -1;
file.localHandle = -1;
file.generation = 0;
return;
}
driveTable[file.driveNumber]->Close(file.localHandle);
driver->Close(file.localHandle);
vfsLock.Release();
file.driveNumber = -1;
file.localHandle = -1;
file.generation = 0;
}
int WriteBackendFile(const BackendFile& file, const uint8_t* buffer, uint64_t offset, uint64_t size) {
vfsLock.Acquire();
if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || driveTable[file.driveNumber] == nullptr ||
FsDriver* driver = (file.driveNumber >= 0 && file.driveNumber < MaxDrives)
? driveTable[file.driveNumber]
: nullptr;
if (file.driveNumber < 0 || file.driveNumber >= MaxDrives || !driveActive[file.driveNumber] ||
driver == nullptr ||
file.generation != driveGeneration[file.driveNumber] ||
file.localHandle < 0) {
vfsLock.Release();
return -1;
}
if (driveTable[file.driveNumber]->Write == nullptr) { vfsLock.Release(); return -1; }
int result = driveTable[file.driveNumber]->Write(file.localHandle, buffer, offset, size);
if (driver->Write == nullptr) { vfsLock.Release(); return -1; }
int result = driver->Write(file.localHandle, buffer, offset, size);
vfsLock.Release();
return result;
}
@@ -149,22 +247,27 @@ namespace Fs::Vfs {
int CreateBackendFile(const char* path, BackendFile& outFile) {
outFile.driveNumber = -1;
outFile.localHandle = -1;
outFile.generation = 0;
int drive;
const char* localPath;
if (!ParsePath(path, drive, localPath)) return -1;
if (drive < 0 || drive >= MaxDrives || driveTable[drive] == nullptr) return -1;
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
if (driveTable[drive]->Create == nullptr) return -1;
vfsLock.Acquire();
int localHandle = driveTable[drive]->Create(localPath);
FsDriver* driver = driveTable[drive];
uint32_t generation = driveGeneration[drive];
int localHandle = (driveActive[drive] && driver && driver->Create)
? driver->Create(localPath)
: -1;
vfsLock.Release();
if (localHandle < 0) return -1;
outFile.driveNumber = drive;
outFile.localHandle = localHandle;
outFile.generation = generation;
return 0;
}
@@ -173,11 +276,14 @@ namespace Fs::Vfs {
const char* localPath;
if (!ParsePath(path, drive, localPath)) return -1;
if (drive < 0 || drive >= MaxDrives || driveTable[drive] == nullptr) return -1;
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
if (driveTable[drive]->Delete == nullptr) return -1;
vfsLock.Acquire();
int result = driveTable[drive]->Delete(localPath);
FsDriver* driver = driveTable[drive];
int result = (driveActive[drive] && driver && driver->Delete)
? driver->Delete(localPath)
: -1;
vfsLock.Release();
return result;
}
@@ -187,11 +293,14 @@ namespace Fs::Vfs {
const char* localPath;
if (!ParsePath(path, drive, localPath)) return -1;
if (drive < 0 || drive >= MaxDrives || driveTable[drive] == nullptr) return -1;
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
if (driveTable[drive]->Mkdir == nullptr) return -1;
vfsLock.Acquire();
int result = driveTable[drive]->Mkdir(localPath);
FsDriver* driver = driveTable[drive];
int result = (driveActive[drive] && driver && driver->Mkdir)
? driver->Mkdir(localPath)
: -1;
vfsLock.Release();
return result;
}
@@ -200,7 +309,7 @@ namespace Fs::Vfs {
vfsLock.Acquire();
int count = 0;
for (int i = 0; i < MaxDrives && count < maxEntries; i++) {
if (driveTable[i] != nullptr) {
if (driveActive[i]) {
outDrives[count++] = i;
}
}
@@ -213,7 +322,8 @@ namespace Fs::Vfs {
outLabel[0] = '\0';
vfsLock.Acquire();
if (driveNumber < 0 || driveNumber >= MaxDrives || driveTable[driveNumber] == nullptr) {
if (driveNumber < 0 || driveNumber >= MaxDrives || !driveActive[driveNumber] ||
driveTable[driveNumber] == nullptr) {
vfsLock.Release();
return -1;
}
@@ -250,11 +360,15 @@ namespace Fs::Vfs {
// Cross-drive rename not supported
if (oldDrive != newDrive) return -1;
if (oldDrive < 0 || oldDrive >= MaxDrives || driveTable[oldDrive] == nullptr) return -1;
if (oldDrive < 0 || oldDrive >= MaxDrives || !driveActive[oldDrive] ||
driveTable[oldDrive] == nullptr) return -1;
if (driveTable[oldDrive]->Rename == nullptr) return -1;
vfsLock.Acquire();
int result = driveTable[oldDrive]->Rename(oldLocal, newLocal);
FsDriver* driver = driveTable[oldDrive];
int result = (driveActive[oldDrive] && driver && driver->Rename)
? driver->Rename(oldLocal, newLocal)
: -1;
vfsLock.Release();
return result;
}
@@ -264,10 +378,13 @@ namespace Fs::Vfs {
const char* localPath;
if (!ParsePath(path, drive, localPath)) return -1;
if (drive < 0 || drive >= MaxDrives || driveTable[drive] == nullptr) return -1;
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
vfsLock.Acquire();
int result = driveTable[drive]->ReadDir(localPath, outNames, maxEntries);
FsDriver* driver = driveTable[drive];
int result = (driveActive[drive] && driver && driver->ReadDir)
? driver->ReadDir(localPath, outNames, maxEntries)
: -1;
vfsLock.Release();
return result;
}