* Sync with recent trunk (r52637).
[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 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 VOID
122 NTAPI
123 CmpEnableLazyFlushDpcRoutine(IN PKDPC Dpc,
124 IN PVOID DeferredContext,
125 IN PVOID SystemArgument1,
126 IN PVOID SystemArgument2)
127 {
128 /* Don't stop lazy flushing from happening anymore */
129 CmpHoldLazyFlush = FALSE;
130 }
131
132 VOID
133 NTAPI
134 CmpLazyFlushDpcRoutine(IN PKDPC Dpc,
135 IN PVOID DeferredContext,
136 IN PVOID SystemArgument1,
137 IN PVOID SystemArgument2)
138 {
139 /* Check if we should queue the lazy flush worker */
140 if ((!CmpLazyFlushPending) && (!CmpHoldLazyFlush))
141 {
142 CmpLazyFlushPending = TRUE;
143 ExQueueWorkItem(&CmpLazyWorkItem, DelayedWorkQueue);
144 }
145 }
146
147 VOID
148 NTAPI
149 CmpLazyFlush(VOID)
150 {
151 LARGE_INTEGER DueTime;
152 PAGED_CODE();
153
154 /* Check if we should set the lazy flush timer */
155 if ((!CmpNoWrite) && (!CmpHoldLazyFlush))
156 {
157 /* Do it */
158 DueTime.QuadPart = Int32x32To64(CmpLazyFlushIntervalInSeconds,
159 -10 * 1000 * 1000);
160 KeSetTimer(&CmpLazyFlushTimer, DueTime, &CmpLazyFlushDpc);
161 }
162 }
163
164 VOID
165 NTAPI
166 CmpLazyFlushWorker(IN PVOID Parameter)
167 {
168 BOOLEAN ForceFlush, Result, MoreWork = FALSE;
169 ULONG DirtyCount = 0;
170 PAGED_CODE();
171
172 /* Don't do anything if lazy flushing isn't enabled yet */
173 if (CmpHoldLazyFlush) return;
174
175 /* Check if we are forcing a flush */
176 ForceFlush = CmpForceForceFlush;
177 if (ForceFlush)
178 {
179 /* Lock the registry exclusively */
180 CmpLockRegistryExclusive();
181 }
182 else
183 {
184 /* Do a normal lock */
185 CmpLockRegistry();
186 InterlockedIncrement(&CmpFlushStarveWriters);
187 }
188
189 /* Flush the next hive */
190 MoreWork = CmpDoFlushNextHive(ForceFlush, &Result, &DirtyCount);
191 if (!MoreWork)
192 {
193 /* We're done */
194 InterlockedIncrement((PLONG)&CmpLazyFlushCount);
195 }
196
197 /* Check if we have starved writers */
198 if (!ForceFlush) InterlockedDecrement(&CmpFlushStarveWriters);
199
200 /* Not pending anymore, release the registry lock */
201 CmpLazyFlushPending = FALSE;
202 CmpUnlockRegistry();
203
204 /* Check if we need to flush another hive */
205 if ((MoreWork) || (DirtyCount)) CmpLazyFlush();
206 }
207
208 VOID
209 NTAPI
210 CmpCmdInit(IN BOOLEAN SetupBoot)
211 {
212 LARGE_INTEGER DueTime;
213 PAGED_CODE();
214
215 /* Setup the lazy DPC */
216 KeInitializeDpc(&CmpLazyFlushDpc, CmpLazyFlushDpcRoutine, NULL);
217
218 /* Setup the lazy timer */
219 KeInitializeTimer(&CmpLazyFlushTimer);
220
221 /* Setup the lazy worker */
222 ExInitializeWorkItem(&CmpLazyWorkItem, CmpLazyFlushWorker, NULL);
223
224 /* Setup the forced-lazy DPC and timer */
225 KeInitializeDpc(&CmpEnableLazyFlushDpc,
226 CmpEnableLazyFlushDpcRoutine,
227 NULL);
228 KeInitializeTimer(&CmpEnableLazyFlushTimer);
229
230 /* Enable lazy flushing after 10 minutes */
231 DueTime.QuadPart = Int32x32To64(600, -10 * 1000 * 1000);
232 KeSetTimer(&CmpEnableLazyFlushTimer, DueTime, &CmpEnableLazyFlushDpc);
233
234 /* Setup flush variables */
235 CmpNoWrite = CmpMiniNTBoot;
236 CmpWasSetupBoot = SetupBoot;
237
238 /* Testing: Force Lazy Flushing */
239 CmpHoldLazyFlush = FALSE;
240
241 /* Setup the hive list */
242 CmpInitializeHiveList(SetupBoot);
243 }
244
245 NTSTATUS
246 NTAPI
247 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes,
248 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext,
249 IN OUT PBOOLEAN Allocate,
250 OUT PCMHIVE *NewHive,
251 IN ULONG CheckFlags)
252 {
253 PUNICODE_STRING FileName;
254 NTSTATUS Status;
255 PAGED_CODE();
256
257 /* Open the file in the current security context */
258 FileName = FileAttributes->ObjectName;
259 Status = CmpInitHiveFromFile(FileName,
260 0,
261 NewHive,
262 Allocate,
263 CheckFlags);
264 if (((Status == STATUS_ACCESS_DENIED) ||
265 (Status == STATUS_NO_SUCH_USER) ||
266 (Status == STATUS_WRONG_PASSWORD) ||
267 (Status == STATUS_ACCOUNT_EXPIRED) ||
268 (Status == STATUS_ACCOUNT_DISABLED) ||
269 (Status == STATUS_ACCOUNT_RESTRICTION)) &&
270 (ImpersonationContext))
271 {
272 /* We failed due to an account/security error, impersonate SYSTEM */
273 Status = SeImpersonateClientEx(ImpersonationContext, NULL);
274 if (NT_SUCCESS(Status))
275 {
276 /* Now try again */
277 Status = CmpInitHiveFromFile(FileName,
278 0,
279 NewHive,
280 Allocate,
281 CheckFlags);
282
283 /* Restore impersonation token */
284 PsRevertToSelf();
285 }
286 }
287
288 /* Return status of open attempt */
289 return Status;
290 }
291
292 VOID
293 NTAPI
294 CmpShutdownWorkers(VOID)
295 {
296 /* Stop lazy flushing */
297 PAGED_CODE();
298 KeCancelTimer(&CmpLazyFlushTimer);
299 }
300
301 VOID
302 NTAPI
303 CmSetLazyFlushState(IN BOOLEAN Enable)
304 {
305 /* Set state for lazy flusher */
306 CmpHoldLazyFlush = !Enable;
307 }
308
309 /* EOF */