2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/cmlazy.c
5 * PURPOSE: Configuration Manager - Internal Registry APIs
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES *******************************************************************/
15 /* GLOBALS ********************************************************************/
17 KTIMER CmpLazyFlushTimer
;
19 WORK_QUEUE_ITEM CmpLazyWorkItem
;
20 KTIMER CmpEnableLazyFlushTimer
;
21 KDPC CmpEnableLazyFlushDpc
;
22 BOOLEAN CmpLazyFlushPending
;
23 BOOLEAN CmpForceForceFlush
;
24 BOOLEAN CmpHoldLazyFlush
= TRUE
;
25 ULONG CmpLazyFlushIntervalInSeconds
= 5;
26 static ULONG CmpLazyFlushHiveCount
= 7;
27 ULONG CmpLazyFlushCount
= 1;
28 LONG CmpFlushStarveWriters
;
30 /* FUNCTIONS ******************************************************************/
34 CmpDoFlushNextHive(_In_ BOOLEAN ForceFlush
,
36 _Out_ PULONG DirtyCount
)
39 PLIST_ENTRY NextEntry
;
42 ULONG HiveCount
= CmpLazyFlushHiveCount
;
48 /* Don't do anything if we're not supposed to */
49 if (CmpNoWrite
) return TRUE
;
51 /* Make sure we have to flush at least one hive */
52 if (!HiveCount
) HiveCount
= 1;
54 /* Acquire the list lock and loop */
55 ExAcquirePushLockShared(&CmpHiveListHeadLock
);
56 NextEntry
= CmpHiveListHead
.Flink
;
57 while ((NextEntry
!= &CmpHiveListHead
) && HiveCount
)
59 /* Get the hive and check if we should flush it */
60 CmHive
= CONTAINING_RECORD(NextEntry
, CMHIVE
, HiveList
);
61 if (!(CmHive
->Hive
.HiveFlags
& HIVE_NOLAZYFLUSH
) &&
62 (CmHive
->FlushCount
!= CmpLazyFlushCount
))
67 /* One less to flush */
70 /* Ignore clean or volatile hives */
71 if ((!CmHive
->Hive
.DirtyCount
&& !ForceFlush
) ||
72 (CmHive
->Hive
.HiveFlags
& HIVE_VOLATILE
))
74 /* Don't do anything but do update the count */
75 CmHive
->FlushCount
= CmpLazyFlushCount
;
76 DPRINT("Hive %wZ is clean.\n", &CmHive
->FileFullPath
);
81 DPRINT("Flushing: %wZ\n", &CmHive
->FileFullPath
);
82 DPRINT("Handle: %p\n", CmHive
->FileHandles
[HFILE_TYPE_PRIMARY
]);
83 Status
= HvSyncHive(&CmHive
->Hive
);
84 if(!NT_SUCCESS(Status
))
86 /* Let them know we failed */
87 DPRINT1("Failed to flush %wZ on handle %p (status 0x%08lx)\n",
88 &CmHive
->FileFullPath
, CmHive
->FileHandles
[HFILE_TYPE_PRIMARY
], Status
);
93 CmHive
->FlushCount
= CmpLazyFlushCount
;
96 else if ((CmHive
->Hive
.DirtyCount
) &&
97 (!(CmHive
->Hive
.HiveFlags
& HIVE_VOLATILE
)) &&
98 (!(CmHive
->Hive
.HiveFlags
& HIVE_NOLAZYFLUSH
)))
100 /* Use another lazy flusher for this hive */
101 ASSERT(CmHive
->FlushCount
== CmpLazyFlushCount
);
102 *DirtyCount
+= CmHive
->Hive
.DirtyCount
;
103 DPRINT("CmHive %wZ already uptodate.\n", &CmHive
->FileFullPath
);
106 /* Try the next one */
107 NextEntry
= NextEntry
->Flink
;
110 /* Check if we've flushed everything */
111 if (NextEntry
== &CmpHiveListHead
)
113 /* We have, tell the caller we're done */
118 /* We need to be called again */
122 /* Unlock the list and return the result */
123 ExReleasePushLock(&CmpHiveListHeadLock
);
127 _Function_class_(KDEFERRED_ROUTINE
)
130 CmpEnableLazyFlushDpcRoutine(IN PKDPC Dpc
,
131 IN PVOID DeferredContext
,
132 IN PVOID SystemArgument1
,
133 IN PVOID SystemArgument2
)
135 /* Don't stop lazy flushing from happening anymore */
136 CmpHoldLazyFlush
= FALSE
;
139 _Function_class_(KDEFERRED_ROUTINE
)
142 CmpLazyFlushDpcRoutine(IN PKDPC Dpc
,
143 IN PVOID DeferredContext
,
144 IN PVOID SystemArgument1
,
145 IN PVOID SystemArgument2
)
147 /* Check if we should queue the lazy flush worker */
148 DPRINT("Flush pending: %s, Holding lazy flush: %s.\n", CmpLazyFlushPending
? "yes" : "no", CmpHoldLazyFlush
? "yes" : "no");
149 if ((!CmpLazyFlushPending
) && (!CmpHoldLazyFlush
))
151 CmpLazyFlushPending
= TRUE
;
152 ExQueueWorkItem(&CmpLazyWorkItem
, DelayedWorkQueue
);
160 LARGE_INTEGER DueTime
;
163 /* Check if we should set the lazy flush timer */
164 if ((!CmpNoWrite
) && (!CmpHoldLazyFlush
))
167 DueTime
.QuadPart
= Int32x32To64(CmpLazyFlushIntervalInSeconds
,
169 KeSetTimer(&CmpLazyFlushTimer
, DueTime
, &CmpLazyFlushDpc
);
173 _Function_class_(WORKER_THREAD_ROUTINE
)
176 CmpLazyFlushWorker(IN PVOID Parameter
)
178 BOOLEAN ForceFlush
, Result
, MoreWork
= FALSE
;
179 ULONG DirtyCount
= 0;
182 /* Don't do anything if lazy flushing isn't enabled yet */
183 if (CmpHoldLazyFlush
)
185 DPRINT1("Lazy flush held. Bye bye.\n");
186 CmpLazyFlushPending
= FALSE
;
190 /* Check if we are forcing a flush */
191 ForceFlush
= CmpForceForceFlush
;
194 DPRINT("Forcing flush.\n");
195 /* Lock the registry exclusively */
196 CmpLockRegistryExclusive();
200 DPRINT("Not forcing flush.\n");
201 /* Starve writers before locking */
202 InterlockedIncrement(&CmpFlushStarveWriters
);
206 /* Flush the next hive */
207 MoreWork
= CmpDoFlushNextHive(ForceFlush
, &Result
, &DirtyCount
);
211 InterlockedIncrement((PLONG
)&CmpLazyFlushCount
);
214 /* Check if we have starved writers */
216 InterlockedDecrement(&CmpFlushStarveWriters
);
218 /* Not pending anymore, release the registry lock */
219 CmpLazyFlushPending
= FALSE
;
222 DPRINT("Lazy flush done. More work to be done: %s. Entries still dirty: %u.\n",
223 MoreWork
? "Yes" : "No", DirtyCount
);
227 /* Relaunch the flush timer, so the remaining hives get flushed */
234 CmpCmdInit(IN BOOLEAN SetupBoot
)
236 LARGE_INTEGER DueTime
;
239 /* Setup the lazy DPC */
240 KeInitializeDpc(&CmpLazyFlushDpc
, CmpLazyFlushDpcRoutine
, NULL
);
242 /* Setup the lazy timer */
243 KeInitializeTimer(&CmpLazyFlushTimer
);
245 /* Setup the lazy worker */
246 ExInitializeWorkItem(&CmpLazyWorkItem
, CmpLazyFlushWorker
, NULL
);
248 /* Setup the forced-lazy DPC and timer */
249 KeInitializeDpc(&CmpEnableLazyFlushDpc
,
250 CmpEnableLazyFlushDpcRoutine
,
252 KeInitializeTimer(&CmpEnableLazyFlushTimer
);
254 /* Enable lazy flushing after 10 minutes */
255 DueTime
.QuadPart
= Int32x32To64(600, -10 * 1000 * 1000);
256 KeSetTimer(&CmpEnableLazyFlushTimer
, DueTime
, &CmpEnableLazyFlushDpc
);
258 /* Setup flush variables */
259 CmpNoWrite
= CmpMiniNTBoot
;
260 CmpWasSetupBoot
= SetupBoot
;
262 /* Testing: Force Lazy Flushing */
263 CmpHoldLazyFlush
= FALSE
;
265 /* Setup the hive list */
266 CmpInitializeHiveList(SetupBoot
);
271 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes
,
272 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext
,
273 IN OUT PBOOLEAN Allocate
,
274 OUT PCMHIVE
*NewHive
,
278 UNICODE_STRING FileName
;
280 UCHAR Buffer
[sizeof(OBJECT_NAME_INFORMATION
) + MAX_PATH
* sizeof(WCHAR
)];
281 ULONG Length
= sizeof(Buffer
);
282 POBJECT_NAME_INFORMATION FileNameInfo
= (POBJECT_NAME_INFORMATION
)Buffer
;
286 if (FileAttributes
->RootDirectory
)
289 * Validity check: The ObjectName is relative to RootDirectory,
290 * therefore it must not start with a path separator.
292 if (FileAttributes
->ObjectName
&& FileAttributes
->ObjectName
->Buffer
&&
293 FileAttributes
->ObjectName
->Length
>= sizeof(WCHAR
) &&
294 *FileAttributes
->ObjectName
->Buffer
== OBJ_NAME_PATH_SEPARATOR
)
296 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
299 /* Try to get the value */
300 Status
= ZwQueryObject(FileAttributes
->RootDirectory
,
301 ObjectNameInformation
,
305 if (!NT_SUCCESS(Status
))
308 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name query failed, Status = 0x%08lx\n", Status
);
312 /* Null-terminate and add the length of the terminator */
313 Length
-= sizeof(OBJECT_NAME_INFORMATION
);
314 FilePath
= FileNameInfo
->Name
.Buffer
;
315 FilePath
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
316 Length
+= sizeof(UNICODE_NULL
);
318 /* Compute the size of the full path; Length already counts the terminating NULL */
319 Length
= Length
+ sizeof(WCHAR
) + FileAttributes
->ObjectName
->Length
;
320 if (Length
> MAXUSHORT
)
322 /* Name size too long, bail out */
323 return STATUS_OBJECT_PATH_INVALID
;
326 /* Build the full path */
327 RtlInitEmptyUnicodeString(&FileName
, NULL
, 0);
328 FileName
.Buffer
= ExAllocatePoolWithTag(PagedPool
, Length
, TAG_CM
);
329 if (!FileName
.Buffer
)
332 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n");
333 return STATUS_INSUFFICIENT_RESOURCES
;
335 FileName
.MaximumLength
= Length
;
336 RtlCopyUnicodeString(&FileName
, &FileNameInfo
->Name
);
339 * Append a path terminator if needed (we have already accounted
340 * for a possible extra one when allocating the buffer).
342 if (/* FileAttributes->ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR && */ // We excluded ObjectName starting with a path separator above.
343 FileName
.Length
> 0 && FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
) - 1] != OBJ_NAME_PATH_SEPARATOR
)
345 /* ObjectName does not start with '\' and PathBuffer does not end with '\' */
346 FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
)] = OBJ_NAME_PATH_SEPARATOR
;
347 FileName
.Length
+= sizeof(WCHAR
);
348 FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
351 /* Append the object name */
352 Status
= RtlAppendUnicodeStringToString(&FileName
, FileAttributes
->ObjectName
);
353 if (!NT_SUCCESS(Status
))
356 DPRINT1("CmpCmdHiveOpen(): RtlAppendUnicodeStringToString() failed, Status = 0x%08lx\n", Status
);
357 ExFreePoolWithTag(FileName
.Buffer
, TAG_CM
);
363 FileName
= *FileAttributes
->ObjectName
;
366 /* Open the file in the current security context */
367 Status
= CmpInitHiveFromFile(&FileName
,
372 if (((Status
== STATUS_ACCESS_DENIED
) ||
373 (Status
== STATUS_NO_SUCH_USER
) ||
374 (Status
== STATUS_WRONG_PASSWORD
) ||
375 (Status
== STATUS_ACCOUNT_EXPIRED
) ||
376 (Status
== STATUS_ACCOUNT_DISABLED
) ||
377 (Status
== STATUS_ACCOUNT_RESTRICTION
)) &&
378 (ImpersonationContext
))
380 /* We failed due to an account/security error, impersonate SYSTEM */
381 Status
= SeImpersonateClientEx(ImpersonationContext
, NULL
);
382 if (NT_SUCCESS(Status
))
385 Status
= CmpInitHiveFromFile(&FileName
,
391 /* Restore impersonation token */
396 if (FileAttributes
->RootDirectory
)
398 ExFreePoolWithTag(FileName
.Buffer
, TAG_CM
);
401 /* Return status of open attempt */
407 CmpShutdownWorkers(VOID
)
409 /* Stop lazy flushing */
411 KeCancelTimer(&CmpLazyFlushTimer
);
416 CmSetLazyFlushState(IN BOOLEAN Enable
)
418 /* Set state for lazy flusher */
419 CmpHoldLazyFlush
= !Enable
;