You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2554 lines
71 KiB
2554 lines
71 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cmmapvw.c
|
|
|
|
Abstract:
|
|
|
|
This module contains mapped view support for hives.
|
|
|
|
Author:
|
|
|
|
Dragos C. Sambotin (dragoss) 14-Jun-1999
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "cmp.h"
|
|
|
|
#define CM_TRACK_DIRTY_PAGES
|
|
|
|
#ifdef CM_TRACK_DIRTY_PAGES
|
|
#include "..\cache\cc.h"
|
|
#endif
|
|
|
|
|
|
VOID
|
|
CmpUnmapCmView(
|
|
IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView,
|
|
IN BOOLEAN MapIsValid,
|
|
IN BOOLEAN MoveToEnd
|
|
);
|
|
|
|
PCM_VIEW_OF_FILE
|
|
CmpAllocateCmView (
|
|
IN PCMHIVE CmHive
|
|
);
|
|
|
|
VOID
|
|
CmpFreeCmView (
|
|
PCM_VIEW_OF_FILE CmView
|
|
);
|
|
|
|
VOID
|
|
CmpUnmapCmViewSurroundingOffset(
|
|
IN PCMHIVE CmHive,
|
|
IN ULONG FileOffset
|
|
);
|
|
|
|
VOID
|
|
CmpUnmapUnusedViews(
|
|
IN PCMHIVE CmHive
|
|
);
|
|
|
|
#ifdef CMP_CMVIEW_VALIDATION
|
|
|
|
VOID
|
|
CmpCheckCmView(
|
|
IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView
|
|
);
|
|
|
|
#endif //CMP_CMVIEW_VALIDATION
|
|
|
|
|
|
BOOLEAN
|
|
CmIsFileLoadedAsHive(PFILE_OBJECT FileObject);
|
|
|
|
VOID
|
|
CmpReferenceHiveView( IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView
|
|
);
|
|
VOID
|
|
CmpDereferenceHiveView( IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView
|
|
);
|
|
|
|
VOID
|
|
CmpReferenceHiveViewWithLock( IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView
|
|
);
|
|
|
|
VOID
|
|
CmpDereferenceHiveViewWithLock( IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView
|
|
);
|
|
|
|
|
|
|
|
extern LIST_ENTRY CmpHiveListHead;
|
|
extern PUCHAR CmpStashBuffer;
|
|
extern ULONG CmpStashBufferSize;
|
|
|
|
BOOLEAN CmpTrackHiveClose = FALSE;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,CmpUnmapCmView)
|
|
#pragma alloc_text(PAGE,CmpTouchView)
|
|
#pragma alloc_text(PAGE,CmpMapCmView)
|
|
#pragma alloc_text(PAGE,CmpAquireFileObjectForFile)
|
|
#pragma alloc_text(PAGE,CmpDropFileObjectForHive)
|
|
#pragma alloc_text(PAGE,CmpInitHiveViewList)
|
|
#pragma alloc_text(PAGE,CmpDestroyHiveViewList)
|
|
#pragma alloc_text(PAGE,CmpAllocateCmView)
|
|
#pragma alloc_text(PAGE,CmpFreeCmView)
|
|
#pragma alloc_text(PAGE,CmpPinCmView)
|
|
#pragma alloc_text(PAGE,CmpUnPinCmView)
|
|
#pragma alloc_text(PAGE,CmpMapThisBin)
|
|
#pragma alloc_text(PAGE,CmpFixHiveUsageCount)
|
|
#pragma alloc_text(PAGE,CmpUnmapUnusedViews)
|
|
|
|
#if 0
|
|
#pragma alloc_text(PAGE,CmpMapEntireFileInFakeViews)
|
|
#pragma alloc_text(PAGE,CmpUnmapFakeViews)
|
|
#pragma alloc_text(PAGE,CmpUnmapAditionalViews)
|
|
#endif
|
|
|
|
#ifdef CMP_CMVIEW_VALIDATION
|
|
#pragma alloc_text(PAGE,CmpCheckCmView)
|
|
#endif //CMP_CMVIEW_VALIDATION
|
|
|
|
#pragma alloc_text(PAGE,CmpUnmapCmViewSurroundingOffset)
|
|
#pragma alloc_text(PAGE,CmpPrefetchHiveFile)
|
|
#pragma alloc_text(PAGE,CmPrefetchHivePages)
|
|
#pragma alloc_text(PAGE,CmIsFileLoadedAsHive)
|
|
#pragma alloc_text(PAGE,CmpReferenceHiveView)
|
|
#pragma alloc_text(PAGE,CmpDereferenceHiveView)
|
|
#pragma alloc_text(PAGE,CmpReferenceHiveViewWithLock)
|
|
#pragma alloc_text(PAGE,CmpDereferenceHiveViewWithLock)
|
|
#endif
|
|
|
|
//
|
|
// this controls how many views we allow per each hive (bassically how many address space we
|
|
// allow per hive). We use this to optimize boot time.
|
|
//
|
|
ULONG CmMaxViewsPerHive = MAX_VIEWS_PER_HIVE;
|
|
|
|
VOID
|
|
CmpUnmapCmView(
|
|
IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView,
|
|
IN BOOLEAN MapIsValid,
|
|
IN BOOLEAN MoveToEnd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unmaps the view by marking all the bins that maps inside of it as invalid.
|
|
|
|
Arguments:
|
|
|
|
Hive - Hive containing the section
|
|
|
|
CmView - pointer to the view to operate on
|
|
|
|
MapIsValid - Hive's map has been successfully inited (and not yet freed)
|
|
|
|
MoveToEnd - moves the view to the end of the LRUList after unmapping
|
|
This is normally TRUE, unless we want to be able to iterate through
|
|
the entire list and unmap views in the same time
|
|
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
|
|
--*/
|
|
{
|
|
|
|
ULONG Offset;
|
|
ULONG_PTR Address;
|
|
ULONG_PTR AddressEnd;
|
|
PHMAP_ENTRY Me;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( (CmView->FileOffset + CmView->Size) != 0 && (CmView->ViewAddress != 0));
|
|
//
|
|
// it is forbidden to unmap a view still in use!
|
|
//
|
|
ASSERT( CmView->UseCount == 0 );
|
|
|
|
//
|
|
// only if the map is still valid
|
|
//
|
|
if( MapIsValid == TRUE ) {
|
|
Offset = CmView->FileOffset;
|
|
|
|
AddressEnd = Address = (ULONG_PTR)(CmView->ViewAddress);
|
|
AddressEnd += CmView->Size;
|
|
|
|
if( Offset == 0 ) {
|
|
//
|
|
// oops; we are at the beginning, we have to skip the base block
|
|
//
|
|
Address += HBLOCK_SIZE;
|
|
} else {
|
|
//
|
|
// we are in the middle of the file. just adjust the offset
|
|
//
|
|
Offset -= HBLOCK_SIZE;
|
|
}
|
|
|
|
while((Address < AddressEnd) && (Offset < CmHive->Hive.Storage[Stable].Length))
|
|
{
|
|
Me = HvpGetCellMap(&(CmHive->Hive), Offset);
|
|
VALIDATE_CELL_MAP(__LINE__,Me,&(CmHive->Hive),Offset);
|
|
|
|
if( Me->BinAddress & HMAP_INPAGEDPOOL ) {
|
|
//
|
|
// if bin is mapped in paged pool for some ubiquitous reason,
|
|
// leave it like that (don't alter it's mapping).
|
|
//
|
|
} else {
|
|
//
|
|
// Invalidate the bin
|
|
//
|
|
//ASSERT_BIN_INVIEW(Me);
|
|
|
|
Me->BinAddress &= (~HMAP_INVIEW);
|
|
|
|
// we don't need to set it - just for debug purposes
|
|
ASSERT( (Me->CmView = NULL) == NULL );
|
|
}
|
|
|
|
Offset += HBLOCK_SIZE;
|
|
Address += HBLOCK_SIZE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Invalidate the view
|
|
//
|
|
|
|
CcUnpinData( CmView->Bcb );
|
|
/*
|
|
MmUnmapViewInSystemCache (CmView->ViewAddress,CmHive->HiveSection,FALSE);
|
|
*/
|
|
#if 0 //this code gave me a lot of headache
|
|
{
|
|
UNICODE_STRING HiveName;
|
|
RtlInitUnicodeString(&HiveName, (PCWSTR)CmHive->Hive.BaseBlock->FileName);
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"CmpUnmapCmView for hive (%p) (%.*S), Address = %p Size = %lx\n",CmHive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer,CmView->ViewAddress,CmView->Size));
|
|
}
|
|
#endif
|
|
|
|
CmView->FileOffset = 0;
|
|
CmView->Size = 0;
|
|
CmView->ViewAddress = 0;
|
|
CmView->Bcb = NULL;
|
|
CmView->UseCount = 0;
|
|
|
|
if( MoveToEnd == TRUE ) {
|
|
//
|
|
// remove the view from the LRU list
|
|
//
|
|
RemoveEntryList(&(CmView->LRUViewList));
|
|
|
|
//
|
|
// add it to the end of LRU list
|
|
//
|
|
InsertTailList(
|
|
&(CmHive->LRUViewListHead),
|
|
&(CmView->LRUViewList)
|
|
);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CmpTouchView(
|
|
IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView,
|
|
IN ULONG Cell
|
|
)
|
|
/*++
|
|
|
|
Warning:
|
|
|
|
This function should be called with the viewlock held!!!
|
|
|
|
Routine Description:
|
|
|
|
Touches the view by moving it at the top of the LRU list.
|
|
This function is to be called from HvpGetCellPaged, every
|
|
time a view is touched.
|
|
|
|
Arguments:
|
|
|
|
Hive - Hive containing the section
|
|
|
|
CmView - pointer to the view to operate on
|
|
|
|
Return Value:
|
|
|
|
<none>
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
{
|
|
UNICODE_STRING HiveName;
|
|
RtlInitUnicodeString(&HiveName, (PCWSTR)CmHive->Hive.BaseBlock->FileName);
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"CmpTouchView for hive (%p) (%.*S),",CmHive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"Cell = %8lx ViewAddress = %p ViewSize = %lx\n",Cell,CmView->ViewAddress,CmView->Size));
|
|
}
|
|
#else
|
|
UNREFERENCED_PARAMETER (Cell);
|
|
#endif
|
|
|
|
ASSERT( (CmView->FileOffset + CmView->Size) != 0 && (CmView->ViewAddress != 0));
|
|
|
|
if( IsListEmpty(&(CmView->PinViewList)) == FALSE ) {
|
|
//
|
|
// the view is pinned; don't mess with it as it is guaranteed
|
|
// that it'll be in memory until the next flush
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// optimization: if already is first, do nothing
|
|
//
|
|
|
|
if( CmHive->LRUViewListHead.Flink == &(CmView->LRUViewList) ) {
|
|
// remove the bp after making sure it's working properly
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"CmView %p already first\n",CmView));
|
|
/*
|
|
DbgBreakPoint();
|
|
*/
|
|
//it's already first
|
|
return;
|
|
}
|
|
|
|
//
|
|
// remove the view from the LRU list
|
|
//
|
|
RemoveEntryList(&(CmView->LRUViewList));
|
|
|
|
//
|
|
// add it on top of LRU list
|
|
//
|
|
InsertHeadList(
|
|
&(CmHive->LRUViewListHead),
|
|
&(CmView->LRUViewList)
|
|
);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CmpMapCmView(
|
|
IN PCMHIVE CmHive,
|
|
IN ULONG FileOffset,
|
|
OUT PCM_VIEW_OF_FILE *CmView,
|
|
IN BOOLEAN MapInited
|
|
)
|
|
/*++
|
|
|
|
Warning:
|
|
|
|
This function should be called with the hivelock held!!!
|
|
|
|
Routine Description:
|
|
|
|
Unmaps the view by marking all the bins that maps inside of it as invalid.
|
|
|
|
Arguments:
|
|
|
|
CmHive - Hive containing the section
|
|
|
|
FileOffset - Offset where to map the view
|
|
|
|
CmView - pointer to the view to operate on
|
|
|
|
MapInited - when TRUE, we can rely on the map info.
|
|
|
|
Return Value:
|
|
|
|
status of the operation
|
|
|
|
--*/
|
|
{
|
|
|
|
PHMAP_ENTRY Me;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
LARGE_INTEGER SectionOffset;
|
|
ULONG Offset;
|
|
ULONG_PTR Address;
|
|
ULONG_PTR AddressEnd;
|
|
ULONG_PTR BinAddress;
|
|
PHBIN Bin;
|
|
LONG PrevMappedBinSize;
|
|
BOOLEAN FirstTry = TRUE;
|
|
|
|
PAGED_CODE();
|
|
|
|
if( CmHive->MappedViews == 0 ){
|
|
//
|
|
// we've run out of views; all are pinned
|
|
//
|
|
ASSERT( IsListEmpty(&(CmHive->LRUViewListHead)) == TRUE );
|
|
*CmView = CmpAllocateCmView(CmHive);
|
|
|
|
} else {
|
|
//
|
|
// Remove the last view from LRU list (i.e. the LEAST recently used)
|
|
//
|
|
*CmView = (PCM_VIEW_OF_FILE)CmHive->LRUViewListHead.Blink;
|
|
*CmView = CONTAINING_RECORD( *CmView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
|
|
|
|
if( (*CmView)->ViewAddress != 0 ) {
|
|
PCM_VIEW_OF_FILE TempCmView = NULL;
|
|
//
|
|
// the last view is mapped
|
|
//
|
|
if( CmHive->MappedViews < CmMaxViewsPerHive ) {
|
|
//
|
|
// we are still allowed to add views
|
|
//
|
|
TempCmView = CmpAllocateCmView(CmHive);
|
|
}
|
|
if( TempCmView == NULL ) {
|
|
//
|
|
// we couldn't allocate a new view, or we need to use an existent one
|
|
//
|
|
if( (*CmView)->UseCount != 0 ) {
|
|
BOOLEAN FoundView = FALSE;
|
|
//
|
|
// view is in use; try walking to the top and find an unused view
|
|
//
|
|
while( (*CmView)->LRUViewList.Blink != CmHive->LRUViewListHead.Flink ) {
|
|
*CmView = (PCM_VIEW_OF_FILE)(*CmView)->LRUViewList.Blink;
|
|
*CmView = CONTAINING_RECORD( *CmView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
if( (*CmView)->UseCount == 0 ) {
|
|
//
|
|
// this one is free go ahead and use it !
|
|
// first unmap, then signal that we found it
|
|
//
|
|
if( (*CmView)->ViewAddress != 0 ) {
|
|
//
|
|
// unnmap only if mapped
|
|
//
|
|
CmpUnmapCmView(CmHive,(*CmView),TRUE,TRUE);
|
|
}
|
|
FoundView = TRUE;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
if( FoundView == FALSE ) {
|
|
//
|
|
// no luck, all views are in use allocate a new one (we are forced to do so)
|
|
//
|
|
*CmView = CmpAllocateCmView(CmHive);
|
|
}
|
|
} else {
|
|
//
|
|
// unmap it!
|
|
//
|
|
CmpUnmapCmView(CmHive,(*CmView),TRUE,TRUE);
|
|
}
|
|
} else {
|
|
//
|
|
// we successfully allocated a new view
|
|
//
|
|
(*CmView) = TempCmView;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( (*CmView) == NULL ) {
|
|
//
|
|
// we're running low on resources
|
|
//
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
#if DBG
|
|
{
|
|
UNICODE_STRING HiveName;
|
|
RtlInitUnicodeString(&HiveName, (PCWSTR)CmHive->Hive.BaseBlock->FileName);
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"CmpMapCmView for hive (%p) (%.*S),",CmHive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer));
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP," FileOfset = %lx ... ",FileOffset));
|
|
}
|
|
#endif
|
|
//
|
|
// On this call, FileOffset must be a multiple of CM_VIEW_SIZE
|
|
//
|
|
|
|
|
|
//
|
|
// adjust the file offset to respect the CM_VIEW_SIZE alingment
|
|
//
|
|
Offset = ((FileOffset+HBLOCK_SIZE) & ~(CM_VIEW_SIZE - 1) );
|
|
SectionOffset.LowPart = Offset;
|
|
SectionOffset.HighPart = 0;
|
|
|
|
(*CmView)->Size = CM_VIEW_SIZE;//(FileOffset + Size) - Offset;
|
|
|
|
if( (Offset + (*CmView)->Size) > (CmHive->Hive.Storage[Stable].Length + HBLOCK_SIZE ) ){
|
|
(*CmView)->Size = CmHive->Hive.Storage[Stable].Length + HBLOCK_SIZE - Offset;
|
|
}
|
|
|
|
|
|
/*
|
|
Status = MmMapViewInSystemCache ( CmHive->HiveSection,
|
|
&((*CmView)->ViewAddress),
|
|
&SectionOffset,
|
|
&((*CmView)->Size));
|
|
|
|
*/
|
|
RetryToMap:
|
|
|
|
try {
|
|
|
|
ASSERT( (*CmView)->Size != 0 );
|
|
ASSERT( (*CmView)->ViewAddress == NULL );
|
|
ASSERT( (*CmView)->UseCount == 0 );
|
|
|
|
if (!CcMapData( CmHive->FileObject,
|
|
(PLARGE_INTEGER)&SectionOffset,
|
|
(*CmView)->Size,
|
|
MAP_WAIT
|
|
#ifdef CM_MAP_NO_READ
|
|
| MAP_NO_READ
|
|
#endif
|
|
,
|
|
(PVOID *)(&((*CmView)->Bcb)),
|
|
(PVOID *)(&((*CmView)->ViewAddress)) )) {
|
|
Status = STATUS_CANT_WAIT;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// in low-memory scenarios, CcMapData throws a STATUS_IN_PAGE_ERROR
|
|
// this happens when the IO issued to touch the just-mapped data fails (usually with
|
|
// STATUS_INSUFFICIENT_RESOURCES; We want to catch this and treat as a
|
|
// "not enough resources" problem, rather than letting it to surface the kernel call
|
|
//
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpMapCmView : CcMapData has raised :%08lx\n",GetExceptionCode()));
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if(!NT_SUCCESS(Status) ){
|
|
if( FirstTry == TRUE ) {
|
|
//
|
|
// unmap all unneccessary views and try again
|
|
//
|
|
FirstTry = FALSE;
|
|
CmpUnmapUnusedViews(CmHive);
|
|
Status = STATUS_SUCCESS;
|
|
goto RetryToMap;
|
|
}
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"Fail\n"));
|
|
ASSERT(FALSE);
|
|
return Status;
|
|
}
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"Succedeed Address = %p Size = %lx\n",(*CmView)->ViewAddress,(*CmView)->Size));
|
|
|
|
(*CmView)->FileOffset = SectionOffset.LowPart;
|
|
|
|
ASSERT( Offset == (*CmView)->FileOffset);
|
|
|
|
AddressEnd = Address = (ULONG_PTR)((*CmView)->ViewAddress);
|
|
AddressEnd += (*CmView)->Size;
|
|
|
|
if( Offset == 0 ) {
|
|
//
|
|
// oops; we are at the beginning, we have to skip the base block
|
|
//
|
|
Address += HBLOCK_SIZE;
|
|
} else {
|
|
//
|
|
// we are in the middle of the file. just adjust the offset
|
|
//
|
|
Offset -= HBLOCK_SIZE;
|
|
}
|
|
|
|
#ifdef CMP_CMVIEW_VALIDATION
|
|
CmpCheckCmView(CmHive,*CmView);
|
|
#endif //CMP_CMVIEW_VALIDATION
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"CmpMapCmView :: Address = %p AddressEnd = %p ; Size = %lx\n",Address,AddressEnd,(*CmView)->Size));
|
|
|
|
//
|
|
// here we can optimize not to touch all the bins!!!
|
|
//
|
|
|
|
//
|
|
// we don't know yet if the first bin is mapped.
|
|
//
|
|
PrevMappedBinSize = 0;
|
|
BinAddress = Address;
|
|
while(Address < AddressEnd)
|
|
{
|
|
Me = HvpGetCellMap(&(CmHive->Hive), Offset);
|
|
VALIDATE_CELL_MAP(__LINE__,Me,&(CmHive->Hive),Offset);
|
|
|
|
if( Me->BinAddress & HMAP_INPAGEDPOOL ) {
|
|
//
|
|
// if bin is mapped in paged pool for some reason,
|
|
// leave it like that (don't alter it's mapping).
|
|
//
|
|
|
|
//
|
|
// next mapped bin should start updating his bin address
|
|
//
|
|
PrevMappedBinSize = 0;
|
|
} else {
|
|
//
|
|
// at this point the bin should be invalid.
|
|
//
|
|
ASSERT_BIN_INVALID(Me);
|
|
|
|
Me->BinAddress |= HMAP_INVIEW;
|
|
Me->CmView = *CmView;
|
|
|
|
//
|
|
// set the new BinAddress, but take care to preserve the flags
|
|
//
|
|
ASSERT( HBIN_FLAGS(Address) == 0 );
|
|
|
|
|
|
|
|
//
|
|
// new bins are Always tagged with this flag (we can start updating BinAddress)
|
|
//
|
|
if( MapInited && ( Me->BinAddress & HMAP_NEWALLOC ) ) {
|
|
#ifdef CM_CHECK_MAP_NO_READ_SCHEME
|
|
ASSERT( PrevMappedBinSize == 0 );
|
|
//
|
|
// Validate the bin
|
|
//
|
|
Bin = (PHBIN)Address;
|
|
//ASSERT( Bin->Signature == HBIN_SIGNATURE );
|
|
PrevMappedBinSize = (LONG)Bin->Size;
|
|
#endif //CM_CHECK_MAP_NO_READ_SCHEME
|
|
|
|
//
|
|
// we are at the beginning of a new bin
|
|
//
|
|
BinAddress = Address;
|
|
} else if( (!MapInited) &&(PrevMappedBinSize == 0) ) {
|
|
//
|
|
// we cannot rely on the map to cary the bin flags; we have to fault data in
|
|
//
|
|
//
|
|
// Validate the bin
|
|
//
|
|
Bin = (PHBIN)Address;
|
|
//ASSERT( Bin->Signature == HBIN_SIGNATURE );
|
|
PrevMappedBinSize = (LONG)Bin->Size;
|
|
//
|
|
// we are at the beginning of a new bin
|
|
//
|
|
BinAddress = Address;
|
|
}
|
|
|
|
//
|
|
// common sense
|
|
//
|
|
ASSERT( (!MapInited) || ((PrevMappedBinSize >=0) && (PrevMappedBinSize%HBLOCK_SIZE == 0)) );
|
|
|
|
#ifdef CM_CHECK_MAP_NO_READ_SCHEME
|
|
ASSERT( (PrevMappedBinSize >=0) && (PrevMappedBinSize%HBLOCK_SIZE == 0) );
|
|
#endif //CM_CHECK_MAP_NO_READ_SCHEME
|
|
|
|
Me->BinAddress = ( HBIN_BASE(BinAddress) | HBIN_FLAGS(Me->BinAddress) );
|
|
if( (Me->BinAddress & HMAP_DISCARDABLE) == 0 ) {
|
|
//
|
|
// for discardable bins do not alter this member, as it contains
|
|
// the address of the free bin
|
|
//
|
|
Me->BlockAddress = Address;
|
|
}
|
|
|
|
if( !MapInited ) {
|
|
//
|
|
// compute the remaining size of this bin; next iteration will update BinAddress only if
|
|
// this variable reaches 0
|
|
//
|
|
PrevMappedBinSize -= HBLOCK_SIZE;
|
|
} else {
|
|
#ifdef CM_CHECK_MAP_NO_READ_SCHEME
|
|
//
|
|
// compute the remaining size of this bin; next iteration will update BinAddress only if
|
|
// this variable reaches 0
|
|
//
|
|
PrevMappedBinSize -= HBLOCK_SIZE;
|
|
#endif //CM_CHECK_MAP_NO_READ_SCHEME
|
|
}
|
|
|
|
ASSERT_BIN_INVIEW(Me);
|
|
}
|
|
|
|
Offset += HBLOCK_SIZE;
|
|
Address += HBLOCK_SIZE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
CmpUnmapCmViewSurroundingOffset(
|
|
IN PCMHIVE CmHive,
|
|
IN ULONG FileOffset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parses the mapped view list and if it finds one surrounding this offest, unmaps it.
|
|
|
|
Arguments:
|
|
|
|
CmHive - Hive in question
|
|
|
|
FileOffset - the offest in question
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
Note:
|
|
|
|
Offset is an absolute value,
|
|
--*/
|
|
{
|
|
PCM_VIEW_OF_FILE CmView;
|
|
USHORT NrViews;
|
|
BOOLEAN UnMap = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Walk through the LRU list and compare view addresses
|
|
//
|
|
CmView = (PCM_VIEW_OF_FILE)CmHive->LRUViewListHead.Flink;
|
|
|
|
for(NrViews = CmHive->MappedViews;NrViews;NrViews--) {
|
|
CmView = CONTAINING_RECORD( CmView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
|
|
if( ((CmView->Size + CmView->FileOffset) != 0) && (CmView->ViewAddress != 0) ) {
|
|
//
|
|
// view is valid
|
|
//
|
|
if( (CmView->FileOffset <= FileOffset) && ((CmView->FileOffset + CmView->Size) > FileOffset) ) {
|
|
//
|
|
// the file offset is surrounded by this view
|
|
//
|
|
UnMap = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
CmView = (PCM_VIEW_OF_FILE)CmView->LRUViewList.Flink;
|
|
}
|
|
|
|
if( UnMap == TRUE ) {
|
|
// unmap the view anyway (this implies unpinning).
|
|
ASSERT_VIEW_MAPPED( CmView );
|
|
ASSERT( CmView->UseCount == 0 );
|
|
CmpUnmapCmView(CmHive,CmView,TRUE,TRUE);
|
|
}
|
|
}
|
|
|
|
PCM_VIEW_OF_FILE
|
|
CmpAllocateCmView (
|
|
IN PCMHIVE CmHive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate a CM-view.
|
|
Insert it in various list.
|
|
|
|
Arguments:
|
|
|
|
CmHive - Hive in question
|
|
|
|
|
|
Return Value:
|
|
|
|
TBS - the new view
|
|
|
|
--*/
|
|
{
|
|
PCM_VIEW_OF_FILE CmView;
|
|
|
|
PAGED_CODE();
|
|
|
|
CmView = ExAllocatePoolWithTag(PagedPool,sizeof(CM_VIEW_OF_FILE),CM_MAPPEDVIEW_TAG | PROTECTED_POOL);
|
|
|
|
if (CmView == NULL) {
|
|
//
|
|
// we're low on resources; we should handle the error path for this.
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Init the view
|
|
//
|
|
CmView->FileOffset = 0;
|
|
CmView->Size = 0;
|
|
CmView->ViewAddress = NULL;
|
|
CmView->Bcb = NULL;
|
|
CmView->UseCount =0;
|
|
|
|
//
|
|
// add it to the list(s)
|
|
//
|
|
InitializeListHead(&(CmView->PinViewList));
|
|
|
|
InsertTailList(
|
|
&(CmHive->LRUViewListHead),
|
|
&(CmView->LRUViewList)
|
|
);
|
|
|
|
CmHive->MappedViews++;
|
|
return CmView;
|
|
}
|
|
|
|
VOID
|
|
CmpInitHiveViewList (
|
|
IN PCMHIVE CmHive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
adds the first view to the LRU list.
|
|
others are added as needed.!
|
|
|
|
Arguments:
|
|
|
|
CmHive - Hive in question
|
|
|
|
|
|
Return Value:
|
|
|
|
TBS - status of the operation
|
|
|
|
--*/
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Init the heads.
|
|
//
|
|
InitializeListHead(&(CmHive->PinViewListHead));
|
|
InitializeListHead(&(CmHive->LRUViewListHead));
|
|
#if 0
|
|
InitializeListHead(&(CmHive->FakeViewListHead));
|
|
CmHive->FakeViews = 0;
|
|
#endif
|
|
|
|
CmHive->MappedViews = 0;
|
|
CmHive->PinnedViews = 0;
|
|
CmHive->UseCount = 0;
|
|
}
|
|
|
|
VOID
|
|
CmpDestroyHiveViewList (
|
|
IN PCMHIVE CmHive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees the storage fo all the views used by this hive
|
|
|
|
Arguments:
|
|
|
|
CmHive - Hive in question
|
|
|
|
Purge - whether to purge the cache or not.
|
|
|
|
|
|
Return Value:
|
|
|
|
TBS - status of the operation
|
|
|
|
--*/
|
|
{
|
|
PCM_VIEW_OF_FILE CmView;
|
|
|
|
PAGED_CODE();
|
|
|
|
if( CmHive->FileObject == NULL ) {
|
|
//
|
|
// hive is not mapped.
|
|
//
|
|
return;
|
|
}
|
|
#if 0
|
|
//
|
|
// get rid of fake views first; we shouldn't have any fake views here, unless we are on
|
|
// some error path (the hive is corrupted).
|
|
//
|
|
CmpUnmapFakeViews(CmHive);
|
|
#endif
|
|
|
|
//
|
|
// Walk through the Pinned View list and free all the views
|
|
//
|
|
while( IsListEmpty( &(CmHive->PinViewListHead) ) == FALSE ) {
|
|
CmView = (PCM_VIEW_OF_FILE)RemoveHeadList(&(CmHive->PinViewListHead));
|
|
CmView = CONTAINING_RECORD( CmView,
|
|
CM_VIEW_OF_FILE,
|
|
PinViewList);
|
|
|
|
ASSERT_VIEW_PINNED(CmView);
|
|
//
|
|
// we need to move this view to the mapped view list and remember to purge after all
|
|
// views have been unmapped. Otherwise we rick deadlock on CcWaitOnActiveCount, when we purge
|
|
|
|
//
|
|
//
|
|
// sanity check; we shouldn't get here for a read-only hive
|
|
//
|
|
ASSERT( CmHive->Hive.ReadOnly == FALSE );
|
|
|
|
//
|
|
// empty the LRUList for this view
|
|
//
|
|
InitializeListHead(&(CmView->PinViewList));
|
|
|
|
//
|
|
// update the counters
|
|
//
|
|
CmHive->PinnedViews--;
|
|
CmHive->MappedViews++;
|
|
|
|
//
|
|
// add it at the tail of LRU list for this hive
|
|
//
|
|
InsertTailList(
|
|
&(CmHive->LRUViewListHead),
|
|
&(CmView->LRUViewList)
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// At this point, there should be no pinned view
|
|
//
|
|
ASSERT( IsListEmpty(&(CmHive->PinViewListHead)) == TRUE);
|
|
ASSERT( CmHive->PinnedViews == 0 );
|
|
|
|
//
|
|
// Walk through the LRU list and free all the views
|
|
//
|
|
while( IsListEmpty( &(CmHive->LRUViewListHead) ) == FALSE ) {
|
|
CmView = (PCM_VIEW_OF_FILE)CmHive->LRUViewListHead.Flink;
|
|
CmView = CONTAINING_RECORD( CmView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
if( CmView->ViewAddress != 0 ) {
|
|
//
|
|
// view is mapped; unmap it
|
|
// we should not encounter that in sane systems
|
|
// this can happen only when a hive-loading fails
|
|
// in HvpMapFileImageAndBuildMap
|
|
// no need move it as we are going to free it anyway
|
|
//
|
|
CmpUnmapCmView(CmHive,CmView,FALSE,FALSE);
|
|
}
|
|
|
|
//
|
|
// update the counter
|
|
//
|
|
CmHive->MappedViews--;
|
|
|
|
//
|
|
// remove the view from the LRU list
|
|
//
|
|
RemoveEntryList(&(CmView->LRUViewList));
|
|
|
|
ExFreePoolWithTag(CmView, CM_MAPPEDVIEW_TAG | PROTECTED_POOL);
|
|
}
|
|
|
|
ASSERT( CmHive->MappedViews == 0 );
|
|
ASSERT( CmHive->UseCount == 0 );
|
|
|
|
//
|
|
// we need to purge as the FS cannot do it for us (private writers)
|
|
// valid data is already on the disk by now (it should!)
|
|
// purge and flush everything
|
|
//
|
|
CcPurgeCacheSection(CmHive->FileObject->SectionObjectPointer,(PLARGE_INTEGER)(((ULONG_PTR)NULL) + 1)/*we are private writers*/,0/*ignored*/,FALSE);
|
|
//
|
|
// This is for the case where the last flush failed (we couldn't write the log file....)
|
|
// .... then : flush the cache to clear dirty hints added by the purge
|
|
//
|
|
CcFlushCache (CmHive->FileObject->SectionObjectPointer,(PLARGE_INTEGER)(((ULONG_PTR)NULL) + 1)/*we are private writers*/,0/*ignored*/,NULL);
|
|
|
|
//
|
|
// Flush again to take care of the dirty pages that may appear due to FS page zeroing
|
|
//
|
|
CcFlushCache (CmHive->FileObject->SectionObjectPointer,(PLARGE_INTEGER)(((ULONG_PTR)NULL) + 1)/*we are private writers*/,0/*ignored*/,NULL);
|
|
|
|
#ifdef CM_TRACK_DIRTY_PAGES
|
|
if( ((PSHARED_CACHE_MAP)(CmHive->FileObject->SectionObjectPointer->SharedCacheMap))->DirtyPages != 0 ) {
|
|
DbgPrint("SharedCacheMap still has dirty pages after purge and flush; FileObject = %p \n",CmHive->FileObject);
|
|
DbgBreakPoint();
|
|
}
|
|
#endif //CM_TRACK_DIRTY_PAGES
|
|
|
|
}
|
|
|
|
VOID
|
|
CmpDropFileObjectForHive(
|
|
IN PCMHIVE CmHive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Drops the extra reference kept on the file object (if any)
|
|
and frees the name
|
|
|
|
Arguments:
|
|
|
|
CmHive
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
if( CmHive->FileUserName.Buffer != NULL ) {
|
|
ExFreePoolWithTag(CmHive->FileUserName.Buffer, CM_NAME_TAG | PROTECTED_POOL);
|
|
CmHive->FileUserName.Buffer = NULL;
|
|
CmHive->FileUserName.Length = 0;
|
|
CmHive->FileUserName.MaximumLength = 0;
|
|
} else {
|
|
ASSERT(CmHive->FileUserName.Length == 0);
|
|
ASSERT(CmHive->FileUserName.MaximumLength == 0);
|
|
}
|
|
|
|
if( CmHive->FileObject == NULL ) {
|
|
// debug only code
|
|
ASSERT(CmHive->FileFullPath.Buffer == NULL);
|
|
ASSERT(CmHive->FileFullPath.Length == 0);
|
|
ASSERT(CmHive->FileFullPath.MaximumLength == 0);
|
|
return;
|
|
}
|
|
|
|
// debug only code
|
|
if( CmHive->FileFullPath.Buffer != NULL ) {
|
|
ExFreePoolWithTag(CmHive->FileFullPath.Buffer, CM_NAME_TAG | PROTECTED_POOL);
|
|
CmHive->FileFullPath.Buffer = NULL;
|
|
CmHive->FileFullPath.Length = 0;
|
|
CmHive->FileFullPath.MaximumLength = 0;
|
|
} else {
|
|
ASSERT(CmHive->FileFullPath.Length == 0);
|
|
ASSERT(CmHive->FileFullPath.MaximumLength == 0);
|
|
}
|
|
|
|
ObDereferenceObject((PVOID)(CmHive->FileObject));
|
|
|
|
CmHive->FileObject = NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
CmpAquireFileObjectForFile(
|
|
IN PCMHIVE CmHive,
|
|
IN HANDLE FileHandle,
|
|
OUT PFILE_OBJECT *FileObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates the section for the given file handle.
|
|
the section is used to map/unmap views of the file
|
|
|
|
Arguments:
|
|
|
|
FileHandle - Handle of the file
|
|
|
|
SectionPointer - the section object
|
|
|
|
Return Value:
|
|
|
|
TBS - status of the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status,Status2;
|
|
POBJECT_NAME_INFORMATION FileNameInfo;
|
|
ULONG ReturnedLength;
|
|
ULONG FileNameLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = ObReferenceObjectByHandle ( FileHandle,
|
|
FILE_READ_DATA | FILE_WRITE_DATA,
|
|
IoFileObjectType,
|
|
KernelMode,
|
|
(PVOID *)FileObject,
|
|
NULL );
|
|
if (!NT_SUCCESS(Status)) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"[CmpAquireFileObjectForFile] Could not reference file object status = %x\n",Status));
|
|
} else {
|
|
//
|
|
// call cc private to mark the stream as Modify-No-Write
|
|
//
|
|
if( !CcSetPrivateWriteFile(*FileObject) ) {
|
|
//
|
|
// filter out invalid failures to initialize the cache
|
|
// top-level routine CmpInitHiveFromFile will retry to load the hive in the old fashion way.
|
|
//
|
|
CmpDropFileObjectForHive(CmHive);
|
|
(*FileObject) = NULL;
|
|
return STATUS_RETRY;
|
|
}
|
|
|
|
LOCK_STASH_BUFFER();
|
|
//
|
|
// capture the full path of the file
|
|
//
|
|
ASSERT( CmpStashBuffer != NULL );
|
|
|
|
FileNameInfo = (POBJECT_NAME_INFORMATION)CmpStashBuffer;
|
|
|
|
//
|
|
// we need to protect against multiple threads using the stash buffer
|
|
// this could happen only during the paralel hive loading at boot
|
|
//
|
|
LOCK_HIVE_LIST();
|
|
//
|
|
// Try to get the name for the file object.
|
|
//
|
|
Status2 = ObQueryNameString(*FileObject,
|
|
FileNameInfo,
|
|
CmpStashBufferSize,
|
|
&ReturnedLength);
|
|
if (NT_SUCCESS(Status2)) {
|
|
|
|
//
|
|
// Allocate a file name buffer and copy into it.
|
|
// The file names will be NUL terminated. Allocate extra for that.
|
|
//
|
|
|
|
FileNameLength = FileNameInfo->Name.Length / sizeof(WCHAR);
|
|
|
|
CmHive->FileFullPath.Buffer = ExAllocatePoolWithTag(PagedPool,
|
|
(FileNameLength + 1) * sizeof(WCHAR),
|
|
CM_NAME_TAG | PROTECTED_POOL);
|
|
|
|
if (CmHive->FileFullPath.Buffer) {
|
|
|
|
RtlCopyMemory(CmHive->FileFullPath.Buffer,
|
|
FileNameInfo->Name.Buffer,
|
|
FileNameLength * sizeof(WCHAR));
|
|
|
|
//
|
|
// Make sure it is NUL terminated.
|
|
//
|
|
|
|
CmHive->FileFullPath.Buffer[FileNameLength] = 0;
|
|
CmHive->FileFullPath.Length = FileNameInfo->Name.Length;
|
|
CmHive->FileFullPath.MaximumLength = FileNameInfo->Name.Length + sizeof(WCHAR);
|
|
|
|
} else {
|
|
//
|
|
// not fatal, just that we won't be able to prefetch this hive
|
|
//
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"[CmpAquireFileObjectForFile] Could not allocate buffer for fullpath for fileobject %p\n",*FileObject));
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// not fatal, just that we won't be able to prefetch this hive
|
|
//
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"[CmpAquireFileObjectForFile] Could not retrieve name for fileobject %p, Status = %lx\n",*FileObject,Status2));
|
|
CmHive->FileFullPath.Buffer = NULL;
|
|
}
|
|
UNLOCK_HIVE_LIST();
|
|
UNLOCK_STASH_BUFFER();
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CmpMapThisBin(
|
|
PCMHIVE CmHive,
|
|
HCELL_INDEX Cell,
|
|
BOOLEAN Touch
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Makes sure the bin is mapped in memory.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PCM_VIEW_OF_FILE CmView;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// ViewLock must be held
|
|
//
|
|
|
|
//
|
|
// bin is either mapped, or invalid
|
|
//
|
|
ASSERT( HvGetCellType(Cell) == Stable );
|
|
//
|
|
// map the bin
|
|
//
|
|
if( !NT_SUCCESS (CmpMapCmView(CmHive,Cell,&CmView,TRUE) ) ) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if( Touch == TRUE ) {
|
|
//
|
|
// touch the view
|
|
//
|
|
CmpTouchView(CmHive,CmView,(ULONG)Cell);
|
|
} else {
|
|
//
|
|
// if we are here; we should have either the reg lock exclusive
|
|
// or the reg lock shared AND the hive lock.
|
|
// Find a way to assert that!!!
|
|
//
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CmpPinCmView (
|
|
IN PCMHIVE CmHive,
|
|
PCM_VIEW_OF_FILE CmView
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pins the specified view into memory
|
|
|
|
The view is removed from the LRU list.
|
|
Then, the view is moved to the PinList
|
|
|
|
Arguments:
|
|
|
|
CmHive - Hive in question
|
|
|
|
CmView - View in question
|
|
|
|
Return Value:
|
|
|
|
TBS - the new view
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER SectionOffset;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PVOID SaveBcb;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
{
|
|
UNICODE_STRING HiveName;
|
|
RtlInitUnicodeString(&HiveName, (PCWSTR)CmHive->Hive.BaseBlock->FileName);
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"CmpPinCmView %lx for hive (%p) (%.*S), Address = %p Size = %lx\n",CmView,CmHive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer,CmView->ViewAddress,CmView->Size));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// We only pin mapped views
|
|
//
|
|
ASSERT_VIEW_MAPPED(CmView);
|
|
|
|
//
|
|
// sanity check; we shouldn't get here for a read-only hive
|
|
//
|
|
ASSERT( CmHive->Hive.ReadOnly == FALSE );
|
|
|
|
// we may need this later
|
|
SaveBcb = CmView->Bcb;
|
|
|
|
SectionOffset.LowPart = CmView->FileOffset;
|
|
SectionOffset.HighPart = 0;
|
|
try {
|
|
//
|
|
// the MOST important: pin the view
|
|
//
|
|
if( !CcPinMappedData( CmHive->FileObject,
|
|
&SectionOffset,
|
|
CmView->Size,
|
|
TRUE, // wait == syncronous call
|
|
&(CmView->Bcb) )) {
|
|
//
|
|
// this should never happen; handle it, though
|
|
//
|
|
|
|
ASSERT( FALSE );
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// in low-memory scenarios, CcPinMappedData throws a STATUS_INSUFFICIENT_RESOURCES
|
|
// We want to catch this and treat as a "not enough resources" problem,
|
|
// rather than letting it to surface the kernel call
|
|
//
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpPinCmView : CcPinMappedData has raised :%08lx\n",GetExceptionCode()));
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if( NT_SUCCESS(Status) ) {
|
|
//
|
|
// Pin succeeded, move the view to the pinned list
|
|
// remove the view from the LRU list
|
|
//
|
|
RemoveEntryList(&(CmView->LRUViewList));
|
|
//
|
|
// empty the LRUList for this view
|
|
//
|
|
InitializeListHead(&(CmView->LRUViewList));
|
|
|
|
//
|
|
// add it at the tail of pinned list for this hive
|
|
//
|
|
InsertTailList(
|
|
&(CmHive->PinViewListHead),
|
|
&(CmView->PinViewList)
|
|
);
|
|
|
|
//
|
|
// update the counters
|
|
//
|
|
CmHive->MappedViews--;
|
|
CmHive->PinnedViews++;
|
|
} else {
|
|
//
|
|
// pin failed; we need to restore view data that may have been altered by the pin call
|
|
// view will remain mapped
|
|
//
|
|
CmView->Bcb = SaveBcb;
|
|
}
|
|
|
|
// make sure we didn't unmapped/punned more than we mapped/pinned
|
|
ASSERT( (CmHive->MappedViews >= 0) ); // && (CmHive->MappedViews < CmMaxViewsPerHive) );
|
|
ASSERT( (CmHive->PinnedViews >= 0) );
|
|
|
|
#ifdef CMP_CMVIEW_VALIDATION
|
|
CmpCheckCmView(CmHive,CmView);
|
|
#endif //CMP_CMVIEW_VALIDATION
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
CmpUnPinCmView (
|
|
IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView,
|
|
IN BOOLEAN SetClean,
|
|
IN BOOLEAN MapValid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
UnPins the specified view from memory
|
|
|
|
The view is NOT in the PinViewList !!! (it has already been removed !!!!!!)
|
|
Then, the view is moved to the LRUList.
|
|
If more than CmMaxViewsPerHive are in LRU list, the view is freed
|
|
|
|
This function always grabs the ViewLock for the hive!!!
|
|
|
|
Arguments:
|
|
|
|
CmHive - Hive in question
|
|
|
|
CmView - View in question
|
|
|
|
SetClean - Tells whether the changes made to this view should be discarded
|
|
|
|
Return Value:
|
|
|
|
TBS - the new view
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER FileOffset; // where the mapping starts
|
|
ULONG Size; // size the view maps
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
#if 0 // this gave me a lot of headaches
|
|
{
|
|
UNICODE_STRING HiveName;
|
|
RtlInitUnicodeString(&HiveName, (PCWSTR)CmHive->Hive.BaseBlock->FileName);
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BIN_MAP,"CmpUnPinCmView %lx for hive (%p) (%.*S), Address = %p Size = %lx\n",CmView,CmHive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer,CmView->ViewAddress,CmView->Size));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Grab the viewLock, to protect the viewlist
|
|
//
|
|
CmLockHiveViews (CmHive);
|
|
|
|
//
|
|
// We only pin mapped views
|
|
//
|
|
ASSERT_VIEW_PINNED(CmView);
|
|
|
|
//
|
|
// sanity check; we shouldn't get here for a read-only hive
|
|
//
|
|
ASSERT( CmHive->Hive.ReadOnly == FALSE );
|
|
|
|
//
|
|
// empty the LRUList for this view
|
|
//
|
|
InitializeListHead(&(CmView->PinViewList));
|
|
|
|
//
|
|
// update the counters
|
|
//
|
|
CmHive->PinnedViews--;
|
|
CmHive->MappedViews++;
|
|
|
|
//
|
|
// add it at the tail of LRU list for this hive
|
|
//
|
|
InsertTailList(
|
|
&(CmHive->LRUViewListHead),
|
|
&(CmView->LRUViewList)
|
|
);
|
|
|
|
//
|
|
// store the FileOffset and size as we will need them for purging
|
|
//
|
|
FileOffset.LowPart = CmView->FileOffset;
|
|
FileOffset.HighPart = 0;
|
|
Size = CmView->Size;
|
|
|
|
if( SetClean == TRUE ) {
|
|
ASSERT( CmView->UseCount == 0 );
|
|
//
|
|
// unmap the view (this implies unpinning).
|
|
//
|
|
CmpUnmapCmView(CmHive,CmView,MapValid,TRUE);
|
|
//
|
|
// purge cache data
|
|
//
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
CcPurgeCacheSection(CmHive->FileObject->SectionObjectPointer,(PLARGE_INTEGER)(((ULONG_PTR)(&FileOffset)) + 1)/*we are private writers*/,Size,FALSE);
|
|
} else {
|
|
PVOID NewBcb;
|
|
PULONG_PTR NewViewAddress;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// the data is to be saved to the file,
|
|
// notify the cache manager that the data is dirty
|
|
//
|
|
CcSetDirtyPinnedData (CmView->Bcb,NULL);
|
|
|
|
//
|
|
// remap this view so we don't lose the refcount on this address range
|
|
//
|
|
try {
|
|
if (!CcMapData( CmHive->FileObject,
|
|
(PLARGE_INTEGER)&FileOffset,
|
|
CmView->Size,
|
|
MAP_WAIT
|
|
#ifdef CM_MAP_NO_READ
|
|
| MAP_NO_READ
|
|
#endif
|
|
,
|
|
(PVOID *)(&NewBcb),
|
|
(PVOID *)(&NewViewAddress) )) {
|
|
|
|
Status = STATUS_CANT_WAIT;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// in low-memory scenarios, CcMapData throws a STATUS_IN_PAGE_ERROR
|
|
// this happens when the IO issued to touch the just-mapped data fails (usually with
|
|
// STATUS_INSUFFICIENT_RESOURCES; We want to catch this and treat as a
|
|
// "not enough resources" problem, rather than letting it to surface the kernel call
|
|
//
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpUnPinCmView : CcMapData has raised :%08lx\n",GetExceptionCode()));
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
NewBcb = NULL;
|
|
NewViewAddress = NULL;
|
|
}
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
//
|
|
// CcMap didn't succeeded; bad luck, just unmap (implies unpinning).
|
|
//
|
|
CmpUnmapCmView(CmHive,CmView,MapValid,TRUE);
|
|
} else {
|
|
BOOLEAN FoundView = FALSE;
|
|
//
|
|
// sanity asserts; Cc guarantees the same address is returned.
|
|
//
|
|
ASSERT( FileOffset.LowPart == CmView->FileOffset );
|
|
ASSERT( NewViewAddress == CmView->ViewAddress );
|
|
//
|
|
// unpin old data
|
|
//
|
|
CcUnpinData( CmView->Bcb );
|
|
//
|
|
// replace the bcb for this view; there is no need to modify the map as the
|
|
// address and the size of the view remains the same; We just need to update the bcb
|
|
//
|
|
CmView->Bcb = NewBcb;
|
|
//
|
|
// move the view on top of the LRU list (consider it as "hot")
|
|
//
|
|
RemoveEntryList(&(CmView->LRUViewList));
|
|
InsertHeadList(
|
|
&(CmHive->LRUViewListHead),
|
|
&(CmView->LRUViewList)
|
|
);
|
|
//
|
|
// walk the LRU list back-wards until we find an unused view
|
|
//
|
|
CmView = (PCM_VIEW_OF_FILE)CmHive->LRUViewListHead.Blink;
|
|
CmView = CONTAINING_RECORD( CmView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
while( CmView->LRUViewList.Blink != CmHive->LRUViewListHead.Flink ) {
|
|
if( CmView->UseCount == 0 ) {
|
|
//
|
|
// this one is free go ahead and use it !
|
|
// first unmap, then signal that we found it
|
|
//
|
|
if( (CmHive->MappedViews >= CmMaxViewsPerHive) && (CmView->Bcb != NULL) ) {
|
|
CmpUnmapCmView(CmHive,CmView,MapValid,TRUE);
|
|
}
|
|
FoundView = TRUE;
|
|
break;
|
|
|
|
}
|
|
CmView = (PCM_VIEW_OF_FILE)CmView->LRUViewList.Blink;
|
|
CmView = CONTAINING_RECORD( CmView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
}
|
|
//
|
|
// all views are in use; bad luck, we just have to live with it (extend past MAX_VIEW_SIZE)
|
|
//
|
|
if( FoundView == FALSE ) {
|
|
CmView = NULL;
|
|
}
|
|
|
|
}
|
|
}
|
|
//
|
|
// immediately flush the cache so these dirty pages won't throttle other IOs
|
|
// in case we did a CcPurge, this will clean out the Cc dirty hints.
|
|
//
|
|
CcFlushCache (CmHive->FileObject->SectionObjectPointer,(PLARGE_INTEGER)(((ULONG_PTR)(&FileOffset)) + 1)/*we are private writers*/,Size,NULL);
|
|
|
|
if( (CmHive->MappedViews >= CmMaxViewsPerHive) && (CmView != NULL) ) {
|
|
|
|
// assert view unmapped
|
|
ASSERT( ((CmView->FileOffset + CmView->Size) == 0) && (CmView->ViewAddress == 0) );
|
|
//
|
|
// no more views are allowed for this hive
|
|
//
|
|
RemoveEntryList(&(CmView->LRUViewList));
|
|
#if DBG
|
|
//
|
|
// do this to signal that LRUViewList is empty.
|
|
//
|
|
InitializeListHead(&(CmView->LRUViewList));
|
|
#endif
|
|
CmpFreeCmView(CmView);
|
|
CmHive->MappedViews --;
|
|
}
|
|
|
|
// make sure we didn't unmapped/unpinned more than we mapped/pinned
|
|
ASSERT( (CmHive->MappedViews >= 0) ); // && (CmHive->MappedViews < CmMaxViewsPerHive) );
|
|
ASSERT( (CmHive->PinnedViews >= 0) );
|
|
|
|
//
|
|
// at last, release the view lock
|
|
//
|
|
CmUnlockHiveViews (CmHive);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
CmpFreeCmView (
|
|
PCM_VIEW_OF_FILE CmView
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
frees a CM View
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TBS - the new view
|
|
|
|
--*/
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
if (CmView == NULL) {
|
|
CM_BUGCHECK(REGISTRY_ERROR,CMVIEW_ERROR,2,0,0);
|
|
}
|
|
|
|
//
|
|
// Init the view
|
|
//
|
|
ASSERT( CmView->FileOffset == 0 );
|
|
ASSERT( CmView->Size == 0 );
|
|
ASSERT( CmView->ViewAddress == NULL );
|
|
ASSERT( CmView->Bcb == NULL );
|
|
ASSERT( CmView->UseCount == 0 );
|
|
ASSERT( IsListEmpty(&(CmView->PinViewList)) == TRUE );
|
|
ASSERT( IsListEmpty(&(CmView->LRUViewList)) == TRUE );
|
|
|
|
ExFreePoolWithTag(CmView, CM_MAPPEDVIEW_TAG | PROTECTED_POOL);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
CmpFixHiveUsageCount(
|
|
IN PCMHIVE CmHive
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is registry's contingency plan against bad and misbehaved apps.
|
|
In a perfect world this should never be called; If we get here, somewhere
|
|
inside a cm function we took an exception and never had a chance to
|
|
release all used cells. We fix that here, and as we hold the reglock exclusive,
|
|
we are safe to do so.
|
|
|
|
We have to clear each view UseCount and the hive UseCount.
|
|
Also, unmap all views that are beyond CmMaxViewsPerHive
|
|
|
|
|
|
Arguments:
|
|
|
|
Hive to be fixed
|
|
|
|
Return Value:
|
|
|
|
none
|
|
--*/
|
|
{
|
|
PCM_VIEW_OF_FILE CmCurrentView;
|
|
USHORT NrViews;
|
|
|
|
PAGED_CODE();
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFixHiveUsageCount : Contingency plan, fixing hive %p UseCount = %lx \n",CmHive,CmHive->UseCount));
|
|
|
|
//
|
|
// lock should be held exclusive and we should have a good reason to come here
|
|
//
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
ASSERT( CmHive->UseCount );
|
|
|
|
//
|
|
// Walk through the LRU list and fix each view
|
|
//
|
|
CmCurrentView = (PCM_VIEW_OF_FILE)CmHive->LRUViewListHead.Flink;
|
|
|
|
for(NrViews = CmHive->MappedViews;NrViews;NrViews--) {
|
|
CmCurrentView = CONTAINING_RECORD( CmCurrentView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
|
|
CmCurrentView->UseCount = 0;
|
|
|
|
CmCurrentView = (PCM_VIEW_OF_FILE)CmCurrentView->LRUViewList.Flink;
|
|
}
|
|
|
|
//
|
|
// unmap views from CmHive->MappedViews to CmMaxViewsPerHive
|
|
//
|
|
while( CmHive->MappedViews >= CmMaxViewsPerHive ) {
|
|
//
|
|
// get the last view from the list
|
|
//
|
|
CmCurrentView = (PCM_VIEW_OF_FILE)CmHive->LRUViewListHead.Blink;
|
|
CmCurrentView = CONTAINING_RECORD( CmCurrentView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
|
|
//
|
|
// unmap it; no need to move it at the end as we shall free it anyway
|
|
//
|
|
CmpUnmapCmView(CmHive,CmCurrentView,TRUE,FALSE);
|
|
|
|
//
|
|
// remove it from LRU list
|
|
//
|
|
RemoveEntryList(&(CmCurrentView->LRUViewList));
|
|
#if DBG
|
|
//
|
|
// do this to signal that LRUViewList is empty.
|
|
//
|
|
InitializeListHead(&(CmCurrentView->LRUViewList));
|
|
#endif
|
|
CmpFreeCmView(CmCurrentView);
|
|
CmHive->MappedViews --;
|
|
|
|
}
|
|
|
|
//
|
|
// Walk through the pinned list and fix each view
|
|
//
|
|
CmCurrentView = (PCM_VIEW_OF_FILE)CmHive->PinViewListHead.Flink;
|
|
|
|
for(NrViews = CmHive->PinnedViews;NrViews;NrViews--) {
|
|
CmCurrentView = CONTAINING_RECORD( CmCurrentView,
|
|
CM_VIEW_OF_FILE,
|
|
PinViewList);
|
|
|
|
CmCurrentView->UseCount = 0;
|
|
|
|
CmCurrentView = (PCM_VIEW_OF_FILE)CmCurrentView->PinViewList.Flink;
|
|
}
|
|
|
|
//
|
|
// finally, fix hive use count
|
|
//
|
|
CmHive->UseCount = 0;
|
|
|
|
}
|
|
|
|
#ifdef CMP_CMVIEW_VALIDATION
|
|
|
|
VOID
|
|
CmpCheckCmView(
|
|
IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Makes sure the view is not mapped or pinned twice
|
|
and that the entire range mapped by the view is correct
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
none
|
|
--*/
|
|
{
|
|
PCM_VIEW_OF_FILE CmCurrentView;
|
|
USHORT NrViews;
|
|
ULONG UseCount = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( ((CmView->Size + CmView->FileOffset) != 0 ) && (CmView->ViewAddress !=0 ) );
|
|
|
|
//
|
|
// Walk through the LRU list and compare view addresses
|
|
//
|
|
CmCurrentView = (PCM_VIEW_OF_FILE)CmHive->LRUViewListHead.Flink;
|
|
|
|
for(NrViews = CmHive->MappedViews;NrViews;NrViews--) {
|
|
CmCurrentView = CONTAINING_RECORD( CmCurrentView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
|
|
if( ((CmCurrentView->Size + CmCurrentView->FileOffset) != 0) && (CmCurrentView->ViewAddress != 0) ) {
|
|
//
|
|
// view is valid
|
|
//
|
|
if( CmCurrentView != CmView ) {
|
|
//
|
|
// and is not the same view
|
|
//
|
|
if( (CmCurrentView->FileOffset == CmView->FileOffset) ||
|
|
(CmCurrentView->ViewAddress == CmView->ViewAddress)
|
|
) {
|
|
//
|
|
// that's really bad! 2 views map the same address
|
|
//
|
|
#ifndef _CM_LDR_
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpCheckCmView:: Two views map the same address (%lx,%p) for hive %p\n",CmView->FileOffset,CmView->ViewAddress,CmHive);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\tView1 = %p, Size = %lx\n",CmView,CmView->Size);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\tView2 = %p, Size = %lx\n",CmCurrentView,CmCurrentView->Size);
|
|
DbgBreakPoint();
|
|
#endif //_CM_LDR_
|
|
}
|
|
}
|
|
UseCount += CmCurrentView->UseCount;
|
|
} else {
|
|
ASSERT( CmCurrentView->UseCount == 0 );
|
|
}
|
|
|
|
CmCurrentView = (PCM_VIEW_OF_FILE)CmCurrentView->LRUViewList.Flink;
|
|
}
|
|
|
|
//
|
|
// Walk through the pinned list and compare view addresses
|
|
//
|
|
CmCurrentView = (PCM_VIEW_OF_FILE)CmHive->PinViewListHead.Flink;
|
|
|
|
for(NrViews = CmHive->PinnedViews;NrViews;NrViews--) {
|
|
CmCurrentView = CONTAINING_RECORD( CmCurrentView,
|
|
CM_VIEW_OF_FILE,
|
|
PinViewList);
|
|
|
|
if( ((CmCurrentView->Size + CmCurrentView->FileOffset) != 0) && (CmCurrentView->ViewAddress != 0) ) {
|
|
//
|
|
// view is valid
|
|
//
|
|
if( CmCurrentView != CmView ) {
|
|
//
|
|
// and is not the same view
|
|
//
|
|
if( (CmCurrentView->FileOffset == CmView->FileOffset) ||
|
|
(CmCurrentView->ViewAddress == CmView->ViewAddress)
|
|
) {
|
|
//
|
|
// that's really bad! 2 views map the same address
|
|
//
|
|
#ifndef _CM_LDR_
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpCheckCmView:: Two views map the same address (%lx,%p) for hive %p\n",CmView->FileOffset,CmView->ViewAddress,CmHive);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\tView1 = %p, Size = %lx\n",CmView,CmView->Size);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\tView2 = %p, Size = %lx\n",CmCurrentView,CmCurrentView->Size);
|
|
DbgBreakPoint();
|
|
#endif //_CM_LDR_
|
|
}
|
|
}
|
|
UseCount += CmCurrentView->UseCount;
|
|
} else {
|
|
ASSERT( CmCurrentView->UseCount == 0 );
|
|
}
|
|
|
|
CmCurrentView = (PCM_VIEW_OF_FILE)CmCurrentView->PinViewList.Flink;
|
|
}
|
|
|
|
if( CmHive->UseCount < UseCount ) {
|
|
#ifndef _CM_LDR_
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpCheckCmView:: Hive's (%p) UseCount smaller than total views UseCount %lu,%lu\n",CmHive,CmHive->UseCount,UseCount);
|
|
DbgBreakPoint();
|
|
#endif //_CM_LDR_
|
|
|
|
}
|
|
}
|
|
|
|
#endif //CMP_CMVIEW_VALIDATION
|
|
|
|
#if 0
|
|
|
|
VOID
|
|
CmpUnmapAditionalViews(
|
|
IN PCMHIVE CmHive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unmap all views that are beyond CmMaxViewsPerHive.
|
|
This routine is to be called at the end of CmpInitializeHiveList
|
|
|
|
Arguments:
|
|
|
|
Hive to be fixed
|
|
|
|
Return Value:
|
|
|
|
none
|
|
--*/
|
|
{
|
|
PCM_VIEW_OF_FILE CmCurrentView;
|
|
USHORT NrViews;
|
|
|
|
PAGED_CODE();
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpUnmapAditionalViews : Fixing hive %p MappedViews = %lx \n",CmHive,CmHive->MappedViews));
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
ASSERT( CmHive->UseCount == 0 );
|
|
|
|
//
|
|
// unmap views from CmHive->MappedViews to CmMaxViewsPerHive
|
|
//
|
|
while( CmHive->MappedViews >= CmMaxViewsPerHive ) {
|
|
//
|
|
// get the last view from the list
|
|
//
|
|
CmCurrentView = (PCM_VIEW_OF_FILE)CmHive->LRUViewListHead.Blink;
|
|
CmCurrentView = CONTAINING_RECORD( CmCurrentView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
|
|
ASSERT( CmCurrentView->UseCount == 0 );
|
|
//
|
|
// unmap it
|
|
//
|
|
CmpUnmapCmView(CmHive,CmCurrentView,TRUE,FALSE);
|
|
|
|
//
|
|
// remove it from LRU list
|
|
//
|
|
RemoveEntryList(&(CmCurrentView->LRUViewList));
|
|
#if DBG
|
|
//
|
|
// do this to signal that LRUViewList is empty.
|
|
//
|
|
InitializeListHead(&(CmCurrentView->LRUViewList));
|
|
#endif
|
|
CmpFreeCmView(CmCurrentView);
|
|
CmHive->MappedViews --;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
CmpMapEntireFileInFakeViews(
|
|
IN PCMHIVE CmHive,
|
|
IN ULONG Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Maps and faults all the file in, in chunks of 256K if possible.
|
|
This should improve boot performance; After the hive is mapped
|
|
(maps are build and hive is checked we'll get rid of this aditional
|
|
views
|
|
|
|
Arguments:
|
|
|
|
CmHive - Hive to be mapped
|
|
|
|
Length - length of the hive ==> add HBLOCK_SIZE
|
|
|
|
Return Value:
|
|
|
|
none
|
|
--*/
|
|
{
|
|
ULONG Offset;
|
|
ULONG Size;
|
|
PCM_VIEW_OF_FILE CmView;
|
|
LARGE_INTEGER SectionOffset;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
ASSERT( IsListEmpty(&(CmHive->FakeViewListHead)) );
|
|
#if DBG
|
|
ASSERT( CmHive->FakeViews == 0 );
|
|
#endif
|
|
|
|
//
|
|
// adjust the size to get the real size of the file
|
|
Length += HBLOCK_SIZE;
|
|
|
|
//
|
|
// start from the beggining and map 256K of data from the hive
|
|
// allocate a view and insert it in the FakeViewList, use LRUViewList for that.
|
|
//
|
|
Offset =0;
|
|
SectionOffset.HighPart = 0;
|
|
|
|
while( Offset < Length ) {
|
|
CmView = ExAllocatePoolWithTag(PagedPool,sizeof(CM_VIEW_OF_FILE),CM_MAPPEDVIEW_TAG | PROTECTED_POOL);
|
|
|
|
if (CmView == NULL) {
|
|
CM_BUGCHECK(REGISTRY_ERROR,CMVIEW_ERROR,2,0,0);
|
|
}
|
|
|
|
//
|
|
// Init the view
|
|
//
|
|
CmView->ViewAddress = NULL;
|
|
CmView->Bcb = NULL;
|
|
|
|
InsertTailList(
|
|
&(CmHive->FakeViewListHead),
|
|
&(CmView->LRUViewList)
|
|
);
|
|
#if DBG
|
|
CmHive->FakeViews++;
|
|
#endif
|
|
|
|
//
|
|
// now try to map the view
|
|
//
|
|
Size = _256K;
|
|
if( (Offset + Size) > Length ) {
|
|
Size = Length - Offset;
|
|
}
|
|
|
|
SectionOffset.LowPart = Offset;
|
|
try {
|
|
if (!CcMapData( CmHive->FileObject,
|
|
(PLARGE_INTEGER)&SectionOffset,
|
|
Size,
|
|
MAP_WAIT
|
|
#ifdef CM_MAP_NO_READ
|
|
| MAP_NO_READ
|
|
#endif
|
|
,
|
|
(PVOID *)(&(CmView->Bcb)),
|
|
(PVOID *)(&(CmView->ViewAddress)) )) {
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpMapEntireFileInFakeViews: Error mapping data at offset %lx for hive %p\n",Offset,CmHive));
|
|
CmView->Bcb = NULL;
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// in low-memory scenarios, CcMapData throws a STATUS_IN_PAGE_ERROR
|
|
// this happens when the IO issued to touch the just-mapped data fails (usually with
|
|
// STATUS_INSUFFICIENT_RESOURCES; We want to catch this and treat as a
|
|
// "not enough resources" problem, rather than letting it to surface the kernel call
|
|
//
|
|
// signal that the view is not mapped
|
|
CmView->Bcb = NULL;
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpMapEntireFileInFakeViews: Error mapping data at offset %lx for hive %p\n",Offset,CmHive));
|
|
}
|
|
|
|
if( CmView->Bcb == NULL ) {
|
|
//
|
|
// we are already short on memory; don't make things worse than they are
|
|
// free what we have already allocated and bail out
|
|
//
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpMapEntireFileInFakeViews: Could not map entire file for hive %p ... bailing out\n",CmHive));
|
|
CmpUnmapFakeViews(CmHive);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// advance the offset
|
|
//
|
|
Offset += Size;
|
|
}
|
|
|
|
#if DBG
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpMapEntireFileInFakeViews: Successfully mapped %lx FakeViews for hive %p\n",CmHive->FakeViews,CmHive));
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
CmpUnmapFakeViews(
|
|
IN PCMHIVE CmHive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Walks through the FakeViewList and unmaps all views.
|
|
|
|
Arguments:
|
|
|
|
CmHive - Hive to be unmapped
|
|
|
|
Return Value:
|
|
|
|
none
|
|
--*/
|
|
{
|
|
PCM_VIEW_OF_FILE CmView;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
#if DBG
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpUnmapFakeViews: Unmapping %lx views for hive %p\n",CmHive->FakeViews,CmHive));
|
|
#endif
|
|
|
|
while( IsListEmpty( &(CmHive->FakeViewListHead) ) == FALSE ) {
|
|
CmView = (PCM_VIEW_OF_FILE)RemoveHeadList(&(CmHive->FakeViewListHead));
|
|
CmView = CONTAINING_RECORD( CmView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
|
|
if( CmView->Bcb != NULL ) {
|
|
//
|
|
// view is mapped; unpin it.
|
|
//
|
|
CcUnpinData( CmView->Bcb );
|
|
}
|
|
|
|
//
|
|
// now free the memory for this view.
|
|
//
|
|
ExFreePoolWithTag(CmView, CM_MAPPEDVIEW_TAG | PROTECTED_POOL);
|
|
#if DBG
|
|
CmHive->FakeViews--;
|
|
#endif
|
|
|
|
}
|
|
|
|
ASSERT( IsListEmpty( &(CmHive->FakeViewListHead) ) == TRUE );
|
|
#if DBG
|
|
ASSERT( CmHive->FakeViews == 0 );
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
VOID
|
|
CmpPrefetchHiveFile(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN ULONG Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prefetch all file into memory.
|
|
We're using MmPrefetchPages fast routine; Pages will be put in the transition
|
|
state, and they'll be used by the hive load worker while mapping data
|
|
|
|
Arguments:
|
|
|
|
FileObject - file object associated with the file to be prefetched
|
|
|
|
Length - length of the file
|
|
|
|
Return Value:
|
|
|
|
none
|
|
--*/
|
|
{
|
|
ULONG NumberOfPages;
|
|
PREAD_LIST *ReadLists;
|
|
PREAD_LIST ReadList;
|
|
ULONG AllocationSize;
|
|
ULONG Offset;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
NumberOfPages = ROUND_UP(Length,PAGE_SIZE) / PAGE_SIZE ;
|
|
|
|
ReadLists = ExAllocatePoolWithTag(NonPagedPool, sizeof(PREAD_LIST), CM_POOL_TAG);
|
|
if (ReadLists == NULL) {
|
|
return;
|
|
}
|
|
|
|
AllocationSize = sizeof(READ_LIST) + (NumberOfPages * sizeof(FILE_SEGMENT_ELEMENT));
|
|
|
|
ReadList = ExAllocatePoolWithTag(NonPagedPool,AllocationSize,CM_POOL_TAG);
|
|
|
|
if (ReadList == NULL) {
|
|
ExFreePool(ReadLists);
|
|
return;
|
|
}
|
|
|
|
ReadList->FileObject = FileObject;
|
|
ReadList->IsImage = FALSE;
|
|
ReadList->NumberOfEntries = 0;
|
|
Offset = 0;
|
|
while( Offset < Length ) {
|
|
ReadList->List[ReadList->NumberOfEntries].Alignment = Offset;
|
|
ReadList->NumberOfEntries++;
|
|
Offset += PAGE_SIZE;
|
|
}
|
|
ASSERT( ReadList->NumberOfEntries == NumberOfPages );
|
|
|
|
ReadLists[0] = ReadList;
|
|
|
|
MmPrefetchPages (1,ReadLists);
|
|
|
|
// just to make sure !
|
|
// this assert has been moved inside CcSetPrivateWriteFile !!!
|
|
// there is no need to assert this here
|
|
//ASSERT( MmDisableModifiedWriteOfSection (FileObject->SectionObjectPointer) );
|
|
|
|
ExFreePool(ReadList);
|
|
ExFreePool(ReadLists);
|
|
}
|
|
|
|
|
|
VOID
|
|
CmpUnmapUnusedViews(
|
|
IN PCMHIVE CmHive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unmaps all mapped views than are not currently in-use.
|
|
|
|
The purpose of this is to allow a retry in case CcMapData failed
|
|
because of the system having to many mapped views.
|
|
|
|
We should not run into this too often ( - at all ).
|
|
|
|
Arguments:
|
|
|
|
CmHive - hive for which we already have the viewlist lock owned
|
|
|
|
Return Value:
|
|
|
|
none
|
|
--*/
|
|
{
|
|
PCM_VIEW_OF_FILE CmView;
|
|
USHORT NrViews;
|
|
PCMHIVE CmCurrentHive;
|
|
PLIST_ENTRY p;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// iterate through the hive list
|
|
//
|
|
LOCK_HIVE_LIST();
|
|
p = CmpHiveListHead.Flink;
|
|
while(p != &CmpHiveListHead) {
|
|
CmCurrentHive = (PCMHIVE)CONTAINING_RECORD(p, CMHIVE, HiveList);
|
|
|
|
if( CmCurrentHive != CmHive ) {
|
|
//
|
|
// we need to be the only ones operating on this list
|
|
//
|
|
CmLockHiveViews (CmCurrentHive);
|
|
} else {
|
|
//
|
|
// we already have the mutex owned
|
|
//
|
|
NOTHING;
|
|
}
|
|
//
|
|
// try to unmap all mapped views
|
|
//
|
|
CmView = (PCM_VIEW_OF_FILE)CmCurrentHive->LRUViewListHead.Flink;
|
|
|
|
for(NrViews = CmCurrentHive->MappedViews;NrViews;NrViews--) {
|
|
CmView = CONTAINING_RECORD( CmView,
|
|
CM_VIEW_OF_FILE,
|
|
LRUViewList);
|
|
|
|
if( (CmView->ViewAddress != 0) && ( CmView->UseCount == 0 ) ) {
|
|
//
|
|
// view is mapped and it is not in use
|
|
//
|
|
ASSERT( (CmView->FileOffset + CmView->Size) != 0 && (CmView->Bcb != 0));
|
|
|
|
//
|
|
// unmap it without altering its position in the list
|
|
//
|
|
CmpUnmapCmView(CmCurrentHive,CmView,TRUE,FALSE);
|
|
}
|
|
|
|
CmView = (PCM_VIEW_OF_FILE)CmView->LRUViewList.Flink;
|
|
}
|
|
|
|
if( CmCurrentHive != CmHive ) {
|
|
CmUnlockHiveViews (CmCurrentHive);
|
|
}
|
|
|
|
p=p->Flink;
|
|
}
|
|
UNLOCK_HIVE_LIST();
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CmPrefetchHivePages(
|
|
IN PUNICODE_STRING FullHivePath,
|
|
IN OUT PREAD_LIST ReadList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Searches through the hive list for a hive with the backing file of name FullHivePath
|
|
Builds a READ_LIST based on the given page offsets array and prefetches the pages
|
|
|
|
Arguments:
|
|
|
|
FullHivePath - Full Path of the file
|
|
|
|
ReadList - read_list of page offsets to be prefetched.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - OK, pages prefetched
|
|
|
|
STATUS_INVALID_PARAMETER - file was not found in the machine's hive list
|
|
|
|
else, status returned by MmPrefetchPages.
|
|
|
|
--*/
|
|
{
|
|
PCMHIVE CmHive = NULL;
|
|
PLIST_ENTRY p;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if( ReadList == NULL ) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
CmpLockRegistry();
|
|
|
|
//
|
|
// iterate through the hive list
|
|
//
|
|
LOCK_HIVE_LIST();
|
|
p = CmpHiveListHead.Flink;
|
|
while(p != &CmpHiveListHead) {
|
|
CmHive = (PCMHIVE)CONTAINING_RECORD(p, CMHIVE, HiveList);
|
|
|
|
if( (CmHive->FileObject != NULL) && (CmHive->FileFullPath.Buffer != NULL) ) {
|
|
//
|
|
// there is a chance this might be the one
|
|
//
|
|
if( RtlCompareUnicodeString(FullHivePath,&(CmHive->FileFullPath),TRUE) == 0 ) {
|
|
//
|
|
// we found it !
|
|
//
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
p=p->Flink;
|
|
}
|
|
UNLOCK_HIVE_LIST();
|
|
|
|
if( p == &CmpHiveListHead ) {
|
|
|
|
//
|
|
// bad luck;
|
|
//
|
|
CmpUnlockRegistry();
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ASSERT( CmHive->FileObject != NULL );
|
|
|
|
//
|
|
// at this point, we have successfully identified the hive
|
|
//
|
|
|
|
//
|
|
// build up the READ_LIST with the requested page offsets
|
|
//
|
|
ReadList->FileObject = CmHive->FileObject;
|
|
ReadList->IsImage = FALSE;
|
|
ASSERT( ReadList->NumberOfEntries != 0 );
|
|
|
|
Status = MmPrefetchPages (1,&ReadList);
|
|
|
|
// just to make sure !
|
|
ASSERT( MmDisableModifiedWriteOfSection (CmHive->FileObject->SectionObjectPointer) );
|
|
|
|
CmpUnlockRegistry();
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
CmIsFileLoadedAsHive(PFILE_OBJECT FileObject)
|
|
{
|
|
PCMHIVE CmHive;
|
|
PLIST_ENTRY p;
|
|
BOOLEAN HiveFound = FALSE;
|
|
|
|
//
|
|
// iterate through the hive list
|
|
//
|
|
LOCK_HIVE_LIST();
|
|
p = CmpHiveListHead.Flink;
|
|
while(p != &CmpHiveListHead) {
|
|
CmHive = (PCMHIVE)CONTAINING_RECORD(p, CMHIVE, HiveList);
|
|
|
|
if( CmHive->FileObject == FileObject ) {
|
|
//
|
|
// we found it !
|
|
//
|
|
HiveFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
p=p->Flink;
|
|
}
|
|
UNLOCK_HIVE_LIST();
|
|
|
|
return HiveFound;
|
|
}
|
|
|
|
VOID
|
|
CmpReferenceHiveView( IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a refcount to the hive and view, to prevent it from going away from under us;
|
|
Assumes the viewlock is held by the caller.
|
|
Can be converted to a macro.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if(CmView && CmHive->Hive.ReleaseCellRoutine) {
|
|
//
|
|
// up the view use count if any
|
|
//
|
|
CmView->UseCount++;
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
CmpDereferenceHiveView( IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pair of CmpReferenceHiveView
|
|
Assumes the viewlock is held by the caller.
|
|
Can be converted to a macro.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if(CmView && CmHive->Hive.ReleaseCellRoutine) {
|
|
CmView->UseCount--;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
CmpReferenceHiveViewWithLock( IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a refcount to the hive and view, to prevent it from going away from under us;
|
|
Can be converted to a macro.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CmLockHiveViews(CmHive);
|
|
//
|
|
// call the unsafe routine
|
|
//
|
|
CmpReferenceHiveView(CmHive,CmView);
|
|
|
|
CmUnlockHiveViews(CmHive);
|
|
}
|
|
|
|
VOID
|
|
CmpDereferenceHiveViewWithLock( IN PCMHIVE CmHive,
|
|
IN PCM_VIEW_OF_FILE CmView
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pair of CmpDereferenceHiveViewWithLock
|
|
Can be converted to a macro.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CmLockHiveViews(CmHive);
|
|
//
|
|
// call the unsafe routine
|
|
//
|
|
CmpDereferenceHiveView(CmHive,CmView);
|
|
|
|
CmUnlockHiveViews(CmHive);
|
|
}
|