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 if this is not a Setup boot */
267 CmpInitializeHiveList();
272 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes
,
273 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext
,
274 IN OUT PBOOLEAN Allocate
,
275 OUT PCMHIVE
*NewHive
,
279 UNICODE_STRING FileName
;
281 UCHAR Buffer
[sizeof(OBJECT_NAME_INFORMATION
) + MAX_PATH
* sizeof(WCHAR
)];
282 ULONG Length
= sizeof(Buffer
);
283 POBJECT_NAME_INFORMATION FileNameInfo
= (POBJECT_NAME_INFORMATION
)Buffer
;
287 if (FileAttributes
->RootDirectory
)
290 * Validity check: The ObjectName is relative to RootDirectory,
291 * therefore it must not start with a path separator.
293 if (FileAttributes
->ObjectName
&& FileAttributes
->ObjectName
->Buffer
&&
294 FileAttributes
->ObjectName
->Length
>= sizeof(WCHAR
) &&
295 *FileAttributes
->ObjectName
->Buffer
== OBJ_NAME_PATH_SEPARATOR
)
297 return STATUS_OBJECT_PATH_SYNTAX_BAD
;
300 /* Try to get the value */
301 Status
= ZwQueryObject(FileAttributes
->RootDirectory
,
302 ObjectNameInformation
,
306 if (!NT_SUCCESS(Status
))
309 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name query failed, Status = 0x%08lx\n", Status
);
313 /* Null-terminate and add the length of the terminator */
314 Length
-= sizeof(OBJECT_NAME_INFORMATION
);
315 FilePath
= FileNameInfo
->Name
.Buffer
;
316 FilePath
[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
317 Length
+= sizeof(UNICODE_NULL
);
319 /* Compute the size of the full path; Length already counts the terminating NULL */
320 Length
= Length
+ sizeof(WCHAR
) + FileAttributes
->ObjectName
->Length
;
321 if (Length
> MAXUSHORT
)
323 /* Name size too long, bail out */
324 return STATUS_OBJECT_PATH_INVALID
;
327 /* Build the full path */
328 RtlInitEmptyUnicodeString(&FileName
, NULL
, 0);
329 FileName
.Buffer
= ExAllocatePoolWithTag(PagedPool
, Length
, TAG_CM
);
330 if (!FileName
.Buffer
)
333 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n");
334 return STATUS_INSUFFICIENT_RESOURCES
;
336 FileName
.MaximumLength
= Length
;
337 RtlCopyUnicodeString(&FileName
, &FileNameInfo
->Name
);
340 * Append a path terminator if needed (we have already accounted
341 * for a possible extra one when allocating the buffer).
343 if (/* FileAttributes->ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR && */ // We excluded ObjectName starting with a path separator above.
344 FileName
.Length
> 0 && FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
) - 1] != OBJ_NAME_PATH_SEPARATOR
)
346 /* ObjectName does not start with '\' and PathBuffer does not end with '\' */
347 FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
)] = OBJ_NAME_PATH_SEPARATOR
;
348 FileName
.Length
+= sizeof(WCHAR
);
349 FileName
.Buffer
[FileName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
352 /* Append the object name */
353 Status
= RtlAppendUnicodeStringToString(&FileName
, FileAttributes
->ObjectName
);
354 if (!NT_SUCCESS(Status
))
357 DPRINT1("CmpCmdHiveOpen(): RtlAppendUnicodeStringToString() failed, Status = 0x%08lx\n", Status
);
358 ExFreePoolWithTag(FileName
.Buffer
, TAG_CM
);
364 FileName
= *FileAttributes
->ObjectName
;
367 /* Open the file in the current security context */
368 Status
= CmpInitHiveFromFile(&FileName
,
373 if (((Status
== STATUS_ACCESS_DENIED
) ||
374 (Status
== STATUS_NO_SUCH_USER
) ||
375 (Status
== STATUS_WRONG_PASSWORD
) ||
376 (Status
== STATUS_ACCOUNT_EXPIRED
) ||
377 (Status
== STATUS_ACCOUNT_DISABLED
) ||
378 (Status
== STATUS_ACCOUNT_RESTRICTION
)) &&
379 (ImpersonationContext
))
381 /* We failed due to an account/security error, impersonate SYSTEM */
382 Status
= SeImpersonateClientEx(ImpersonationContext
, NULL
);
383 if (NT_SUCCESS(Status
))
386 Status
= CmpInitHiveFromFile(&FileName
,
392 /* Restore impersonation token */
397 if (FileAttributes
->RootDirectory
)
399 ExFreePoolWithTag(FileName
.Buffer
, TAG_CM
);
402 /* Return status of open attempt */
408 CmpShutdownWorkers(VOID
)
410 /* Stop lazy flushing */
412 KeCancelTimer(&CmpLazyFlushTimer
);
417 CmSetLazyFlushState(IN BOOLEAN Enable
)
419 /* Set state for lazy flusher */
420 CmpHoldLazyFlush
= !Enable
;