mirror of https://github.com/lianthony/NT4.0
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.
527 lines
13 KiB
527 lines
13 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.
|
|
//
|
|
#define LAZY_FLUSH_INTERVAL_IN_SECONDS 5
|
|
|
|
//
|
|
// 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;
|
|
ULONG CmpAttachCount=0;
|
|
|
|
extern BOOLEAN CmpNoWrite;
|
|
extern BOOLEAN CmpWasSetupBoot;
|
|
extern BOOLEAN HvShutdownComplete;
|
|
|
|
#if DBG
|
|
PKTHREAD CmpCallerThread = NULL;
|
|
#endif
|
|
|
|
//
|
|
// Local function prototypes
|
|
//
|
|
VOID
|
|
CmpWorker(
|
|
IN PREGISTRY_COMMAND CommandArea
|
|
);
|
|
|
|
VOID
|
|
CmpLazyFlushWorker(
|
|
IN PVOID Parameter
|
|
);
|
|
|
|
VOID
|
|
CmpLazyFlushDpcRoutine(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,CmpWorkerCommand)
|
|
#pragma alloc_text(PAGE,CmpWorker)
|
|
#pragma alloc_text(PAGE,CmpLazyFlush)
|
|
#pragma alloc_text(PAGE,CmpLazyFlushWorker)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
CmpWorkerCommand(
|
|
IN OUT PREGISTRY_COMMAND Command
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine just encapsulates all the necessary synchronization for
|
|
sending a command to the worker thread.
|
|
|
|
Arguments:
|
|
|
|
Command - Supplies a pointer to an initialized REGISTRY_COMMAND structure
|
|
which will be copied into the global communication structure.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS = Command.Status
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN OldHardErrorMode;
|
|
|
|
PAGED_CODE();
|
|
|
|
CmpLockRegistryExclusive();
|
|
|
|
if (++CmpAttachCount == 1) {
|
|
//
|
|
// Disable hard error popups so that when the filesystems corrupt
|
|
// our files, they don't try to queue a hard error popup APC to
|
|
// our thread while we are attached.
|
|
//
|
|
OldHardErrorMode = PsGetCurrentThread()->HardErrorsAreDisabled;
|
|
PsGetCurrentThread()->HardErrorsAreDisabled = TRUE;
|
|
KeAttachProcess(CmpSystemProcess);
|
|
}
|
|
|
|
CmpWorker(Command);
|
|
|
|
Status = Command->Status;
|
|
|
|
if (--CmpAttachCount == 0) {
|
|
KeDetachProcess();
|
|
PsGetCurrentThread()->HardErrorsAreDisabled = OldHardErrorMode;
|
|
}
|
|
|
|
CmpUnlockRegistry();
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
VOID
|
|
CmpWorker(
|
|
IN PREGISTRY_COMMAND CommandArea
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Actually execute the command specified in CommandArea, and
|
|
report its completion.
|
|
|
|
Arguments:
|
|
|
|
Parameter - supplies a pointer to the REGISTRY_COMMAND structure to
|
|
be executed.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PCMHIVE CmHive;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PUNICODE_STRING FileName;
|
|
ULONG i;
|
|
HANDLE Handle;
|
|
PFILE_RENAME_INFORMATION RenameInfo;
|
|
PLIST_ENTRY p;
|
|
BOOLEAN result;
|
|
HANDLE NullHandle;
|
|
|
|
PAGED_CODE();
|
|
|
|
switch (CommandArea->Command) {
|
|
|
|
case REG_CMD_INIT:
|
|
//
|
|
// Initialize lazy flush timer and DPC
|
|
//
|
|
KeInitializeDpc(&CmpLazyFlushDpc,
|
|
CmpLazyFlushDpcRoutine,
|
|
NULL);
|
|
|
|
KeInitializeTimer(&CmpLazyFlushTimer);
|
|
|
|
ExInitializeWorkItem(&CmpLazyWorkItem, CmpLazyFlushWorker, NULL);
|
|
|
|
CmpNoWrite = FALSE;
|
|
|
|
CmpWasSetupBoot = CommandArea->SetupBoot;
|
|
if (CommandArea->SetupBoot == FALSE) {
|
|
CmpInitializeHiveList();
|
|
}
|
|
|
|
//
|
|
// flush dirty data to disk
|
|
//
|
|
CmpDoFlushAll();
|
|
break;
|
|
|
|
case REG_CMD_FLUSH_KEY:
|
|
CommandArea->Status =
|
|
CmFlushKey(CommandArea->Hive, CommandArea->Cell);
|
|
break;
|
|
|
|
case REG_CMD_REFRESH_HIVE:
|
|
//
|
|
// Refresh hive to match last flushed version
|
|
//
|
|
HvRefreshHive(CommandArea->Hive);
|
|
break;
|
|
|
|
case REG_CMD_FILE_SET_SIZE:
|
|
CommandArea->Status = CmpDoFileSetSize(
|
|
CommandArea->Hive,
|
|
CommandArea->FileType,
|
|
CommandArea->FileSize
|
|
);
|
|
break;
|
|
|
|
case REG_CMD_HIVE_OPEN:
|
|
|
|
//
|
|
// Open the file.
|
|
//
|
|
FileName = CommandArea->FileAttributes->ObjectName;
|
|
|
|
CommandArea->Status = CmpInitHiveFromFile(FileName,
|
|
0,
|
|
&CommandArea->CmHive,
|
|
&CommandArea->Allocate);
|
|
if ((CommandArea->Status == STATUS_ACCESS_DENIED) &&
|
|
(CommandArea->ImpersonationContext != NULL)) {
|
|
//
|
|
// Impersonate the caller and try it again. This
|
|
// lets us open hives on a remote machine.
|
|
//
|
|
SeImpersonateClient(CommandArea->ImpersonationContext,NULL);
|
|
|
|
CommandArea->Status = CmpInitHiveFromFile(FileName,
|
|
0,
|
|
&CommandArea->CmHive,
|
|
&CommandArea->Allocate);
|
|
NullHandle = NULL;
|
|
Status = ZwSetInformationThread(NtCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
&NullHandle,
|
|
sizeof(NullHandle));
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case REG_CMD_HIVE_CLOSE:
|
|
|
|
//
|
|
// Close the files associated with this hive.
|
|
//
|
|
CmHive = CommandArea->CmHive;
|
|
|
|
for (i=0; i<HFILE_TYPE_MAX; i++) {
|
|
if (CmHive->FileHandles[i] != NULL) {
|
|
NtClose(CmHive->FileHandles[i]);
|
|
CmHive->FileHandles[i] = NULL;
|
|
}
|
|
}
|
|
CommandArea->Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case REG_CMD_HIVE_READ:
|
|
|
|
//
|
|
// Used by special case of savekey, just do a read
|
|
//
|
|
result = CmpFileRead(
|
|
(PHHIVE)CommandArea->CmHive,
|
|
CommandArea->FileType,
|
|
CommandArea->Offset,
|
|
CommandArea->Buffer,
|
|
CommandArea->FileSize // read length
|
|
);
|
|
if (result) {
|
|
CommandArea->Status = STATUS_SUCCESS;
|
|
} else {
|
|
CommandArea->Status = STATUS_REGISTRY_IO_FAILED;
|
|
}
|
|
break;
|
|
|
|
case REG_CMD_SHUTDOWN:
|
|
|
|
//
|
|
// shut down the registry
|
|
//
|
|
CmpDoFlushAll();
|
|
|
|
//
|
|
// close all the hive files
|
|
//
|
|
p=CmpHiveListHead.Flink;
|
|
while (p!=&CmpHiveListHead) {
|
|
CmHive = CONTAINING_RECORD(p, CMHIVE, HiveList);
|
|
for (i=0; i<HFILE_TYPE_MAX; i++) {
|
|
if (CmHive->FileHandles[i] != NULL) {
|
|
NtClose(CmHive->FileHandles[i]);
|
|
CmHive->FileHandles[i] = NULL;
|
|
}
|
|
}
|
|
p=p->Flink;
|
|
}
|
|
|
|
break;
|
|
|
|
case REG_CMD_RENAME_HIVE:
|
|
//
|
|
// Rename a CmHive's primary handle
|
|
//
|
|
Handle = CommandArea->CmHive->FileHandles[HFILE_TYPE_PRIMARY];
|
|
if (CommandArea->OldName != NULL) {
|
|
Status = ZwQueryObject(Handle,
|
|
ObjectNameInformation,
|
|
CommandArea->OldName,
|
|
CommandArea->NameInfoLength,
|
|
&CommandArea->NameInfoLength);
|
|
if (!NT_SUCCESS(Status)) {
|
|
CommandArea->Status = Status;
|
|
break;
|
|
}
|
|
}
|
|
|
|
RenameInfo = ExAllocatePool(PagedPool,
|
|
sizeof(FILE_RENAME_INFORMATION) +
|
|
CommandArea->NewName->Length);
|
|
if (RenameInfo == NULL) {
|
|
CommandArea->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
RenameInfo->ReplaceIfExists = FALSE;
|
|
RenameInfo->RootDirectory = NULL;
|
|
RenameInfo->FileNameLength = CommandArea->NewName->Length;
|
|
RtlMoveMemory(RenameInfo->FileName,
|
|
CommandArea->NewName->Buffer,
|
|
CommandArea->NewName->Length);
|
|
|
|
Status = ZwSetInformationFile(Handle,
|
|
&IoStatusBlock,
|
|
(PVOID)RenameInfo,
|
|
sizeof(FILE_RENAME_INFORMATION) +
|
|
CommandArea->NewName->Length,
|
|
FileRenameInformation);
|
|
ExFreePool(RenameInfo);
|
|
CommandArea->Status = Status;
|
|
break;
|
|
|
|
case REG_CMD_ADD_HIVE_LIST:
|
|
//
|
|
// Add a hive to the hive file list
|
|
//
|
|
Status = CmpAddToHiveFileList(CommandArea->CmHive);
|
|
CommandArea->Status = Status;
|
|
break;
|
|
|
|
case REG_CMD_REMOVE_HIVE_LIST:
|
|
//
|
|
// Remove a hive from the hive file list
|
|
//
|
|
CmpRemoveFromHiveFileList(CommandArea->CmHive);
|
|
CommandArea->Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
KeBugCheckEx(REGISTRY_ERROR,6,1,0,0);
|
|
|
|
} // switch
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
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();
|
|
CMLOG(CML_FLOW, CMS_IO) {
|
|
KdPrint(("CmpLazyFlush: setting lazy flush timer\n"));
|
|
}
|
|
if ((!CmpNoWrite) &&
|
|
(!CmpLazyFlushPending)) {
|
|
|
|
CmpLazyFlushPending = TRUE;
|
|
|
|
DueTime.QuadPart = Int32x32To64(LAZY_FLUSH_INTERVAL_IN_SECONDS,
|
|
- 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.
|
|
|
|
--*/
|
|
|
|
{
|
|
CMLOG(CML_FLOW, CMS_IO) {
|
|
KdPrint(("CmpLazyFlushDpc: queuing lazy flush work item\n"));
|
|
}
|
|
|
|
ExQueueWorkItem(&CmpLazyWorkItem, DelayedWorkQueue);
|
|
|
|
}
|
|
|
|
|
|
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. (note that it is IMPORTANT this routine
|
|
gets called in the system process, since it does file I/O)
|
|
|
|
Since this runs as a worker thread, it is pretty important not to wait
|
|
forever for the registry lock. If we wait forever, it is easier to
|
|
deadlock the registry, since some NT APIs get the lock, queue a work
|
|
item, and wait for it to complete. If we are hogging the worker thread
|
|
waiting for the registry lock, that work item may never get a chance
|
|
to run. So if our wait on the registry lock times out, we just
|
|
give up and set the lazy flush timer again. Better luck next time.
|
|
|
|
Arguments:
|
|
|
|
Parameter - not used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CMLOG(CML_FLOW, CMS_IO) {
|
|
KdPrint(("CmpLazyFlushWorker: flushing hives\n"));
|
|
}
|
|
|
|
CmpLockRegistryExclusive();
|
|
CmpLazyFlushPending = FALSE;
|
|
if (!HvShutdownComplete) {
|
|
CmpDoFlushAll();
|
|
}
|
|
CmpUnlockRegistry();
|
|
}
|