Completely implemented CcPinRead and CcPinMappedData In future Refactor with BCB Iterator with bounds checking.
1046 lines
28 KiB
C++
1046 lines
28 KiB
C++
/*
|
|
* PROJECT: Alcyone System Kernel
|
|
* LICENSE: BSD Clause 3
|
|
* PURPOSE: Cache Controller:: Pin Support
|
|
* NT KERNEL: 5.11.9360
|
|
* COPYRIGHT: 2023-2029 Dibymartanda Samanta <>
|
|
*/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NTDEBUG
|
|
#include <debug.h>
|
|
#include "ccinternal.hpp"
|
|
|
|
|
|
extern "C"
|
|
|
|
/* Move Typedef later to cctypes in sdk */
|
|
/* NOTE: This structure have reconstructed analyzing CcAllocateObcb, might be icomplete to one in Windows NT Kernel*/
|
|
/* We just need API Compatibility, so it doesn't matter */
|
|
typedef struct _OBCB
|
|
{
|
|
short NodeTypeCode;
|
|
short NodeByteSize;
|
|
ULONG ByteLength;
|
|
LARGE_INTEGER FileOffset;
|
|
struct PCC_BCB Bcbs[1];
|
|
long __PADDING__[1];
|
|
} OBCB, *POBCB;
|
|
|
|
/*Internal Function*/
|
|
POBCB
|
|
NTAPI
|
|
CcAllocateObcb(
|
|
_In_ PLARGE_INTEGER FileOffset,
|
|
_In_ ULONG Length,
|
|
_In_ PCC_BCB Bcb)
|
|
{
|
|
|
|
|
|
/*Calculate the size needed for the OBCB*/
|
|
SIZE_T AllocationSize = sizeof(OBCB) + ((Length + PAGE_SIZE - 1 + FileOffset->LowPart - *reinterpret_cast<ULONG*>(reinterpret_cast<ULONG_PTR>(FirstBcb) & ~1UL) - *reinterpret_cast<ULONG*>((reinterpret_cast<ULONG_PTR>(FirstBcb) & ~1UL) + sizeof(ULONG))) >> PAGE_SHIFT) * sizeof(PVOID);
|
|
|
|
/*Allocate memory for the OBCB*/
|
|
POBCB NewObcb = reinterpret_cast<POBCB>(ExAllocatePoolWithTag(NonPagedPool,AllocationSize,'cObC'));
|
|
|
|
if (NewObcb == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/* Initialize the OBCB */
|
|
RtlZeroMemory(NewObcb, AllocationSize);
|
|
|
|
NewObcb->NodeByteSize = static_cast<USHORT>(AllocationSize);
|
|
NewObcb->NodeTypeCode = 762;
|
|
NewObcb->ByteLength = Length;
|
|
NewObcb->FileOffset = *FileOffset;
|
|
NewObcb->Bcbs[0] = Bcb;
|
|
|
|
return NewObcb;
|
|
}
|
|
|
|
VOID
|
|
VECTORCALL
|
|
CcMapDataForOverwrite(
|
|
PFILE_OBJECT FileObject,
|
|
PLARGE_INTEGER FileOffset,
|
|
ULONG Length,
|
|
PVOID *Bcb,
|
|
PVOID *Buffer)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PETHREAD currentThread = nullptr;
|
|
PSHARED_CACHE_MAP sharedCacheMap = nullptr;
|
|
ULONG pageCount = {0};
|
|
PVOID outBcb = nullptr;
|
|
PVOID localBuffer = nullptr;
|
|
ULONG savedState = {0};
|
|
ULONG readClusterSize = {0};
|
|
|
|
|
|
pageCount = CalculatePageCount(Length,FileOffset);
|
|
currentThread = (PETHREAD)KeGetCurrentThread();
|
|
sharedCacheMap = (PSHARED_CACHE_MAP)FileObject->SectionObjectPointer->SharedCacheMap;
|
|
|
|
if ((sharedCacheMap->Flags & 0x20000) == 0)
|
|
{
|
|
SpinLockGuard lockGuard(LockQueueMasterLock);
|
|
sharedCacheMap->Flags |= 0x20000;
|
|
|
|
}
|
|
|
|
savedState = currentThread->CacheManagerActive + 2 * currentThread->ReadClusterSize;
|
|
readClusterSize = currentThread->ReadClusterSize;
|
|
|
|
CcMapDataCommon(FileObject, FileOffset, Length, TRUE, &outBcb, &localBuffer);
|
|
|
|
|
|
auto SystemCacheAddress = *localBuffer;
|
|
ULONG remainingPages = pageCount;
|
|
|
|
while (remainingPages)
|
|
{
|
|
currentThread->CacheManagerActive = 1;
|
|
if (--remainingPages > readClusterSize)
|
|
{
|
|
currentThread->ReadClusterSize = (remainingPages > 0xF) ? 15 : remainingPages;
|
|
}
|
|
|
|
if (!MmCheckCachedPageState(SystemCacheAddress,true))
|
|
{
|
|
MmCheckCachedPageState(SystemCacheAddress,false);
|
|
}
|
|
|
|
SystemCacheAddress += 4096;
|
|
}
|
|
|
|
currentThread->CacheManagerActive = savedState & 1;
|
|
currentThread->ReadClusterSize = savedState >> 1;
|
|
*Bcb = outBcb;
|
|
*Buffer = localBuffer;
|
|
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
CcSetDirtyPinnedData(
|
|
IN POBCB BcbVoid,
|
|
IN PLARGE_INTEGER Lsn OPTIONAL)
|
|
{
|
|
PSHARED_CACHE_MAP SharedCacheMap;
|
|
PCC_BCB Bcb;
|
|
PCC_BCB* BcbArray;
|
|
PFAST_MUTEX Mutex;
|
|
KIRQL OldIrql;
|
|
ULONG PagesToWrite;
|
|
PETHREAD CurrentThread;
|
|
|
|
|
|
if (BcbVoid->NodeTypeCode == 762)
|
|
{
|
|
BcbArray = reinterpret_cast<PCC_BCB>(BcbVoid->Bcbs);
|
|
}
|
|
|
|
SharedCacheMap = (*BcbArray)->SharedCacheMap;
|
|
|
|
/* Handle logging if necessary */
|
|
auto VolumeLogHandleContext = &SharedCacheMap->VolumeCacheMap->LogHandleContext;
|
|
if ( (SharedCacheMap->Flags & 0x1000000) != 0 && LogHandleContext.LastLWTimeStamp.QuadPart = -1 )
|
|
{
|
|
KeQuerySystemTime(&LogHandleContext.LastLWTimeStamp)
|
|
}
|
|
|
|
if (!(SharedCacheMap->Flags & 0x200))
|
|
{
|
|
CcSetDirtyInMask(SharedCacheMap, &BcbVoid->FileOffset, BcbVoid->PagesToWrite, NULL);
|
|
return;
|
|
}
|
|
|
|
Bcb = *BcbArray;
|
|
while (Bcb)
|
|
{
|
|
if ((ULONG_PTR)Bcb & 1)
|
|
{
|
|
KeBugCheckEx(CACHE_MANAGER, 0xE94, 0xC0000420, 0, 0);
|
|
}
|
|
|
|
SharedCacheMap = Bcb->SharedCacheMap;
|
|
Mutex = &SharedCacheMap->BcbLock;
|
|
|
|
/* Acquire the BCB lock */
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); // KeAcquireInStackQueuedSpinLock((PKSPIN_LOCK)(v6 + 184), &LockHandle)
|
|
|
|
KeAcquireQueuedSpinLockAtDpcLevel((ULONG_PTR)&KeGetPcr()->Prcb->LockQueue[5]);
|
|
|
|
if (!Bcb->Dirty)
|
|
{
|
|
PagesToWrite = Bcb->PagesToWrite >> PAGE_SHIFT;
|
|
Bcb->Dirty = TRUE;
|
|
|
|
if (Lsn)
|
|
{
|
|
Bcb->OldestLsn = *Lsn;
|
|
Bcb->NewestLsn = *Lsn;
|
|
}
|
|
|
|
/* Update dirty page statistics */
|
|
if (!SharedCacheMap->DirtyPages && !(SharedCacheMap->Flags & PRIVATE_CACHE_MAP_FLAGS_WRITE_THROUGH))
|
|
{
|
|
CcScheduleLazyWriteScan(FALSE, FALSE);
|
|
CcInsertIntoDirtySharedCacheMapList(SharedCacheMap);
|
|
}
|
|
CcChargeDirtyPages(SharedCacheMap, 0, 0, PagesToWrite);
|
|
|
|
/* Handle process disk counters if necessary */
|
|
if (SharedCacheMap->Flags & PRIVATE_CACHE_MAP_FLAGS_TRACK_DIRTY_PAGES)
|
|
{
|
|
PsUpdateDiskCounters(PsGetCurrentThread()->ThreadsProcess, 0, PagesToWrite << PAGE_SHIFT, 0, 1, 0);
|
|
}
|
|
}
|
|
KeReleaseQueuedSpinLockFromDpcLevel((ULONG_PTR)&Prcb->LockQueue[5]);
|
|
|
|
/* Update LSN information */
|
|
if (Lsn)
|
|
{
|
|
if (!Bcb->MostRecentlyDirtiedPage || Lsn->QuadPart < Bcb->MostRecentlyDirtiedPage)
|
|
{
|
|
Bcb->OldestLsn = *Lsn;
|
|
}
|
|
if (!Bcb->NewestLsn.QuadPart || Lsn->QuadPart > Bcb->NewestLsn.QuadPart)
|
|
{
|
|
Bcb->NewestLsn = *Lsn;
|
|
}
|
|
if (Lsn->QuadPart > SharedCacheMap->LargestLSN.QuadPart)
|
|
{
|
|
SharedCacheMap->LargestLSN = *Lsn;
|
|
}
|
|
}
|
|
|
|
/* Update ValidDataGoal if necessary */
|
|
if (Bcb->ResumeWritePage > SharedCacheMap->ValidDataGoal.QuadPart)
|
|
{
|
|
SharedCacheMap->ValidDataGoal.QuadPart = Bcb->ResumeWritePage;
|
|
}
|
|
|
|
/* Release the BCB lock */
|
|
|
|
KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); // KeAcquireInStackQueuedSpinLock
|
|
|
|
/* Move to the next BCB */
|
|
BcbArray++;
|
|
Bcb = *BcbArray;
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CcPinFileData(
|
|
_In_ PFILE_OBJECT FileObject,
|
|
_In_ PLARGE_INTEGER FileOffset,
|
|
_In_ ULONG Length,
|
|
_In_ BOOLEAN IsNoWrite,
|
|
_In_ BOOLEAN Flags,
|
|
_In_ ULONG PinFlags,
|
|
_Out_ PCC_BCB* Bcb,
|
|
_Out_ PVOID* Buffer,
|
|
_Out_ LARGE_INTEGER* BeyondLastByte)
|
|
{
|
|
|
|
KLOCK_QUEUE_HANDLE LockHandle ={0};
|
|
PSHARED_CACHE_MAP SharedMap = nullptr;
|
|
PVACB Vacb = nullptr;
|
|
PCC_BCB Bcb = nullptr;
|
|
LARGE_INTEGER EndFileOffset ={0};
|
|
LARGE_INTEGER offset = {0};
|
|
LARGE_INTEGER length = {0};
|
|
ULONG ActivePage = {0};
|
|
ULONG ReceivedLength ={0};
|
|
ULONG MapFlags = {0};
|
|
BOOLEAN IsLocked = false;
|
|
BOOLEAN IsVacbLocked = false;
|
|
BOOLEAN Result = false;
|
|
|
|
|
|
SharedMap = FileObject->SectionObjectPointer->SharedCacheMap;
|
|
|
|
CcGetActiveVacb(SharedMap, &Vacb, &ActivePage, &IsVacbLocked);
|
|
|
|
if (Vacb || SharedMap->NeedToZero)
|
|
{
|
|
CcFreeActiveVacb(SharedMap, Vacb, ActivePage, IsVacbLocked);
|
|
Vacb = nullptr;
|
|
}
|
|
|
|
*Bcb = nullptr;
|
|
*Buffer = nullptr;
|
|
|
|
if (PinFlags & PIN_NO_READ)
|
|
{
|
|
ReceivedLength = PAGE_SIZE - (FileOffset->LowPart & (PAGE_SIZE - 1));
|
|
CcReferenceFileOffset(SharedMap, *FileOffset);
|
|
}
|
|
else
|
|
{
|
|
*Buffer = CcGetVirtualAddress(SharedMap, *FileOffset, &Vacb, &ReceivedLength);
|
|
}
|
|
|
|
KeAcquireInStackQueuedSpinLock(&SharedMap->BcbSpinLock, &LockHandle);
|
|
IsLocked = true;
|
|
|
|
_try{
|
|
|
|
EndFileOffset.QuadPart = (FileOffset->QuadPart + Length);
|
|
|
|
if (CcFindBcb(SharedMap, FileOffset, &EndFileOffset, &Bcb))
|
|
{
|
|
if (!(SharedMap->Flags & SHARE_FL_MODIFIED_NO_WRITE))
|
|
IsNoWrite = true;
|
|
|
|
if (Bcb->BaseAddress)
|
|
{
|
|
if (PinFlags & PIN_WAIT)
|
|
{
|
|
Bcb->PinCount++;
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
IsLocked = false;
|
|
|
|
if (!IsNoWrite)
|
|
{
|
|
if (PinFlags & PIN_EXCLUSIVE)
|
|
ExAcquireResourceExclusiveLite(&Bcb->BcbResource, true);
|
|
else
|
|
ExAcquireSharedStarveExclusive(&Bcb->BcbResource, true);
|
|
}
|
|
}
|
|
if (!(PinFlags & PIN_WAIT))
|
|
{
|
|
if (!IsNoWrite && !ExAcquireSharedStarveExclusive(&Bcb->BcbResource, false))
|
|
{
|
|
Bcb = nullptr;
|
|
Result = false;
|
|
goto finish;
|
|
}
|
|
InterlockedIncrement(&Bcb->PinCount);
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
IsLocked = false;
|
|
Result = CcMapAndRead(SharedMap, &offset, length.LowPart, 0, false, *Buffer);
|
|
if (!Result)
|
|
goto finish;
|
|
}
|
|
|
|
|
|
*Buffer = reinterpret_cast<PVOID>(reinterpret_cast<ULONG_PTR>(Bcb->BaseAddress) + (FileOffset->LowPart - Bcb->FileOffset.LowPart));
|
|
|
|
Result = true;
|
|
goto finish;
|
|
}
|
|
else
|
|
{
|
|
*Buffer = reinterpret_cast<PVOID>(reinterpret_cast<ULONG_PTR>(*Buffer) + (Bcb->FileOffset.LowPart - FileOffset->LowPart));
|
|
|
|
offset.QuadPart = Bcb->FileOffset.QuadPart;
|
|
length.QuadPart = Bcb->Length;
|
|
|
|
if (PinFlags & PIN_WAIT)
|
|
{
|
|
Bcb->PinCount++;
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
IsLocked = false;
|
|
|
|
if (!IsNoWrite)
|
|
{
|
|
if (PinFlags & PIN_EXCLUSIVE)
|
|
ExAcquireResourceExclusiveLite(&Bcb->BcbResource, true);
|
|
else
|
|
ExAcquireSharedStarveExclusive(&Bcb->BcbResource, true);
|
|
}
|
|
|
|
if (PinFlags & PIN_NO_READ)
|
|
{
|
|
Result = true;
|
|
goto finish;
|
|
}
|
|
|
|
CcMapAndRead(SharedMap, &offset, length.LowPart, 0, true, *Buffer);
|
|
|
|
KeAcquireInStackQueuedSpinLock(&SharedMap->BcbSpinLock, &LockHandle);
|
|
if (!Bcb->BaseAddress)
|
|
{
|
|
Bcb->BaseAddress = *Buffer;
|
|
Bcb->Vacb = Vacb;
|
|
Vacb = nullptr;
|
|
}
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
|
|
|
|
*Buffer = reinterpret_cast<PVOID>(reinterpret_cast<ULONG_PTR>(Bcb->BaseAddress) + (FileOffset->LowPart - Bcb->FileOffset.LowPart));
|
|
|
|
Result = true;
|
|
goto finish;
|
|
}
|
|
|
|
*Buffer = reinterpret_cast<PVOID>(reinterpret_cast<ULONG_PTR>(Bcb->BaseAddress) + (FileOffset->LowPart - Bcb->FileOffset.LowPart));
|
|
|
|
Result = true;
|
|
goto finish;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PinFlags & PIN_IF_BCB)
|
|
{
|
|
Bcb = nullptr;
|
|
Result = false;
|
|
goto finish;
|
|
}
|
|
|
|
offset.QuadPart = FileOffset->QuadPart;
|
|
length.QuadPart = (EndFileOffset.QuadPart - offset.QuadPart);
|
|
|
|
length.LowPart += (offset.LowPart & (PAGE_SIZE - 1));
|
|
ReceivedLength += (offset.LowPart & (PAGE_SIZE - 1));
|
|
|
|
if ((!IsNoWrite && !(SharedMap->Flags & SHARE_FL_PIN_ACCESS)) || Flags)
|
|
{
|
|
if (!(offset.LowPart & (PAGE_SIZE - 1)) && Length >= PAGE_SIZE)
|
|
MapFlags = 3;
|
|
else
|
|
MapFlags = 2;
|
|
|
|
if (!(length.LowPart & (PAGE_SIZE - 1)))
|
|
MapFlags |= 4;
|
|
}
|
|
|
|
if (!(SharedMap->Flags & SHARE_FL_MODIFIED_NO_WRITE))
|
|
IsNoWrite = true;
|
|
|
|
*Buffer = reinterpret_cast<PVOID>(reinterpret_cast<ULONG_PTR>(*Buffer) - (FileOffset->LowPart & (PAGE_SIZE - 1)));
|
|
|
|
offset.LowPart &= ~(PAGE_SIZE - 1);
|
|
|
|
length.LowPart = ROUND_TO_PAGES(length.LowPart);
|
|
|
|
if (length.LowPart > ReceivedLength)
|
|
length.LowPart = ReceivedLength;
|
|
|
|
Bcb = CcAllocateInitializeBcb(SharedMap, Bcb, &offset, &length);
|
|
|
|
if (PinFlags & PIN_WAIT)
|
|
{
|
|
if (!Bcb)
|
|
{
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
IsLocked = false;
|
|
RtlRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
if (!IsNoWrite)
|
|
{
|
|
/* For writing need acquire Bcb */
|
|
if (PinFlags & PIN_EXCLUSIVE)
|
|
ExAcquireResourceExclusiveLite(&Bcb->BcbResource, true);
|
|
else
|
|
ExAcquireSharedStarveExclusive(&Bcb->BcbResource, true);
|
|
}
|
|
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
IsLocked = false;
|
|
|
|
if (PinFlags & PIN_NO_READ)
|
|
{
|
|
Result = true;
|
|
goto finish;
|
|
}
|
|
|
|
CcMapAndRead(SharedMap, &offset, length.LowPart, MapFlags, true, *Buffer);
|
|
|
|
KeAcquireInStackQueuedSpinLock(&SharedMap->BcbSpinLock, &LockHandle);
|
|
if (!Bcb->BaseAddress)
|
|
{
|
|
Bcb->BaseAddress = *Buffer;
|
|
Bcb->Vacb = Vacb;
|
|
Vacb = nullptr;
|
|
}
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
|
|
*Buffer = reinterpret_cast<PVOID>(
|
|
reinterpret_cast<ULONG_PTR>(Bcb->BaseAddress) +
|
|
(FileOffset->LowPart - Bcb->FileOffset.LowPart));
|
|
|
|
Result = true;
|
|
goto finish;
|
|
}
|
|
else
|
|
{
|
|
|
|
if (!IsNoWrite && !ExAcquireSharedStarveExclusive(&Bcb->BcbResource, false))
|
|
{
|
|
Bcb = nullptr;
|
|
Result = false;
|
|
goto Finish;
|
|
}
|
|
_InterlockedIncrement(&Bcb->PinCount);
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
IsLocked = false;
|
|
Result = CcMapAndRead(SharedMap, &offset, length.LowPart, MapFlags, false, *Buffer);
|
|
if (!Result)
|
|
goto finish;
|
|
|
|
}
|
|
}
|
|
}
|
|
finish:
|
|
|
|
if ((PinFlags & PIN_NO_READ) && (PinFlags & PIN_EXCLUSIVE) && Bcb && Bcb->BaseAddress)
|
|
{
|
|
CcFreeVirtualAddress(Bcb->Vacb);
|
|
|
|
Bcb->BaseAddress = nullptr;
|
|
Bcb->Vacb = nullptr;
|
|
}
|
|
__finally{
|
|
|
|
if (IsLocked)
|
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|
|
|
if (Vacb)
|
|
CcFreeVirtualAddress(Vacb);
|
|
|
|
if (PinFlags & PIN_NO_READ)
|
|
{
|
|
CcDereferenceFileOffset(SharedMap, *FileOffset);
|
|
|
|
}
|
|
if (Result)
|
|
{
|
|
*Bcb = Bcb;
|
|
BeyondLastByte->QuadPart = Bcb->BeyondLastByte.QuadPart;
|
|
}
|
|
else
|
|
{
|
|
*Buffer = nullptr;
|
|
|
|
if (Bcb)
|
|
CcUnpinFileDataEx(Bcb, IsNoWrite, 0);
|
|
}
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CcMapDataCommon(
|
|
_In_ PFILE_OBJECT FileObject,
|
|
_In_ PLARGE_INTEGER FileOffset,
|
|
_In_ ULONG Length,
|
|
_In_ ULONG Flags,
|
|
_Out_ PVOID *Bcb,
|
|
_Out_ PVOID *Buffer
|
|
)
|
|
{
|
|
if (Flags & FILE_PIN_SEQUENTIAL_READ)
|
|
InterlockedIncrement(&CcSequentialReadCount);
|
|
else
|
|
InterlockedIncrement(&CcRandomReadCount);
|
|
|
|
KeGetCurrentThread()->WaitIrql = PASSIVE_LEVEL;
|
|
|
|
if (Flags & FILE_PIN_SEQUENTIAL_READ)
|
|
{
|
|
*Buffer = CcGetVirtualAddress(SharedMap, *FileOffset, (PVACB *)&Bcb, &ReceivedLength);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
LARGE_INTEGER BeyondLastByte;
|
|
if (CcPinFileData(FileObject, FileOffset, Length, TRUE, 0, Flags, Bcb, Buffer, &BeyondLastByte))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
InterlockedIncrement(&CCFailedPinReadCount);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
CcUnpinFileDataEx(
|
|
_In_ PCC_BCB Bcb,
|
|
_In_ BOOLEAN& WriteStatus,
|
|
_In_ ULONG& UnPinType)
|
|
{
|
|
PSHARED_CACHE_MAP SharedCacheMap = nullptr;
|
|
KIRQL OldIrql = {0};
|
|
ULONG NumberOfPages = {0};
|
|
BOOLEAN ReadOnly = false;
|
|
|
|
|
|
/* If it is not BCB Ptr,it must be VACB Pointer, free it*/
|
|
if (Bcb->NodeTypeCode != BCB_NODE_TYPE_CODE)
|
|
{
|
|
PVACB Vacb = reinterpret_cast<PVACB>(Bcb);
|
|
|
|
CcFreeVirtualAddress(Vacb);
|
|
|
|
return;
|
|
}
|
|
|
|
SharedCacheMap = Bcb->SharedCacheMap;
|
|
/* Is it only Unpinning if so is their no write performed by Cache Controller */
|
|
if (!(SharedCacheMap->Flags & SHARED_CACHE_MAP_MODIFIED_NO_WRITE) || UnPinType == UnPin)
|
|
ReadOnly = TRUE;
|
|
|
|
OldIrql = KeAcquireSpinLockRaiseToDpc(&SharedCacheMap->BcbSpinLock);
|
|
/* Just reduce pin count if BCB is not set to be cleaned else perform cleaning of BCB*/
|
|
if (UnPinType < CLEAN_BCB_PIN)
|
|
{
|
|
ASSERT(Bcb->PinCount > 0);
|
|
Bcb->PinCount--;
|
|
}
|
|
else
|
|
{
|
|
if (UnPinType != CLEAN_BCB_PIN)
|
|
KeBugCheckEx(UNEXPECTED_VALUE, UnPinType, (ULONG_PTR)Bcb, 0, 0);
|
|
|
|
if (Bcb->IsBCBDirty)
|
|
{
|
|
NumberOfPages = (Bcb->Length / PAGE_SIZE);
|
|
|
|
Bcb->IsBCBDirty = FALSE;
|
|
Bcb->NewestDirtiedPage = 0;
|
|
Bcb->NewestLsn.QuadPart = 0;
|
|
|
|
CcDeductIsBCBDirtyPages(SharedCacheMap, NumberOfPages);
|
|
/*Adjust page count */
|
|
if (CcPagesYetToWrite <= NumberOfPages)
|
|
CcPagesYetToWrite = 0;
|
|
else
|
|
CcPagesYetToWrite -= NumberOfPages;
|
|
|
|
if (!SharedCacheMap->IsBCBDirtyPages && SharedCacheMap->OpenCount)
|
|
CcInsertIntoCleanSharedCacheMapList(SharedCacheMap);
|
|
}
|
|
}
|
|
|
|
if (Bcb->PinCount)
|
|
{ /* If not read only BCB, release resources,their no point being pinned at that case */
|
|
if (!ReadOnly)
|
|
ExReleaseResourceLite(&Bcb->BcbResource);
|
|
|
|
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
|
|
}
|
|
/*if BCB is dirty, free the virtual adress*/
|
|
else if (Bcb->IsBCBDirty)
|
|
{
|
|
if (Bcb->BaseAddress)
|
|
{
|
|
CcFreeVirtualAddress(Bcb->Vacb);
|
|
|
|
Bcb->BaseAddress = NULL;
|
|
Bcb->Vacb = NULL;
|
|
}
|
|
|
|
if (!ReadOnly)
|
|
ExReleaseResourceLite(&Bcb->BcbResource);
|
|
|
|
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
|
|
}
|
|
else
|
|
{
|
|
KeAcquireSpinLockAtDpcLevel(&SharedCacheMap->VacbSpinLock);
|
|
|
|
RemoveEntryList(&Bcb->Link);
|
|
|
|
if (SharedCacheMap->SectionSize.QuadPart > CACHE_OVERALL_SIZE &&
|
|
(SharedCacheMap->Flags & SHARED_CACHE_MAP_MODIFIED_NO_WRITE))
|
|
{
|
|
CcAdjustVacbLevelLockCount(SharedCacheMap, &Bcb->FileOffset.QuadPart, -1);
|
|
}
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&SharedCacheMap->VacbSpinLock);
|
|
|
|
if (Bcb->BaseAddress)
|
|
CcFreeVirtualAddress(Bcb->Vacb);
|
|
|
|
if (!ReadOnly)
|
|
ExReleaseResourceLite(&Bcb->BcbResource);
|
|
|
|
KeReleaseSpinLock(&SharedCacheMap->BcbSpinLock, OldIrql);
|
|
|
|
CcDeallocateBcb(Bcb);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CcMapData(
|
|
_In_ In PFILE_OBJECT FileObject,
|
|
_In_ PLARGE_INTEGER FileOffset,
|
|
_In_ ULONG Length,
|
|
_In_ ULONG Flags,
|
|
_Out_ PVOID *Bcb,
|
|
_Out_ PVOID *Buffer)
|
|
{
|
|
PVOID LocalBuffer = nullptr;
|
|
/* Call CcMapDataCommon to perform the actual mapping */
|
|
if (!CcMapDataCommon(FileObject,FileOffset,Length,Flags,Bcb,&LocalBuffer))
|
|
{
|
|
return false;
|
|
}
|
|
/* Check if we need to read the data */
|
|
if (!(Flags & MAP_NO_READ))
|
|
{
|
|
/* Read the data */
|
|
if (!CcMapAndRead(Length, 0, true, LocalBuffer))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
/* Set Bcb to point after the LocalBuffer */
|
|
*Bcb = reinterpret_cast<PVOID>(reinterpret_cast<ULONG_PTR>(LocalBuffer) + sizeof(PVOID));
|
|
/* Set the output Buffer */
|
|
*Buffer = LocalBuffer;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CcPinMappedData(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN ULONG Flags,
|
|
IN OUT PCC_BCB Bcb)
|
|
{
|
|
PSHARED_CACHE_MAP SharedCacheMap = nullptr;
|
|
LARGE_INTEGER LocalFileOffset = {0};
|
|
LARGE_INTEGER BeyondLastByte ={0};
|
|
PVOID Buffer = nullptr;
|
|
POBCB localbcb = nullptr;
|
|
PCC_BCB *localbcbptr = nullptr;
|
|
PCC_BCB *Bcbs = nullptr;
|
|
bool Result = false;
|
|
|
|
|
|
LocalFileOffset = *FileOffset;
|
|
|
|
if (!(reinterpret_cast<PULONG>(Bcb) & 1))
|
|
return true;
|
|
|
|
*Bcb = reinterpret_cast<PVOID>(reinterpret_cast<ULONG_PTR>(*Bcb) - 1);
|
|
|
|
SharedCacheMap = reinterpret_cast<PSHARED_CACHE_MAP>(FileObject->SectionObjectPointer->SharedCacheMap);
|
|
InterlockedIncrement(&SharedCacheMap->PinCount);
|
|
|
|
if (ExAcquireSharedStarveExclusive(&((PCC_BCB)*Bcb)->BcbResource, ((Flags & 1) == 1)))
|
|
{
|
|
Result = true;
|
|
}
|
|
else
|
|
{
|
|
LARGE_INTEGER localoffset = LocalFileOffset;
|
|
ULONG RemainingLength = Length;
|
|
|
|
while (true)
|
|
{
|
|
if (localbcb)
|
|
{
|
|
if (localbcbptr == reinterpret_cast<PCC_BCB*>(&localbcb))
|
|
{
|
|
localbcb = CcAllocateObcb(FileOffset, Length, reinterpret_cast<PCC_BCB>(localbcb));
|
|
Bcbs = localbcb->Bcbs;
|
|
localbcbptr = localbcb->Bcbs;
|
|
}
|
|
RemainingLength += localoffset.LowPart - BeyondLastByte.LowPart;
|
|
localoffset = BeyondLastByte;
|
|
LocalFileOffset = BeyondLastByte;
|
|
localbcbptr = ++Bcbs;
|
|
}
|
|
|
|
if (!CcPinFileData(FileObject,&LocalFileOffset,RemainingLength,!(SharedCacheMap->Flags & 0x200),false,Flags,localbcbptr,&Buffer,&BeyondLastByte))
|
|
{
|
|
Result = false;
|
|
break;
|
|
}
|
|
|
|
if ((BeyondLastByte.QuadPart - localoffset.QuadPart) >= RemainingLength)
|
|
{
|
|
CcFreeVirtualAddress(reinterpret_cast<PVACB>(*Bcb));
|
|
*Bcb = localbcb;
|
|
Result = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Result)
|
|
{
|
|
*Bcb = reinterpret_cast<PVOID>(reinterpret_cast<ULONG_PTR>(*Bcb) + 1);
|
|
if (localbcb)
|
|
{
|
|
CcUnpinData(localbcb);
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CcPinRead(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN ULONG Flags,
|
|
OUT PVOID *Bcb,
|
|
OUT PVOID *Buffer)
|
|
{
|
|
PSHARED_CACHE_MAP SharedCacheMap = nullptr;
|
|
LARGE_INTEGER LocalFileOffset ={0};
|
|
LARGE_INTEGER BeyondLastByte ={0};
|
|
PVOID LocalBuffer = nullptr;
|
|
POBCB localbcb = nullptr;
|
|
PCC_BCB *localBcbPtr = nullptr;
|
|
bool Result = false;
|
|
ULONG RemainingLength = Length;
|
|
|
|
|
|
|
|
LocalFileOffset = *FileOffset;
|
|
|
|
if (Flags & 1) // 1 Signifies Pin is waiting
|
|
CcPinReadWait++;
|
|
else
|
|
CcPinReadNoWait++;
|
|
|
|
|
|
SharedCacheMap = reinterpret_cast<PSHARED_CACHE_MAP>(FileObject->SectionObjectPointer->SharedCacheMap);
|
|
/* Main Pinning Loop*/
|
|
while (true)
|
|
{
|
|
if (localbcb)
|
|
{
|
|
if (localBcbPtr == reinterpret_cast<PCC_BCB*>(&localbcb))
|
|
{
|
|
localbcb = CcAllocateObcb(FileOffset, RemainingLength, reinterpret_cast<PCC_BCB>(localbcb));
|
|
localBcbPtr = localbcb->Bcbs;
|
|
*Buffer = LocalBuffer;
|
|
}
|
|
RemainingLength += LocalFileOffset.LowPart - BeyondLastByte.LowPart;
|
|
LocalFileOffset = BeyondLastByte;
|
|
++localBcbPtr;
|
|
}
|
|
|
|
if (!CcPinFileData(FileObject,&LocalFileOffset,RemainingLength,(SharedCacheMap->Flags & 0x200) == 0,false,Flags,localBcbPtr,&LocalBuffer,&BeyondLastByte))
|
|
{
|
|
Result = false;
|
|
break;
|
|
}
|
|
|
|
if (BeyondLastByte.QuadPart - LocalFileOffset.QuadPart >= RemainingLength)
|
|
{
|
|
*Bcb = localbcb;
|
|
if (localBcbPtr == reinterpret_cast<PCC_BCB*>(&localbcb))
|
|
{
|
|
*Buffer = LocalBuffer;
|
|
}
|
|
Result = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (!Result && localbcb)
|
|
{
|
|
CcUnpinData(localbcb);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CcPreparePinWrite(
|
|
_In_ PFILE_OBJECT FileObject,
|
|
_In_ PLARGE_INTEGER FileOffset,
|
|
_In_ ULONG Length,
|
|
_In_ BOOLEAN Zero,
|
|
_In_ ULONG Flags,
|
|
_Out_ PVOID *Bcb,
|
|
_Out_ PVOID *Buffer)
|
|
{
|
|
LARGE_INTEGER LocalFileOffset = *FileOffset;
|
|
LARGE_INTEGER BeyondLastByte ={0};
|
|
ULONG RemainingLength = Length;
|
|
POBCB localbcb = nullptr;
|
|
PCC_BCB *localbcbPtr = nullptr;
|
|
PVOID LocalBuffer = nullptr;
|
|
BOOLEAN Result = false;
|
|
|
|
|
|
|
|
|
|
if (Flags & PIN_WAIT)
|
|
{
|
|
return CcMapDataForOverwrite(FileObject, FileOffset, Length, Bcb, Buffer);
|
|
}
|
|
|
|
/*Pinning Loop*/
|
|
while(true)
|
|
{
|
|
if (localbcb)
|
|
{
|
|
if (localbcbPtr == reinterpret_cast<PCC_BCB>(&localbcb))
|
|
{
|
|
localbcb = CcAllocateObcb(&LocalFileOffset, RemainingLength, localbcb);
|
|
localbcbPtr = localbcb->Bcbs;
|
|
*Buffer = LocalBuffer;
|
|
}
|
|
RemainingLength += LocalFileOffset.QuadPart - BeyondLastByte.QuadPart;
|
|
LocalFileOffset.QuadPart = BeyondLastByte.QuadPart;
|
|
localbcbPtr++;
|
|
}
|
|
|
|
if (!CcPinFileData(FileObject, &LocalFileOffset,RemainingLength,false,false,Flags,localbcbPtr,&LocalBuffer, &BeyondLastByte))
|
|
{
|
|
/* Pinning failed */
|
|
if (localbcb)
|
|
{
|
|
CcUnpinData(localbcb);
|
|
}
|
|
return false;
|
|
}
|
|
if(BeyondLastByte.QuadPart > LocalFileOffset.QuadPart + RemainingLength)
|
|
break;
|
|
|
|
}
|
|
|
|
if (localbcbPtr == reinterpret_cast<PCC_BCB*>(&localbcb))
|
|
{
|
|
*Buffer = LocalBuffer;
|
|
}
|
|
|
|
if (Zero)
|
|
{
|
|
RtlZeroMemory(*Buffer, Length);
|
|
}
|
|
|
|
if(localbcb != nullptr)
|
|
{
|
|
CcSetDirtyPinnedData(localbcb, nullptr);
|
|
*Bcb = localbcb;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
CcSetBcbOwnerPointer(
|
|
IN PMBCB Bcb,
|
|
IN PVOID OwnerPointer)
|
|
{
|
|
if (Bcb->NodeTypeCode == 762)
|
|
{
|
|
/* Iterate through all bitmap ranges */
|
|
for (auto it = ListEntryIterator::begin(&Bcb->BitmapRanges); it != ListEntryIterator::end(&Bcb->BitmapRanges); ++it)
|
|
{ auto* Bcb = CONTAINING_RECORD(it->Blink,MBCB,BitmapRanges);
|
|
ExSetResourceOwnerPointer(&Bcb->DirtyPages,OwnerPointer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Single bitmap range */
|
|
ExSetResourceOwnerPointer(&Bcb->BitmapRange1.FirstDirtyPage,OwnerPointer);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
NTAPI
|
|
CcUnpinData(
|
|
_In_ PVOID BcbPtr)
|
|
{
|
|
PCC_BCB Bcb = reinterpret_cast<PCC_BCB>(BcbPtr);
|
|
BOOLEAN WRITE_FLAG = NULL;
|
|
|
|
if (reinterpret_cast<ULONG_PTR>((Bcb) & 1))
|
|
{
|
|
WRITE_FLAG = TRUE;
|
|
Bcb = reinterpret_cast<PCC_BCB>(reinterpret_cast<ULONG_PTR>(Bcb) & ~(1));
|
|
|
|
CcUnpinFileDataEx(Bcb, WRITE_FLAG, UNPIN_BCB);
|
|
|
|
return;
|
|
}
|
|
|
|
if (Bcb->NodeTypeCode != 762)
|
|
{
|
|
WRITE_FLAG = FALSE;
|
|
CcUnpinFileDataEx(Bcb, WRITE_FLAG, UNPIN_BCB);
|
|
return;
|
|
}
|
|
for (auto it = ListEntryIterator::begin(&Bcb->BcbLinks); it != ListEntryIterator::end(&Bcb->BcbLinks); ++it)
|
|
{
|
|
auto* BCB_CURRENT = CONTAINING_RECORD(it->Blink, CC_BCB, BcbLinks);
|
|
|
|
CcUnpinData(BCB_CURRENT);
|
|
|
|
}
|
|
ExFreePoolWithTag(Bcb,NULL);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
CcUnpinDataForThread(IN PVOID BcbPtr,
|
|
IN ERESOURCE_THREAD ResourceThreadId)
|
|
{
|
|
PCC_BCB Bcb = BcbPtr;
|
|
BOOLEAN WRITE_FLAG = NULL;
|
|
|
|
if (reinterpret_cast<ULONG_PTR>((Bcb) & 1))
|
|
{
|
|
WRITE_FLAG = TRUE;
|
|
Bcb = reinterpret_cast<PCC_BCB>(reinterpret_cast<ULONG_PTR>(Bcb) & ~(1));
|
|
|
|
CcUnpinFileDataEx(Bcb, WRITE_FLAG, );
|
|
|
|
return;
|
|
}
|
|
|
|
if (Bcb->NodeTypeCode != 762)
|
|
{
|
|
WRITE_FLAG = FALSE;
|
|
ExReleaseResourceForThreadLite(&Bcb->Resource, ResourceThreadId);
|
|
CcUnpinFileDataEx(Bcb, WRITE_FLAG, UNPIN_BCB);
|
|
return;
|
|
}
|
|
for (auto it = ListEntryIterator::begin(&Bcb->BcbLinks); it != ListEntryIterator::end(&Bcb->BcbLinks); ++it)
|
|
{
|
|
auto* BCB_CURRENT = CONTAINING_RECORD(it->Blink, CC_BCB, BcbLinks);
|
|
|
|
CcUnpinData(BCB_CURRENT);
|
|
|
|
}
|
|
ExFreePoolWithTag(Bcb,NULL);
|
|
|
|
}
|
|
|
|
|