487 lines
14 KiB
C++
487 lines
14 KiB
C++
/*
|
|
* PROJECT: Alcyone System Kernel
|
|
* LICENSE: BSD Clause 3
|
|
* PURPOSE: Cache Controllerm, Cache Utility Handler.
|
|
* NT KERNEL: 5.11.9360
|
|
* COPYRIGHT: 2023-2029 Dibymartanda Samanta <>
|
|
*/
|
|
|
|
constexpr ULONG LastpageLimit = {0x200};
|
|
constexpr LONGLONG BASE_PAGE_INITIALIZER = {-1};
|
|
constexpr ULONG DIRTY_PAGE_INITIALIZER = {-1};
|
|
constexpr ULONG DIRTY_PAGE_INIT = {0};
|
|
|
|
class BitmapRangeManager {
|
|
private:
|
|
static constexpr LONGLONG CalculateBasePage(LONGLONG InputPage) {
|
|
return (InputPage & ~(0x1000 - 1));
|
|
}
|
|
public:
|
|
static PBITMAP_RANGE FindBitmapRangeToDirty(PMBCB Mbcb, LONGLONG InputPage, PULONG* pBitmap) {
|
|
PBITMAP_RANGE CurrentRange = nullptr;
|
|
PBITMAP_RANGE NewRange = nullptr;
|
|
PLIST_ENTRY HeadList = nullptr;
|
|
LONGLONG BasePage = {0};
|
|
|
|
|
|
HeadList = &Mbcb->BitmapRanges;
|
|
BasePage = CalculateBasePage(InputPage);
|
|
|
|
CurrentRange = CONTAINING_RECORD(Mbcb->BitmapRanges.Flink, BITMAP_RANGE, Links);
|
|
|
|
while(true){
|
|
if (BasePage == CurrentRange->BasePage)
|
|
return CurrentRange;
|
|
|
|
if (CurrentRange->DirtyPages || NewRange) {
|
|
if (BasePage > CurrentRange->BasePage)
|
|
HeadList = &CurrentRange->Links;
|
|
}
|
|
else {
|
|
NewRange = CurrentRange;
|
|
}
|
|
|
|
if (CurrentRange->Links.Flink == &Mbcb->BitmapRanges)
|
|
break;
|
|
|
|
CurrentRange = CONTAINING_RECORD(CurrentRange->Links.Flink, BITMAP_RANGE, Links);
|
|
|
|
if (CurrentRange->BasePage > BasePage && NewRange)
|
|
break;
|
|
}
|
|
|
|
if (NewRange) {
|
|
RemoveEntryList(&NewRange->Links);
|
|
}
|
|
else {
|
|
NewRange = reinterpret_cast<PBITMAP_RANGE>(ExAllocatePoolWithTag(NonPagedPool, sizeof(*NewRange), 'rBcC'));
|
|
if (!NewRange) {
|
|
return nullptr;
|
|
}
|
|
|
|
RtlZeroMemory(NewRange, sizeof(*NewRange));
|
|
}
|
|
|
|
InsertHeadList(HeadList, &NewRange->Links);
|
|
|
|
NewRange->BasePage = BasePage;
|
|
NewRange->FirstDirtyPage = DIRTY_PAGE_INITIALIZER;
|
|
NewRange->LastDirtyPage = 0;
|
|
|
|
if (!NewRange->Bitmap) {
|
|
|
|
NewRange->Bitmap = *pBitmap;
|
|
*pBitmap = nullptr;
|
|
}
|
|
|
|
return NewRange;
|
|
}
|
|
};
|
|
|
|
/* Internal Functions */
|
|
LONG
|
|
CcCopyReadExceptionFilter(
|
|
_In_ PEXCEPTION_POINTERS ExceptionInfo,
|
|
_Out_ NTSTATUS* OutStatus)
|
|
{
|
|
|
|
|
|
NT_ASSERT(!NT_SUCCESS(*OutStatus));
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
PBITMAP_RANGE
|
|
NTAPI
|
|
CcFindBitmapRangeToDirty(
|
|
_In_ PMBCB Mbcb,
|
|
_In_ LONGLONG InputPage,
|
|
_Inout_ PULONG* PBITMAP)
|
|
{
|
|
return BitmapRangeManager::FindBitmapRangeToDirty(Mbcb, InputPage, pBitmap);
|
|
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
CcSetDirtyInMask(
|
|
_In_ PSHARED_CACHE_MAP SharedCacheMap,
|
|
_In_ PLARGE_INTEGER FileOffset,
|
|
_In_ ULONG Length)
|
|
{
|
|
KLOCK_QUEUE_HANDLE LockHandle = {0};
|
|
LARGE_INTEGER EndOffset = {0};
|
|
ULONGLONG StartPage ={0};
|
|
ULONGLONG EndPage = {0};
|
|
ULONG CurrentPage = {0};
|
|
PMBCB Mbcb = nullptr;
|
|
PBITMAP_RANGE BitmapRange = nullptr;
|
|
PULONG Bitmap = nullptr;
|
|
PULONG VacbLevel = nullptr;
|
|
ULONG BitMask = {0};
|
|
|
|
|
|
|
|
// Calculate start and end pages
|
|
StartPage = FileOffset->QuadPart >> PAGE_SHIFT;
|
|
EndOffset.QuadPart = FileOffset->QuadPart + Length;
|
|
EndPage = (EndOffset.QuadPart - 1) >> PAGE_SHIFT;
|
|
|
|
for(;;)
|
|
{
|
|
if (SharedCacheMap->SectionSize.QuadPart > VACB_MAPPING_GRANULARITY)
|
|
{
|
|
if (!CcPrefillVacbLevelZone(1, &LockHandle, 0, 0, 0))
|
|
return;
|
|
VacbLevel = CcAllocateVacbLevel(0);
|
|
KeLowerIrql(LockHandle.OldIrql);
|
|
}
|
|
|
|
KeAcquireInStackQueuedSpinLock(&SharedCacheMap->BcbSpinLock, &LockHandle);
|
|
|
|
Mbcb = SharedCacheMap->Mbcb;
|
|
if (Mbcb == nullptr)
|
|
{
|
|
Mbcb = CcAllocateInitializeBcb();
|
|
if (Mbcb == nullptr)
|
|
goto ReleaseAndExit;
|
|
|
|
Mbcb->NodeTypeCode = CACHE_NTC_BCB;
|
|
InitializeListHead(&Mbcb->BitmapRanges);
|
|
InsertHeadList(&Mbcb->BitmapRanges, &Mbcb->BitmapRange1.Links);
|
|
Mbcb->BitmapRange1.FirstDirtyPage = (ULONG)-1;
|
|
Mbcb->BitmapRange1.Bitmap = (PULONG)&Mbcb->BitmapRange2;
|
|
SharedCacheMap->Mbcb = Mbcb;
|
|
}
|
|
|
|
if (EndPage < 512 || Mbcb->NodeTypeCode == 0x02F9)
|
|
{
|
|
BitmapRange = CcFindBitmapRangeToDirty(Mbcb, StartPage, &Bitmap);
|
|
if (BitmapRange == nullptr)
|
|
break;
|
|
|
|
if (StartPage < BitmapRange->BasePage + BitmapRange->FirstDirtyPage)
|
|
BitmapRange->FirstDirtyPage = StartPage - BitmapRange->BasePage;
|
|
if (EndPage > BitmapRange->BasePage + BitmapRange->LastDirtyPage)
|
|
BitmapRange->LastDirtyPage = EndPage - BitmapRange->BasePage;
|
|
|
|
if (SharedCacheMap->DirtyPages == 0)
|
|
{
|
|
CcScheduleLazyWriteScan(FALSE);
|
|
RemoveEntryList(&SharedCacheMap->SharedCacheMapLinks);
|
|
InsertTailList(&CcDirtySharedCacheMapList, &SharedCacheMap->SharedCacheMapLinks);
|
|
Mbcb->ResumeWritePage = StartPage;
|
|
}
|
|
|
|
Bitmap = &BitmapRange->Bitmap[(StartPage - BitmapRange->BasePage) >> 5];
|
|
BitMask = 1 << (StartPage & 0x1F);
|
|
|
|
for (CurrentPage = StartPage; CurrentPage <= EndPage; CurrentPage++)
|
|
{
|
|
if ((*Bitmap & BitMask) == 0)
|
|
{
|
|
CcTotalDirtyPages++;
|
|
Mbcb->DirtyPages++;
|
|
BitmapRange->DirtyPages++;
|
|
SharedCacheMap->DirtyPages++;
|
|
*Bitmap |= BitMask;
|
|
}
|
|
BitMask <<= 1;
|
|
if (BitMask == 0)
|
|
{
|
|
Bitmap++;
|
|
BitMask = 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Handle large files (>2MB)
|
|
if (Mbcb->BitmapRange1.DirtyPages)
|
|
{
|
|
RtlCopyMemory(VacbLevel, Mbcb->BitmapRange1.Bitmap, (2 * sizeof(BITMAP_RANGE)));
|
|
RtlZeroMemory(Mbcb->BitmapRange1.Bitmap, (2 * sizeof(BITMAP_RANGE)));
|
|
}
|
|
Mbcb->BitmapRange1.Bitmap = VacbLevel;
|
|
|
|
// Initialize BitmapRange2
|
|
InsertTailList(&Mbcb->BitmapRanges, &Mbcb->BitmapRange2.Links);
|
|
Mbcb->BitmapRange2.BasePage = BASE_PAGE_INITIALIZER;
|
|
Mbcb->BitmapRange2.FirstDirtyPage = DIRTY_PAGE_INITIALIZER;
|
|
|
|
// Initialize BitmapRange3
|
|
InsertTailList(&Mbcb->BitmapRanges, &Mbcb->BitmapRange3.Links);
|
|
Mbcb->BitmapRange3.BasePage = BASE_PAGE_INITIALIZER;
|
|
Mbcb->BitmapRange3.FirstDirtyPage = DIRTY_PAGE_INITIALIZER;
|
|
|
|
VacbLevel = nullptr;
|
|
Mbcb->NodeTypeCode = 0x02F9;
|
|
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
continue;
|
|
}
|
|
|
|
// Update ValidDataGoal if necessary
|
|
if (EndOffset.QuadPart > SharedCacheMap->ValidDataGoal.QuadPart)
|
|
{
|
|
SharedCacheMap->ValidDataGoal = EndOffset;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
ReleaseAndExit:
|
|
if (VacbLevel != nullptr)
|
|
{
|
|
*VacbLevel = (ULONG)CcVacbLevelFreeList;
|
|
CcVacbLevelEntries++;
|
|
CcVacbLevelFreeList = VacbLevel;
|
|
}
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CcMapAndCopy(
|
|
_In_ PSHARED_CACHE_MAP SharedCacheMap,
|
|
_In_ PVOID Buffer,
|
|
_In_ PLARGE_INTEGER FileOffset,
|
|
_In_ ULONG Length,
|
|
_In_ ULONG Flags,
|
|
_In_ PFILE_OBJECT FileObject,
|
|
_In_ PLARGE_INTEGER ValidDataLength,
|
|
_In_ BOOLEAN Wait)
|
|
{
|
|
NT_DBGBREAK("UNIMPLEMENTED\n");
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* EXTERNAL API FUNCTIONS */
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CcCanIWrite(
|
|
_In_ PFILE_OBJECT FileObject,
|
|
_In_ ULONG BytesToWrite,
|
|
_In_ BOOLEAN Wait,
|
|
_In_ UCHAR Retrying)
|
|
{
|
|
PFSRTL_COMMON_FCB_HEADER FsContext = nullptr;
|
|
PSHARED_CACHE_MAP SharedCacheMap = nullptr;
|
|
DEFERRED_WRITE DeferredWrite = {0};
|
|
KEVENT Event = {0};
|
|
ULONG WriteSize = {0};
|
|
ULONG Pages = {0};
|
|
KIRQL OldIrql = {0};
|
|
BOOLEAN IsSmallThreshold = false;
|
|
|
|
|
|
/* Quick checks for immediate return */
|
|
if (FileObject->Flags & FO_WRITE_THROUGH)
|
|
return true;
|
|
|
|
if (IoIsFileOriginRemote(FileObject) && Retrying < 0xFD)
|
|
return true;
|
|
|
|
/* Calculate size and pages */
|
|
WriteSize = min(BytesToWrite, 0x40000);
|
|
Pages = ROUND_UP(WriteSize, PAGE_SIZE) / PAGE_SIZE;
|
|
|
|
FsContext = FileObject->FsContext;
|
|
|
|
/* Check threshold conditions */
|
|
if (Retrying >= 0xFE || (FsContext->Flags & FSRTL_FLAG_LIMIT_MODIFIED_PAGES))
|
|
{
|
|
if (Retrying != 0xFF)
|
|
{
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
|
|
}
|
|
|
|
if (FileObject->SectionObjectPointer)
|
|
{
|
|
SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
if (SharedCacheMap &&
|
|
SharedCacheMap->DirtyPageThreshold &&
|
|
SharedCacheMap->DirtyPages)
|
|
{
|
|
if (SharedCacheMap->DirtyPageThreshold < (SharedCacheMap->DirtyPages + Pages))
|
|
{
|
|
IsSmallThreshold = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Retrying != 0xFF)
|
|
{
|
|
KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
|
|
}
|
|
}
|
|
|
|
/* Check if we can write immediately */
|
|
if ((Retrying || IsListEmpty(&CcDeferredWrites)) &&
|
|
(CcDirtyPageThreshold > (CcTotalDirtyPages + Pages)))
|
|
{
|
|
if ((MmAvailablePages > MmThrottleTop && !IsSmallThreshold) ||
|
|
(MmModifiedPageListHead.Total < 1000 &&
|
|
MmAvailablePages > MmThrottleBottom &&
|
|
!IsSmallThreshold))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!Wait)
|
|
return FALSE;
|
|
|
|
/* Initialize deferred write */
|
|
if (IsListEmpty(&CcDeferredWrites))
|
|
{
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
|
|
CcScheduleLazyWriteScan(TRUE);
|
|
KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
|
|
}
|
|
|
|
KeInitializeEvent(&Event, NotificationEvent, false);
|
|
|
|
DeferredWrite.NodeTypeCode = NODE_TYPE_DEFERRED_WRITE;
|
|
DeferredWrite.NodeByteSize = sizeof(DEFERRED_WRITE);
|
|
DeferredWrite.FileObject = FileObject;
|
|
DeferredWrite.BytesToWrite = BytesToWrite;
|
|
DeferredWrite.Event = &Event;
|
|
DeferredWrite.LimitModifiedPages = (FsContext->Flags & FSRTL_FLAG_LIMIT_MODIFIED_PAGES) != 0;
|
|
|
|
/* Insert into deferred writes list */
|
|
/* Insert into deferred writes list */
|
|
if (Retrying)
|
|
{
|
|
ExInterlockedInsertHeadList(&CcDeferredWrites,
|
|
&DeferredWrite.DeferredWriteLinks,
|
|
&CcDeferredWriteSpinLock);
|
|
}
|
|
else
|
|
{
|
|
ExInterlockedInsertTailList(&CcDeferredWrites,
|
|
&DeferredWrite.DeferredWriteLinks,
|
|
&CcDeferredWriteSpinLock);
|
|
}
|
|
|
|
/* Wait for the write to complete */
|
|
do
|
|
{
|
|
CcPostDeferredWrites();
|
|
} while (KeWaitForSingleObject(&Event,
|
|
Executive,
|
|
KernelMode,
|
|
false,
|
|
&CcIdleDelay) != STATUS_SUCCESS);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CcCopyRead(
|
|
_In_ PFILE_OBJECT FileObject,
|
|
_In_ PLARGE_INTEGER FileOffset,
|
|
_In_ ULONG Length,
|
|
_In_ BOOLEAN Wait,
|
|
_Out_ PVOID Buffer,
|
|
_Out_ IO_STATUS_BLOCK* OutIoStatus)
|
|
{
|
|
NT_DBGBREAK("UNIMPLEMENTED\n");
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CcCopyWrite(
|
|
_In_ PFILE_OBJECT FileObject,
|
|
_In_ PLARGE_INTEGER FileOffset,
|
|
_In_ ULONG Length,
|
|
_In_ BOOLEAN Wait,
|
|
_In_ PVOID Buffer)
|
|
{
|
|
NT_DBGBREAK("UNIMPLEMENTED\n");
|
|
}
|
|
|
|
|
|
VOID
|
|
CcDeferWrite(
|
|
_In_ PFILE_OBJECT FileObject,
|
|
_In_ PCC_POST_DEFERRED_WRITE PostRoutine,
|
|
_In_ PVOID Context1,
|
|
_In_ PVOID Context2,
|
|
_In_ ULONG BytesToWrite,
|
|
_In_ BOOLEAN Retrying
|
|
)
|
|
{
|
|
|
|
KIRQL OldIrql;
|
|
|
|
|
|
PDEFERRED_WRITE DeferredWrite = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(DEFERRED_WRITE), 'wDcC');
|
|
if (DeferredWrite != nullptr)
|
|
{
|
|
RtlZeroMemory(DeferredWrite, sizeof(DEFERRED_WRITE));
|
|
|
|
DeferredWrite->NodeTypeCode = NODE_TYPE_DEFERRED_WRITE;
|
|
DeferredWrite->NodeByteSize = sizeof(DEFERRED_WRITE);
|
|
DeferredWrite->FileObject = FileObject;
|
|
DeferredWrite->BytesToWrite = BytesToWrite;
|
|
DeferredWrite->PostRoutine = PostRoutine;
|
|
DeferredWrite->Context1 = Context1;
|
|
DeferredWrite->Context2 = Context2;
|
|
|
|
if (Retrying)
|
|
ExInterlockedInsertHeadList(&CcDeferredWrites,
|
|
&DeferredWrite->DeferredWriteLinks,
|
|
&CcDeferredWriteSpinLock);
|
|
else
|
|
ExInterlockedInsertTailList(&CcDeferredWrites,
|
|
&DeferredWrite->DeferredWriteLinks,
|
|
&CcDeferredWriteSpinLock);
|
|
|
|
CcPostDeferredWrites();
|
|
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
|
|
CcScheduleLazyWriteScan(TRUE);
|
|
KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
|
|
}
|
|
else
|
|
{
|
|
PostRoutine(Context1, Context2);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
CcFastCopyRead(
|
|
_In_ PFILE_OBJECT FileObject,
|
|
_In_ ULONG FileOffset,
|
|
_In_ ULONG Length,
|
|
_In_ ULONG PageCount,
|
|
_Out_ PVOID Buffer,
|
|
_Out_ PIO_STATUS_BLOCK IoStatus)
|
|
{
|
|
NT_DBGBREAK("UNIMPLEMENTED\n");
|
|
}
|
|
|
|
|
|
VOID
|
|
NTAPI
|
|
CcFastCopyWrite(
|
|
_In_ PFILE_OBJECT FileObject,
|
|
_In_ ULONG FileOffset,
|
|
_In_ ULONG Length,
|
|
_In_ PVOID InBuffer)
|
|
{
|
|
NT_DBGBREAK("UNIMPLEMENTED\n");
|
|
}
|
|
|
|
|
|
|