2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * PURPOSE: Configuration Manager - Internal Registry APIs
5 * PROGRAMMERS: Alex Ionescu <alex.ionescu@reactos.org>
8 /* INCLUDES *******************************************************************/
14 /* GLOBALS ********************************************************************/
16 KTIMER CmpLazyFlushTimer
;
18 WORK_QUEUE_ITEM CmpLazyWorkItem
;
19 KTIMER CmpEnableLazyFlushTimer
;
20 KDPC CmpEnableLazyFlushDpc
;
21 BOOLEAN CmpLazyFlushPending
;
22 BOOLEAN CmpForceForceFlush
;
23 BOOLEAN CmpHoldLazyFlush
= TRUE
;
24 ULONG CmpLazyFlushIntervalInSeconds
= 5;
25 static ULONG CmpLazyFlushHiveCount
= 7;
26 ULONG CmpLazyFlushCount
= 1;
27 LONG CmpFlushStarveWriters
;
29 /* FUNCTIONS ******************************************************************/
33 CmpDoFlushNextHive(_In_ BOOLEAN ForceFlush
,
35 _Out_ PULONG DirtyCount
)
38 PLIST_ENTRY NextEntry
;
41 ULONG HiveCount
= CmpLazyFlushHiveCount
;
47 /* Don't do anything if we're not supposed to */
48 if (CmpNoWrite
) return TRUE
;
50 /* Make sure we have to flush at least one hive */
51 if (!HiveCount
) HiveCount
= 1;
53 /* Acquire the list lock and loop */
54 ExAcquirePushLockShared(&CmpHiveListHeadLock
);
55 NextEntry
= CmpHiveListHead
.Flink
;
56 while ((NextEntry
!= &CmpHiveListHead
) && HiveCount
)
58 /* Get the hive and check if we should flush it */
59 CmHive
= CONTAINING_RECORD(NextEntry
, CMHIVE
, HiveList
);
60 if (!(CmHive
->Hive
.HiveFlags
& HIVE_NOLAZYFLUSH
) &&
61 (CmHive
->FlushCount
!= CmpLazyFlushCount
))
66 /* One less to flush */
69 /* Ignore clean or volatile hives */
70 if ((!CmHive
->Hive
.DirtyCount
&& !ForceFlush
) ||
71 (CmHive
->Hive
.HiveFlags
& HIVE_VOLATILE
))
73 /* Don't do anything but do update the count */
74 CmHive
->FlushCount
= CmpLazyFlushCount
;
75 DPRINT("Hive %wZ is clean.\n", &CmHive
->FileFullPath
);
80 DPRINT("Flushing: %wZ\n", &CmHive
->FileFullPath
);
81 DPRINT("Handle: %p\n", CmHive
->FileHandles
[HFILE_TYPE_PRIMARY
]);
82 Status
= HvSyncHive(&CmHive
->Hive
);
83 if(!NT_SUCCESS(Status
))
85 /* Let them know we failed */
86 DPRINT1("Failed to flush %wZ on handle %p (status 0x%08lx)\n",
87 &CmHive
->FileFullPath
, CmHive
->FileHandles
[HFILE_TYPE_PRIMARY
], Status
);
92 CmHive
->FlushCount
= CmpLazyFlushCount
;
95 else if (CmHive
->Hive
.DirtyCount
&&
96 !(CmHive
->Hive
.HiveFlags
& HIVE_VOLATILE
) &&
97 !(CmHive
->Hive
.HiveFlags
& HIVE_NOLAZYFLUSH
))
99 /* Use another lazy flusher for this hive */
100 ASSERT(CmHive
->FlushCount
== CmpLazyFlushCount
);
101 *DirtyCount
+= CmHive
->Hive
.DirtyCount
;
102 DPRINT("CmHive %wZ already uptodate.\n", &CmHive
->FileFullPath
);
105 /* Try the next one */
106 NextEntry
= NextEntry
->Flink
;
109 /* Check if we've flushed everything */
110 if (NextEntry
== &CmpHiveListHead
)
112 /* We have, tell the caller we're done */
117 /* We need to be called again */
121 /* Unlock the list and return the result */
122 ExReleasePushLock(&CmpHiveListHeadLock
);
126 _Function_class_(KDEFERRED_ROUTINE
)
129 CmpEnableLazyFlushDpcRoutine(IN PKDPC Dpc
,
130 IN PVOID DeferredContext
,
131 IN PVOID SystemArgument1
,
132 IN PVOID SystemArgument2
)
134 /* Don't stop lazy flushing from happening anymore */
135 CmpHoldLazyFlush
= FALSE
;
138 _Function_class_(KDEFERRED_ROUTINE
)
141 CmpLazyFlushDpcRoutine(IN PKDPC Dpc
,
142 IN PVOID DeferredContext
,
143 IN PVOID SystemArgument1
,
144 IN PVOID SystemArgument2
)
146 /* Check if we should queue the lazy flush worker */
147 DPRINT("Flush pending: %s, Holding lazy flush: %s.\n", CmpLazyFlushPending
? "yes" : "no", CmpHoldLazyFlush
? "yes" : "no");
148 if (!CmpLazyFlushPending
&& !CmpHoldLazyFlush
)
150 CmpLazyFlushPending
= TRUE
;
151 ExQueueWorkItem(&CmpLazyWorkItem
, DelayedWorkQueue
);
159 LARGE_INTEGER DueTime
;
162 /* Check if we should set the lazy flush timer */
163 if (!CmpNoWrite
&& !CmpHoldLazyFlush
)
166 DueTime
.QuadPart
= Int32x32To64(CmpLazyFlushIntervalInSeconds
,
168 KeSetTimer(&CmpLazyFlushTimer
, DueTime
, &CmpLazyFlushDpc
);
172 _Function_class_(WORKER_THREAD_ROUTINE
)
175 CmpLazyFlushWorker(IN PVOID Parameter
)
177 BOOLEAN ForceFlush
, Result
, MoreWork
= FALSE
;
178 ULONG DirtyCount
= 0;
181 /* Don't do anything if lazy flushing isn't enabled yet */
182 if (CmpHoldLazyFlush
)
184 DPRINT1("Lazy flush held. Bye bye.\n");
185 CmpLazyFlushPending
= FALSE
;
189 /* Check if we are forcing a flush */
190 ForceFlush
= CmpForceForceFlush
;
193 DPRINT("Forcing flush.\n");
194 /* Lock the registry exclusively */
195 CmpLockRegistryExclusive();
199 DPRINT("Not forcing flush.\n");
200 /* Starve writers before locking */
201 InterlockedIncrement(&CmpFlushStarveWriters
);
205 /* Flush the next hive */
206 MoreWork
= CmpDoFlushNextHive(ForceFlush
, &Result
, &DirtyCount
);
210 InterlockedIncrement((PLONG
)&CmpLazyFlushCount
);
213 /* Check if we have starved writers */
215 InterlockedDecrement(&CmpFlushStarveWriters
);
217 /* Not pending anymore, release the registry lock */
218 CmpLazyFlushPending
= FALSE
;
221 DPRINT("Lazy flush done. More work to be done: %s. Entries still dirty: %u.\n",
222 MoreWork
? "Yes" : "No", DirtyCount
);
226 /* Relaunch the flush timer, so the remaining hives get flushed */
233 CmpCmdInit(IN BOOLEAN SetupBoot
)
235 LARGE_INTEGER DueTime
;
238 /* Setup the lazy DPC */
239 KeInitializeDpc(&CmpLazyFlushDpc
, CmpLazyFlushDpcRoutine
, NULL
);
241 /* Setup the lazy timer */
242 KeInitializeTimer(&CmpLazyFlushTimer
);
244 /* Setup the lazy worker */
245 ExInitializeWorkItem(&CmpLazyWorkItem
, CmpLazyFlushWorker
, NULL
);
247 /* Setup the forced-lazy DPC and timer */
248 KeInitializeDpc(&CmpEnableLazyFlushDpc
,
249 CmpEnableLazyFlushDpcRoutine
,
251 KeInitializeTimer(&CmpEnableLazyFlushTimer
);
253 /* Enable lazy flushing after 10 minutes */
254 DueTime
.QuadPart
= Int32x32To64(600, -10 * 1000 * 1000);
255 KeSetTimer(&CmpEnableLazyFlushTimer
, DueTime
, &CmpEnableLazyFlushDpc
);
257 /* Setup flush variables */
258 CmpNoWrite
= CmpMiniNTBoot
;
259 CmpWasSetupBoot
= SetupBoot
;
261 /* Testing: Force Lazy Flushing */
262 CmpHoldLazyFlush
= FALSE
;
264 /* Setup the system hives list if this is not a Setup boot */
266 CmpInitializeHiveList();
268 /* Now that the system hives are loaded, if we are in PE mode,
269 * all other hives will be loaded with full access */
271 CmpShareSystemHives
= FALSE
;
276 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes
,
277 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext
,
278 IN OUT PBOOLEAN Allocate
,
279 OUT PCMHIVE
*NewHive
,
283 UNICODE_STRING FileName
;
286 OBJECT_NAME_INFORMATION DummyNameInfo
;
287 POBJECT_NAME_INFORMATION FileNameInfo
;
291 if (FileAttributes
->RootDirectory
)
294 * Validity check: The ObjectName is relative to RootDirectory,
295 * therefore it must not start with a path separator.
297 if (FileAttributes
->ObjectName
&& FileAttributes
->ObjectName
->Buffer
&&
298 FileAttributes
->ObjectName
->Length
>= sizeof(WCHAR
) &&
299 *FileAttributes
->ObjectName
->Buffer
== OBJ_NAME_PATH_SEPARATOR
)
301 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
304 /* Determine the right buffer size and allocate */
305 Status
= ZwQueryObject(FileAttributes
->RootDirectory
,
306 ObjectNameInformation
,
308 sizeof(DummyNameInfo
),
310 if (Status
!= STATUS_BUFFER_OVERFLOW
)
312 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name size query failed, Status = 0x%08lx\n", Status
);
316 FileNameInfo
= ExAllocatePoolWithTag(PagedPool
,
317 Length
+ sizeof(UNICODE_NULL
),
319 if (FileNameInfo
== NULL
)
321 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n");
322 return STATUS_INSUFFICIENT_RESOURCES
;
325 /* Try to get the value */
326 Status
= ZwQueryObject(FileAttributes
->RootDirectory
,
327 ObjectNameInformation
,
331 if (!NT_SUCCESS(Status
))
334 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name query failed, Status = 0x%08lx\n", Status
);
335 ExFreePoolWithTag(FileNameInfo
, TAG_CM
);
339 /* Null-terminate and add the length of the terminator */
340 Length
-= sizeof(OBJECT_NAME_INFORMATION
);
341 FilePath
= FileNameInfo
->Name
.Buffer
;
342 FilePath
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
343 Length
+= sizeof(UNICODE_NULL
);
345 /* Compute the size of the full path; Length already counts the terminating NULL */
346 Length
= Length
+ sizeof(WCHAR
) + FileAttributes
->ObjectName
->Length
;
347 if (Length
> MAXUSHORT
)
349 /* Name size too long, bail out */
350 ExFreePoolWithTag(FileNameInfo
, TAG_CM
);
351 return STATUS_OBJECT_PATH_INVALID
;
354 /* Build the full path */
355 RtlInitEmptyUnicodeString(&FileName
, NULL
, 0);
356 FileName
.Buffer
= ExAllocatePoolWithTag(PagedPool
, Length
, TAG_CM
);
357 if (!FileName
.Buffer
)
360 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n");
361 ExFreePoolWithTag(FileNameInfo
, TAG_CM
);
362 return STATUS_INSUFFICIENT_RESOURCES
;
364 FileName
.MaximumLength
= Length
;
365 RtlCopyUnicodeString(&FileName
, &FileNameInfo
->Name
);
366 ExFreePoolWithTag(FileNameInfo
, TAG_CM
);
369 * Append a path terminator if needed (we have already accounted
370 * for a possible extra one when allocating the buffer).
372 if (/* FileAttributes->ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR && */ // We excluded ObjectName starting with a path separator above.
373 FileName
.Length
> 0 && FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
) - 1] != OBJ_NAME_PATH_SEPARATOR
)
375 /* ObjectName does not start with '\' and PathBuffer does not end with '\' */
376 FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
)] = OBJ_NAME_PATH_SEPARATOR
;
377 FileName
.Length
+= sizeof(WCHAR
);
378 FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
381 /* Append the object name */
382 Status
= RtlAppendUnicodeStringToString(&FileName
, FileAttributes
->ObjectName
);
383 if (!NT_SUCCESS(Status
))
386 DPRINT1("CmpCmdHiveOpen(): RtlAppendUnicodeStringToString() failed, Status = 0x%08lx\n", Status
);
387 ExFreePoolWithTag(FileName
.Buffer
, TAG_CM
);
393 FileName
= *FileAttributes
->ObjectName
;
396 /* Open the file in the current security context */
397 Status
= CmpInitHiveFromFile(&FileName
,
402 if (((Status
== STATUS_ACCESS_DENIED
) ||
403 (Status
== STATUS_NO_SUCH_USER
) ||
404 (Status
== STATUS_WRONG_PASSWORD
) ||
405 (Status
== STATUS_ACCOUNT_EXPIRED
) ||
406 (Status
== STATUS_ACCOUNT_DISABLED
) ||
407 (Status
== STATUS_ACCOUNT_RESTRICTION
)) &&
408 ImpersonationContext
)
410 /* We failed due to an account/security error, impersonate SYSTEM */
411 Status
= SeImpersonateClientEx(ImpersonationContext
, NULL
);
412 if (NT_SUCCESS(Status
))
415 Status
= CmpInitHiveFromFile(&FileName
,
421 /* Restore impersonation token */
426 if (FileAttributes
->RootDirectory
)
428 ExFreePoolWithTag(FileName
.Buffer
, TAG_CM
);
431 /* Return status of open attempt */
437 CmpShutdownWorkers(VOID
)
439 /* Stop lazy flushing */
441 KeCancelTimer(&CmpLazyFlushTimer
);
446 CmSetLazyFlushState(IN BOOLEAN Enable
)
448 /* Set state for lazy flusher */
449 CmpHoldLazyFlush
= !Enable
;