mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
f86360fdbc
Actually fixes ntdll_apitest:NtLoadUnloadKey.
448 lines
14 KiB
C
448 lines
14 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/config/cmlazy.c
|
|
* PURPOSE: Configuration Manager - Internal Registry APIs
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include "ntoskrnl.h"
|
|
#define NDEBUG
|
|
#include "debug.h"
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
KTIMER CmpLazyFlushTimer;
|
|
KDPC CmpLazyFlushDpc;
|
|
WORK_QUEUE_ITEM CmpLazyWorkItem;
|
|
KTIMER CmpEnableLazyFlushTimer;
|
|
KDPC CmpEnableLazyFlushDpc;
|
|
BOOLEAN CmpLazyFlushPending;
|
|
BOOLEAN CmpForceForceFlush;
|
|
BOOLEAN CmpHoldLazyFlush = TRUE;
|
|
ULONG CmpLazyFlushIntervalInSeconds = 5;
|
|
static ULONG CmpLazyFlushHiveCount = 7;
|
|
ULONG CmpLazyFlushCount = 1;
|
|
LONG CmpFlushStarveWriters;
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
CmpDoFlushNextHive(_In_ BOOLEAN ForceFlush,
|
|
_Out_ PBOOLEAN Error,
|
|
_Out_ PULONG DirtyCount)
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY NextEntry;
|
|
PCMHIVE CmHive;
|
|
BOOLEAN Result;
|
|
ULONG HiveCount = CmpLazyFlushHiveCount;
|
|
|
|
/* Set Defaults */
|
|
*Error = FALSE;
|
|
*DirtyCount = 0;
|
|
|
|
/* Don't do anything if we're not supposed to */
|
|
if (CmpNoWrite) return TRUE;
|
|
|
|
/* Make sure we have to flush at least one hive */
|
|
if (!HiveCount) HiveCount = 1;
|
|
|
|
/* Acquire the list lock and loop */
|
|
ExAcquirePushLockShared(&CmpHiveListHeadLock);
|
|
NextEntry = CmpHiveListHead.Flink;
|
|
while ((NextEntry != &CmpHiveListHead) && HiveCount)
|
|
{
|
|
/* Get the hive and check if we should flush it */
|
|
CmHive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList);
|
|
if (!(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH) &&
|
|
(CmHive->FlushCount != CmpLazyFlushCount))
|
|
{
|
|
/* Great sucess! */
|
|
Result = TRUE;
|
|
|
|
/* One less to flush */
|
|
HiveCount--;
|
|
|
|
/* Ignore clean or volatile hives */
|
|
if ((!CmHive->Hive.DirtyCount && !ForceFlush) ||
|
|
(CmHive->Hive.HiveFlags & HIVE_VOLATILE))
|
|
{
|
|
/* Don't do anything but do update the count */
|
|
CmHive->FlushCount = CmpLazyFlushCount;
|
|
DPRINT("Hive %wZ is clean.\n", &CmHive->FileFullPath);
|
|
}
|
|
else
|
|
{
|
|
/* Do the sync */
|
|
DPRINT("Flushing: %wZ\n", &CmHive->FileFullPath);
|
|
DPRINT("Handle: %p\n", CmHive->FileHandles[HFILE_TYPE_PRIMARY]);
|
|
Status = HvSyncHive(&CmHive->Hive);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
/* Let them know we failed */
|
|
DPRINT1("Failed to flush %wZ on handle %p (status 0x%08lx)\n",
|
|
&CmHive->FileFullPath, CmHive->FileHandles[HFILE_TYPE_PRIMARY], Status);
|
|
*Error = TRUE;
|
|
Result = FALSE;
|
|
break;
|
|
}
|
|
CmHive->FlushCount = CmpLazyFlushCount;
|
|
}
|
|
}
|
|
else if ((CmHive->Hive.DirtyCount) &&
|
|
(!(CmHive->Hive.HiveFlags & HIVE_VOLATILE)) &&
|
|
(!(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH)))
|
|
{
|
|
/* Use another lazy flusher for this hive */
|
|
ASSERT(CmHive->FlushCount == CmpLazyFlushCount);
|
|
*DirtyCount += CmHive->Hive.DirtyCount;
|
|
DPRINT("CmHive %wZ already uptodate.\n", &CmHive->FileFullPath);
|
|
}
|
|
|
|
/* Try the next one */
|
|
NextEntry = NextEntry->Flink;
|
|
}
|
|
|
|
/* Check if we've flushed everything */
|
|
if (NextEntry == &CmpHiveListHead)
|
|
{
|
|
/* We have, tell the caller we're done */
|
|
Result = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* We need to be called again */
|
|
Result = TRUE;
|
|
}
|
|
|
|
/* Unlock the list and return the result */
|
|
ExReleasePushLock(&CmpHiveListHeadLock);
|
|
return Result;
|
|
}
|
|
|
|
_Function_class_(KDEFERRED_ROUTINE)
|
|
VOID
|
|
NTAPI
|
|
CmpEnableLazyFlushDpcRoutine(IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2)
|
|
{
|
|
/* Don't stop lazy flushing from happening anymore */
|
|
CmpHoldLazyFlush = FALSE;
|
|
}
|
|
|
|
_Function_class_(KDEFERRED_ROUTINE)
|
|
VOID
|
|
NTAPI
|
|
CmpLazyFlushDpcRoutine(IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2)
|
|
{
|
|
/* Check if we should queue the lazy flush worker */
|
|
DPRINT("Flush pending: %s, Holding lazy flush: %s.\n", CmpLazyFlushPending ? "yes" : "no", CmpHoldLazyFlush ? "yes" : "no");
|
|
if ((!CmpLazyFlushPending) && (!CmpHoldLazyFlush))
|
|
{
|
|
CmpLazyFlushPending = TRUE;
|
|
ExQueueWorkItem(&CmpLazyWorkItem, DelayedWorkQueue);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
CmpLazyFlush(VOID)
|
|
{
|
|
LARGE_INTEGER DueTime;
|
|
PAGED_CODE();
|
|
|
|
/* Check if we should set the lazy flush timer */
|
|
if ((!CmpNoWrite) && (!CmpHoldLazyFlush))
|
|
{
|
|
/* Do it */
|
|
DueTime.QuadPart = Int32x32To64(CmpLazyFlushIntervalInSeconds,
|
|
-10 * 1000 * 1000);
|
|
KeSetTimer(&CmpLazyFlushTimer, DueTime, &CmpLazyFlushDpc);
|
|
}
|
|
}
|
|
|
|
_Function_class_(WORKER_THREAD_ROUTINE)
|
|
VOID
|
|
NTAPI
|
|
CmpLazyFlushWorker(IN PVOID Parameter)
|
|
{
|
|
BOOLEAN ForceFlush, Result, MoreWork = FALSE;
|
|
ULONG DirtyCount = 0;
|
|
PAGED_CODE();
|
|
|
|
/* Don't do anything if lazy flushing isn't enabled yet */
|
|
if (CmpHoldLazyFlush)
|
|
{
|
|
DPRINT1("Lazy flush held. Bye bye.\n");
|
|
CmpLazyFlushPending = FALSE;
|
|
return;
|
|
}
|
|
|
|
/* Check if we are forcing a flush */
|
|
ForceFlush = CmpForceForceFlush;
|
|
if (ForceFlush)
|
|
{
|
|
DPRINT("Forcing flush.\n");
|
|
/* Lock the registry exclusively */
|
|
CmpLockRegistryExclusive();
|
|
}
|
|
else
|
|
{
|
|
DPRINT("Not forcing flush.\n");
|
|
/* Starve writers before locking */
|
|
InterlockedIncrement(&CmpFlushStarveWriters);
|
|
CmpLockRegistry();
|
|
}
|
|
|
|
/* Flush the next hive */
|
|
MoreWork = CmpDoFlushNextHive(ForceFlush, &Result, &DirtyCount);
|
|
if (!MoreWork)
|
|
{
|
|
/* We're done */
|
|
InterlockedIncrement((PLONG)&CmpLazyFlushCount);
|
|
}
|
|
|
|
/* Check if we have starved writers */
|
|
if (!ForceFlush)
|
|
InterlockedDecrement(&CmpFlushStarveWriters);
|
|
|
|
/* Not pending anymore, release the registry lock */
|
|
CmpLazyFlushPending = FALSE;
|
|
CmpUnlockRegistry();
|
|
|
|
DPRINT("Lazy flush done. More work to be done: %s. Entries still dirty: %u.\n",
|
|
MoreWork ? "Yes" : "No", DirtyCount);
|
|
|
|
if (MoreWork)
|
|
{
|
|
/* Relaunch the flush timer, so the remaining hives get flushed */
|
|
CmpLazyFlush();
|
|
}
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
CmpCmdInit(IN BOOLEAN SetupBoot)
|
|
{
|
|
LARGE_INTEGER DueTime;
|
|
PAGED_CODE();
|
|
|
|
/* Setup the lazy DPC */
|
|
KeInitializeDpc(&CmpLazyFlushDpc, CmpLazyFlushDpcRoutine, NULL);
|
|
|
|
/* Setup the lazy timer */
|
|
KeInitializeTimer(&CmpLazyFlushTimer);
|
|
|
|
/* Setup the lazy worker */
|
|
ExInitializeWorkItem(&CmpLazyWorkItem, CmpLazyFlushWorker, NULL);
|
|
|
|
/* Setup the forced-lazy DPC and timer */
|
|
KeInitializeDpc(&CmpEnableLazyFlushDpc,
|
|
CmpEnableLazyFlushDpcRoutine,
|
|
NULL);
|
|
KeInitializeTimer(&CmpEnableLazyFlushTimer);
|
|
|
|
/* Enable lazy flushing after 10 minutes */
|
|
DueTime.QuadPart = Int32x32To64(600, -10 * 1000 * 1000);
|
|
KeSetTimer(&CmpEnableLazyFlushTimer, DueTime, &CmpEnableLazyFlushDpc);
|
|
|
|
/* Setup flush variables */
|
|
CmpNoWrite = CmpMiniNTBoot;
|
|
CmpWasSetupBoot = SetupBoot;
|
|
|
|
/* Testing: Force Lazy Flushing */
|
|
CmpHoldLazyFlush = FALSE;
|
|
|
|
/* Setup the hive list if this is not a Setup boot */
|
|
if (!SetupBoot)
|
|
CmpInitializeHiveList();
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes,
|
|
IN PSECURITY_CLIENT_CONTEXT ImpersonationContext,
|
|
IN OUT PBOOLEAN Allocate,
|
|
OUT PCMHIVE *NewHive,
|
|
IN ULONG CheckFlags)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING FileName;
|
|
PWCHAR FilePath;
|
|
ULONG Length;
|
|
OBJECT_NAME_INFORMATION DummyNameInfo;
|
|
POBJECT_NAME_INFORMATION FileNameInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (FileAttributes->RootDirectory)
|
|
{
|
|
/*
|
|
* Validity check: The ObjectName is relative to RootDirectory,
|
|
* therefore it must not start with a path separator.
|
|
*/
|
|
if (FileAttributes->ObjectName && FileAttributes->ObjectName->Buffer &&
|
|
FileAttributes->ObjectName->Length >= sizeof(WCHAR) &&
|
|
*FileAttributes->ObjectName->Buffer == OBJ_NAME_PATH_SEPARATOR)
|
|
{
|
|
return STATUS_OBJECT_PATH_SYNTAX_BAD;
|
|
}
|
|
|
|
/* Determine the right buffer size and allocate */
|
|
Status = ZwQueryObject(FileAttributes->RootDirectory,
|
|
ObjectNameInformation,
|
|
&DummyNameInfo,
|
|
sizeof(DummyNameInfo),
|
|
&Length);
|
|
if (Status != STATUS_BUFFER_OVERFLOW)
|
|
{
|
|
DPRINT1("CmpCmdHiveOpen(): Root directory handle object name size query failed, Status = 0x%08lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
FileNameInfo = ExAllocatePoolWithTag(PagedPool,
|
|
Length + sizeof(UNICODE_NULL),
|
|
TAG_CM);
|
|
if (FileNameInfo == NULL)
|
|
{
|
|
DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* Try to get the value */
|
|
Status = ZwQueryObject(FileAttributes->RootDirectory,
|
|
ObjectNameInformation,
|
|
FileNameInfo,
|
|
Length,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
DPRINT1("CmpCmdHiveOpen(): Root directory handle object name query failed, Status = 0x%08lx\n", Status);
|
|
ExFreePoolWithTag(FileNameInfo, TAG_CM);
|
|
return Status;
|
|
}
|
|
|
|
/* Null-terminate and add the length of the terminator */
|
|
Length -= sizeof(OBJECT_NAME_INFORMATION);
|
|
FilePath = FileNameInfo->Name.Buffer;
|
|
FilePath[Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
Length += sizeof(UNICODE_NULL);
|
|
|
|
/* Compute the size of the full path; Length already counts the terminating NULL */
|
|
Length = Length + sizeof(WCHAR) + FileAttributes->ObjectName->Length;
|
|
if (Length > MAXUSHORT)
|
|
{
|
|
/* Name size too long, bail out */
|
|
ExFreePoolWithTag(FileNameInfo, TAG_CM);
|
|
return STATUS_OBJECT_PATH_INVALID;
|
|
}
|
|
|
|
/* Build the full path */
|
|
RtlInitEmptyUnicodeString(&FileName, NULL, 0);
|
|
FileName.Buffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);
|
|
if (!FileName.Buffer)
|
|
{
|
|
/* Fail */
|
|
DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n");
|
|
ExFreePoolWithTag(FileNameInfo, TAG_CM);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
FileName.MaximumLength = Length;
|
|
RtlCopyUnicodeString(&FileName, &FileNameInfo->Name);
|
|
ExFreePoolWithTag(FileNameInfo, TAG_CM);
|
|
|
|
/*
|
|
* Append a path terminator if needed (we have already accounted
|
|
* for a possible extra one when allocating the buffer).
|
|
*/
|
|
if (/* FileAttributes->ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR && */ // We excluded ObjectName starting with a path separator above.
|
|
FileName.Length > 0 && FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] != OBJ_NAME_PATH_SEPARATOR)
|
|
{
|
|
/* ObjectName does not start with '\' and PathBuffer does not end with '\' */
|
|
FileName.Buffer[FileName.Length / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
|
|
FileName.Length += sizeof(WCHAR);
|
|
FileName.Buffer[FileName.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
}
|
|
|
|
/* Append the object name */
|
|
Status = RtlAppendUnicodeStringToString(&FileName, FileAttributes->ObjectName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
DPRINT1("CmpCmdHiveOpen(): RtlAppendUnicodeStringToString() failed, Status = 0x%08lx\n", Status);
|
|
ExFreePoolWithTag(FileName.Buffer, TAG_CM);
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FileName = *FileAttributes->ObjectName;
|
|
}
|
|
|
|
/* Open the file in the current security context */
|
|
Status = CmpInitHiveFromFile(&FileName,
|
|
0,
|
|
NewHive,
|
|
Allocate,
|
|
CheckFlags);
|
|
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))
|
|
{
|
|
/* We failed due to an account/security error, impersonate SYSTEM */
|
|
Status = SeImpersonateClientEx(ImpersonationContext, NULL);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Now try again */
|
|
Status = CmpInitHiveFromFile(&FileName,
|
|
0,
|
|
NewHive,
|
|
Allocate,
|
|
CheckFlags);
|
|
|
|
/* Restore impersonation token */
|
|
PsRevertToSelf();
|
|
}
|
|
}
|
|
|
|
if (FileAttributes->RootDirectory)
|
|
{
|
|
ExFreePoolWithTag(FileName.Buffer, TAG_CM);
|
|
}
|
|
|
|
/* Return status of open attempt */
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
CmpShutdownWorkers(VOID)
|
|
{
|
|
/* Stop lazy flushing */
|
|
PAGED_CODE();
|
|
KeCancelTimer(&CmpLazyFlushTimer);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
CmSetLazyFlushState(IN BOOLEAN Enable)
|
|
{
|
|
/* Set state for lazy flusher */
|
|
CmpHoldLazyFlush = !Enable;
|
|
}
|
|
|
|
/* EOF */
|