mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 21:13:52 +00:00
333 lines
9.5 KiB
C
333 lines
9.5 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 */
|
|
CmpInitializeHiveList(SetupBoot);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes,
|
|
IN PSECURITY_CLIENT_CONTEXT ImpersonationContext,
|
|
IN OUT PBOOLEAN Allocate,
|
|
OUT PCMHIVE *NewHive,
|
|
IN ULONG CheckFlags)
|
|
{
|
|
PUNICODE_STRING FileName;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
/* Open the file in the current security context */
|
|
FileName = FileAttributes->ObjectName;
|
|
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();
|
|
}
|
|
}
|
|
|
|
/* 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 */
|