feat: vfs - add dynamic filesystem mounts and safe unmount lifecycle
This commit is contained in:
1 parent
b1c55073c7
commit
ca5331c9db
12 files changed
+520
-259
No files matched your search
+86
-90
@@ -19,6 +19,11 @@ namespace Fs::Vfs {
|
||||
// flips driveActive so an interrupted dispatch never sees a null driver.
|
||||
static kcp::Mutex vfsLock;
|
||||
|
||||
// Upper bound on digits in a drive number. Without it the accumulator in
|
||||
// ParsePath overflows on a long digit run and wraps into a valid drive --
|
||||
// "4294967296:/x" would otherwise resolve to drive 0.
|
||||
static constexpr int MaxDriveDigits = 4;
|
||||
|
||||
// Parse "N:/path" into drive number and local path.
|
||||
// Returns true on success, sets outDrive and outPath.
|
||||
static bool ParsePath(const char* path, int& outDrive, const char*& outPath) {
|
||||
@@ -27,15 +32,15 @@ namespace Fs::Vfs {
|
||||
// Parse decimal drive number before ':'
|
||||
int drive = 0;
|
||||
int i = 0;
|
||||
bool hasDigit = false;
|
||||
int digits = 0;
|
||||
|
||||
while (path[i] >= '0' && path[i] <= '9') {
|
||||
if (++digits > MaxDriveDigits) return false;
|
||||
drive = drive * 10 + (path[i] - '0');
|
||||
hasDigit = true;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!hasDigit) return false;
|
||||
if (digits == 0) return false;
|
||||
if (path[i] != ':') return false;
|
||||
|
||||
// Everything after "N:" is the local path
|
||||
@@ -44,6 +49,27 @@ namespace Fs::Vfs {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Resolve a drive number to its driver, or nullptr if the slot is out of
|
||||
// range, unregistered, or empty. Caller must hold vfsLock: the returned
|
||||
// pointer is only valid for as long as the lock is held.
|
||||
static FsDriver* DriverForLocked(int driveNumber) {
|
||||
if (driveNumber < 0 || driveNumber >= MaxDrives) return nullptr;
|
||||
if (!driveActive[driveNumber]) return nullptr;
|
||||
return driveTable[driveNumber];
|
||||
}
|
||||
|
||||
// As DriverForLocked, but for an already-open handle: also rejects handles
|
||||
// whose generation stamp is stale (the drive was unmounted and reused).
|
||||
static FsDriver* DriverForFileLocked(const BackendFile& file) {
|
||||
if (file.localHandle < 0) return nullptr;
|
||||
|
||||
FsDriver* driver = DriverForLocked(file.driveNumber);
|
||||
if (driver == nullptr) return nullptr;
|
||||
if (file.generation != driveGeneration[file.driveNumber]) return nullptr;
|
||||
|
||||
return driver;
|
||||
}
|
||||
|
||||
static void BumpDriveGeneration(int driveNumber) {
|
||||
driveGeneration[driveNumber]++;
|
||||
if (driveGeneration[driveNumber] == 0) {
|
||||
@@ -95,7 +121,16 @@ namespace Fs::Vfs {
|
||||
return -1;
|
||||
}
|
||||
driveActive[driveNumber] = false;
|
||||
FsDriver* driver = driveTable[driveNumber];
|
||||
driveTable[driveNumber] = nullptr;
|
||||
BumpDriveGeneration(driveNumber);
|
||||
|
||||
// Safe to tear down here: every dispatch path runs under vfsLock, so
|
||||
// holding it means no call into this driver can still be in flight.
|
||||
// Unmount frees the FsDriver itself, so nothing may touch it after.
|
||||
if (driver != nullptr && driver->Unmount != nullptr) {
|
||||
driver->Unmount(driver->ctx);
|
||||
}
|
||||
vfsLock.Release();
|
||||
|
||||
Kt::KernelLogStream(Kt::OK, "VFS") << "Unregistered drive " << driveNumber;
|
||||
@@ -135,12 +170,15 @@ namespace Fs::Vfs {
|
||||
const char* localPath;
|
||||
|
||||
if (!ParsePath(path, drive, localPath)) return -1;
|
||||
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
|
||||
|
||||
vfsLock.Acquire();
|
||||
FsDriver* driver = driveTable[drive];
|
||||
FsDriver* driver = DriverForLocked(drive);
|
||||
if (driver == nullptr || driver->Open == nullptr) {
|
||||
vfsLock.Release();
|
||||
return -1;
|
||||
}
|
||||
uint32_t generation = driveGeneration[drive];
|
||||
int localHandle = (driveActive[drive] && driver) ? driver->Open(localPath) : -1;
|
||||
int localHandle = driver->Open(driver->ctx, localPath);
|
||||
vfsLock.Release();
|
||||
if (localHandle < 0) return -1;
|
||||
|
||||
@@ -152,72 +190,44 @@ namespace Fs::Vfs {
|
||||
|
||||
int ReadBackendFile(const BackendFile& file, uint8_t* buffer, uint64_t offset, uint64_t size) {
|
||||
vfsLock.Acquire();
|
||||
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) {
|
||||
FsDriver* driver = DriverForFileLocked(file);
|
||||
if (driver == nullptr || driver->Read == nullptr) {
|
||||
vfsLock.Release();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result = driver->Read(file.localHandle, buffer, offset, size);
|
||||
int result = driver->Read(driver->ctx, file.localHandle, buffer, offset, size);
|
||||
vfsLock.Release();
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t GetBackendFileSize(const BackendFile& file) {
|
||||
vfsLock.Acquire();
|
||||
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) {
|
||||
FsDriver* driver = DriverForFileLocked(file);
|
||||
if (driver == nullptr || driver->GetSize == nullptr) {
|
||||
vfsLock.Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t result = driver->GetSize(file.localHandle);
|
||||
uint64_t result = driver->GetSize(driver->ctx, 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 &&
|
||||
driveActive[file.driveNumber] &&
|
||||
driver != nullptr &&
|
||||
file.generation == driveGeneration[file.driveNumber] &&
|
||||
file.localHandle >= 0 &&
|
||||
driver->Write != nullptr;
|
||||
FsDriver* driver = DriverForFileLocked(file);
|
||||
bool canWrite = driver != nullptr && driver->Write != nullptr;
|
||||
vfsLock.Release();
|
||||
return canWrite;
|
||||
}
|
||||
|
||||
void CloseBackendFile(BackendFile& file) {
|
||||
vfsLock.Acquire();
|
||||
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;
|
||||
FsDriver* driver = DriverForFileLocked(file);
|
||||
if (driver != nullptr && driver->Close != nullptr) {
|
||||
driver->Close(driver->ctx, file.localHandle);
|
||||
}
|
||||
|
||||
driver->Close(file.localHandle);
|
||||
vfsLock.Release();
|
||||
|
||||
file.driveNumber = -1;
|
||||
@@ -227,19 +237,13 @@ namespace Fs::Vfs {
|
||||
|
||||
int WriteBackendFile(const BackendFile& file, const uint8_t* buffer, uint64_t offset, uint64_t size) {
|
||||
vfsLock.Acquire();
|
||||
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) {
|
||||
FsDriver* driver = DriverForFileLocked(file);
|
||||
if (driver == nullptr || driver->Write == nullptr) {
|
||||
vfsLock.Release();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (driver->Write == nullptr) { vfsLock.Release(); return -1; }
|
||||
int result = driver->Write(file.localHandle, buffer, offset, size);
|
||||
int result = driver->Write(driver->ctx, file.localHandle, buffer, offset, size);
|
||||
vfsLock.Release();
|
||||
return result;
|
||||
}
|
||||
@@ -253,15 +257,15 @@ namespace Fs::Vfs {
|
||||
const char* localPath;
|
||||
|
||||
if (!ParsePath(path, drive, localPath)) return -1;
|
||||
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
|
||||
if (driveTable[drive]->Create == nullptr) return -1;
|
||||
|
||||
vfsLock.Acquire();
|
||||
FsDriver* driver = driveTable[drive];
|
||||
FsDriver* driver = DriverForLocked(drive);
|
||||
if (driver == nullptr || driver->Create == nullptr) {
|
||||
vfsLock.Release();
|
||||
return -1;
|
||||
}
|
||||
uint32_t generation = driveGeneration[drive];
|
||||
int localHandle = (driveActive[drive] && driver && driver->Create)
|
||||
? driver->Create(localPath)
|
||||
: -1;
|
||||
int localHandle = driver->Create(driver->ctx, localPath);
|
||||
vfsLock.Release();
|
||||
if (localHandle < 0) return -1;
|
||||
|
||||
@@ -276,13 +280,11 @@ namespace Fs::Vfs {
|
||||
const char* localPath;
|
||||
|
||||
if (!ParsePath(path, drive, localPath)) return -1;
|
||||
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
|
||||
if (driveTable[drive]->Delete == nullptr) return -1;
|
||||
|
||||
vfsLock.Acquire();
|
||||
FsDriver* driver = driveTable[drive];
|
||||
int result = (driveActive[drive] && driver && driver->Delete)
|
||||
? driver->Delete(localPath)
|
||||
FsDriver* driver = DriverForLocked(drive);
|
||||
int result = (driver != nullptr && driver->Delete != nullptr)
|
||||
? driver->Delete(driver->ctx, localPath)
|
||||
: -1;
|
||||
vfsLock.Release();
|
||||
return result;
|
||||
@@ -295,13 +297,11 @@ namespace Fs::Vfs {
|
||||
out = StatInfo{};
|
||||
|
||||
if (!ParsePath(path, drive, localPath)) return -1;
|
||||
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
|
||||
if (driveTable[drive]->Stat == nullptr) return -1;
|
||||
|
||||
vfsLock.Acquire();
|
||||
FsDriver* driver = driveTable[drive];
|
||||
int result = (driveActive[drive] && driver && driver->Stat)
|
||||
? driver->Stat(localPath, &out)
|
||||
FsDriver* driver = DriverForLocked(drive);
|
||||
int result = (driver != nullptr && driver->Stat != nullptr)
|
||||
? driver->Stat(driver->ctx, localPath, &out)
|
||||
: -1;
|
||||
vfsLock.Release();
|
||||
return result;
|
||||
@@ -312,19 +312,19 @@ namespace Fs::Vfs {
|
||||
const char* localPath;
|
||||
|
||||
if (!ParsePath(path, drive, localPath)) return -1;
|
||||
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
|
||||
if (driveTable[drive]->Mkdir == nullptr) return -1;
|
||||
|
||||
vfsLock.Acquire();
|
||||
FsDriver* driver = driveTable[drive];
|
||||
int result = (driveActive[drive] && driver && driver->Mkdir)
|
||||
? driver->Mkdir(localPath)
|
||||
FsDriver* driver = DriverForLocked(drive);
|
||||
int result = (driver != nullptr && driver->Mkdir != nullptr)
|
||||
? driver->Mkdir(driver->ctx, localPath)
|
||||
: -1;
|
||||
vfsLock.Release();
|
||||
return result;
|
||||
}
|
||||
|
||||
int VfsDriveList(int* outDrives, int maxEntries) {
|
||||
if (outDrives == nullptr || maxEntries <= 0) return 0;
|
||||
|
||||
vfsLock.Acquire();
|
||||
int count = 0;
|
||||
for (int i = 0; i < MaxDrives && count < maxEntries; i++) {
|
||||
@@ -341,18 +341,18 @@ namespace Fs::Vfs {
|
||||
outLabel[0] = '\0';
|
||||
|
||||
vfsLock.Acquire();
|
||||
if (driveNumber < 0 || driveNumber >= MaxDrives || !driveActive[driveNumber] ||
|
||||
driveTable[driveNumber] == nullptr) {
|
||||
FsDriver* driver = DriverForLocked(driveNumber);
|
||||
if (driver == nullptr) {
|
||||
vfsLock.Release();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (driveTable[driveNumber]->GetLabel == nullptr) {
|
||||
if (driver->GetLabel == nullptr) {
|
||||
vfsLock.Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* label = driveTable[driveNumber]->GetLabel();
|
||||
const char* label = driver->GetLabel(driver->ctx);
|
||||
if (label == nullptr || label[0] == '\0') {
|
||||
vfsLock.Release();
|
||||
return 0;
|
||||
@@ -379,14 +379,11 @@ namespace Fs::Vfs {
|
||||
|
||||
// Cross-drive rename not supported
|
||||
if (oldDrive != newDrive) return -1;
|
||||
if (oldDrive < 0 || oldDrive >= MaxDrives || !driveActive[oldDrive] ||
|
||||
driveTable[oldDrive] == nullptr) return -1;
|
||||
if (driveTable[oldDrive]->Rename == nullptr) return -1;
|
||||
|
||||
vfsLock.Acquire();
|
||||
FsDriver* driver = driveTable[oldDrive];
|
||||
int result = (driveActive[oldDrive] && driver && driver->Rename)
|
||||
? driver->Rename(oldLocal, newLocal)
|
||||
FsDriver* driver = DriverForLocked(oldDrive);
|
||||
int result = (driver != nullptr && driver->Rename != nullptr)
|
||||
? driver->Rename(driver->ctx, oldLocal, newLocal)
|
||||
: -1;
|
||||
vfsLock.Release();
|
||||
return result;
|
||||
@@ -402,18 +399,17 @@ namespace Fs::Vfs {
|
||||
|
||||
if (startIndex < 0) return -1;
|
||||
if (!ParsePath(path, drive, localPath)) return -1;
|
||||
if (drive < 0 || drive >= MaxDrives || !driveActive[drive] || driveTable[drive] == nullptr) return -1;
|
||||
|
||||
vfsLock.Acquire();
|
||||
FsDriver* driver = driveTable[drive];
|
||||
FsDriver* driver = DriverForLocked(drive);
|
||||
int result;
|
||||
if (!driveActive[drive] || !driver) {
|
||||
if (driver == nullptr) {
|
||||
result = -1;
|
||||
} else if (driver->ReadDirAt) {
|
||||
result = driver->ReadDirAt(localPath, outNames, maxEntries, startIndex);
|
||||
result = driver->ReadDirAt(driver->ctx, localPath, outNames, maxEntries, startIndex);
|
||||
} else if (driver->ReadDir) {
|
||||
// Driver without pagination support: only the first page is reachable.
|
||||
result = (startIndex == 0) ? driver->ReadDir(localPath, outNames, maxEntries) : 0;
|
||||
result = (startIndex == 0) ? driver->ReadDir(driver->ctx, localPath, outNames, maxEntries) : 0;
|
||||
} else {
|
||||
result = -1;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user