1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * PURPOSE: Configuration Manager - Internal Registry APIs 5 * PROGRAMMERS: Alex Ionescu <alex.ionescu@reactos.org> 6 */ 7 8 /* INCLUDES *******************************************************************/ 9 10 #include "ntoskrnl.h" 11 #define NDEBUG 12 #include "debug.h" 13 14 /* GLOBALS ********************************************************************/ 15 16 KTIMER CmpLazyFlushTimer; 17 KDPC CmpLazyFlushDpc; 18 WORK_QUEUE_ITEM CmpLazyWorkItem; 19 KTIMER CmpEnableLazyFlushTimer; 20 KDPC CmpEnableLazyFlushDpc; 21 BOOLEAN CmpLazyFlushPending; 22 BOOLEAN CmpForceForceFlush; 23 BOOLEAN CmpHoldLazyFlush = TRUE; 24 ULONG CmpLazyFlushIntervalInSeconds = 5; 25 static ULONG CmpLazyFlushHiveCount = 7; 26 ULONG CmpLazyFlushCount = 1; 27 LONG CmpFlushStarveWriters; 28 29 /* FUNCTIONS ******************************************************************/ 30 31 BOOLEAN 32 NTAPI 33 CmpDoFlushNextHive(_In_ BOOLEAN ForceFlush, 34 _Out_ PBOOLEAN Error, 35 _Out_ PULONG DirtyCount) 36 { 37 NTSTATUS Status; 38 PLIST_ENTRY NextEntry; 39 PCMHIVE CmHive; 40 BOOLEAN Result; 41 ULONG HiveCount = CmpLazyFlushHiveCount; 42 43 /* Set Defaults */ 44 *Error = FALSE; 45 *DirtyCount = 0; 46 47 /* Don't do anything if we're not supposed to */ 48 if (CmpNoWrite) return TRUE; 49 50 /* Make sure we have to flush at least one hive */ 51 if (!HiveCount) HiveCount = 1; 52 53 /* Acquire the list lock and loop */ 54 ExAcquirePushLockShared(&CmpHiveListHeadLock); 55 NextEntry = CmpHiveListHead.Flink; 56 while ((NextEntry != &CmpHiveListHead) && HiveCount) 57 { 58 /* Get the hive and check if we should flush it */ 59 CmHive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList); 60 if (!(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH) && 61 (CmHive->FlushCount != CmpLazyFlushCount)) 62 { 63 /* Great sucess! */ 64 Result = TRUE; 65 66 /* One less to flush */ 67 HiveCount--; 68 69 /* Ignore clean or volatile hives */ 70 if ((!CmHive->Hive.DirtyCount && !ForceFlush) || 71 (CmHive->Hive.HiveFlags & HIVE_VOLATILE)) 72 { 73 /* Don't do anything but do update the count */ 74 CmHive->FlushCount = CmpLazyFlushCount; 75 DPRINT("Hive %wZ is clean.\n", &CmHive->FileFullPath); 76 } 77 else 78 { 79 /* Do the sync */ 80 DPRINT("Flushing: %wZ\n", &CmHive->FileFullPath); 81 DPRINT("Handle: %p\n", CmHive->FileHandles[HFILE_TYPE_PRIMARY]); 82 Status = HvSyncHive(&CmHive->Hive); 83 if(!NT_SUCCESS(Status)) 84 { 85 /* Let them know we failed */ 86 DPRINT1("Failed to flush %wZ on handle %p (status 0x%08lx)\n", 87 &CmHive->FileFullPath, CmHive->FileHandles[HFILE_TYPE_PRIMARY], Status); 88 *Error = TRUE; 89 Result = FALSE; 90 break; 91 } 92 CmHive->FlushCount = CmpLazyFlushCount; 93 } 94 } 95 else if (CmHive->Hive.DirtyCount && 96 !(CmHive->Hive.HiveFlags & HIVE_VOLATILE) && 97 !(CmHive->Hive.HiveFlags & HIVE_NOLAZYFLUSH)) 98 { 99 /* Use another lazy flusher for this hive */ 100 ASSERT(CmHive->FlushCount == CmpLazyFlushCount); 101 *DirtyCount += CmHive->Hive.DirtyCount; 102 DPRINT("CmHive %wZ already uptodate.\n", &CmHive->FileFullPath); 103 } 104 105 /* Try the next one */ 106 NextEntry = NextEntry->Flink; 107 } 108 109 /* Check if we've flushed everything */ 110 if (NextEntry == &CmpHiveListHead) 111 { 112 /* We have, tell the caller we're done */ 113 Result = FALSE; 114 } 115 else 116 { 117 /* We need to be called again */ 118 Result = TRUE; 119 } 120 121 /* Unlock the list and return the result */ 122 ExReleasePushLock(&CmpHiveListHeadLock); 123 return Result; 124 } 125 126 _Function_class_(KDEFERRED_ROUTINE) 127 VOID 128 NTAPI 129 CmpEnableLazyFlushDpcRoutine(IN PKDPC Dpc, 130 IN PVOID DeferredContext, 131 IN PVOID SystemArgument1, 132 IN PVOID SystemArgument2) 133 { 134 /* Don't stop lazy flushing from happening anymore */ 135 CmpHoldLazyFlush = FALSE; 136 } 137 138 _Function_class_(KDEFERRED_ROUTINE) 139 VOID 140 NTAPI 141 CmpLazyFlushDpcRoutine(IN PKDPC Dpc, 142 IN PVOID DeferredContext, 143 IN PVOID SystemArgument1, 144 IN PVOID SystemArgument2) 145 { 146 /* Check if we should queue the lazy flush worker */ 147 DPRINT("Flush pending: %s, Holding lazy flush: %s.\n", CmpLazyFlushPending ? "yes" : "no", CmpHoldLazyFlush ? "yes" : "no"); 148 if (!CmpLazyFlushPending && !CmpHoldLazyFlush) 149 { 150 CmpLazyFlushPending = TRUE; 151 ExQueueWorkItem(&CmpLazyWorkItem, DelayedWorkQueue); 152 } 153 } 154 155 VOID 156 NTAPI 157 CmpLazyFlush(VOID) 158 { 159 LARGE_INTEGER DueTime; 160 PAGED_CODE(); 161 162 /* Check if we should set the lazy flush timer */ 163 if (!CmpNoWrite && !CmpHoldLazyFlush) 164 { 165 /* Do it */ 166 DueTime.QuadPart = Int32x32To64(CmpLazyFlushIntervalInSeconds, 167 -10 * 1000 * 1000); 168 KeSetTimer(&CmpLazyFlushTimer, DueTime, &CmpLazyFlushDpc); 169 } 170 } 171 172 _Function_class_(WORKER_THREAD_ROUTINE) 173 VOID 174 NTAPI 175 CmpLazyFlushWorker(IN PVOID Parameter) 176 { 177 BOOLEAN ForceFlush, Result, MoreWork = FALSE; 178 ULONG DirtyCount = 0; 179 PAGED_CODE(); 180 181 /* Don't do anything if lazy flushing isn't enabled yet */ 182 if (CmpHoldLazyFlush) 183 { 184 DPRINT1("Lazy flush held. Bye bye.\n"); 185 CmpLazyFlushPending = FALSE; 186 return; 187 } 188 189 /* Check if we are forcing a flush */ 190 ForceFlush = CmpForceForceFlush; 191 if (ForceFlush) 192 { 193 DPRINT("Forcing flush.\n"); 194 /* Lock the registry exclusively */ 195 CmpLockRegistryExclusive(); 196 } 197 else 198 { 199 DPRINT("Not forcing flush.\n"); 200 /* Starve writers before locking */ 201 InterlockedIncrement(&CmpFlushStarveWriters); 202 CmpLockRegistry(); 203 } 204 205 /* Flush the next hive */ 206 MoreWork = CmpDoFlushNextHive(ForceFlush, &Result, &DirtyCount); 207 if (!MoreWork) 208 { 209 /* We're done */ 210 InterlockedIncrement((PLONG)&CmpLazyFlushCount); 211 } 212 213 /* Check if we have starved writers */ 214 if (!ForceFlush) 215 InterlockedDecrement(&CmpFlushStarveWriters); 216 217 /* Not pending anymore, release the registry lock */ 218 CmpLazyFlushPending = FALSE; 219 CmpUnlockRegistry(); 220 221 DPRINT("Lazy flush done. More work to be done: %s. Entries still dirty: %u.\n", 222 MoreWork ? "Yes" : "No", DirtyCount); 223 224 if (MoreWork) 225 { 226 /* Relaunch the flush timer, so the remaining hives get flushed */ 227 CmpLazyFlush(); 228 } 229 } 230 231 VOID 232 NTAPI 233 CmpCmdInit(IN BOOLEAN SetupBoot) 234 { 235 LARGE_INTEGER DueTime; 236 PAGED_CODE(); 237 238 /* Setup the lazy DPC */ 239 KeInitializeDpc(&CmpLazyFlushDpc, CmpLazyFlushDpcRoutine, NULL); 240 241 /* Setup the lazy timer */ 242 KeInitializeTimer(&CmpLazyFlushTimer); 243 244 /* Setup the lazy worker */ 245 ExInitializeWorkItem(&CmpLazyWorkItem, CmpLazyFlushWorker, NULL); 246 247 /* Setup the forced-lazy DPC and timer */ 248 KeInitializeDpc(&CmpEnableLazyFlushDpc, 249 CmpEnableLazyFlushDpcRoutine, 250 NULL); 251 KeInitializeTimer(&CmpEnableLazyFlushTimer); 252 253 /* Enable lazy flushing after 10 minutes */ 254 DueTime.QuadPart = Int32x32To64(600, -10 * 1000 * 1000); 255 KeSetTimer(&CmpEnableLazyFlushTimer, DueTime, &CmpEnableLazyFlushDpc); 256 257 /* Setup flush variables */ 258 CmpNoWrite = CmpMiniNTBoot; 259 CmpWasSetupBoot = SetupBoot; 260 261 /* Testing: Force Lazy Flushing */ 262 CmpHoldLazyFlush = FALSE; 263 264 /* Setup the system hives list if this is not a Setup boot */ 265 if (!SetupBoot) 266 CmpInitializeHiveList(); 267 268 /* Now that the system hives are loaded, if we are in PE mode, 269 * all other hives will be loaded with full access */ 270 if (CmpMiniNTBoot) 271 CmpShareSystemHives = FALSE; 272 } 273 274 NTSTATUS 275 NTAPI 276 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes, 277 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext, 278 IN OUT PBOOLEAN Allocate, 279 OUT PCMHIVE *NewHive, 280 IN ULONG CheckFlags) 281 { 282 NTSTATUS Status; 283 UNICODE_STRING FileName; 284 PWCHAR FilePath; 285 ULONG Length; 286 OBJECT_NAME_INFORMATION DummyNameInfo; 287 POBJECT_NAME_INFORMATION FileNameInfo; 288 289 PAGED_CODE(); 290 291 if (FileAttributes->RootDirectory) 292 { 293 /* 294 * Validity check: The ObjectName is relative to RootDirectory, 295 * therefore it must not start with a path separator. 296 */ 297 if (FileAttributes->ObjectName && FileAttributes->ObjectName->Buffer && 298 FileAttributes->ObjectName->Length >= sizeof(WCHAR) && 299 *FileAttributes->ObjectName->Buffer == OBJ_NAME_PATH_SEPARATOR) 300 { 301 return STATUS_OBJECT_PATH_SYNTAX_BAD; 302 } 303 304 /* Determine the right buffer size and allocate */ 305 Status = ZwQueryObject(FileAttributes->RootDirectory, 306 ObjectNameInformation, 307 &DummyNameInfo, 308 sizeof(DummyNameInfo), 309 &Length); 310 if (Status != STATUS_BUFFER_OVERFLOW) 311 { 312 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name size query failed, Status = 0x%08lx\n", Status); 313 return Status; 314 } 315 316 FileNameInfo = ExAllocatePoolWithTag(PagedPool, 317 Length + sizeof(UNICODE_NULL), 318 TAG_CM); 319 if (FileNameInfo == NULL) 320 { 321 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n"); 322 return STATUS_INSUFFICIENT_RESOURCES; 323 } 324 325 /* Try to get the value */ 326 Status = ZwQueryObject(FileAttributes->RootDirectory, 327 ObjectNameInformation, 328 FileNameInfo, 329 Length, 330 &Length); 331 if (!NT_SUCCESS(Status)) 332 { 333 /* Fail */ 334 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name query failed, Status = 0x%08lx\n", Status); 335 ExFreePoolWithTag(FileNameInfo, TAG_CM); 336 return Status; 337 } 338 339 /* Null-terminate and add the length of the terminator */ 340 Length -= sizeof(OBJECT_NAME_INFORMATION); 341 FilePath = FileNameInfo->Name.Buffer; 342 FilePath[Length / sizeof(WCHAR)] = UNICODE_NULL; 343 Length += sizeof(UNICODE_NULL); 344 345 /* Compute the size of the full path; Length already counts the terminating NULL */ 346 Length = Length + sizeof(WCHAR) + FileAttributes->ObjectName->Length; 347 if (Length > MAXUSHORT) 348 { 349 /* Name size too long, bail out */ 350 ExFreePoolWithTag(FileNameInfo, TAG_CM); 351 return STATUS_OBJECT_PATH_INVALID; 352 } 353 354 /* Build the full path */ 355 RtlInitEmptyUnicodeString(&FileName, NULL, 0); 356 FileName.Buffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM); 357 if (!FileName.Buffer) 358 { 359 /* Fail */ 360 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n"); 361 ExFreePoolWithTag(FileNameInfo, TAG_CM); 362 return STATUS_INSUFFICIENT_RESOURCES; 363 } 364 FileName.MaximumLength = Length; 365 RtlCopyUnicodeString(&FileName, &FileNameInfo->Name); 366 ExFreePoolWithTag(FileNameInfo, TAG_CM); 367 368 /* 369 * Append a path terminator if needed (we have already accounted 370 * for a possible extra one when allocating the buffer). 371 */ 372 if (/* FileAttributes->ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR && */ // We excluded ObjectName starting with a path separator above. 373 FileName.Length > 0 && FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] != OBJ_NAME_PATH_SEPARATOR) 374 { 375 /* ObjectName does not start with '\' and PathBuffer does not end with '\' */ 376 FileName.Buffer[FileName.Length / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; 377 FileName.Length += sizeof(WCHAR); 378 FileName.Buffer[FileName.Length / sizeof(WCHAR)] = UNICODE_NULL; 379 } 380 381 /* Append the object name */ 382 Status = RtlAppendUnicodeStringToString(&FileName, FileAttributes->ObjectName); 383 if (!NT_SUCCESS(Status)) 384 { 385 /* Fail */ 386 DPRINT1("CmpCmdHiveOpen(): RtlAppendUnicodeStringToString() failed, Status = 0x%08lx\n", Status); 387 ExFreePoolWithTag(FileName.Buffer, TAG_CM); 388 return Status; 389 } 390 } 391 else 392 { 393 FileName = *FileAttributes->ObjectName; 394 } 395 396 /* Open the file in the current security context */ 397 Status = CmpInitHiveFromFile(&FileName, 398 0, 399 NewHive, 400 Allocate, 401 CheckFlags); 402 if (((Status == STATUS_ACCESS_DENIED) || 403 (Status == STATUS_NO_SUCH_USER) || 404 (Status == STATUS_WRONG_PASSWORD) || 405 (Status == STATUS_ACCOUNT_EXPIRED) || 406 (Status == STATUS_ACCOUNT_DISABLED) || 407 (Status == STATUS_ACCOUNT_RESTRICTION)) && 408 ImpersonationContext) 409 { 410 /* We failed due to an account/security error, impersonate SYSTEM */ 411 Status = SeImpersonateClientEx(ImpersonationContext, NULL); 412 if (NT_SUCCESS(Status)) 413 { 414 /* Now try again */ 415 Status = CmpInitHiveFromFile(&FileName, 416 0, 417 NewHive, 418 Allocate, 419 CheckFlags); 420 421 /* Restore impersonation token */ 422 PsRevertToSelf(); 423 } 424 } 425 426 if (FileAttributes->RootDirectory) 427 { 428 ExFreePoolWithTag(FileName.Buffer, TAG_CM); 429 } 430 431 /* Return status of open attempt */ 432 return Status; 433 } 434 435 VOID 436 NTAPI 437 CmpShutdownWorkers(VOID) 438 { 439 /* Stop lazy flushing */ 440 PAGED_CODE(); 441 KeCancelTimer(&CmpLazyFlushTimer); 442 } 443 444 VOID 445 NTAPI 446 CmSetLazyFlushState(IN BOOLEAN Enable) 447 { 448 /* Set state for lazy flusher */ 449 CmpHoldLazyFlush = !Enable; 450 } 451 452 /* EOF */ 453