[NTOSKRNL]
[reactos.git] / reactos / 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 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 /* Don't force flush */
55 CmpForceForceFlush = FALSE;
56
57 /* Acquire the list lock and loop */
58 ExAcquirePushLockShared(&CmpHiveListHeadLock);
59 NextEntry = CmpHiveListHead.Flink;
60 while (NextEntry != &CmpHiveListHead)
61 {
62 /* Get the hive and check if we should flush it */
63 CmHive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList);
64 if (!(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH) &&
65 (CmHive->FlushCount != CmpLazyFlushCount))
66 {
67 /* Great sucess! */
68 Result = TRUE;
69
70 /* Ignore clean or volatile hves */
71 if (!(CmHive->Hive.DirtyCount) ||
72 (CmHive->Hive.HiveFlags & HIVE_VOLATILE))
73 {
74 /* Don't do anything but do update the count */
75 CmHive->FlushCount = CmpLazyFlushCount;
76 }
77 else
78 {
79 /* Do the sync */
80 DPRINT1("Flushing: %wZ\n", CmHive->FileFullPath);
81 DPRINT1("Handle: %lx\n", CmHive->FileHandles[HFILE_TYPE_PRIMARY]);
82 Status = HvSyncHive(&CmHive->Hive);
83 if(!NT_SUCCESS(Status))
84 {
85 /* Let them know we failed */
86 *Error = TRUE;
87 Result = FALSE;
88 }
89 }
90 }
91 else if ((CmHive->Hive.DirtyCount) &&
92 (!(CmHive->Hive.HiveFlags & HIVE_VOLATILE)) &&
93 (!(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH)))
94 {
95 /* Use another lazy flusher for this hive */
96 ASSERT(CmHive->FlushCount == CmpLazyFlushCount);
97 *DirtyCount += CmHive->Hive.DirtyCount;
98 }
99
100 /* Try the next one */
101 NextEntry = NextEntry->Flink;
102 }
103
104 /* Check if we've flushed everything */
105 if (NextEntry == &CmpHiveListHead)
106 {
107 /* We have, tell the caller we're done */
108 Result = FALSE;
109 }
110 else
111 {
112 /* We need to be called again */
113 Result = TRUE;
114 }
115
116 /* Unlock the list and return the result */
117 ExReleasePushLock(&CmpHiveListHeadLock);
118 return Result;
119 }
120
121 _Function_class_(KDEFERRED_ROUTINE)
122 VOID
123 NTAPI
124 CmpEnableLazyFlushDpcRoutine(IN PKDPC Dpc,
125 IN PVOID DeferredContext,
126 IN PVOID SystemArgument1,
127 IN PVOID SystemArgument2)
128 {
129 /* Don't stop lazy flushing from happening anymore */
130 CmpHoldLazyFlush = FALSE;
131 }
132
133 _Function_class_(KDEFERRED_ROUTINE)
134 VOID
135 NTAPI
136 CmpLazyFlushDpcRoutine(IN PKDPC Dpc,
137 IN PVOID DeferredContext,
138 IN PVOID SystemArgument1,
139 IN PVOID SystemArgument2)
140 {
141 /* Check if we should queue the lazy flush worker */
142 if ((!CmpLazyFlushPending) && (!CmpHoldLazyFlush))
143 {
144 CmpLazyFlushPending = TRUE;
145 ExQueueWorkItem(&CmpLazyWorkItem, DelayedWorkQueue);
146 }
147 }
148
149 VOID
150 NTAPI
151 CmpLazyFlush(VOID)
152 {
153 LARGE_INTEGER DueTime;
154 PAGED_CODE();
155
156 /* Check if we should set the lazy flush timer */
157 if ((!CmpNoWrite) && (!CmpHoldLazyFlush))
158 {
159 /* Do it */
160 DueTime.QuadPart = Int32x32To64(CmpLazyFlushIntervalInSeconds,
161 -10 * 1000 * 1000);
162 KeSetTimer(&CmpLazyFlushTimer, DueTime, &CmpLazyFlushDpc);
163 }
164 }
165
166 _Function_class_(WORKER_THREAD_ROUTINE)
167 VOID
168 NTAPI
169 CmpLazyFlushWorker(IN PVOID Parameter)
170 {
171 BOOLEAN ForceFlush, Result, MoreWork = FALSE;
172 ULONG DirtyCount = 0;
173 PAGED_CODE();
174
175 /* Don't do anything if lazy flushing isn't enabled yet */
176 if (CmpHoldLazyFlush) return;
177
178 /* Check if we are forcing a flush */
179 ForceFlush = CmpForceForceFlush;
180 if (ForceFlush)
181 {
182 /* Lock the registry exclusively */
183 CmpLockRegistryExclusive();
184 }
185 else
186 {
187 /* Do a normal lock */
188 CmpLockRegistry();
189 InterlockedIncrement(&CmpFlushStarveWriters);
190 }
191
192 /* Flush the next hive */
193 MoreWork = CmpDoFlushNextHive(ForceFlush, &Result, &DirtyCount);
194 if (!MoreWork)
195 {
196 /* We're done */
197 InterlockedIncrement((PLONG)&CmpLazyFlushCount);
198 }
199
200 /* Check if we have starved writers */
201 if (!ForceFlush) InterlockedDecrement(&CmpFlushStarveWriters);
202
203 /* Not pending anymore, release the registry lock */
204 CmpLazyFlushPending = FALSE;
205 CmpUnlockRegistry();
206
207 /* Check if we need to flush another hive */
208 if ((MoreWork) || (DirtyCount)) CmpLazyFlush();
209 }
210
211 VOID
212 NTAPI
213 CmpCmdInit(IN BOOLEAN SetupBoot)
214 {
215 LARGE_INTEGER DueTime;
216 PAGED_CODE();
217
218 /* Setup the lazy DPC */
219 KeInitializeDpc(&CmpLazyFlushDpc, CmpLazyFlushDpcRoutine, NULL);
220
221 /* Setup the lazy timer */
222 KeInitializeTimer(&CmpLazyFlushTimer);
223
224 /* Setup the lazy worker */
225 ExInitializeWorkItem(&CmpLazyWorkItem, CmpLazyFlushWorker, NULL);
226
227 /* Setup the forced-lazy DPC and timer */
228 KeInitializeDpc(&CmpEnableLazyFlushDpc,
229 CmpEnableLazyFlushDpcRoutine,
230 NULL);
231 KeInitializeTimer(&CmpEnableLazyFlushTimer);
232
233 /* Enable lazy flushing after 10 minutes */
234 DueTime.QuadPart = Int32x32To64(600, -10 * 1000 * 1000);
235 KeSetTimer(&CmpEnableLazyFlushTimer, DueTime, &CmpEnableLazyFlushDpc);
236
237 /* Setup flush variables */
238 CmpNoWrite = CmpMiniNTBoot;
239 CmpWasSetupBoot = SetupBoot;
240
241 /* Testing: Force Lazy Flushing */
242 CmpHoldLazyFlush = FALSE;
243
244 /* Setup the hive list */
245 CmpInitializeHiveList(SetupBoot);
246 }
247
248 NTSTATUS
249 NTAPI
250 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes,
251 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext,
252 IN OUT PBOOLEAN Allocate,
253 OUT PCMHIVE *NewHive,
254 IN ULONG CheckFlags)
255 {
256 PUNICODE_STRING FileName;
257 NTSTATUS Status;
258 PAGED_CODE();
259
260 /* Open the file in the current security context */
261 FileName = FileAttributes->ObjectName;
262 Status = CmpInitHiveFromFile(FileName,
263 0,
264 NewHive,
265 Allocate,
266 CheckFlags);
267 if (((Status == STATUS_ACCESS_DENIED) ||
268 (Status == STATUS_NO_SUCH_USER) ||
269 (Status == STATUS_WRONG_PASSWORD) ||
270 (Status == STATUS_ACCOUNT_EXPIRED) ||
271 (Status == STATUS_ACCOUNT_DISABLED) ||
272 (Status == STATUS_ACCOUNT_RESTRICTION)) &&
273 (ImpersonationContext))
274 {
275 /* We failed due to an account/security error, impersonate SYSTEM */
276 Status = SeImpersonateClientEx(ImpersonationContext, NULL);
277 if (NT_SUCCESS(Status))
278 {
279 /* Now try again */
280 Status = CmpInitHiveFromFile(FileName,
281 0,
282 NewHive,
283 Allocate,
284 CheckFlags);
285
286 /* Restore impersonation token */
287 PsRevertToSelf();
288 }
289 }
290
291 /* Return status of open attempt */
292 return Status;
293 }
294
295 VOID
296 NTAPI
297 CmpShutdownWorkers(VOID)
298 {
299 /* Stop lazy flushing */
300 PAGED_CODE();
301 KeCancelTimer(&CmpLazyFlushTimer);
302 }
303
304 VOID
305 NTAPI
306 CmSetLazyFlushState(IN BOOLEAN Enable)
307 {
308 /* Set state for lazy flusher */
309 CmpHoldLazyFlush = !Enable;
310 }
311
312 /* EOF */