[NTOS] Remove some hacks that are not needed anymore, since a real registry hive...
[reactos.git] / ntoskrnl / config / cmlazy.c
1 /*
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)
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "ntoskrnl.h"
12 #define NDEBUG
13 #include "debug.h"
14
15 /* GLOBALS ********************************************************************/
16
17 KTIMER CmpLazyFlushTimer;
18 KDPC CmpLazyFlushDpc;
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;
29
30 /* FUNCTIONS ******************************************************************/
31
32 BOOLEAN
33 NTAPI
34 CmpDoFlushNextHive(_In_ BOOLEAN ForceFlush,
35 _Out_ PBOOLEAN Error,
36 _Out_ PULONG DirtyCount)
37 {
38 NTSTATUS Status;
39 PLIST_ENTRY NextEntry;
40 PCMHIVE CmHive;
41 BOOLEAN Result;
42 ULONG HiveCount = CmpLazyFlushHiveCount;
43
44 /* Set Defaults */
45 *Error = FALSE;
46 *DirtyCount = 0;
47
48 /* Don't do anything if we're not supposed to */
49 if (CmpNoWrite) return TRUE;
50
51 /* Make sure we have to flush at least one hive */
52 if (!HiveCount) HiveCount = 1;
53
54 /* Acquire the list lock and loop */
55 ExAcquirePushLockShared(&CmpHiveListHeadLock);
56 NextEntry = CmpHiveListHead.Flink;
57 while ((NextEntry != &CmpHiveListHead) && HiveCount)
58 {
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))
63 {
64 /* Great sucess! */
65 Result = TRUE;
66
67 /* One less to flush */
68 HiveCount--;
69
70 /* Ignore clean or volatile hives */
71 if ((!CmHive->Hive.DirtyCount && !ForceFlush) ||
72 (CmHive->Hive.HiveFlags & HIVE_VOLATILE))
73 {
74 /* Don't do anything but do update the count */
75 CmHive->FlushCount = CmpLazyFlushCount;
76 DPRINT("Hive %wZ is clean.\n", &CmHive->FileFullPath);
77 }
78 else
79 {
80 /* Do the sync */
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))
85 {
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);
89 *Error = TRUE;
90 Result = FALSE;
91 break;
92 }
93 CmHive->FlushCount = CmpLazyFlushCount;
94 }
95 }
96 else if ((CmHive->Hive.DirtyCount) &&
97 (!(CmHive->Hive.HiveFlags & HIVE_VOLATILE)) &&
98 (!(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH)))
99 {
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);
104 }
105
106 /* Try the next one */
107 NextEntry = NextEntry->Flink;
108 }
109
110 /* Check if we've flushed everything */
111 if (NextEntry == &CmpHiveListHead)
112 {
113 /* We have, tell the caller we're done */
114 Result = FALSE;
115 }
116 else
117 {
118 /* We need to be called again */
119 Result = TRUE;
120 }
121
122 /* Unlock the list and return the result */
123 ExReleasePushLock(&CmpHiveListHeadLock);
124 return Result;
125 }
126
127 _Function_class_(KDEFERRED_ROUTINE)
128 VOID
129 NTAPI
130 CmpEnableLazyFlushDpcRoutine(IN PKDPC Dpc,
131 IN PVOID DeferredContext,
132 IN PVOID SystemArgument1,
133 IN PVOID SystemArgument2)
134 {
135 /* Don't stop lazy flushing from happening anymore */
136 CmpHoldLazyFlush = FALSE;
137 }
138
139 _Function_class_(KDEFERRED_ROUTINE)
140 VOID
141 NTAPI
142 CmpLazyFlushDpcRoutine(IN PKDPC Dpc,
143 IN PVOID DeferredContext,
144 IN PVOID SystemArgument1,
145 IN PVOID SystemArgument2)
146 {
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))
150 {
151 CmpLazyFlushPending = TRUE;
152 ExQueueWorkItem(&CmpLazyWorkItem, DelayedWorkQueue);
153 }
154 }
155
156 VOID
157 NTAPI
158 CmpLazyFlush(VOID)
159 {
160 LARGE_INTEGER DueTime;
161 PAGED_CODE();
162
163 /* Check if we should set the lazy flush timer */
164 if ((!CmpNoWrite) && (!CmpHoldLazyFlush))
165 {
166 /* Do it */
167 DueTime.QuadPart = Int32x32To64(CmpLazyFlushIntervalInSeconds,
168 -10 * 1000 * 1000);
169 KeSetTimer(&CmpLazyFlushTimer, DueTime, &CmpLazyFlushDpc);
170 }
171 }
172
173 _Function_class_(WORKER_THREAD_ROUTINE)
174 VOID
175 NTAPI
176 CmpLazyFlushWorker(IN PVOID Parameter)
177 {
178 BOOLEAN ForceFlush, Result, MoreWork = FALSE;
179 ULONG DirtyCount = 0;
180 PAGED_CODE();
181
182 /* Don't do anything if lazy flushing isn't enabled yet */
183 if (CmpHoldLazyFlush)
184 {
185 DPRINT1("Lazy flush held. Bye bye.\n");
186 CmpLazyFlushPending = FALSE;
187 return;
188 }
189
190 /* Check if we are forcing a flush */
191 ForceFlush = CmpForceForceFlush;
192 if (ForceFlush)
193 {
194 DPRINT("Forcing flush.\n");
195 /* Lock the registry exclusively */
196 CmpLockRegistryExclusive();
197 }
198 else
199 {
200 DPRINT("Not forcing flush.\n");
201 /* Starve writers before locking */
202 InterlockedIncrement(&CmpFlushStarveWriters);
203 CmpLockRegistry();
204 }
205
206 /* Flush the next hive */
207 MoreWork = CmpDoFlushNextHive(ForceFlush, &Result, &DirtyCount);
208 if (!MoreWork)
209 {
210 /* We're done */
211 InterlockedIncrement((PLONG)&CmpLazyFlushCount);
212 }
213
214 /* Check if we have starved writers */
215 if (!ForceFlush)
216 InterlockedDecrement(&CmpFlushStarveWriters);
217
218 /* Not pending anymore, release the registry lock */
219 CmpLazyFlushPending = FALSE;
220 CmpUnlockRegistry();
221
222 DPRINT("Lazy flush done. More work to be done: %s. Entries still dirty: %u.\n",
223 MoreWork ? "Yes" : "No", DirtyCount);
224
225 if (MoreWork)
226 {
227 /* Relaunch the flush timer, so the remaining hives get flushed */
228 CmpLazyFlush();
229 }
230 }
231
232 VOID
233 NTAPI
234 CmpCmdInit(IN BOOLEAN SetupBoot)
235 {
236 LARGE_INTEGER DueTime;
237 PAGED_CODE();
238
239 /* Setup the lazy DPC */
240 KeInitializeDpc(&CmpLazyFlushDpc, CmpLazyFlushDpcRoutine, NULL);
241
242 /* Setup the lazy timer */
243 KeInitializeTimer(&CmpLazyFlushTimer);
244
245 /* Setup the lazy worker */
246 ExInitializeWorkItem(&CmpLazyWorkItem, CmpLazyFlushWorker, NULL);
247
248 /* Setup the forced-lazy DPC and timer */
249 KeInitializeDpc(&CmpEnableLazyFlushDpc,
250 CmpEnableLazyFlushDpcRoutine,
251 NULL);
252 KeInitializeTimer(&CmpEnableLazyFlushTimer);
253
254 /* Enable lazy flushing after 10 minutes */
255 DueTime.QuadPart = Int32x32To64(600, -10 * 1000 * 1000);
256 KeSetTimer(&CmpEnableLazyFlushTimer, DueTime, &CmpEnableLazyFlushDpc);
257
258 /* Setup flush variables */
259 CmpNoWrite = CmpMiniNTBoot;
260 CmpWasSetupBoot = SetupBoot;
261
262 /* Testing: Force Lazy Flushing */
263 CmpHoldLazyFlush = FALSE;
264
265 /* Setup the hive list */
266 CmpInitializeHiveList(SetupBoot);
267 }
268
269 NTSTATUS
270 NTAPI
271 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes,
272 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext,
273 IN OUT PBOOLEAN Allocate,
274 OUT PCMHIVE *NewHive,
275 IN ULONG CheckFlags)
276 {
277 NTSTATUS Status;
278 UNICODE_STRING FileName;
279 PWCHAR FilePath;
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;
283
284 PAGED_CODE();
285
286 if (FileAttributes->RootDirectory)
287 {
288 /*
289 * Validity check: The ObjectName is relative to RootDirectory,
290 * therefore it must not start with a path separator.
291 */
292 if (FileAttributes->ObjectName && FileAttributes->ObjectName->Buffer &&
293 FileAttributes->ObjectName->Length >= sizeof(WCHAR) &&
294 *FileAttributes->ObjectName->Buffer == OBJ_NAME_PATH_SEPARATOR)
295 {
296 return STATUS_OBJECT_PATH_SYNTAX_BAD;
297 }
298
299 /* Try to get the value */
300 Status = ZwQueryObject(FileAttributes->RootDirectory,
301 ObjectNameInformation,
302 FileNameInfo,
303 Length,
304 &Length);
305 if (!NT_SUCCESS(Status))
306 {
307 /* Fail */
308 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name query failed, Status = 0x%08lx\n", Status);
309 return Status;
310 }
311
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);
317
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)
321 {
322 /* Name size too long, bail out */
323 return STATUS_OBJECT_PATH_INVALID;
324 }
325
326 /* Build the full path */
327 RtlInitEmptyUnicodeString(&FileName, NULL, 0);
328 FileName.Buffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM);
329 if (!FileName.Buffer)
330 {
331 /* Fail */
332 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n");
333 return STATUS_INSUFFICIENT_RESOURCES;
334 }
335 FileName.MaximumLength = Length;
336 RtlCopyUnicodeString(&FileName, &FileNameInfo->Name);
337
338 /*
339 * Append a path terminator if needed (we have already accounted
340 * for a possible extra one when allocating the buffer).
341 */
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)
344 {
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;
349 }
350
351 /* Append the object name */
352 Status = RtlAppendUnicodeStringToString(&FileName, FileAttributes->ObjectName);
353 if (!NT_SUCCESS(Status))
354 {
355 /* Fail */
356 DPRINT1("CmpCmdHiveOpen(): RtlAppendUnicodeStringToString() failed, Status = 0x%08lx\n", Status);
357 ExFreePoolWithTag(FileName.Buffer, TAG_CM);
358 return Status;
359 }
360 }
361 else
362 {
363 FileName = *FileAttributes->ObjectName;
364 }
365
366 /* Open the file in the current security context */
367 Status = CmpInitHiveFromFile(&FileName,
368 0,
369 NewHive,
370 Allocate,
371 CheckFlags);
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))
379 {
380 /* We failed due to an account/security error, impersonate SYSTEM */
381 Status = SeImpersonateClientEx(ImpersonationContext, NULL);
382 if (NT_SUCCESS(Status))
383 {
384 /* Now try again */
385 Status = CmpInitHiveFromFile(&FileName,
386 0,
387 NewHive,
388 Allocate,
389 CheckFlags);
390
391 /* Restore impersonation token */
392 PsRevertToSelf();
393 }
394 }
395
396 if (FileAttributes->RootDirectory)
397 {
398 ExFreePoolWithTag(FileName.Buffer, TAG_CM);
399 }
400
401 /* Return status of open attempt */
402 return Status;
403 }
404
405 VOID
406 NTAPI
407 CmpShutdownWorkers(VOID)
408 {
409 /* Stop lazy flushing */
410 PAGED_CODE();
411 KeCancelTimer(&CmpLazyFlushTimer);
412 }
413
414 VOID
415 NTAPI
416 CmSetLazyFlushState(IN BOOLEAN Enable)
417 {
418 /* Set state for lazy flusher */
419 CmpHoldLazyFlush = !Enable;
420 }
421
422 /* EOF */