windows-nt/Source/XPSP1/NT/base/ntos/config/cmmapvw.c

2550 lines
68 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
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));
}
#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;
IO_STATUS_BLOCK IoStatus;
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;
}
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;
PLIST_ENTRY p;
BOOLEAN HiveFound = FALSE;
NTSTATUS Status;
ULONG i;
PAGED_CODE();
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 !
//
HiveFound = TRUE;
break;
}
}
p=p->Flink;
}
UNLOCK_HIVE_LIST();
if( (HiveFound == FALSE) || ( ReadList == NULL ) ) {
//
// 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);
}