isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
This commit is contained in:
1 parent
3dacbd32c8
commit
73abd6f90d
441 files changed
+187073
-6549
No files matched your search
@@ -0,0 +1,755 @@
|
||||
// FAT synchronous file and directory operations.
|
||||
|
||||
#include "fat_internal.h"
|
||||
|
||||
// Open / Close.
|
||||
KSTATUS FatOpen(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, void** fileCtx) {
|
||||
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
|
||||
KSTATUS s = FatLookupPathV(v, path, &cluster, &size, NULL, &direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)MmAllocatePool(sizeof(FAT_FILE));
|
||||
if (!f) return NTLSTATUS_NO_MEMORY;
|
||||
f->StartCluster = cluster; f->FileSize = size; f->Vol = v;
|
||||
f->DirentLba = direntLba; f->DirentOffset = direntOffset;
|
||||
FatFileInitChainCache(f);
|
||||
*fileCtx = f;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Flushes FAT metadata and the backing device write cache.
|
||||
KSTATUS FatFlush(IO_VOLUME_PROVIDER* fs, void* fileCtx) {
|
||||
(void)fs;
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
if (!f || !f->Vol)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FatFlushCache(f->Vol);
|
||||
return IoFlushDisk(f->Vol->DriveIndex);
|
||||
}
|
||||
|
||||
// Releases the requested object.
|
||||
void FatClose(IO_VOLUME_PROVIDER* fs, void* fileCtx) {
|
||||
(void)fs;
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FatFlushCache(f->Vol);
|
||||
MmFreePool(fileCtx);
|
||||
}
|
||||
|
||||
// Read (fix #4: contiguous-run coalescing)
|
||||
// Cluster-chain lookup is cached per open file, and data spans are allowed to
|
||||
// grow beyond the AHCI bounce size because the device layer now splits large
|
||||
// sector requests for the concrete controller.
|
||||
|
||||
KSTATUS FatRead(IO_VOLUME_PROVIDER* fs, void* fileCtx,
|
||||
uint64_t offset, void* buf,
|
||||
size_t size, size_t* bytesRead) {
|
||||
(void)fs;
|
||||
if (!bytesRead) return NTLSTATUS_INVALID_PARAMETER;
|
||||
*bytesRead = 0;
|
||||
if (!fileCtx || !buf) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
if (offset >= f->FileSize || size == 0) return NTLSTATUS_SUCCESS;
|
||||
if (size > (size_t)(f->FileSize - offset)) size = (size_t)(f->FileSize - offset);
|
||||
size_t wanted = size;
|
||||
|
||||
uint32_t initialOffsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
|
||||
uint64_t initialBytesCovered = (uint64_t)initialOffsetInCluster + (uint64_t)size;
|
||||
uint32_t maxUsefulSectors = (uint32_t)((initialBytesCovered + 511u) / 512u);
|
||||
if (maxUsefulSectors < v->SectorsPerCluster) maxUsefulSectors = v->SectorsPerCluster;
|
||||
if (maxUsefulSectors > FAT_READ_RUN_MAX_SECTORS)
|
||||
maxUsefulSectors = FAT_READ_RUN_MAX_SECTORS;
|
||||
|
||||
uint8_t* runBuf = (uint8_t*)MmAllocatePool((size_t)maxUsefulSectors * 512u);
|
||||
if (!runBuf) return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
size_t totalRead = 0;
|
||||
uint8_t* out = (uint8_t*)buf;
|
||||
KSTATUS readStatus = NTLSTATUS_SUCCESS;
|
||||
uint32_t logicalCluster = (uint32_t)(offset / v->BytesPerCluster);
|
||||
uint32_t offsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
|
||||
uint32_t cluster = FatResolveFileCluster(f, logicalCluster);
|
||||
|
||||
while (size > 0 && FatIsDataCluster(v, cluster)) {
|
||||
uint64_t bytesCovered = (uint64_t)offsetInCluster + (uint64_t)size;
|
||||
uint32_t usefulSectors = (uint32_t)((bytesCovered + 511u) / 512u);
|
||||
if (usefulSectors < v->SectorsPerCluster) usefulSectors = v->SectorsPerCluster;
|
||||
if (usefulSectors > FAT_READ_RUN_MAX_SECTORS)
|
||||
usefulSectors = FAT_READ_RUN_MAX_SECTORS;
|
||||
if (usefulSectors > maxUsefulSectors) usefulSectors = maxUsefulSectors;
|
||||
|
||||
uint32_t runSectors = 0;
|
||||
uint32_t next = 0;
|
||||
if (!FatBuildReadRun(f, logicalCluster, cluster, usefulSectors,
|
||||
&runSectors, &next)) {
|
||||
readStatus = NTLSTATUS_DEVICE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
KSTATUS rs = FatReadSectors(v, FatClusterToLba(v, cluster), runSectors, runBuf);
|
||||
if (!KSUCCESS(rs)) {
|
||||
readStatus = rs;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t runBytes = runSectors * 512u;
|
||||
size_t avail = runBytes - offsetInCluster;
|
||||
size_t copy = avail < size ? avail : size;
|
||||
uint32_t runClusters = runSectors / v->SectorsPerCluster;
|
||||
uint32_t merges = runClusters ? runClusters - 1u : 0;
|
||||
FatStatsAddReadRun(v, runSectors, merges);
|
||||
|
||||
memcpy(out, runBuf + offsetInCluster, copy);
|
||||
out += copy;
|
||||
totalRead += copy;
|
||||
size -= copy;
|
||||
offsetInCluster = 0;
|
||||
logicalCluster += runClusters;
|
||||
cluster = next;
|
||||
}
|
||||
|
||||
if (totalRead != wanted) {
|
||||
// The directory entry says the file has more data than the cluster chain
|
||||
// or disk read actually supplied. Clear the caller's remainder so stale
|
||||
// memory cannot be interpreted as file contents.
|
||||
memset(out, 0, wanted - totalRead);
|
||||
}
|
||||
|
||||
MmFreePool(runBuf);
|
||||
*bytesRead = totalRead;
|
||||
if (totalRead != wanted)
|
||||
return KSUCCESS(readStatus) ? NTLSTATUS_DEVICE_ERROR : readStatus;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Write (fix #4: contiguous-run coalescing)
|
||||
// Same strategy as read. Whole-cluster-aligned runs skip the read-modify-
|
||||
// write; partial head/tail clusters still require it.
|
||||
|
||||
KSTATUS FatWrite(IO_VOLUME_PROVIDER* fs, void* fileCtx,
|
||||
uint64_t offset, const void* buf,
|
||||
size_t size, size_t* bytesWritten) {
|
||||
(void)fs;
|
||||
if (!size) { *bytesWritten = 0; return NTLSTATUS_SUCCESS; }
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
const uint8_t* src = (const uint8_t*)buf;
|
||||
FatFileInvalidateChainCache(f);
|
||||
|
||||
// Ensure at least one cluster exists.
|
||||
if (f->StartCluster < 2) {
|
||||
uint32_t first = FatAllocCluster(v);
|
||||
if (!first) { *bytesWritten = 0; return NTLSTATUS_NO_MEMORY; }
|
||||
FatFlushCache(v);
|
||||
f->StartCluster = first;
|
||||
FatDirentUpdateFirstCluster(v, f->DirentLba, f->DirentOffset, first);
|
||||
}
|
||||
|
||||
// Walk to the starting cluster, allocating as needed.
|
||||
uint32_t clusterIndex = (uint32_t)(offset / v->BytesPerCluster);
|
||||
uint32_t offsetInCluster = (uint32_t)(offset % v->BytesPerCluster);
|
||||
uint32_t cluster = f->StartCluster;
|
||||
|
||||
for (uint32_t i = 0; i < clusterIndex; i++) {
|
||||
uint32_t next = FatEntry(v, cluster);
|
||||
if (!FatIsDataCluster(v, next)) {
|
||||
next = FatAppendCluster(v, cluster);
|
||||
if (!next) { *bytesWritten = 0; return NTLSTATUS_NO_MEMORY; }
|
||||
}
|
||||
cluster = next;
|
||||
}
|
||||
|
||||
uint8_t* runBuf = (uint8_t*)MmAllocatePool(FAT_WRITE_RUN_MAX_SECTORS * 512u);
|
||||
if (!runBuf) { *bytesWritten = 0; return NTLSTATUS_NO_MEMORY; }
|
||||
|
||||
size_t written = 0, remaining = size;
|
||||
|
||||
while (remaining > 0 && cluster >= 2) {
|
||||
// Build a contiguous run (only when cluster-aligned to avoid a
|
||||
// partial read-modify-write spanning multiple clusters).
|
||||
uint32_t runStart = cluster;
|
||||
uint32_t runSectors = v->SectorsPerCluster;
|
||||
uint32_t next = FatEntry(v, cluster);
|
||||
|
||||
if (offsetInCluster == 0) {
|
||||
while (next == cluster + 1 && FatIsDataCluster(v, next)
|
||||
&& runSectors + v->SectorsPerCluster <= FAT_WRITE_RUN_MAX_SECTORS) {
|
||||
cluster = next;
|
||||
next = FatEntry(v, cluster);
|
||||
runSectors += v->SectorsPerCluster;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t runBytes = runSectors * 512u;
|
||||
size_t avail = runBytes - offsetInCluster;
|
||||
size_t copy = avail < remaining ? avail : remaining;
|
||||
|
||||
// Read existing data only for partial-cluster writes.
|
||||
bool partial = (offsetInCluster > 0) || (copy < (size_t)runBytes);
|
||||
if (partial) {
|
||||
if (!KSUCCESS(FatReadSectors(v, FatClusterToLba(v, runStart), runSectors, runBuf)))
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(runBuf + offsetInCluster, src, copy);
|
||||
|
||||
if (!KSUCCESS(FatWriteSectors(v, FatClusterToLba(v, runStart), runSectors, runBuf)))
|
||||
break;
|
||||
|
||||
src += copy;
|
||||
written += copy;
|
||||
remaining -= copy;
|
||||
offsetInCluster = 0;
|
||||
|
||||
if (remaining > 0) {
|
||||
// Advance: if end-of-chain, append a new cluster.
|
||||
cluster = next;
|
||||
if (!FatIsDataCluster(v, cluster)) {
|
||||
// Find the actual last cluster in the run we just wrote.
|
||||
uint32_t last = runStart;
|
||||
for (uint32_t tmp = FatEntry(v, last);
|
||||
FatIsDataCluster(v, tmp);
|
||||
tmp = FatEntry(v, last))
|
||||
last = tmp;
|
||||
cluster = FatAppendCluster(v, last);
|
||||
if (!cluster) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MmFreePool(runBuf);
|
||||
|
||||
uint64_t newEnd = offset + written;
|
||||
if (written > 0) {
|
||||
FAT_DIRENT_UPDATE u;
|
||||
memset(&u, 0, sizeof(u));
|
||||
FatQueryEncodedTime(&u.Timestamp);
|
||||
u.Flags = FAT_DIRENT_UPDATE_WRITE_TIME;
|
||||
if (newEnd > f->FileSize) {
|
||||
f->FileSize = (uint32_t)newEnd;
|
||||
u.Flags |= FAT_DIRENT_UPDATE_SIZE;
|
||||
u.Size = f->FileSize;
|
||||
}
|
||||
KSTATUS ms = FatDirentUpdateMetadata(v, f->DirentLba, f->DirentOffset, &u);
|
||||
if (KSUCCESS(ms))
|
||||
FatPathCacheInvalidate(v);
|
||||
}
|
||||
|
||||
*bytesWritten = written;
|
||||
return (written == size) ? NTLSTATUS_SUCCESS : NTLSTATUS_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// Create.
|
||||
// Creates the requested file and fails if the name already exists.
|
||||
KSTATUS FatCreate(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, void** fileCtx) {
|
||||
if (!fs || !fileCtx || !SysSvIsValid(path) || path.Length == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint64_t direntLba = 0;
|
||||
uint32_t direntOffset = 0;
|
||||
KSTATUS s = FatCreateDirent(v, path, ATTR_ARCHIVE, 0, 0,
|
||||
&direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)MmAllocatePool(sizeof(FAT_FILE));
|
||||
if (!f) return NTLSTATUS_NO_MEMORY;
|
||||
f->StartCluster = 0;
|
||||
f->FileSize = 0;
|
||||
f->Vol = v;
|
||||
f->DirentLba = direntLba;
|
||||
f->DirentOffset = direntOffset;
|
||||
FatFileInitChainCache(f);
|
||||
*fileCtx = f;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Creates a directory and initializes its dot entries.
|
||||
KSTATUS FatCreateDir(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path) {
|
||||
if (!fs || !SysSvIsValid(path) || path.Length == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint32_t parentCluster = 0;
|
||||
SYS_STRING_VIEW filenameView;
|
||||
KSTATUS s = FatResolveParentForMutation(v, path, &parentCluster, &filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
(void)filenameView;
|
||||
|
||||
uint32_t dirCluster = FatAllocCluster(v);
|
||||
if (!dirCluster) return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
s = FatInitializeDirectoryCluster(v, dirCluster, parentCluster);
|
||||
if (!KSUCCESS(s)) {
|
||||
FatFreeClusterChain(v, dirCluster);
|
||||
FatFlushCache(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
FatFlushCache(v);
|
||||
|
||||
s = FatCreateDirent(v, path, ATTR_DIRECTORY, dirCluster, 0, NULL, NULL);
|
||||
if (!KSUCCESS(s)) {
|
||||
FatFreeClusterChain(v, dirCluster);
|
||||
FatFlushCache(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
FatFlushCache(v);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Removes an empty directory.
|
||||
KSTATUS FatRemoveDir(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path) {
|
||||
if (!fs || !SysSvIsValid(path) || path.Length == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint32_t cluster = 0;
|
||||
uint32_t size = 0;
|
||||
uint8_t attr = 0;
|
||||
uint64_t direntLba = 0;
|
||||
uint32_t direntOffset = 0;
|
||||
KSTATUS s = FatLookupPathV(v, path, &cluster, &size, &attr,
|
||||
&direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!direntLba) return NTLSTATUS_ACCESS_DENIED;
|
||||
if (!(attr & ATTR_DIRECTORY)) return NTLSTATUS_ACCESS_DENIED;
|
||||
|
||||
int empty = 0;
|
||||
s = FatDirectoryIsEmpty(v, cluster, &empty);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!empty) return NTLSTATUS_ACCESS_DENIED;
|
||||
|
||||
uint32_t parentCluster = 0;
|
||||
SYS_STRING_VIEW filenameView;
|
||||
s = FatResolveParentForMutation(v, path, &parentCluster, &filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
s = FatDeleteDirentRunV(v, parentCluster, filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
if (cluster >= 2) FatFreeClusterChain(v, cluster);
|
||||
FatFlushCache(v);
|
||||
FatPathCacheInvalidate(v);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Truncate.
|
||||
KSTATUS FatTruncate(IO_VOLUME_PROVIDER* fs, void* fileCtx, uint64_t newSize, uint32_t fileFlags) {
|
||||
(void)fs;
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
FatFileInvalidateChainCache(f);
|
||||
int zeroNewClusters = (fileFlags & IO_FILE_FLAG_SKIP_ZERO_EXTEND) ? 0 : 1;
|
||||
if ((uint32_t)newSize == f->FileSize) {
|
||||
FAT_DIRENT_UPDATE u;
|
||||
memset(&u, 0, sizeof(u));
|
||||
FatQueryEncodedTime(&u.Timestamp);
|
||||
u.Flags = FAT_DIRENT_UPDATE_WRITE_TIME;
|
||||
KSTATUS s = FatDirentUpdateMetadata(v, f->DirentLba, f->DirentOffset, &u);
|
||||
if (KSUCCESS(s)) FatPathCacheInvalidate(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
if (newSize == 0) {
|
||||
if (f->StartCluster >= 2) {
|
||||
FatFreeClusterChain(v, f->StartCluster);
|
||||
f->StartCluster = 0;
|
||||
FatDirentUpdateFirstCluster(v, f->DirentLba, f->DirentOffset, 0);
|
||||
}
|
||||
f->FileSize = 0;
|
||||
KSTATUS s = FatDirentUpdateSize(v, f->DirentLba, f->DirentOffset, 0);
|
||||
FatFlushCache(v);
|
||||
if (KSUCCESS(s)) FatPathCacheInvalidate(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
uint32_t newClusters = (uint32_t)
|
||||
((newSize + v->BytesPerCluster - 1) / v->BytesPerCluster);
|
||||
|
||||
if (newSize < f->FileSize) {
|
||||
if (f->StartCluster < 2) goto update_size;
|
||||
uint32_t cluster = f->StartCluster, lastGood = cluster;
|
||||
for (uint32_t i = 1; i < newClusters && FatIsDataCluster(v, cluster); i++) {
|
||||
lastGood = cluster; cluster = FatEntry(v, cluster);
|
||||
}
|
||||
uint32_t tail = FatEntry(v, lastGood);
|
||||
FatWriteEntry(v, lastGood, FatEocMark(v));
|
||||
FatFreeClusterChain(v, tail);
|
||||
} else {
|
||||
uint32_t curClusters = f->FileSize
|
||||
? (f->FileSize + v->BytesPerCluster - 1) / v->BytesPerCluster : 0;
|
||||
uint32_t lastCluster = 0;
|
||||
if (f->StartCluster < 2) {
|
||||
uint32_t first = FatAllocClusterEx(v, zeroNewClusters);
|
||||
if (!first) return NTLSTATUS_NO_MEMORY;
|
||||
f->StartCluster = first;
|
||||
FatDirentUpdateFirstCluster(v, f->DirentLba, f->DirentOffset, first);
|
||||
lastCluster = first; curClusters = 1;
|
||||
} else {
|
||||
lastCluster = FatGetChainEnd(v, f->StartCluster, NULL);
|
||||
}
|
||||
for (uint32_t i = curClusters; i < newClusters; i++) {
|
||||
uint32_t next = FatAppendClusterEx(v, lastCluster, zeroNewClusters);
|
||||
if (!next) return NTLSTATUS_NO_MEMORY;
|
||||
lastCluster = next;
|
||||
}
|
||||
}
|
||||
|
||||
update_size:
|
||||
f->FileSize = (uint32_t)newSize;
|
||||
{ KSTATUS s = FatDirentUpdateSize(v, f->DirentLba, f->DirentOffset, (uint32_t)newSize);
|
||||
FatFlushCache(v);
|
||||
if (KSUCCESS(s)) FatPathCacheInvalidate(v);
|
||||
return s; }
|
||||
}
|
||||
|
||||
// Delete.
|
||||
KSTATUS FatDelete(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path) {
|
||||
if (!fs || !SysSvIsValid(path) || path.Length == 0)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint32_t cluster, size; uint64_t direntLba; uint32_t direntOffset;
|
||||
KSTATUS s = FatLookupPathV(v, path, &cluster, &size, NULL, &direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
if (!direntLba) return NTLSTATUS_ACCESS_DENIED;
|
||||
|
||||
uint32_t parentCluster = 0;
|
||||
SYS_STRING_VIEW filenameView;
|
||||
s = FatResolveParentForMutation(v, path, &parentCluster, &filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
// Delete the complete entry run so long-name metadata cannot survive the
|
||||
// short entry and keep writable vvfat mirrors visibly present.
|
||||
s = FatDeleteDirentRunV(v, parentCluster, filenameView);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
|
||||
if (cluster >= 2) FatFreeClusterChain(v, cluster);
|
||||
FatFlushCache(v);
|
||||
FatPathCacheInvalidate(v);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns size.
|
||||
uint64_t FatGetSize(IO_VOLUME_PROVIDER* fs, void* fileCtx) {
|
||||
(void)fs; return ((FAT_FILE*)fileCtx)->FileSize;
|
||||
}
|
||||
|
||||
// Converts FAT attributes to the public file type.
|
||||
static uint32_t FatFileTypeFromAttr(uint8_t attr) {
|
||||
return (attr & ATTR_DIRECTORY) ? SYS_FILE_TYPE_DIRECTORY : SYS_FILE_TYPE_REGULAR;
|
||||
}
|
||||
|
||||
// Fills public metadata from one FAT directory entry.
|
||||
static void FatFillStatFromDirent(FAT_VOL* v, const DIRENT* d,
|
||||
uint64_t direntLba, uint32_t direntOffset,
|
||||
SYS_FILE_STAT* out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Size = sizeof(*out);
|
||||
out->Type = FatFileTypeFromAttr(d->Attr);
|
||||
out->Attributes = d->Attr;
|
||||
out->LinkCount = 1;
|
||||
out->FileSize = d->FileSize;
|
||||
|
||||
uint32_t firstCluster = FatDirentFirstCluster(v, d);
|
||||
out->FileId = direntLba ^
|
||||
((uint64_t)direntOffset << 32) ^
|
||||
((uint64_t)firstCluster << 1);
|
||||
|
||||
FatDecodeDateTime(d->CrtDate, d->CrtTime, d->CrtTimeTenth, &out->CreationTime);
|
||||
FatDecodeAccessDate(d->LastAccDate, &out->LastAccessTime);
|
||||
FatDecodeDateTime(d->WrtDate, d->WrtTime, 0, &out->LastWriteTime);
|
||||
out->ChangeTime = out->LastWriteTime;
|
||||
}
|
||||
|
||||
// Fills public metadata for the filesystem root.
|
||||
static void FatFillRootStat(FAT_VOL* v, SYS_FILE_STAT* out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Size = sizeof(*out);
|
||||
out->Type = SYS_FILE_TYPE_DIRECTORY;
|
||||
out->Attributes = SYS_FILE_ATTR_DIRECTORY;
|
||||
out->LinkCount = 1;
|
||||
out->FileSize = 0;
|
||||
out->FileId = ((uint64_t)v->RootCluster << 1) ^ 0x524f4f54ULL;
|
||||
}
|
||||
|
||||
// Queries metadata for an open FAT file.
|
||||
KSTATUS FatQueryInfo(IO_VOLUME_PROVIDER* fs, void* fileCtx, SYS_FILE_STAT* out) {
|
||||
(void)fs;
|
||||
if (!fileCtx || !out) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
if (!v) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
if (f->DirentLba == 0) {
|
||||
if (f->StartCluster == v->RootCluster && f->FileSize == 0) {
|
||||
FatFillRootStat(v, out);
|
||||
} else {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Size = sizeof(*out);
|
||||
out->Type = SYS_FILE_TYPE_REGULAR;
|
||||
out->Attributes = SYS_FILE_ATTR_ARCHIVE;
|
||||
out->LinkCount = 1;
|
||||
out->FileSize = f->FileSize;
|
||||
out->FileId = ((uint64_t)f->StartCluster << 1);
|
||||
}
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t* sec = (uint8_t*)MmAllocatePool(v->BytesPerSector);
|
||||
if (!sec) return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
KSTATUS s = FatReadSectors(v, f->DirentLba, 1, sec);
|
||||
if (!KSUCCESS(s)) { MmFreePool(sec); return s; }
|
||||
|
||||
DIRENT* d = (DIRENT*)(sec + f->DirentOffset);
|
||||
if ((uint8_t)d->Name[0] == 0x00 || (uint8_t)d->Name[0] == 0xE5) {
|
||||
MmFreePool(sec);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
FatFillStatFromDirent(v, d, f->DirentLba, f->DirentOffset, out);
|
||||
f->StartCluster = FatDirentFirstCluster(v, d);
|
||||
f->FileSize = d->FileSize;
|
||||
MmFreePool(sec);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Queries metadata for a FAT path.
|
||||
KSTATUS FatStat(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW path, SYS_FILE_STAT* out) {
|
||||
if (!fs || !out || !SysSvIsValid(path)) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
if (!v) return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uint32_t cluster = 0;
|
||||
uint32_t size = 0;
|
||||
uint8_t attr = ATTR_DIRECTORY;
|
||||
uint64_t direntLba = 0;
|
||||
uint32_t direntOffset = 0;
|
||||
KSTATUS s = FatLookupPathV(v, path, &cluster, &size, &attr, &direntLba, &direntOffset);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
(void)cluster;
|
||||
|
||||
if (direntLba == 0) {
|
||||
FatFillRootStat(v, out);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t* sec = (uint8_t*)MmAllocatePool(v->BytesPerSector);
|
||||
if (!sec) return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
s = FatReadSectors(v, direntLba, 1, sec);
|
||||
if (!KSUCCESS(s)) { MmFreePool(sec); return s; }
|
||||
|
||||
DIRENT* d = (DIRENT*)(sec + direntOffset);
|
||||
if ((uint8_t)d->Name[0] == 0x00 || (uint8_t)d->Name[0] == 0xE5) {
|
||||
MmFreePool(sec);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
FatFillStatFromDirent(v, d, direntLba, direntOffset, out);
|
||||
out->Type = FatFileTypeFromAttr(attr);
|
||||
out->FileSize = size;
|
||||
MmFreePool(sec);
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Mixes FAT directory metadata into a per-file content generation.
|
||||
static uint64_t FatDirentGeneration(FAT_VOL* v, const DIRENT* d) {
|
||||
if (!d)
|
||||
return 0;
|
||||
|
||||
uint32_t firstCluster = FatDirentFirstCluster(v, d);
|
||||
uint64_t generation = ((uint64_t)d->WrtDate << 48) |
|
||||
((uint64_t)d->WrtTime << 32) |
|
||||
((uint64_t)d->LastAccDate << 16) |
|
||||
(uint64_t)d->Attr;
|
||||
generation ^= ((uint64_t)d->CrtDate << 40);
|
||||
generation ^= ((uint64_t)d->CrtTime << 24);
|
||||
generation ^= ((uint64_t)d->CrtTimeTenth << 16);
|
||||
generation ^= ((uint64_t)firstCluster << 1);
|
||||
generation ^= ((uint64_t)d->FileSize << 17);
|
||||
return generation ? generation : 1;
|
||||
}
|
||||
|
||||
// Returns a stable FAT file identity for cross-open image-section caches.
|
||||
KSTATUS FatQueryIdentity(IO_VOLUME_PROVIDER* fs, void* fileCtx, IO_FILE_IDENTITY* out) {
|
||||
(void)fs;
|
||||
if (!fileCtx || !out)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_FILE* f = (FAT_FILE*)fileCtx;
|
||||
FAT_VOL* v = f->Vol;
|
||||
if (!v)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
uint32_t startCluster = f->StartCluster;
|
||||
uint32_t fileSize = f->FileSize;
|
||||
uint64_t generation = ((uint64_t)startCluster << 1) ^
|
||||
((uint64_t)fileSize << 17);
|
||||
|
||||
if (f->DirentLba != 0) {
|
||||
uint8_t* sec = (uint8_t*)MmAllocatePool(v->BytesPerSector);
|
||||
if (!sec)
|
||||
return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
KSTATUS s = FatReadSectors(v, f->DirentLba, 1, sec);
|
||||
if (!KSUCCESS(s)) {
|
||||
MmFreePool(sec);
|
||||
return s;
|
||||
}
|
||||
|
||||
DIRENT* d = (DIRENT*)(sec + f->DirentOffset);
|
||||
if ((uint8_t)d->Name[0] == 0x00 || (uint8_t)d->Name[0] == 0xE5) {
|
||||
MmFreePool(sec);
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
startCluster = FatDirentFirstCluster(v, d);
|
||||
fileSize = d->FileSize;
|
||||
generation = FatDirentGeneration(v, d);
|
||||
MmFreePool(sec);
|
||||
}
|
||||
|
||||
out->FileId = f->DirentLba ^
|
||||
((uint64_t)f->DirentOffset << 32) ^
|
||||
((uint64_t)startCluster << 1);
|
||||
out->Generation = generation ? generation : 1;
|
||||
out->Size = fileSize;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Directory enumeration (fix #2: cluster buffer cached in context)
|
||||
// Previously: allocate + FatReadSectors + return 1 entry + free, every call.
|
||||
// Now: buffer lives in FAT_DIR_CTX; FatReadSectors fires only on cluster
|
||||
// boundaries.
|
||||
|
||||
typedef struct {
|
||||
FAT_VOL* Vol;
|
||||
uint32_t DirCluster;
|
||||
uint32_t Cursor;
|
||||
uint32_t LoadedCursor;
|
||||
uint32_t LoadedSectors;
|
||||
uint64_t LoadedLba;
|
||||
uint32_t EntryIndex;
|
||||
char LfnBuf[FAT_MAX_LFN];
|
||||
int HasLfn;
|
||||
uint8_t* ClusterBuf; // allocated once in OpenDir, freed in CloseDir
|
||||
} FAT_DIR_CTX;
|
||||
|
||||
// Opens dir.
|
||||
KSTATUS FatOpenDir(IO_VOLUME_PROVIDER* fs, SYS_STRING_VIEW dirPath, void** dirCtx) {
|
||||
if (!fs || !dirCtx || !SysSvIsValid(dirPath))
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
|
||||
FAT_VOL* v = (FAT_VOL*)fs->Context;
|
||||
uint32_t cluster;
|
||||
if (dirPath.Length == 0) {
|
||||
cluster = v->RootCluster;
|
||||
} else {
|
||||
uint32_t size;
|
||||
KSTATUS s = FatTraverseV(v, dirPath, &cluster, &size);
|
||||
if (!KSUCCESS(s)) return s;
|
||||
}
|
||||
|
||||
uint32_t cursor; uint64_t lba; uint32_t sectors;
|
||||
if (!FatDirectoryFirstSegment(v, cluster, &cursor, &lba, §ors))
|
||||
return NTLSTATUS_NOT_FOUND;
|
||||
|
||||
FAT_DIR_CTX* ctx = (FAT_DIR_CTX*)MmAllocatePool(sizeof(FAT_DIR_CTX));
|
||||
if (!ctx) return NTLSTATUS_NO_MEMORY;
|
||||
|
||||
ctx->ClusterBuf = (uint8_t*)MmAllocatePool(v->BytesPerCluster);
|
||||
if (!ctx->ClusterBuf) { MmFreePool(ctx); return NTLSTATUS_NO_MEMORY; }
|
||||
|
||||
ctx->Vol = v;
|
||||
ctx->DirCluster = cluster;
|
||||
ctx->Cursor = cursor;
|
||||
ctx->LoadedCursor = (uint32_t)-1;
|
||||
ctx->LoadedSectors = sectors;
|
||||
ctx->LoadedLba = lba;
|
||||
ctx->EntryIndex = 0;
|
||||
ctx->HasLfn = 0;
|
||||
memset(ctx->LfnBuf, 0, sizeof(ctx->LfnBuf));
|
||||
*dirCtx = ctx;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Returns dir.
|
||||
KSTATUS FatReadDir(IO_VOLUME_PROVIDER* fs, void* dirCtx, KDIR_ENTRY* out) {
|
||||
(void)fs;
|
||||
FAT_DIR_CTX* ctx = (FAT_DIR_CTX*)dirCtx;
|
||||
FAT_VOL* v = ctx->Vol;
|
||||
|
||||
while (true) {
|
||||
if (ctx->Cursor != ctx->LoadedCursor) {
|
||||
if (!KSUCCESS(FatReadSectors(v, ctx->LoadedLba, ctx->LoadedSectors, ctx->ClusterBuf)))
|
||||
return NTLSTATUS_NO_MORE_FILES;
|
||||
ctx->LoadedCursor = ctx->Cursor;
|
||||
}
|
||||
|
||||
uint32_t epc = (ctx->LoadedSectors * v->BytesPerSector) / sizeof(DIRENT);
|
||||
DIRENT* dir = (DIRENT*)ctx->ClusterBuf;
|
||||
while (ctx->EntryIndex < epc) {
|
||||
DIRENT* e = &dir[ctx->EntryIndex++];
|
||||
|
||||
if (e->Name[0] == 0x00) return NTLSTATUS_NO_MORE_FILES;
|
||||
if ((uint8_t)e->Name[0] == 0xE5) { ctx->HasLfn = 0; memset(ctx->LfnBuf, 0, sizeof(ctx->LfnBuf)); continue; }
|
||||
if (e->Attr == ATTR_LFN) { FatLfnAppend(ctx->LfnBuf, (LFN_ENTRY*)e); ctx->HasLfn = 1; continue; }
|
||||
if (e->Attr & 0x08 && !(e->Attr & ATTR_DIRECTORY)) { ctx->HasLfn = 0; memset(ctx->LfnBuf, 0, sizeof(ctx->LfnBuf)); continue; }
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
if (ctx->HasLfn && ctx->LfnBuf[0]) {
|
||||
size_t len = strlen(ctx->LfnBuf);
|
||||
if (len >= KDIR_NAME_MAX) len = KDIR_NAME_MAX - 1;
|
||||
memcpy(out->Name, ctx->LfnBuf, len); out->Name[len] = '\0';
|
||||
} else {
|
||||
char name83[13]; int n = 0;
|
||||
for (int i = 0; i < 8 && e->Name[i] != ' '; i++)
|
||||
name83[n++] = (char)(e->Name[i] >= 'A' && e->Name[i] <= 'Z' ? e->Name[i]+32 : e->Name[i]);
|
||||
if (e->Ext[0] != ' ') {
|
||||
name83[n++] = '.';
|
||||
for (int i = 0; i < 3 && e->Ext[i] != ' '; i++)
|
||||
name83[n++] = (char)(e->Ext[i] >= 'A' && e->Ext[i] <= 'Z' ? e->Ext[i]+32 : e->Ext[i]);
|
||||
}
|
||||
name83[n] = '\0';
|
||||
size_t len = (size_t)n; if (len >= KDIR_NAME_MAX) len = KDIR_NAME_MAX - 1;
|
||||
memcpy(out->Name, name83, len); out->Name[len] = '\0';
|
||||
}
|
||||
|
||||
out->Attr = e->Attr;
|
||||
out->FileSize = e->FileSize;
|
||||
FatDecodeDateTime(e->CrtDate, e->CrtTime, e->CrtTimeTenth, &out->CreateTime);
|
||||
FatDecodeAccessDate(e->LastAccDate, &out->LastAccessTime);
|
||||
FatDecodeDateTime(e->WrtDate, e->WrtTime, 0, &out->LastWriteTime);
|
||||
ctx->HasLfn = 0; memset(ctx->LfnBuf, 0, sizeof(ctx->LfnBuf));
|
||||
return NTLSTATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (!FatDirectoryNextSegment(v, ctx->DirCluster, &ctx->Cursor,
|
||||
&ctx->LoadedLba, &ctx->LoadedSectors))
|
||||
return NTLSTATUS_NO_MORE_FILES;
|
||||
ctx->LoadedCursor = (uint32_t)-1;
|
||||
ctx->EntryIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Releases dir.
|
||||
void FatCloseDir(IO_VOLUME_PROVIDER* fs, void* dirCtx) {
|
||||
(void)fs;
|
||||
FAT_DIR_CTX* ctx = (FAT_DIR_CTX*)dirCtx;
|
||||
MmFreePool(ctx->ClusterBuf);
|
||||
MmFreePool(ctx);
|
||||
}
|
||||
Reference in new issue
Block a user