Sync with trunk r64509.
[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/cmapi.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 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))
85 {
86 /* Let them know we failed */
87 *Error = TRUE;
88 Result = FALSE;
89 break;
90 }
91 CmHive->FlushCount = CmpLazyFlushCount;
92 }
93 }
94 else if ((CmHive->Hive.DirtyCount) &&
95 (!(CmHive->Hive.HiveFlags & HIVE_VOLATILE)) &&
96 (!(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH)))
97 {
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);
102 }
103
104 /* Try the next one */
105 NextEntry = NextEntry->Flink;
106 }
107
108 /* Check if we've flushed everything */
109 if (NextEntry == &CmpHiveListHead)
110 {
111 /* We have, tell the caller we're done */
112 Result = FALSE;
113 }
114 else
115 {
116 /* We need to be called again */
117 Result = TRUE;
118 }
119
120 /* Unlock the list and return the result */
121 ExReleasePushLock(&CmpHiveListHeadLock);
122 return Result;
123 }
124
125 _Function_class_(KDEFERRED_ROUTINE)
126 VOID
127 NTAPI
128 CmpEnableLazyFlushDpcRoutine(IN PKDPC Dpc,
129 IN PVOID DeferredContext,
130 IN PVOID SystemArgument1,
131 IN PVOID SystemArgument2)
132 {
133 /* Don't stop lazy flushing from happening anymore */
134 CmpHoldLazyFlush = FALSE;
135 }
136
137 _Function_class_(KDEFERRED_ROUTINE)
138 VOID
139 NTAPI
140 CmpLazyFlushDpcRoutine(IN PKDPC Dpc,
141 IN PVOID DeferredContext,
142 IN PVOID SystemArgument1,
143 IN PVOID SystemArgument2)
144 {
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))
148 {
149 CmpLazyFlushPending = TRUE;
150 ExQueueWorkItem(&CmpLazyWorkItem, DelayedWorkQueue);
151 }
152 }
153
154 VOID
155 NTAPI
156 CmpLazyFlush(VOID)
157 {
158 LARGE_INTEGER DueTime;
159 PAGED_CODE();
160
161 /* Check if we should set the lazy flush timer */
162 if ((!CmpNoWrite) && (!CmpHoldLazyFlush))
163 {
164 /* Do it */
165 DueTime.QuadPart = Int32x32To64(CmpLazyFlushIntervalInSeconds,
166 -10 * 1000 * 1000);
167 KeSetTimer(&CmpLazyFlushTimer, DueTime, &CmpLazyFlushDpc);
168 }
169 }
170
171 _Function_class_(WORKER_THREAD_ROUTINE)
172 VOID
173 NTAPI
174 CmpLazyFlushWorker(IN PVOID Parameter)
175 {
176 BOOLEAN ForceFlush, Result, MoreWork = FALSE;
177 ULONG DirtyCount = 0;
178 PAGED_CODE();
179
180 /* Don't do anything if lazy flushing isn't enabled yet */
181 if (CmpHoldLazyFlush)
182 {
183 DPRINT1("Lazy flush held. Bye bye.\n");
184 CmpLazyFlushPending = FALSE;
185 return;
186 }
187
188 /* Check if we are forcing a flush */
189 ForceFlush = CmpForceForceFlush;
190 if (ForceFlush)
191 {
192 DPRINT("Forcing flush.\n");
193 /* Lock the registry exclusively */
194 CmpLockRegistryExclusive();
195 }
196 else
197 {
198 DPRINT("Not forcing flush.\n");
199 /* Starve writers before locking */
200 InterlockedIncrement(&CmpFlushStarveWriters);
201 CmpLockRegistry();
202 }
203
204 /* Flush the next hive */
205 MoreWork = CmpDoFlushNextHive(ForceFlush, &Result, &DirtyCount);
206 if (!MoreWork)
207 {
208 /* We're done */
209 InterlockedIncrement((PLONG)&CmpLazyFlushCount);
210 }
211
212 /* Check if we have starved writers */
213 if (!ForceFlush)
214 InterlockedDecrement(&CmpFlushStarveWriters);
215
216 /* Not pending anymore, release the registry lock */
217 CmpLazyFlushPending = FALSE;
218 CmpUnlockRegistry();
219
220 DPRINT("Lazy flush done. More work to be done: %s. Entries still dirty: %u.\n",
221 MoreWork ? "Yes" : "No", DirtyCount);
222
223 if (MoreWork)
224 {
225 /* Relaunch the flush timer, so the remaining hives get flushed */
226 CmpLazyFlush();
227 }
228 }
229
230 VOID
231 NTAPI
232 CmpCmdInit(IN BOOLEAN SetupBoot)
233 {
234 LARGE_INTEGER DueTime;
235 PAGED_CODE();
236
237 /* Setup the lazy DPC */
238 KeInitializeDpc(&CmpLazyFlushDpc, CmpLazyFlushDpcRoutine, NULL);
239
240 /* Setup the lazy timer */
241 KeInitializeTimer(&CmpLazyFlushTimer);
242
243 /* Setup the lazy worker */
244 ExInitializeWorkItem(&CmpLazyWorkItem, CmpLazyFlushWorker, NULL);
245
246 /* Setup the forced-lazy DPC and timer */
247 KeInitializeDpc(&CmpEnableLazyFlushDpc,
248 CmpEnableLazyFlushDpcRoutine,
249 NULL);
250 KeInitializeTimer(&CmpEnableLazyFlushTimer);
251
252 /* Enable lazy flushing after 10 minutes */
253 DueTime.QuadPart = Int32x32To64(600, -10 * 1000 * 1000);
254 KeSetTimer(&CmpEnableLazyFlushTimer, DueTime, &CmpEnableLazyFlushDpc);
255
256 /* Setup flush variables */
257 CmpNoWrite = CmpMiniNTBoot;
258 CmpWasSetupBoot = SetupBoot;
259
260 /* Testing: Force Lazy Flushing */
261 CmpHoldLazyFlush = FALSE;
262
263 /* Setup the hive list */
264 CmpInitializeHiveList(SetupBoot);
265 }
266
267 NTSTATUS
268 NTAPI
269 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes,
270 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext,
271 IN OUT PBOOLEAN Allocate,
272 OUT PCMHIVE *NewHive,
273 IN ULONG CheckFlags)
274 {
275 PUNICODE_STRING FileName;
276 NTSTATUS Status;
277 PAGED_CODE();
278
279 /* Open the file in the current security context */
280 FileName = FileAttributes->ObjectName;
281 Status = CmpInitHiveFromFile(FileName,
282 0,
283 NewHive,
284 Allocate,
285 CheckFlags);
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))
293 {
294 /* We failed due to an account/security error, impersonate SYSTEM */
295 Status = SeImpersonateClientEx(ImpersonationContext, NULL);
296 if (NT_SUCCESS(Status))
297 {
298 /* Now try again */
299 Status = CmpInitHiveFromFile(FileName,
300 0,
301 NewHive,
302 Allocate,
303 CheckFlags);
304
305 /* Restore impersonation token */
306 PsRevertToSelf();
307 }
308 }
309
310 /* Return status of open attempt */
311 return Status;
312 }
313
314 VOID
315 NTAPI
316 CmpShutdownWorkers(VOID)
317 {
318 /* Stop lazy flushing */
319 PAGED_CODE();
320 KeCancelTimer(&CmpLazyFlushTimer);
321 }
322
323 VOID
324 NTAPI
325 CmSetLazyFlushState(IN BOOLEAN Enable)
326 {
327 /* Set state for lazy flusher */
328 CmpHoldLazyFlush = !Enable;
329 }
330
331 /* EOF */