Windows2000/private/ntos/config/cmworker.c
2020-09-30 17:12:32 +02:00

391 lines
13 KiB
C

/*++
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
--*/
#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;
BOOLEAN CmpLazyFlushPending = 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;
#if DBG
PKTHREAD CmpCallerThread = NULL;
#endif
// Local function prototypes
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);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,CmpWorker)
#pragma alloc_text(PAGE,CmpLazyFlush)
#pragma alloc_text(PAGE,CmpLazyFlushWorker)
#pragma alloc_text(PAGE,CmpDiskFullWarningWorker)
#pragma alloc_text(PAGE,CmpDiskFullWarning)
#endif
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,
&CommandArea->RegistryLockAquired);
// 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 (((CommandArea->Status == STATUS_ACCESS_DENIED) ||
(CommandArea->Status == STATUS_NO_SUCH_USER) ||
(CommandArea->Status == STATUS_WRONG_PASSWORD) ||
(CommandArea->Status == STATUS_ACCOUNT_EXPIRED) ||
(CommandArea->Status == STATUS_ACCOUNT_DISABLED) ||
(CommandArea->Status == STATUS_ACCOUNT_RESTRICTION)) &&
(CommandArea->ImpersonationContext != NULL)) {
// Impersonate the caller and try it again. This
// lets us open hives on a remote machine.
Status = SeImpersonateClientEx(CommandArea->ImpersonationContext, NULL);
if ( NT_SUCCESS( Status ) ) {
CommandArea->Status = CmpInitHiveFromFile(FileName,
0,
&CommandArea->CmHive,
&CommandArea->Allocate,
&CommandArea->RegistryLockAquired);
NullHandle = NULL;
PsRevertToSelf();
}
}
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) {
ZwClose(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:
CmpDoFlushAll();// shut down the registry
// 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) {
ZwClose(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) {
ASSERT_PASSIVE_LEVEL();
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
}
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).
--*/
{
LARGE_INTEGER DueTime;
PAGED_CODE();
CMLOG(CML_FLOW, CMS_IO) {
KdPrint(("CmpLazyFlush: setting lazy flush timer\n"));
}
if (!CmpNoWrite) {
DueTime.QuadPart = Int32x32To64(LAZY_FLUSH_INTERVAL_IN_SECONDS, - SECOND_MULT);
KeSetTimer(&CmpLazyFlushTimer, DueTime, &CmpLazyFlushDpc);// Indicate relative time
}
}
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
--*/
{
CMLOG(CML_FLOW, CMS_IO) {
KdPrint(("CmpLazyFlushDpc: queuing lazy flush work item\n"));
}
if (!CmpLazyFlushPending) {
CmpLazyFlushPending = TRUE;
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.
Arguments:
Parameter - not used.
--*/
{
BOOLEAN Result = TRUE;
PAGED_CODE();
CMLOG(CML_FLOW, CMS_IO) {
KdPrint(("CmpLazyFlushWorker: flushing hives\n"));
}
CmpLockRegistry();
if (!HvShutdownComplete) {
Result = CmpDoFlushAll();
}
CmpLazyFlushPending = FALSE;
CmpUnlockRegistry();
if( CmpCannotWriteConfiguration ) {
// Disk full; system hive haven't been save 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();
}
}
}
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.
--*/
{
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
--*/
{
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);
}
}
}