2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/cmapi.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 DPRINT1("Flushing: %wZ\n", &CmHive
->FileFullPath
);
82 DPRINT1("Handle: %p\n", CmHive
->FileHandles
[HFILE_TYPE_PRIMARY
]);
83 Status
= HvSyncHive(&CmHive
->Hive
);
84 if(!NT_SUCCESS(Status
))
86 /* Let them know we failed */
91 CmHive
->FlushCount
= CmpLazyFlushCount
;
94 else if ((CmHive
->Hive
.DirtyCount
) &&
95 (!(CmHive
->Hive
.HiveFlags
& HIVE_VOLATILE
)) &&
96 (!(CmHive
->Hive
.HiveFlags
& HIVE_NOLAZYFLUSH
)))
98 /* Use another lazy flusher for this hive */
99 ASSERT(CmHive
->FlushCount
== CmpLazyFlushCount
);
100 *DirtyCount
+= CmHive
->Hive
.DirtyCount
;
101 DPRINT("CmHive %wZ already uptodate.\n", &CmHive
->FileFullPath
);
104 /* Try the next one */
105 NextEntry
= NextEntry
->Flink
;
108 /* Check if we've flushed everything */
109 if (NextEntry
== &CmpHiveListHead
)
111 /* We have, tell the caller we're done */
116 /* We need to be called again */
120 /* Unlock the list and return the result */
121 ExReleasePushLock(&CmpHiveListHeadLock
);
125 _Function_class_(KDEFERRED_ROUTINE
)
128 CmpEnableLazyFlushDpcRoutine(IN PKDPC Dpc
,
129 IN PVOID DeferredContext
,
130 IN PVOID SystemArgument1
,
131 IN PVOID SystemArgument2
)
133 /* Don't stop lazy flushing from happening anymore */
134 CmpHoldLazyFlush
= FALSE
;
137 _Function_class_(KDEFERRED_ROUTINE
)
140 CmpLazyFlushDpcRoutine(IN PKDPC Dpc
,
141 IN PVOID DeferredContext
,
142 IN PVOID SystemArgument1
,
143 IN PVOID SystemArgument2
)
145 /* Check if we should queue the lazy flush worker */
146 DPRINT("Flush pending: %s, Holding lazy flush: %s.\n", CmpLazyFlushPending
? "yes" : "no", CmpHoldLazyFlush
? "yes" : "no");
147 if ((!CmpLazyFlushPending
) && (!CmpHoldLazyFlush
))
149 CmpLazyFlushPending
= TRUE
;
150 ExQueueWorkItem(&CmpLazyWorkItem
, DelayedWorkQueue
);
158 LARGE_INTEGER DueTime
;
161 /* Check if we should set the lazy flush timer */
162 if ((!CmpNoWrite
) && (!CmpHoldLazyFlush
))
165 DueTime
.QuadPart
= Int32x32To64(CmpLazyFlushIntervalInSeconds
,
167 KeSetTimer(&CmpLazyFlushTimer
, DueTime
, &CmpLazyFlushDpc
);
171 _Function_class_(WORKER_THREAD_ROUTINE
)
174 CmpLazyFlushWorker(IN PVOID Parameter
)
176 BOOLEAN ForceFlush
, Result
, MoreWork
= FALSE
;
177 ULONG DirtyCount
= 0;
180 /* Don't do anything if lazy flushing isn't enabled yet */
181 if (CmpHoldLazyFlush
)
183 DPRINT1("Lazy flush held. Bye bye.\n");
184 CmpLazyFlushPending
= FALSE
;
188 /* Check if we are forcing a flush */
189 ForceFlush
= CmpForceForceFlush
;
192 DPRINT("Forcing flush.\n");
193 /* Lock the registry exclusively */
194 CmpLockRegistryExclusive();
198 DPRINT("Not forcing flush.\n");
199 /* Starve writers before locking */
200 InterlockedIncrement(&CmpFlushStarveWriters
);
204 /* Flush the next hive */
205 MoreWork
= CmpDoFlushNextHive(ForceFlush
, &Result
, &DirtyCount
);
209 InterlockedIncrement((PLONG
)&CmpLazyFlushCount
);
212 /* Check if we have starved writers */
214 InterlockedDecrement(&CmpFlushStarveWriters
);
216 /* Not pending anymore, release the registry lock */
217 CmpLazyFlushPending
= FALSE
;
220 DPRINT("Lazy flush done. More work to be done: %s. Entries still dirty: %u.\n",
221 MoreWork
? "Yes" : "No", DirtyCount
);
225 /* Relaunch the flush timer, so the remaining hives get flushed */
232 CmpCmdInit(IN BOOLEAN SetupBoot
)
234 LARGE_INTEGER DueTime
;
237 /* Setup the lazy DPC */
238 KeInitializeDpc(&CmpLazyFlushDpc
, CmpLazyFlushDpcRoutine
, NULL
);
240 /* Setup the lazy timer */
241 KeInitializeTimer(&CmpLazyFlushTimer
);
243 /* Setup the lazy worker */
244 ExInitializeWorkItem(&CmpLazyWorkItem
, CmpLazyFlushWorker
, NULL
);
246 /* Setup the forced-lazy DPC and timer */
247 KeInitializeDpc(&CmpEnableLazyFlushDpc
,
248 CmpEnableLazyFlushDpcRoutine
,
250 KeInitializeTimer(&CmpEnableLazyFlushTimer
);
252 /* Enable lazy flushing after 10 minutes */
253 DueTime
.QuadPart
= Int32x32To64(600, -10 * 1000 * 1000);
254 KeSetTimer(&CmpEnableLazyFlushTimer
, DueTime
, &CmpEnableLazyFlushDpc
);
256 /* Setup flush variables */
257 CmpNoWrite
= CmpMiniNTBoot
;
258 CmpWasSetupBoot
= SetupBoot
;
260 /* Testing: Force Lazy Flushing */
261 CmpHoldLazyFlush
= FALSE
;
263 /* Setup the hive list */
264 CmpInitializeHiveList(SetupBoot
);
269 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes
,
270 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext
,
271 IN OUT PBOOLEAN Allocate
,
272 OUT PCMHIVE
*NewHive
,
275 PUNICODE_STRING FileName
;
279 /* Open the file in the current security context */
280 FileName
= FileAttributes
->ObjectName
;
281 Status
= CmpInitHiveFromFile(FileName
,
286 if (((Status
== STATUS_ACCESS_DENIED
) ||
287 (Status
== STATUS_NO_SUCH_USER
) ||
288 (Status
== STATUS_WRONG_PASSWORD
) ||
289 (Status
== STATUS_ACCOUNT_EXPIRED
) ||
290 (Status
== STATUS_ACCOUNT_DISABLED
) ||
291 (Status
== STATUS_ACCOUNT_RESTRICTION
)) &&
292 (ImpersonationContext
))
294 /* We failed due to an account/security error, impersonate SYSTEM */
295 Status
= SeImpersonateClientEx(ImpersonationContext
, NULL
);
296 if (NT_SUCCESS(Status
))
299 Status
= CmpInitHiveFromFile(FileName
,
305 /* Restore impersonation token */
310 /* Return status of open attempt */
316 CmpShutdownWorkers(VOID
)
318 /* Stop lazy flushing */
320 KeCancelTimer(&CmpLazyFlushTimer
);
325 CmSetLazyFlushState(IN BOOLEAN Enable
)
327 /* Set state for lazy flusher */
328 CmpHoldLazyFlush
= !Enable
;