Windows NT 4.0 source code leak
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

/*++
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();
}