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.
1035 lines
30 KiB
1035 lines
30 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cmworker.c
|
|
|
|
Abstract:
|
|
|
|
This module contains support for the worker thread of the registry.
|
|
The worker thread (actually an executive worker thread is used) is
|
|
required for operations that must take place in the context of the
|
|
system process. (particularly file I/O)
|
|
|
|
Author:
|
|
|
|
John Vert (jvert) 21-Oct-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "cmp.h"
|
|
|
|
extern LIST_ENTRY CmpHiveListHead;
|
|
|
|
VOID
|
|
CmpInitializeHiveList(
|
|
VOID
|
|
);
|
|
|
|
//
|
|
// ----- LAZY FLUSH CONTROL -----
|
|
//
|
|
// LAZY_FLUSH_INTERVAL_IN_SECONDS controls how many seconds will elapse
|
|
// between when the hive is marked dirty and when the lazy flush worker
|
|
// thread is queued to write the data to disk.
|
|
//
|
|
ULONG CmpLazyFlushIntervalInSeconds = 5;
|
|
//
|
|
// number of hive flushed at one time (default to all system hive + 2 = current logged on user hives)
|
|
//
|
|
ULONG CmpLazyFlushHiveCount = 7;
|
|
|
|
//
|
|
// LAZY_FLUSH_TIMEOUT_IN_SECONDS controls how long the lazy flush worker
|
|
// thread will wait for the registry lock before giving up and queueing
|
|
// the lazy flush timer again.
|
|
//
|
|
#define LAZY_FLUSH_TIMEOUT_IN_SECONDS 1
|
|
|
|
#define SECOND_MULT 10*1000*1000 // 10->mic, 1000->mil, 1000->second
|
|
|
|
PKPROCESS CmpSystemProcess;
|
|
KTIMER CmpLazyFlushTimer;
|
|
KDPC CmpLazyFlushDpc;
|
|
WORK_QUEUE_ITEM CmpLazyWorkItem;
|
|
|
|
BOOLEAN CmpLazyFlushPending = FALSE;
|
|
BOOLEAN CmpForceForceFlush = FALSE;
|
|
BOOLEAN CmpHoldLazyFlush = TRUE;
|
|
BOOLEAN CmpDontGrowLogFile = FALSE;
|
|
|
|
extern BOOLEAN CmpNoWrite;
|
|
extern BOOLEAN CmpWasSetupBoot;
|
|
extern BOOLEAN HvShutdownComplete;
|
|
extern BOOLEAN CmpProfileLoaded;
|
|
|
|
//
|
|
// Indicate whether the "disk full" popup has been triggered yet or not.
|
|
//
|
|
extern BOOLEAN CmpDiskFullWorkerPopupDisplayed;
|
|
|
|
//
|
|
// set to true if disk full when trying to save the changes made between system hive loading and registry initalization
|
|
//
|
|
extern BOOLEAN CmpCannotWriteConfiguration;
|
|
extern UNICODE_STRING SystemHiveFullPathName;
|
|
extern HIVE_LIST_ENTRY CmpMachineHiveList[];
|
|
extern BOOLEAN CmpTrackHiveClose;
|
|
|
|
#if DBG
|
|
PKTHREAD CmpCallerThread = NULL;
|
|
#endif
|
|
|
|
|
|
#ifdef CMP_STATS
|
|
|
|
#define KCB_STAT_INTERVAL_IN_SECONDS 120 // 2 min.
|
|
|
|
extern struct {
|
|
ULONG CmpMaxKcbNo;
|
|
ULONG CmpKcbNo;
|
|
ULONG CmpStatNo;
|
|
ULONG CmpNtCreateKeyNo;
|
|
ULONG CmpNtDeleteKeyNo;
|
|
ULONG CmpNtDeleteValueKeyNo;
|
|
ULONG CmpNtEnumerateKeyNo;
|
|
ULONG CmpNtEnumerateValueKeyNo;
|
|
ULONG CmpNtFlushKeyNo;
|
|
ULONG CmpNtNotifyChangeMultipleKeysNo;
|
|
ULONG CmpNtOpenKeyNo;
|
|
ULONG CmpNtQueryKeyNo;
|
|
ULONG CmpNtQueryValueKeyNo;
|
|
ULONG CmpNtQueryMultipleValueKeyNo;
|
|
ULONG CmpNtRestoreKeyNo;
|
|
ULONG CmpNtSaveKeyNo;
|
|
ULONG CmpNtSaveMergedKeysNo;
|
|
ULONG CmpNtSetValueKeyNo;
|
|
ULONG CmpNtLoadKeyNo;
|
|
ULONG CmpNtUnloadKeyNo;
|
|
ULONG CmpNtSetInformationKeyNo;
|
|
ULONG CmpNtReplaceKeyNo;
|
|
ULONG CmpNtQueryOpenSubKeysNo;
|
|
} CmpStatsDebug;
|
|
|
|
KTIMER CmpKcbStatTimer;
|
|
KDPC CmpKcbStatDpc;
|
|
KSPIN_LOCK CmpKcbStatLock;
|
|
BOOLEAN CmpKcbStatShutdown;
|
|
|
|
VOID
|
|
CmpKcbStatDpcRoutine(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
);
|
|
|
|
extern ULONG CmpNtFakeCreate;
|
|
|
|
struct {
|
|
ULONG BasicInformation;
|
|
UINT64 BasicInformationTimeCounter;
|
|
UINT64 BasicInformationTimeElapsed;
|
|
|
|
ULONG NodeInformation;
|
|
UINT64 NodeInformationTimeCounter;
|
|
UINT64 NodeInformationTimeElapsed;
|
|
|
|
ULONG FullInformation;
|
|
UINT64 FullInformationTimeCounter;
|
|
UINT64 FullInformationTimeElapsed;
|
|
|
|
ULONG EnumerateKeyBasicInformation;
|
|
UINT64 EnumerateKeyBasicInformationTimeCounter;
|
|
UINT64 EnumerateKeyBasicInformationTimeElapsed;
|
|
|
|
ULONG EnumerateKeyNodeInformation;
|
|
UINT64 EnumerateKeyNodeInformationTimeCounter;
|
|
UINT64 EnumerateKeyNodeInformationTimeElapsed;
|
|
|
|
ULONG EnumerateKeyFullInformation;
|
|
UINT64 EnumerateKeyFullInformationTimeCounter;
|
|
UINT64 EnumerateKeyFullInformationTimeElapsed;
|
|
} CmpQueryKeyDataDebug = {0};
|
|
|
|
#endif
|
|
|
|
|
|
VOID
|
|
CmpLazyFlushWorker(
|
|
IN PVOID Parameter
|
|
);
|
|
|
|
VOID
|
|
CmpLazyFlushDpcRoutine(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
);
|
|
|
|
VOID
|
|
CmpDiskFullWarningWorker(
|
|
IN PVOID WorkItem
|
|
);
|
|
|
|
VOID
|
|
CmpDiskFullWarning(
|
|
VOID
|
|
);
|
|
|
|
BOOLEAN
|
|
CmpDoFlushNextHive(
|
|
BOOLEAN ForceFlush,
|
|
PBOOLEAN PostWarning,
|
|
PULONG DirtyCount
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,CmpLazyFlush)
|
|
#pragma alloc_text(PAGE,CmpLazyFlushWorker)
|
|
#pragma alloc_text(PAGE,CmpDiskFullWarningWorker)
|
|
#pragma alloc_text(PAGE,CmpDiskFullWarning)
|
|
#pragma alloc_text(PAGE,CmpCmdHiveClose)
|
|
#pragma alloc_text(PAGE,CmpCmdInit)
|
|
#pragma alloc_text(PAGE,CmpCmdRenameHive)
|
|
#pragma alloc_text(PAGE,CmpCmdHiveOpen)
|
|
#pragma alloc_text(PAGE,CmSetLazyFlushState)
|
|
|
|
#ifndef CMP_STATS
|
|
#pragma alloc_text(PAGE,CmpShutdownWorkers)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
VOID
|
|
CmpCmdHiveClose(
|
|
PCMHIVE CmHive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes all the file handles for the specified hive
|
|
Arguments:
|
|
|
|
CmHive - the hive to close
|
|
|
|
Return Value:
|
|
none
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
LARGE_INTEGER systemtime;
|
|
BOOLEAN oldFlag;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// disable hard error popups, to workaround ObAttachProcessStack
|
|
//
|
|
oldFlag = IoSetThreadHardErrorMode(FALSE);
|
|
|
|
//
|
|
// Close the files associated with this hive.
|
|
//
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
|
|
for (i=0; i<HFILE_TYPE_MAX; i++) {
|
|
if (CmHive->FileHandles[i] != NULL) {
|
|
//
|
|
// attempt to set change the last write time (profile guys are relying on it!)
|
|
//
|
|
if( i == HFILE_TYPE_PRIMARY ) {
|
|
if( NT_SUCCESS(ZwQueryInformationFile(
|
|
CmHive->FileHandles[i],
|
|
&IoStatusBlock,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
FileBasicInformation) ) ) {
|
|
|
|
KeQuerySystemTime(&systemtime);
|
|
|
|
BasicInfo.LastWriteTime = systemtime;
|
|
BasicInfo.LastAccessTime = systemtime;
|
|
|
|
ZwSetInformationFile(
|
|
CmHive->FileHandles[i],
|
|
&IoStatusBlock,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
FileBasicInformation
|
|
);
|
|
}
|
|
|
|
CmpTrackHiveClose = TRUE;
|
|
CmCloseHandle(CmHive->FileHandles[i]);
|
|
CmpTrackHiveClose = FALSE;
|
|
|
|
} else {
|
|
CmCloseHandle(CmHive->FileHandles[i]);
|
|
}
|
|
|
|
CmHive->FileHandles[i] = NULL;
|
|
}
|
|
}
|
|
//
|
|
// restore hard error popups mode
|
|
//
|
|
IoSetThreadHardErrorMode(oldFlag);
|
|
}
|
|
|
|
VOID
|
|
CmpCmdInit(
|
|
BOOLEAN SetupBoot
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes cm globals and flushes all hives to the disk.
|
|
|
|
Arguments:
|
|
|
|
SetupBoot - whether the boot is from setup or a regular boot
|
|
|
|
Return Value:
|
|
none
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
//
|
|
// Initialize lazy flush timer and DPC
|
|
//
|
|
KeInitializeDpc(&CmpLazyFlushDpc,
|
|
CmpLazyFlushDpcRoutine,
|
|
NULL);
|
|
|
|
KeInitializeTimer(&CmpLazyFlushTimer);
|
|
|
|
ExInitializeWorkItem(&CmpLazyWorkItem, CmpLazyFlushWorker, NULL);
|
|
|
|
#ifdef CMP_STATS
|
|
KeInitializeDpc(&CmpKcbStatDpc,
|
|
CmpKcbStatDpcRoutine,
|
|
NULL);
|
|
|
|
KeInitializeTimer(&CmpKcbStatTimer);
|
|
|
|
KeInitializeSpinLock(&CmpKcbStatLock);
|
|
CmpKcbStatShutdown = FALSE;
|
|
#endif
|
|
|
|
CmpNoWrite = CmpMiniNTBoot;
|
|
|
|
CmpWasSetupBoot = SetupBoot;
|
|
|
|
if (SetupBoot == FALSE) {
|
|
CmpInitializeHiveList();
|
|
}
|
|
|
|
//
|
|
// Since we are done with initialization,
|
|
// disable the hive sharing
|
|
//
|
|
if (CmpMiniNTBoot && CmpShareSystemHives) {
|
|
CmpShareSystemHives = FALSE;
|
|
}
|
|
|
|
#ifdef CMP_STATS
|
|
CmpKcbStat();
|
|
#endif
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CmpCmdRenameHive(
|
|
PCMHIVE CmHive,
|
|
POBJECT_NAME_INFORMATION OldName,
|
|
PUNICODE_STRING NewName,
|
|
ULONG NameInfoLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
rename a cmhive's primary handle
|
|
|
|
replaces old REG_CMD_RENAME_HIVE worker case
|
|
|
|
Arguments:
|
|
|
|
CmHive - hive to rename
|
|
|
|
OldName - old name information
|
|
|
|
NewName - the new name for the file
|
|
|
|
NameInfoLength - sizeof name information structure
|
|
|
|
Return Value:
|
|
|
|
<TBD>
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Handle;
|
|
PFILE_RENAME_INFORMATION RenameInfo;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
|
|
//
|
|
// Rename a CmHive's primary handle
|
|
//
|
|
Handle = CmHive->FileHandles[HFILE_TYPE_PRIMARY];
|
|
if (OldName != NULL) {
|
|
ASSERT_PASSIVE_LEVEL();
|
|
Status = ZwQueryObject(Handle,
|
|
ObjectNameInformation,
|
|
OldName,
|
|
NameInfoLength,
|
|
&NameInfoLength);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
RenameInfo = ExAllocatePool(PagedPool,
|
|
sizeof(FILE_RENAME_INFORMATION) + NewName->Length);
|
|
if (RenameInfo == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RenameInfo->ReplaceIfExists = FALSE;
|
|
RenameInfo->RootDirectory = NULL;
|
|
RenameInfo->FileNameLength = NewName->Length;
|
|
RtlCopyMemory(RenameInfo->FileName,
|
|
NewName->Buffer,
|
|
NewName->Length);
|
|
|
|
Status = ZwSetInformationFile(Handle,
|
|
&IoStatusBlock,
|
|
(PVOID)RenameInfo,
|
|
sizeof(FILE_RENAME_INFORMATION) +
|
|
NewName->Length,
|
|
FileRenameInformation);
|
|
ExFreePool(RenameInfo);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CmpCmdHiveOpen(
|
|
POBJECT_ATTRIBUTES FileAttributes,
|
|
PSECURITY_CLIENT_CONTEXT ImpersonationContext,
|
|
PBOOLEAN Allocate,
|
|
PBOOLEAN RegistryLockAquired, // needed to avoid recursivity deadlock with ZwCreate calls calling back into registry
|
|
PCMHIVE *NewHive,
|
|
ULONG CheckFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
replaces old REG_CMD_HIVE_OPEN worker case
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
<TBD>
|
|
--*/
|
|
{
|
|
PUNICODE_STRING FileName;
|
|
NTSTATUS Status;
|
|
HANDLE NullHandle;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Open the file.
|
|
//
|
|
FileName = FileAttributes->ObjectName;
|
|
|
|
Status = CmpInitHiveFromFile(FileName,
|
|
0,
|
|
NewHive,
|
|
Allocate,
|
|
RegistryLockAquired,
|
|
CheckFlags);
|
|
//
|
|
// NT Servers will return STATUS_ACCESS_DENIED. Netware 3.1x
|
|
// servers could return any of the other error codes if the GUEST
|
|
// account is disabled.
|
|
//
|
|
if (((Status == STATUS_ACCESS_DENIED) ||
|
|
(Status == STATUS_NO_SUCH_USER) ||
|
|
(Status == STATUS_WRONG_PASSWORD) ||
|
|
(Status == STATUS_ACCOUNT_EXPIRED) ||
|
|
(Status == STATUS_ACCOUNT_DISABLED) ||
|
|
(Status == STATUS_ACCOUNT_RESTRICTION)) &&
|
|
(ImpersonationContext != NULL)) {
|
|
//
|
|
// Impersonate the caller and try it again. This
|
|
// lets us open hives on a remote machine.
|
|
//
|
|
Status = SeImpersonateClientEx(
|
|
ImpersonationContext,
|
|
NULL);
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
Status = CmpInitHiveFromFile(FileName,
|
|
0,
|
|
NewHive,
|
|
Allocate,
|
|
RegistryLockAquired,
|
|
CheckFlags);
|
|
NullHandle = NULL;
|
|
|
|
PsRevertToSelf();
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
CmpLazyFlush(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine resets the registry timer to go off at a specified interval
|
|
in the future (LAZY_FLUSH_INTERVAL_IN_SECONDS).
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER DueTime;
|
|
|
|
PAGED_CODE();
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpLazyFlush: setting lazy flush timer\n"));
|
|
if ((!CmpNoWrite) && (!CmpHoldLazyFlush)) {
|
|
|
|
DueTime.QuadPart = Int32x32To64(CmpLazyFlushIntervalInSeconds,
|
|
- SECOND_MULT);
|
|
|
|
//
|
|
// Indicate relative time
|
|
//
|
|
|
|
KeSetTimer(&CmpLazyFlushTimer,
|
|
DueTime,
|
|
&CmpLazyFlushDpc);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
CmpLazyFlushDpcRoutine(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the DPC routine triggered by the lazy flush timer. All it does
|
|
is queue a work item to an executive worker thread. The work item will
|
|
do the actual lazy flush to disk.
|
|
|
|
Arguments:
|
|
|
|
Dpc - Supplies a pointer to the DPC object.
|
|
|
|
DeferredContext - not used
|
|
|
|
SystemArgument1 - not used
|
|
|
|
SystemArgument2 - not used
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER (Dpc);
|
|
UNREFERENCED_PARAMETER (DeferredContext);
|
|
UNREFERENCED_PARAMETER (SystemArgument1);
|
|
UNREFERENCED_PARAMETER (SystemArgument2);
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpLazyFlushDpc: queuing lazy flush work item\n"));
|
|
|
|
if ((!CmpLazyFlushPending) && (!CmpHoldLazyFlush)) {
|
|
CmpLazyFlushPending = TRUE;
|
|
ExQueueWorkItem(&CmpLazyWorkItem, DelayedWorkQueue);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
#define LAZY_FLUSH_CAPTURE_SLOTS 5000
|
|
|
|
typedef struct {
|
|
LARGE_INTEGER SystemTime;
|
|
ULONG ElapsedMSec;
|
|
} CM_LAZY_FLUSH_DATA;
|
|
|
|
ULONG CmpLazyFlushCapturedDataCount = 0;
|
|
CM_LAZY_FLUSH_DATA CmpLazyFlushCapturedData[LAZY_FLUSH_CAPTURE_SLOTS] = {0};
|
|
BOOLEAN CmpCaptureLazyFlushData = FALSE;
|
|
*/
|
|
|
|
ULONG CmpLazyFlushCount = 1;
|
|
|
|
VOID
|
|
CmpLazyFlushWorker(
|
|
IN PVOID Parameter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Worker routine called to do a lazy flush. Called by an executive worker
|
|
thread in the system process.
|
|
|
|
Arguments:
|
|
|
|
Parameter - not used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN Result = TRUE;
|
|
BOOLEAN ForceFlush;
|
|
BOOLEAN PostNewWorker = FALSE;
|
|
ULONG DirtyCount = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
UNREFERENCED_PARAMETER (Parameter);
|
|
|
|
if( CmpHoldLazyFlush ) {
|
|
//
|
|
// lazy flush mode is disabled
|
|
//
|
|
return;
|
|
}
|
|
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpLazyFlushWorker: flushing hives\n"));
|
|
|
|
ForceFlush = CmpForceForceFlush;
|
|
if(ForceFlush == TRUE) {
|
|
//
|
|
// something bad happened and we may need to fix hive's use count
|
|
//
|
|
CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpLazyFlushWorker: Force Flush - getting the reglock exclusive\n"));
|
|
CmpLockRegistryExclusive();
|
|
} else {
|
|
CmpLockRegistry();
|
|
ENTER_FLUSH_MODE();
|
|
}
|
|
if (!HvShutdownComplete) {
|
|
//
|
|
// this call will set CmpForceForceFlush to the right value
|
|
//
|
|
//Result = CmpDoFlushAll(ForceFlush);
|
|
/*
|
|
if( CmpCaptureLazyFlushData && (CmpLazyFlushCapturedDataCount < LAZY_FLUSH_CAPTURE_SLOTS) ) {
|
|
KeQuerySystemTime( &(CmpLazyFlushCapturedData[CmpLazyFlushCapturedDataCount].SystemTime) );
|
|
}
|
|
*/
|
|
PostNewWorker = CmpDoFlushNextHive(ForceFlush,&Result,&DirtyCount);
|
|
|
|
/*
|
|
if( CmpCaptureLazyFlushData && (CmpLazyFlushCapturedDataCount < LAZY_FLUSH_CAPTURE_SLOTS) ) {
|
|
LARGE_INTEGER ElapsedTime;
|
|
|
|
KeQuerySystemTime( &ElapsedTime );
|
|
ElapsedTime.QuadPart -= CmpLazyFlushCapturedData[CmpLazyFlushCapturedDataCount].SystemTime.QuadPart;
|
|
|
|
CmpLazyFlushCapturedData[CmpLazyFlushCapturedDataCount].ElapsedMSec = (ULONG)((LONGLONG)(ElapsedTime.QuadPart / 10000));
|
|
CmpLazyFlushCapturedDataCount++;
|
|
}
|
|
*/
|
|
if( !PostNewWorker ) {
|
|
//
|
|
// we have completed a sweep through the entire list of hives
|
|
//
|
|
InterlockedIncrement( (PLONG)&CmpLazyFlushCount );
|
|
}
|
|
} else {
|
|
CmpForceForceFlush = FALSE;
|
|
}
|
|
|
|
if( ForceFlush == FALSE ) {
|
|
EXIT_FLUSH_MODE();
|
|
}
|
|
|
|
CmpLazyFlushPending = FALSE;
|
|
CmpUnlockRegistry();
|
|
|
|
if( CmpCannotWriteConfiguration ) {
|
|
//
|
|
// Disk full; system hive has not been saved at initialization
|
|
//
|
|
if(!Result) {
|
|
//
|
|
// All hives were saved; No need for disk full warning anymore
|
|
//
|
|
CmpCannotWriteConfiguration = FALSE;
|
|
} else {
|
|
//
|
|
// Issue another hard error (if not already displayed) and postpone a lazy flush operation
|
|
//
|
|
CmpDiskFullWarning();
|
|
CmpLazyFlush();
|
|
}
|
|
}
|
|
//
|
|
// if we have not yet flushed the whole list; or there are still hives dirty from the curently ended iteration.
|
|
//
|
|
if( PostNewWorker || (DirtyCount != 0) ) {
|
|
//
|
|
// post a new worker to flush the next hive
|
|
//
|
|
CmpLazyFlush();
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
CmpDiskFullWarningWorker(
|
|
IN PVOID WorkItem
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays hard error popup that indicates the disk is full.
|
|
|
|
Arguments:
|
|
|
|
WorkItem - Supplies pointer to the work item. This routine will
|
|
free the work item.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Response;
|
|
|
|
ExFreePool(WorkItem);
|
|
|
|
Status = ExRaiseHardError(STATUS_DISK_FULL,
|
|
0,
|
|
0,
|
|
NULL,
|
|
OptionOk,
|
|
&Response);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
CmpDiskFullWarning(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Raises a hard error of type STATUS_DISK_FULL if wasn't already raised
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PWORK_QUEUE_ITEM WorkItem;
|
|
|
|
if( (!CmpDiskFullWorkerPopupDisplayed) && (CmpCannotWriteConfiguration) && (ExReadyForErrors) && (CmpProfileLoaded) ) {
|
|
|
|
//
|
|
// Queue work item to display popup
|
|
//
|
|
WorkItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
|
|
if (WorkItem != NULL) {
|
|
|
|
CmpDiskFullWorkerPopupDisplayed = TRUE;
|
|
ExInitializeWorkItem(WorkItem,
|
|
CmpDiskFullWarningWorker,
|
|
WorkItem);
|
|
ExQueueWorkItem(WorkItem, DelayedWorkQueue);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CmpShutdownWorkers(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shuts down the lazy flush worker (by killing the timer)
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
KeCancelTimer(&CmpLazyFlushTimer);
|
|
|
|
#ifdef CMP_STATS
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock(&CmpKcbStatLock, &OldIrql);
|
|
CmpKcbStatShutdown = TRUE;
|
|
KeCancelTimer(&CmpKcbStatTimer);
|
|
KeReleaseSpinLock(&CmpKcbStatLock, OldIrql);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
CmSetLazyFlushState(BOOLEAN Enable)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enables/Disables the lazy flusher; Designed for the standby/resume case, where
|
|
we we don't want the lazy flush to fire off, blocking registry writers until the
|
|
disk wakes up.
|
|
|
|
Arguments:
|
|
|
|
Enable - TRUE = enable; FALSE = disable
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CmpDontGrowLogFile = CmpHoldLazyFlush = !Enable;
|
|
}
|
|
|
|
|
|
#ifdef CMP_STATS
|
|
|
|
VOID
|
|
CmpKcbStat(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine resets the KcbStat timer to go off at a specified interval
|
|
in the future
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER DueTime;
|
|
KIRQL OldIrql;
|
|
|
|
DueTime.QuadPart = Int32x32To64(KCB_STAT_INTERVAL_IN_SECONDS,
|
|
- SECOND_MULT);
|
|
|
|
//
|
|
// Indicate relative time
|
|
//
|
|
|
|
KeAcquireSpinLock(&CmpKcbStatLock, &OldIrql);
|
|
if (! CmpKcbStatShutdown) {
|
|
KeSetTimer(&CmpKcbStatTimer,
|
|
DueTime,
|
|
&CmpKcbStatDpc);
|
|
}
|
|
KeReleaseSpinLock(&CmpKcbStatLock, OldIrql);
|
|
}
|
|
|
|
VOID
|
|
CmpKcbStatDpcRoutine(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dumps the kcb stats in the debugger and then reschedules another
|
|
Dpc in the future
|
|
|
|
Arguments:
|
|
|
|
Dpc - Supplies a pointer to the DPC object.
|
|
|
|
DeferredContext - not used
|
|
|
|
SystemArgument1 - not used
|
|
|
|
SystemArgument2 - not used
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
#ifndef _CM_LDR_
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"\n*********************************************************************\n");
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* Stat No %8lu KcbNo = %8lu [MaxKcbNo = %8lu] *\n",++CmpStatsDebug.CmpStatNo,CmpStatsDebug.CmpKcbNo,CmpStatsDebug.CmpMaxKcbNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "*********************************************************************\n");
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* *\n");
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* [Nt]API [No. Of]Calls *\n");
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "*-------------------------------------------------------------------*\n");
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtCreateKey %8lu Opens = %8lu *\n",CmpStatsDebug.CmpNtCreateKeyNo,CmpNtFakeCreate);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtOpenKey %8lu *\n",CmpStatsDebug.CmpNtOpenKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtEnumerateKey %8lu *\n",CmpStatsDebug.CmpNtEnumerateKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtQueryKey %8lu *\n",CmpStatsDebug.CmpNtQueryKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtDeleteKey %8lu *\n",CmpStatsDebug.CmpNtDeleteKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtSetInformationKey %8lu *\n",CmpStatsDebug.CmpNtSetInformationKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "*-------------------------------------------------------------------*\n");
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtSetValueKey %8lu *\n",CmpStatsDebug.CmpNtSetValueKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtEnumerateValueKey %8lu *\n",CmpStatsDebug.CmpNtEnumerateValueKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtQueryValueKey %8lu *\n",CmpStatsDebug.CmpNtQueryValueKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtQueryMultipleValueKey %8lu *\n",CmpStatsDebug.CmpNtQueryMultipleValueKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtDeleteValueKey %8lu *\n",CmpStatsDebug.CmpNtDeleteValueKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "*-------------------------------------------------------------------*\n");
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtFlushKey %8lu *\n",CmpStatsDebug.CmpNtFlushKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtLoadKey %8lu *\n",CmpStatsDebug.CmpNtLoadKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtUnloadKey %8lu *\n",CmpStatsDebug.CmpNtUnloadKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtSaveKey %8lu *\n",CmpStatsDebug.CmpNtSaveKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtSaveMergedKeys %8lu *\n",CmpStatsDebug.CmpNtSaveMergedKeysNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtRestoreKey %8lu *\n",CmpStatsDebug.CmpNtRestoreKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtReplaceKey %8lu *\n",CmpStatsDebug.CmpNtReplaceKeyNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "*-------------------------------------------------------------------*\n");
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtNotifyChgMultplKeys %8lu *\n",CmpStatsDebug.CmpNtNotifyChangeMultipleKeysNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtQueryOpenSubKeys %8lu *\n",CmpStatsDebug.CmpNtQueryOpenSubKeysNo);
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "*-------------------------------------------------------------------*\n");
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* [No.Of]Calls [Time] *\n");
|
|
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "*-------------------------------------------------------------------*\n");
|
|
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtQueryKey(KeyBasicInformation) %8lu %8lu *\n",
|
|
CmpQueryKeyDataDebug.BasicInformation,
|
|
(ULONG)(CmpQueryKeyDataDebug.BasicInformationTimeCounter?CmpQueryKeyDataDebug.BasicInformationTimeElapsed/CmpQueryKeyDataDebug.BasicInformationTimeCounter:0));
|
|
CmpQueryKeyDataDebug.BasicInformationTimeCounter = 0;
|
|
CmpQueryKeyDataDebug.BasicInformationTimeElapsed = 0;
|
|
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtQueryKey(KeyNodeInformation ) %8lu %8lu *\n",
|
|
CmpQueryKeyDataDebug.NodeInformation,
|
|
(ULONG)(CmpQueryKeyDataDebug.NodeInformationTimeCounter?CmpQueryKeyDataDebug.NodeInformationTimeElapsed/CmpQueryKeyDataDebug.NodeInformationTimeCounter:0));
|
|
CmpQueryKeyDataDebug.NodeInformationTimeCounter = 0;
|
|
CmpQueryKeyDataDebug.NodeInformationTimeElapsed = 0;
|
|
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtQueryKey(KeyFullInformation ) %8lu %8lu *\n",
|
|
CmpQueryKeyDataDebug.FullInformation,
|
|
(ULONG)(CmpQueryKeyDataDebug.FullInformationTimeCounter?CmpQueryKeyDataDebug.FullInformationTimeElapsed/CmpQueryKeyDataDebug.FullInformationTimeCounter:0));
|
|
CmpQueryKeyDataDebug.FullInformationTimeCounter = 0;
|
|
CmpQueryKeyDataDebug.FullInformationTimeElapsed = 0;
|
|
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtEnumerateKey(KeyBasicInformation) %8lu %8lu *\n",
|
|
CmpQueryKeyDataDebug.EnumerateKeyBasicInformation,
|
|
(ULONG)(CmpQueryKeyDataDebug.EnumerateKeyBasicInformationTimeCounter?CmpQueryKeyDataDebug.EnumerateKeyBasicInformationTimeElapsed/CmpQueryKeyDataDebug.EnumerateKeyBasicInformationTimeCounter:0));
|
|
CmpQueryKeyDataDebug.EnumerateKeyBasicInformationTimeCounter = 0;
|
|
CmpQueryKeyDataDebug.EnumerateKeyBasicInformationTimeElapsed = 0;
|
|
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtEnumerateKey(KeyNodeInformation ) %8lu %8lu *\n",
|
|
CmpQueryKeyDataDebug.EnumerateKeyNodeInformation,
|
|
(ULONG)(CmpQueryKeyDataDebug.EnumerateKeyNodeInformationTimeCounter?CmpQueryKeyDataDebug.EnumerateKeyNodeInformationTimeElapsed/CmpQueryKeyDataDebug.EnumerateKeyNodeInformationTimeCounter:0));
|
|
CmpQueryKeyDataDebug.EnumerateKeyNodeInformationTimeCounter = 0;
|
|
CmpQueryKeyDataDebug.EnumerateKeyNodeInformationTimeElapsed = 0;
|
|
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "* NtEnumerateKey(KeyFullInformation ) %8lu %8lu *\n",
|
|
CmpQueryKeyDataDebug.EnumerateKeyFullInformation,
|
|
(ULONG)(CmpQueryKeyDataDebug.EnumerateKeyFullInformationTimeCounter?CmpQueryKeyDataDebug.EnumerateKeyFullInformationTimeElapsed/CmpQueryKeyDataDebug.EnumerateKeyFullInformationTimeCounter:0));
|
|
CmpQueryKeyDataDebug.EnumerateKeyFullInformationTimeCounter = 0;
|
|
CmpQueryKeyDataDebug.EnumerateKeyFullInformationTimeElapsed = 0;
|
|
DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL, "*********************************************************************\n\n");
|
|
|
|
//
|
|
// reschedule
|
|
//
|
|
#endif //_CM_LDR_
|
|
CmpKcbStat();
|
|
}
|
|
#endif
|
|
|
|
|
|
|