1 /* 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) 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 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)) 85 { 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); 89 *Error = TRUE; 90 Result = FALSE; 91 break; 92 } 93 CmHive->FlushCount = CmpLazyFlushCount; 94 } 95 } 96 else if ((CmHive->Hive.DirtyCount) && 97 (!(CmHive->Hive.HiveFlags & HIVE_VOLATILE)) && 98 (!(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH))) 99 { 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); 104 } 105 106 /* Try the next one */ 107 NextEntry = NextEntry->Flink; 108 } 109 110 /* Check if we've flushed everything */ 111 if (NextEntry == &CmpHiveListHead) 112 { 113 /* We have, tell the caller we're done */ 114 Result = FALSE; 115 } 116 else 117 { 118 /* We need to be called again */ 119 Result = TRUE; 120 } 121 122 /* Unlock the list and return the result */ 123 ExReleasePushLock(&CmpHiveListHeadLock); 124 return Result; 125 } 126 127 _Function_class_(KDEFERRED_ROUTINE) 128 VOID 129 NTAPI 130 CmpEnableLazyFlushDpcRoutine(IN PKDPC Dpc, 131 IN PVOID DeferredContext, 132 IN PVOID SystemArgument1, 133 IN PVOID SystemArgument2) 134 { 135 /* Don't stop lazy flushing from happening anymore */ 136 CmpHoldLazyFlush = FALSE; 137 } 138 139 _Function_class_(KDEFERRED_ROUTINE) 140 VOID 141 NTAPI 142 CmpLazyFlushDpcRoutine(IN PKDPC Dpc, 143 IN PVOID DeferredContext, 144 IN PVOID SystemArgument1, 145 IN PVOID SystemArgument2) 146 { 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)) 150 { 151 CmpLazyFlushPending = TRUE; 152 ExQueueWorkItem(&CmpLazyWorkItem, DelayedWorkQueue); 153 } 154 } 155 156 VOID 157 NTAPI 158 CmpLazyFlush(VOID) 159 { 160 LARGE_INTEGER DueTime; 161 PAGED_CODE(); 162 163 /* Check if we should set the lazy flush timer */ 164 if ((!CmpNoWrite) && (!CmpHoldLazyFlush)) 165 { 166 /* Do it */ 167 DueTime.QuadPart = Int32x32To64(CmpLazyFlushIntervalInSeconds, 168 -10 * 1000 * 1000); 169 KeSetTimer(&CmpLazyFlushTimer, DueTime, &CmpLazyFlushDpc); 170 } 171 } 172 173 _Function_class_(WORKER_THREAD_ROUTINE) 174 VOID 175 NTAPI 176 CmpLazyFlushWorker(IN PVOID Parameter) 177 { 178 BOOLEAN ForceFlush, Result, MoreWork = FALSE; 179 ULONG DirtyCount = 0; 180 PAGED_CODE(); 181 182 /* Don't do anything if lazy flushing isn't enabled yet */ 183 if (CmpHoldLazyFlush) 184 { 185 DPRINT1("Lazy flush held. Bye bye.\n"); 186 CmpLazyFlushPending = FALSE; 187 return; 188 } 189 190 /* Check if we are forcing a flush */ 191 ForceFlush = CmpForceForceFlush; 192 if (ForceFlush) 193 { 194 DPRINT("Forcing flush.\n"); 195 /* Lock the registry exclusively */ 196 CmpLockRegistryExclusive(); 197 } 198 else 199 { 200 DPRINT("Not forcing flush.\n"); 201 /* Starve writers before locking */ 202 InterlockedIncrement(&CmpFlushStarveWriters); 203 CmpLockRegistry(); 204 } 205 206 /* Flush the next hive */ 207 MoreWork = CmpDoFlushNextHive(ForceFlush, &Result, &DirtyCount); 208 if (!MoreWork) 209 { 210 /* We're done */ 211 InterlockedIncrement((PLONG)&CmpLazyFlushCount); 212 } 213 214 /* Check if we have starved writers */ 215 if (!ForceFlush) 216 InterlockedDecrement(&CmpFlushStarveWriters); 217 218 /* Not pending anymore, release the registry lock */ 219 CmpLazyFlushPending = FALSE; 220 CmpUnlockRegistry(); 221 222 DPRINT("Lazy flush done. More work to be done: %s. Entries still dirty: %u.\n", 223 MoreWork ? "Yes" : "No", DirtyCount); 224 225 if (MoreWork) 226 { 227 /* Relaunch the flush timer, so the remaining hives get flushed */ 228 CmpLazyFlush(); 229 } 230 } 231 232 VOID 233 NTAPI 234 CmpCmdInit(IN BOOLEAN SetupBoot) 235 { 236 LARGE_INTEGER DueTime; 237 PAGED_CODE(); 238 239 /* Setup the lazy DPC */ 240 KeInitializeDpc(&CmpLazyFlushDpc, CmpLazyFlushDpcRoutine, NULL); 241 242 /* Setup the lazy timer */ 243 KeInitializeTimer(&CmpLazyFlushTimer); 244 245 /* Setup the lazy worker */ 246 ExInitializeWorkItem(&CmpLazyWorkItem, CmpLazyFlushWorker, NULL); 247 248 /* Setup the forced-lazy DPC and timer */ 249 KeInitializeDpc(&CmpEnableLazyFlushDpc, 250 CmpEnableLazyFlushDpcRoutine, 251 NULL); 252 KeInitializeTimer(&CmpEnableLazyFlushTimer); 253 254 /* Enable lazy flushing after 10 minutes */ 255 DueTime.QuadPart = Int32x32To64(600, -10 * 1000 * 1000); 256 KeSetTimer(&CmpEnableLazyFlushTimer, DueTime, &CmpEnableLazyFlushDpc); 257 258 /* Setup flush variables */ 259 CmpNoWrite = CmpMiniNTBoot; 260 CmpWasSetupBoot = SetupBoot; 261 262 /* Testing: Force Lazy Flushing */ 263 CmpHoldLazyFlush = FALSE; 264 265 /* Setup the hive list if this is not a Setup boot */ 266 if (!SetupBoot) 267 CmpInitializeHiveList(); 268 } 269 270 NTSTATUS 271 NTAPI 272 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes, 273 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext, 274 IN OUT PBOOLEAN Allocate, 275 OUT PCMHIVE *NewHive, 276 IN ULONG CheckFlags) 277 { 278 NTSTATUS Status; 279 UNICODE_STRING FileName; 280 PWCHAR FilePath; 281 ULONG Length; 282 OBJECT_NAME_INFORMATION DummyNameInfo; 283 POBJECT_NAME_INFORMATION FileNameInfo; 284 285 PAGED_CODE(); 286 287 if (FileAttributes->RootDirectory) 288 { 289 /* 290 * Validity check: The ObjectName is relative to RootDirectory, 291 * therefore it must not start with a path separator. 292 */ 293 if (FileAttributes->ObjectName && FileAttributes->ObjectName->Buffer && 294 FileAttributes->ObjectName->Length >= sizeof(WCHAR) && 295 *FileAttributes->ObjectName->Buffer == OBJ_NAME_PATH_SEPARATOR) 296 { 297 return STATUS_OBJECT_PATH_SYNTAX_BAD; 298 } 299 300 /* Determine the right buffer size and allocate */ 301 Status = ZwQueryObject(FileAttributes->RootDirectory, 302 ObjectNameInformation, 303 &DummyNameInfo, 304 sizeof(DummyNameInfo), 305 &Length); 306 if (Status != STATUS_BUFFER_OVERFLOW) 307 { 308 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name size query failed, Status = 0x%08lx\n", Status); 309 return Status; 310 } 311 312 FileNameInfo = ExAllocatePoolWithTag(PagedPool, 313 Length + sizeof(UNICODE_NULL), 314 TAG_CM); 315 if (FileNameInfo == NULL) 316 { 317 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n"); 318 return STATUS_INSUFFICIENT_RESOURCES; 319 } 320 321 /* Try to get the value */ 322 Status = ZwQueryObject(FileAttributes->RootDirectory, 323 ObjectNameInformation, 324 FileNameInfo, 325 Length, 326 &Length); 327 if (!NT_SUCCESS(Status)) 328 { 329 /* Fail */ 330 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name query failed, Status = 0x%08lx\n", Status); 331 ExFreePoolWithTag(FileNameInfo, TAG_CM); 332 return Status; 333 } 334 335 /* Null-terminate and add the length of the terminator */ 336 Length -= sizeof(OBJECT_NAME_INFORMATION); 337 FilePath = FileNameInfo->Name.Buffer; 338 FilePath[Length / sizeof(WCHAR)] = UNICODE_NULL; 339 Length += sizeof(UNICODE_NULL); 340 341 /* Compute the size of the full path; Length already counts the terminating NULL */ 342 Length = Length + sizeof(WCHAR) + FileAttributes->ObjectName->Length; 343 if (Length > MAXUSHORT) 344 { 345 /* Name size too long, bail out */ 346 ExFreePoolWithTag(FileNameInfo, TAG_CM); 347 return STATUS_OBJECT_PATH_INVALID; 348 } 349 350 /* Build the full path */ 351 RtlInitEmptyUnicodeString(&FileName, NULL, 0); 352 FileName.Buffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM); 353 if (!FileName.Buffer) 354 { 355 /* Fail */ 356 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n"); 357 ExFreePoolWithTag(FileNameInfo, TAG_CM); 358 return STATUS_INSUFFICIENT_RESOURCES; 359 } 360 FileName.MaximumLength = Length; 361 RtlCopyUnicodeString(&FileName, &FileNameInfo->Name); 362 ExFreePoolWithTag(FileNameInfo, TAG_CM); 363 364 /* 365 * Append a path terminator if needed (we have already accounted 366 * for a possible extra one when allocating the buffer). 367 */ 368 if (/* FileAttributes->ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR && */ // We excluded ObjectName starting with a path separator above. 369 FileName.Length > 0 && FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] != OBJ_NAME_PATH_SEPARATOR) 370 { 371 /* ObjectName does not start with '\' and PathBuffer does not end with '\' */ 372 FileName.Buffer[FileName.Length / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; 373 FileName.Length += sizeof(WCHAR); 374 FileName.Buffer[FileName.Length / sizeof(WCHAR)] = UNICODE_NULL; 375 } 376 377 /* Append the object name */ 378 Status = RtlAppendUnicodeStringToString(&FileName, FileAttributes->ObjectName); 379 if (!NT_SUCCESS(Status)) 380 { 381 /* Fail */ 382 DPRINT1("CmpCmdHiveOpen(): RtlAppendUnicodeStringToString() failed, Status = 0x%08lx\n", Status); 383 ExFreePoolWithTag(FileName.Buffer, TAG_CM); 384 return Status; 385 } 386 } 387 else 388 { 389 FileName = *FileAttributes->ObjectName; 390 } 391 392 /* Open the file in the current security context */ 393 Status = CmpInitHiveFromFile(&FileName, 394 0, 395 NewHive, 396 Allocate, 397 CheckFlags); 398 if (((Status == STATUS_ACCESS_DENIED) || 399 (Status == STATUS_NO_SUCH_USER) || 400 (Status == STATUS_WRONG_PASSWORD) || 401 (Status == STATUS_ACCOUNT_EXPIRED) || 402 (Status == STATUS_ACCOUNT_DISABLED) || 403 (Status == STATUS_ACCOUNT_RESTRICTION)) && 404 (ImpersonationContext)) 405 { 406 /* We failed due to an account/security error, impersonate SYSTEM */ 407 Status = SeImpersonateClientEx(ImpersonationContext, NULL); 408 if (NT_SUCCESS(Status)) 409 { 410 /* Now try again */ 411 Status = CmpInitHiveFromFile(&FileName, 412 0, 413 NewHive, 414 Allocate, 415 CheckFlags); 416 417 /* Restore impersonation token */ 418 PsRevertToSelf(); 419 } 420 } 421 422 if (FileAttributes->RootDirectory) 423 { 424 ExFreePoolWithTag(FileName.Buffer, TAG_CM); 425 } 426 427 /* Return status of open attempt */ 428 return Status; 429 } 430 431 VOID 432 NTAPI 433 CmpShutdownWorkers(VOID) 434 { 435 /* Stop lazy flushing */ 436 PAGED_CODE(); 437 KeCancelTimer(&CmpLazyFlushTimer); 438 } 439 440 VOID 441 NTAPI 442 CmSetLazyFlushState(IN BOOLEAN Enable) 443 { 444 /* Set state for lazy flusher */ 445 CmpHoldLazyFlush = !Enable; 446 } 447 448 /* EOF */ 449