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
,
277 PUNICODE_STRING FileName
;
281 /* Open the file in the current security context */
282 FileName
= FileAttributes
->ObjectName
;
283 Status
= CmpInitHiveFromFile(FileName
,
288 if (((Status
== STATUS_ACCESS_DENIED
) ||
289 (Status
== STATUS_NO_SUCH_USER
) ||
290 (Status
== STATUS_WRONG_PASSWORD
) ||
291 (Status
== STATUS_ACCOUNT_EXPIRED
) ||
292 (Status
== STATUS_ACCOUNT_DISABLED
) ||
293 (Status
== STATUS_ACCOUNT_RESTRICTION
)) &&
294 (ImpersonationContext
))
296 /* We failed due to an account/security error, impersonate SYSTEM */
297 Status
= SeImpersonateClientEx(ImpersonationContext
, NULL
);
298 if (NT_SUCCESS(Status
))
301 Status
= CmpInitHiveFromFile(FileName
,
307 /* Restore impersonation token */
312 /* Return status of open attempt */
318 CmpShutdownWorkers(VOID
)
320 /* Stop lazy flushing */
322 KeCancelTimer(&CmpLazyFlushTimer
);
327 CmSetLazyFlushState(IN BOOLEAN Enable
)
329 /* Set state for lazy flusher */
330 CmpHoldLazyFlush
= !Enable
;