add page cache with read-ahead and dirty writeback, file-backed and anonymous mmap with CoW, VAD-based virtual memory replacing the old user region model, PFN database with eviction and reclaim, file identity for cross-open section sharing, FAT32 path cache and per-file cluster chain cache, serial.sys for COM1 keyboard input, xHCI MSI with INTx and polled fallback, PciEnableMsiAuto, waitable input queue, KeWaitForAnyEvent, per-process thread info syscall, expanded kernel stats (PFN/PTE/VAD/file-cache/disk), pool allocation tags, async I/O user buffer locking, file flush on close, add docs.md.
This commit is contained in:
1 parent
61ec2442eb
commit
2c4a68a75f
114 files changed
+18069
-2620
No files matched your search
+283
-18
@@ -4,6 +4,8 @@
|
||||
#include <kernel/ke_list.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/mm_file_cache.h>
|
||||
#include <kernel/mm_pfn.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_userenv.h>
|
||||
@@ -13,6 +15,7 @@
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define MAX_DRIVES 8
|
||||
|
||||
@@ -45,11 +48,15 @@ struct IO_ASYNC_FILE_REQUEST_OBJECT {
|
||||
uint8_t CancelRequested;
|
||||
|
||||
FILE_OBJECT* File; // referenced until object destruction
|
||||
EVENT* WaitEvent; // internal completion event
|
||||
EVENT* CompletionEvent; // optional, referenced until object destruction
|
||||
KAPC* CompletionApc; // caller-owned APC storage
|
||||
IO_STATUS_BLOCK* Iosb; // caller/user-owned completion block
|
||||
IO_STATUS_BLOCK IosbSnapshot;
|
||||
MM_LOCKED_USER_BUFFER* LockedBuffer;
|
||||
MM_LOCKED_USER_BUFFER* LockedIosb;
|
||||
|
||||
void* Buffer; // caller/user-owned; must remain valid until completion/cancel
|
||||
void* Buffer; // caller-owned; user buffers must be locked until completion/cancel
|
||||
size_t Size;
|
||||
uint64_t Offset;
|
||||
};
|
||||
@@ -65,6 +72,10 @@ static void KiIoAsyncFileRequestDestroy(OBJECT* obj) {
|
||||
IO_ASYNC_FILE_REQUEST_OBJECT* req =
|
||||
CONTAINING_RECORD(obj, IO_ASYNC_FILE_REQUEST_OBJECT, Header);
|
||||
|
||||
if (req->WaitEvent) {
|
||||
ObDereferenceObject(&req->WaitEvent->Header);
|
||||
req->WaitEvent = (EVENT*)0;
|
||||
}
|
||||
if (req->CompletionEvent) {
|
||||
ObDereferenceObject(&req->CompletionEvent->Header);
|
||||
req->CompletionEvent = (EVENT*)0;
|
||||
@@ -73,6 +84,14 @@ static void KiIoAsyncFileRequestDestroy(OBJECT* obj) {
|
||||
ObDereferenceObject(&req->File->Header);
|
||||
req->File = (FILE_OBJECT*)0;
|
||||
}
|
||||
if (req->LockedBuffer) {
|
||||
MmUnlockUserBuffer(req->LockedBuffer);
|
||||
req->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
|
||||
}
|
||||
if (req->LockedIosb) {
|
||||
MmUnlockUserBuffer(req->LockedIosb);
|
||||
req->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
|
||||
}
|
||||
}
|
||||
|
||||
static void KiIoCompleteAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
|
||||
@@ -80,6 +99,9 @@ static void KiIoCompleteAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
|
||||
size_t information) {
|
||||
if (!req) return;
|
||||
|
||||
req->IosbSnapshot.Status = status;
|
||||
req->IosbSnapshot.Information = information;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gAsyncFileQueueLock);
|
||||
req->State = (status == STATUS_CANCELLED) ? IoAsyncStateCancelled : IoAsyncStateCompleted;
|
||||
KeReleaseSpinlock(&gAsyncFileQueueLock, flags);
|
||||
@@ -99,6 +121,18 @@ static void KiIoCompleteAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
|
||||
|
||||
if (req->CompletionEvent)
|
||||
KeSignalEvent(req->CompletionEvent);
|
||||
|
||||
if (req->WaitEvent)
|
||||
KeSignalEvent(req->WaitEvent);
|
||||
|
||||
if (req->LockedBuffer) {
|
||||
MmUnlockUserBuffer(req->LockedBuffer);
|
||||
req->LockedBuffer = (MM_LOCKED_USER_BUFFER*)0;
|
||||
}
|
||||
if (req->LockedIosb) {
|
||||
MmUnlockUserBuffer(req->LockedIosb);
|
||||
req->LockedIosb = (MM_LOCKED_USER_BUFFER*)0;
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the io Async File Dequeue operation.
|
||||
@@ -226,10 +260,24 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
|
||||
IO_STATUS_BLOCK* iosb,
|
||||
EVENT* completionEvent,
|
||||
KAPC* completionApc,
|
||||
MM_LOCKED_USER_BUFFER* lockedBuffer,
|
||||
MM_LOCKED_USER_BUFFER* lockedIosb,
|
||||
IO_ASYNC_FILE_REQUEST_OBJECT** requestOut) {
|
||||
if (!file || !iosb) return STATUS_INVALID_PARAMETER;
|
||||
if (!buffer && size) return STATUS_INVALID_PARAMETER;
|
||||
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
||||
if (!file || !iosb) {
|
||||
MmUnlockUserBuffer(lockedBuffer);
|
||||
MmUnlockUserBuffer(lockedIosb);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if (!buffer && size) {
|
||||
MmUnlockUserBuffer(lockedBuffer);
|
||||
MmUnlockUserBuffer(lockedIosb);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
if (!KeCanWait()) {
|
||||
MmUnlockUserBuffer(lockedBuffer);
|
||||
MmUnlockUserBuffer(lockedIosb);
|
||||
return STATUS_INVALID_IRQL;
|
||||
}
|
||||
|
||||
iosb->Status = STATUS_PENDING;
|
||||
iosb->Information = 0;
|
||||
@@ -239,6 +287,8 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
|
||||
sizeof(IO_ASYNC_FILE_REQUEST_OBJECT));
|
||||
if (!req) {
|
||||
iosb->Status = STATUS_NO_MEMORY;
|
||||
MmUnlockUserBuffer(lockedBuffer);
|
||||
MmUnlockUserBuffer(lockedIosb);
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
@@ -247,10 +297,23 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
|
||||
req->Op = op;
|
||||
req->State = IoAsyncStateQueued;
|
||||
req->CancelRequested = 0;
|
||||
KSTATUS waitStatus = KeEventCreate(EventManualReset, 0, &req->WaitEvent);
|
||||
if (!KSUCCESS(waitStatus)) {
|
||||
iosb->Status = waitStatus;
|
||||
MmUnlockUserBuffer(lockedBuffer);
|
||||
MmUnlockUserBuffer(lockedIosb);
|
||||
ObDereferenceObject(&req->Header);
|
||||
return waitStatus;
|
||||
}
|
||||
|
||||
req->File = file;
|
||||
req->CompletionEvent = completionEvent;
|
||||
req->CompletionApc = completionApc;
|
||||
req->Iosb = iosb;
|
||||
req->IosbSnapshot.Status = STATUS_PENDING;
|
||||
req->IosbSnapshot.Information = 0;
|
||||
req->LockedBuffer = lockedBuffer;
|
||||
req->LockedIosb = lockedIosb;
|
||||
req->Buffer = buffer;
|
||||
req->Size = size;
|
||||
req->Offset = offset;
|
||||
@@ -288,6 +351,8 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
|
||||
|
||||
ObDereferenceObject(&req->Header); // provider rejected synchronously
|
||||
iosb->Status = ns;
|
||||
req->IosbSnapshot.Status = ns;
|
||||
req->IosbSnapshot.Information = 0;
|
||||
ObDereferenceObject(&req->Header); // creator reference
|
||||
return ns;
|
||||
}
|
||||
@@ -295,6 +360,8 @@ static KSTATUS KiIoSubmitAsyncFileRequest(IO_ASYNC_FILE_OP op,
|
||||
KSTATUS s = KiIoEnsureAsyncFileIoStarted();
|
||||
if (!KSUCCESS(s)) {
|
||||
iosb->Status = s;
|
||||
req->IosbSnapshot.Status = s;
|
||||
req->IosbSnapshot.Information = 0;
|
||||
ObDereferenceObject(&req->Header);
|
||||
return s;
|
||||
}
|
||||
@@ -323,10 +390,12 @@ __declspec(dllexport) KSTATUS IoReadFileAsyncKernel(FILE_OBJECT* file, void* buf
|
||||
IO_STATUS_BLOCK* iosb,
|
||||
EVENT* completionEvent,
|
||||
KAPC* completionApc,
|
||||
MM_LOCKED_USER_BUFFER* lockedBuffer,
|
||||
MM_LOCKED_USER_BUFFER* lockedIosb,
|
||||
IO_ASYNC_FILE_REQUEST_OBJECT** requestOut) {
|
||||
return KiIoSubmitAsyncFileRequest(IoAsyncFileRead, file, buf, size, offset,
|
||||
iosb, completionEvent, completionApc,
|
||||
requestOut);
|
||||
lockedBuffer, lockedIosb, requestOut);
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoWriteFileAsyncKernel(FILE_OBJECT* file, const void* buf,
|
||||
@@ -334,10 +403,12 @@ __declspec(dllexport) KSTATUS IoWriteFileAsyncKernel(FILE_OBJECT* file, const vo
|
||||
IO_STATUS_BLOCK* iosb,
|
||||
EVENT* completionEvent,
|
||||
KAPC* completionApc,
|
||||
MM_LOCKED_USER_BUFFER* lockedBuffer,
|
||||
MM_LOCKED_USER_BUFFER* lockedIosb,
|
||||
IO_ASYNC_FILE_REQUEST_OBJECT** requestOut) {
|
||||
return KiIoSubmitAsyncFileRequest(IoAsyncFileWrite, file, (void*)buf, size, offset,
|
||||
iosb, completionEvent, completionApc,
|
||||
requestOut);
|
||||
lockedBuffer, lockedIosb, requestOut);
|
||||
}
|
||||
|
||||
// Implements the async File Request Object operation.
|
||||
@@ -345,6 +416,19 @@ __declspec(dllexport) OBJECT* IoAsyncFileRequestObject(IO_ASYNC_FILE_REQUEST_OBJ
|
||||
return req ? &req->Header : (OBJECT*)0;
|
||||
}
|
||||
|
||||
|
||||
// Returns the wait event owned by an async file request.
|
||||
__declspec(dllexport) EVENT* IoGetAsyncFileRequestWaitEvent(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
|
||||
return req ? req->WaitEvent : (EVENT*)0;
|
||||
}
|
||||
|
||||
// Waits for an async file request to complete according to wait flags.
|
||||
__declspec(dllexport) KSTATUS IoWaitAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req, uint32_t flags) {
|
||||
if (!req || !req->WaitEvent) return STATUS_INVALID_PARAMETER;
|
||||
if (flags & ~SYS_WAIT_FLAG_ALERTABLE) return STATUS_INVALID_PARAMETER;
|
||||
return KeWaitForEvent(req->WaitEvent, (flags & SYS_WAIT_FLAG_ALERTABLE) ? 1 : 0);
|
||||
}
|
||||
|
||||
// Cancels async File Request.
|
||||
__declspec(dllexport) KSTATUS IoCancelAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req) {
|
||||
if (!req) return STATUS_INVALID_PARAMETER;
|
||||
@@ -377,13 +461,8 @@ __declspec(dllexport) KSTATUS IoCancelAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJ
|
||||
__declspec(dllexport) KSTATUS IoQueryAsyncFileRequest(IO_ASYNC_FILE_REQUEST_OBJECT* req,
|
||||
IO_STATUS_BLOCK* iosbOut) {
|
||||
if (!req || !iosbOut) return STATUS_INVALID_PARAMETER;
|
||||
if (req->Iosb) {
|
||||
iosbOut->Status = req->Iosb->Status;
|
||||
iosbOut->Information = req->Iosb->Information;
|
||||
} else {
|
||||
iosbOut->Status = STATUS_FAILURE;
|
||||
iosbOut->Information = 0;
|
||||
}
|
||||
iosbOut->Status = req->IosbSnapshot.Status;
|
||||
iosbOut->Information = req->IosbSnapshot.Information;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -622,10 +701,51 @@ __declspec(dllexport) bool IoFilesystemPresent(uint8_t volumeIndex) {
|
||||
return gVolumes[volumeIndex].Fs != (IO_FILESYSTEM*)0;
|
||||
}
|
||||
|
||||
// Aggregates filesystem traversal statistics across mounted volumes.
|
||||
__declspec(dllexport) void IoQueryFilesystemStats(IO_FILESYSTEM_STATS* out) {
|
||||
if (!out) return;
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
for (uint8_t i = 0; i < MAX_DRIVES; i++) {
|
||||
IO_FILESYSTEM* fs = gVolumes[i].Fs;
|
||||
if (!fs || !fs->QueryStats) continue;
|
||||
|
||||
IO_FILESYSTEM_STATS one;
|
||||
memset(&one, 0, sizeof(one));
|
||||
fs->QueryStats(fs, &one);
|
||||
|
||||
out->PathLookups += one.PathLookups;
|
||||
out->PathCacheHits += one.PathCacheHits;
|
||||
out->PathCacheMisses += one.PathCacheMisses;
|
||||
out->PathCacheStores += one.PathCacheStores;
|
||||
out->PathCacheInvalidations += one.PathCacheInvalidations;
|
||||
if (one.PathCacheGeneration > out->PathCacheGeneration)
|
||||
out->PathCacheGeneration = one.PathCacheGeneration;
|
||||
out->DirectorySearches += one.DirectorySearches;
|
||||
out->DirectoryClustersRead += one.DirectoryClustersRead;
|
||||
out->DirectoryEntriesScanned += one.DirectoryEntriesScanned;
|
||||
out->FatChainCacheLookups += one.FatChainCacheLookups;
|
||||
out->FatChainCacheHits += one.FatChainCacheHits;
|
||||
out->FatChainCacheMisses += one.FatChainCacheMisses;
|
||||
out->FatChainCacheStores += one.FatChainCacheStores;
|
||||
out->FatChainCacheInvalidations += one.FatChainCacheInvalidations;
|
||||
out->FatChainWalkSteps += one.FatChainWalkSteps;
|
||||
out->DataReadRuns += one.DataReadRuns;
|
||||
out->DataReadSectors += one.DataReadSectors;
|
||||
out->DataReadRunMerges += one.DataReadRunMerges;
|
||||
if (one.MaxDataReadSpanSectors > out->MaxDataReadSpanSectors)
|
||||
out->MaxDataReadSpanSectors = one.MaxDataReadSpanSectors;
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor.
|
||||
static void KiFileDestroy(OBJECT* obj) {
|
||||
FILE_OBJECT* f = CONTAINING_RECORD(obj, FILE_OBJECT, Header);
|
||||
if (f->Fs && f->FileCtx) f->Fs->Close(f->Fs, f->FileCtx);
|
||||
if (f->Fs && f->FileCtx) {
|
||||
(void)IoFlushFile(f);
|
||||
MmFileCacheInvalidateAll(f);
|
||||
f->Fs->Close(f->Fs, f->FileCtx);
|
||||
}
|
||||
}
|
||||
|
||||
// IoOpenFile.
|
||||
@@ -655,11 +775,73 @@ __declspec(dllexport) KSTATUS IoOpenFile(SYS_STRING_VIEW path, FILE_OBJECT **out
|
||||
f->Lock = (SPINLOCK)SPINLOCK_INIT;
|
||||
f->Size = fs->GetSize(fs, fileCtx);
|
||||
f->Offset = 0;
|
||||
f->Flags = 0;
|
||||
f->ReadaheadScore = 0;
|
||||
f->ReadaheadNextOffset = 0;
|
||||
f->ReadaheadIssuedUntil = 0;
|
||||
f->ReadaheadSuppressedUntil = 0;
|
||||
|
||||
*out = f;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static uint64_t KiAlignUpPage64(uint64_t value) {
|
||||
return (value + PAGE_SIZE - 1u) & ~(uint64_t)(PAGE_SIZE - 1u);
|
||||
}
|
||||
|
||||
static void KiIoMaybeScheduleReadAhead(FILE_OBJECT* file, uint64_t offset, size_t bytesRead, uint64_t fileSize) {
|
||||
if (!file || bytesRead < 512)
|
||||
return;
|
||||
if ((file->Flags & IO_FILE_FLAG_NO_CACHE) != 0)
|
||||
return;
|
||||
|
||||
uint64_t end = offset + bytesRead;
|
||||
uint64_t start = 0;
|
||||
uint32_t pages = 0;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&file->Lock);
|
||||
if (offset == file->ReadaheadNextOffset) {
|
||||
if (file->ReadaheadScore < 32)
|
||||
file->ReadaheadScore++;
|
||||
} else {
|
||||
file->ReadaheadScore = 1;
|
||||
file->ReadaheadIssuedUntil = end;
|
||||
}
|
||||
file->ReadaheadNextOffset = end;
|
||||
|
||||
if (end < file->ReadaheadSuppressedUntil) {
|
||||
pages = 0;
|
||||
} else if (file->ReadaheadScore >= 2 || bytesRead >= PAGE_SIZE) {
|
||||
pages = file->ReadaheadScore < 4 ? 8u : (file->ReadaheadScore < 8 ? 16u : 32u);
|
||||
start = KiAlignUpPage64(end);
|
||||
if (start < file->ReadaheadIssuedUntil)
|
||||
start = KiAlignUpPage64(file->ReadaheadIssuedUntil);
|
||||
if (start >= fileSize) {
|
||||
pages = 0;
|
||||
} else {
|
||||
uint64_t maxEnd = start + ((uint64_t)pages * PAGE_SIZE);
|
||||
if (maxEnd > fileSize)
|
||||
pages = (uint32_t)((KiAlignUpPage64(fileSize) - start) / PAGE_SIZE);
|
||||
file->ReadaheadIssuedUntil = start + ((uint64_t)pages * PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
KeReleaseSpinlock(&file->Lock, flags);
|
||||
|
||||
if (pages) {
|
||||
KSTATUS ras = MmFileCacheScheduleReadAhead(file, start, pages);
|
||||
if (ras == STATUS_QUEUE_FULL) {
|
||||
flags = KeAcquireSpinlock(&file->Lock);
|
||||
uint64_t suppressUntil = end + (1024u * 1024u);
|
||||
if (suppressUntil < end || suppressUntil > fileSize)
|
||||
suppressUntil = fileSize;
|
||||
if (file->ReadaheadSuppressedUntil < suppressUntil)
|
||||
file->ReadaheadSuppressedUntil = suppressUntil;
|
||||
KeReleaseSpinlock(&file->Lock, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit-offset file I/O. Async I/O is built on these routines so it does
|
||||
// not race the sequential FILE_OBJECT.Offset used by legacy read/write calls.
|
||||
__declspec(dllexport) KSTATUS IoReadFileAt(FILE_OBJECT* file, uint64_t offset,
|
||||
@@ -679,7 +861,50 @@ __declspec(dllexport) KSTATUS IoReadFileAt(FILE_OBJECT* file, uint64_t offset,
|
||||
size_t remaining = (size_t)(fileSize - offset);
|
||||
if (size > remaining) size = remaining;
|
||||
|
||||
return file->Fs->Read(file->Fs, file->FileCtx, offset, buf, size, bytesRead);
|
||||
if ((file->Flags & IO_FILE_FLAG_NO_CACHE) || size < 512)
|
||||
return file->Fs->Read(file->Fs, file->FileCtx, offset, buf, size, bytesRead);
|
||||
|
||||
size_t total = 0;
|
||||
uint8_t* dst = (uint8_t*)buf;
|
||||
while (total < size) {
|
||||
uint64_t absolute = offset + total;
|
||||
uint64_t pageOffset = absolute & ~(uint64_t)(PAGE_SIZE - 1);
|
||||
size_t inPage = (size_t)(absolute - pageOffset);
|
||||
size_t chunk = PAGE_SIZE - inPage;
|
||||
if (chunk > size - total) chunk = size - total;
|
||||
|
||||
MM_FILE_CACHE_PAGE page;
|
||||
KSTATUS s = MmFileCacheGetPage(file, pageOffset, &page);
|
||||
if (!KSUCCESS(s)) {
|
||||
if (total) {
|
||||
*bytesRead = total;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
if (inPage >= page.ValidBytes) {
|
||||
MmFileCacheReleasePage(&page);
|
||||
break;
|
||||
}
|
||||
size_t available = page.ValidBytes - inPage;
|
||||
if (chunk > available) chunk = available;
|
||||
|
||||
void* srcPage = MmPfnToVirtual(page.Frame);
|
||||
if (!srcPage) {
|
||||
MmFileCacheReleasePage(&page);
|
||||
return total ? STATUS_SUCCESS : STATUS_FAILURE;
|
||||
}
|
||||
memcpy(dst + total, (const uint8_t*)srcPage + inPage, chunk);
|
||||
MmFileCacheReleasePage(&page);
|
||||
|
||||
total += chunk;
|
||||
if (chunk == 0) break;
|
||||
}
|
||||
|
||||
*bytesRead = total;
|
||||
KiIoMaybeScheduleReadAhead(file, offset, total, fileSize);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
__declspec(dllexport) KSTATUS IoWriteFileAt(FILE_OBJECT* file, uint64_t offset,
|
||||
@@ -691,10 +916,21 @@ __declspec(dllexport) KSTATUS IoWriteFileAt(FILE_OBJECT* file, uint64_t offset,
|
||||
if (!file->Fs->Write) return STATUS_NOT_SUPPORTED;
|
||||
if (size == 0) return STATUS_SUCCESS;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&file->Lock);
|
||||
uint64_t oldSize = file->Size;
|
||||
KeReleaseSpinlock(&file->Lock, flags);
|
||||
|
||||
size_t written = 0;
|
||||
KSTATUS s = file->Fs->Write(file->Fs, file->FileCtx, offset, buf, size, &written);
|
||||
KSTATUS s;
|
||||
if (file->Flags & IO_FILE_FLAG_NO_CACHE) {
|
||||
s = file->Fs->Write(file->Fs, file->FileCtx, offset, buf, size, &written);
|
||||
if (KSUCCESS(s) && written)
|
||||
MmFileCacheInvalidate(file, offset, written);
|
||||
} else {
|
||||
s = MmFileCacheWrite(file, offset, buf, size, oldSize, &written);
|
||||
}
|
||||
if (KSUCCESS(s)) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&file->Lock);
|
||||
flags = KeAcquireSpinlock(&file->Lock);
|
||||
uint64_t end = offset + written;
|
||||
if (end > file->Size) file->Size = end;
|
||||
KeReleaseSpinlock(&file->Lock, flags);
|
||||
@@ -704,6 +940,16 @@ __declspec(dllexport) KSTATUS IoWriteFileAt(FILE_OBJECT* file, uint64_t offset,
|
||||
return s;
|
||||
}
|
||||
|
||||
// Flushes dirty mapped and cached pages for one file object.
|
||||
__declspec(dllexport) KSTATUS IoFlushFile(FILE_OBJECT* file) {
|
||||
if (!file) return STATUS_INVALID_PARAMETER;
|
||||
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
||||
|
||||
KSTATUS sync = MmVadSyncFileMappings(file);
|
||||
KSTATUS flush = MmFileCacheFlushFile(file);
|
||||
return KSUCCESS(sync) ? flush : sync;
|
||||
}
|
||||
|
||||
// IoReadFile - sequential read at current offset.
|
||||
__declspec(dllexport) KSTATUS IoReadFile(FILE_OBJECT* file, void* buf,
|
||||
size_t size, size_t* bytesRead) {
|
||||
@@ -768,6 +1014,11 @@ __declspec(dllexport) KSTATUS IoCreateFile(SYS_STRING_VIEW path, FILE_OBJECT** o
|
||||
f->Lock = (SPINLOCK)SPINLOCK_INIT;
|
||||
f->Size = 0;
|
||||
f->Offset = 0;
|
||||
f->Flags = 0;
|
||||
f->ReadaheadScore = 0;
|
||||
f->ReadaheadNextOffset = 0;
|
||||
f->ReadaheadIssuedUntil = 0;
|
||||
f->ReadaheadSuppressedUntil = 0;
|
||||
|
||||
*out = f;
|
||||
return STATUS_SUCCESS;
|
||||
@@ -848,8 +1099,12 @@ __declspec(dllexport) KSTATUS IoTruncateFile(FILE_OBJECT* file, uint64_t newSize
|
||||
if (!KeCanWait()) return STATUS_INVALID_IRQL;
|
||||
if (!file->Fs->Truncate) return STATUS_NOT_SUPPORTED;
|
||||
|
||||
KSTATUS s = file->Fs->Truncate(file->Fs, file->FileCtx, newSize);
|
||||
KSTATUS flush = IoFlushFile(file);
|
||||
if (!KSUCCESS(flush)) return flush;
|
||||
|
||||
KSTATUS s = file->Fs->Truncate(file->Fs, file->FileCtx, newSize, file->Flags);
|
||||
if (KSUCCESS(s)) {
|
||||
MmFileCacheInvalidate(file, newSize, (size_t)-1);
|
||||
file->Size = newSize;
|
||||
// Pull the file pointer back if it now sits past EOF.
|
||||
if (file->Offset > file->Size)
|
||||
@@ -875,6 +1130,16 @@ __declspec(dllexport) uint64_t IoGetFileSize(FILE_OBJECT* file) {
|
||||
return size;
|
||||
}
|
||||
|
||||
// Queries the filesystem identity for a file object.
|
||||
__declspec(dllexport) KSTATUS IoQueryFileIdentity(FILE_OBJECT* file, IO_FILE_IDENTITY* out) {
|
||||
if (!file || !out)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
memset(out, 0, sizeof(*out));
|
||||
if (!file->Fs || !file->Fs->QueryIdentity)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
return file->Fs->QueryIdentity(file->Fs, file->FileCtx, out);
|
||||
}
|
||||
|
||||
// Releases file.
|
||||
__declspec(dllexport) void IoCloseFile(FILE_OBJECT* file) {
|
||||
if (file) ObDereferenceObject(&file->Header);
|
||||
|
||||
Reference in new issue
Block a user