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 hive list if this is not a Setup boot */ 265 if (!SetupBoot) 266 CmpInitializeHiveList(); 267 } 268 269 NTSTATUS 270 NTAPI 271 CmpCmdHiveOpen(IN POBJECT_ATTRIBUTES FileAttributes, 272 IN PSECURITY_CLIENT_CONTEXT ImpersonationContext, 273 IN OUT PBOOLEAN Allocate, 274 OUT PCMHIVE *NewHive, 275 IN ULONG CheckFlags) 276 { 277 NTSTATUS Status; 278 UNICODE_STRING FileName; 279 PWCHAR FilePath; 280 ULONG Length; 281 OBJECT_NAME_INFORMATION DummyNameInfo; 282 POBJECT_NAME_INFORMATION FileNameInfo; 283 284 PAGED_CODE(); 285 286 if (FileAttributes->RootDirectory) 287 { 288 /* 289 * Validity check: The ObjectName is relative to RootDirectory, 290 * therefore it must not start with a path separator. 291 */ 292 if (FileAttributes->ObjectName && FileAttributes->ObjectName->Buffer && 293 FileAttributes->ObjectName->Length >= sizeof(WCHAR) && 294 *FileAttributes->ObjectName->Buffer == OBJ_NAME_PATH_SEPARATOR) 295 { 296 return STATUS_OBJECT_PATH_SYNTAX_BAD; 297 } 298 299 /* Determine the right buffer size and allocate */ 300 Status = ZwQueryObject(FileAttributes->RootDirectory, 301 ObjectNameInformation, 302 &DummyNameInfo, 303 sizeof(DummyNameInfo), 304 &Length); 305 if (Status != STATUS_BUFFER_OVERFLOW) 306 { 307 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name size query failed, Status = 0x%08lx\n", Status); 308 return Status; 309 } 310 311 FileNameInfo = ExAllocatePoolWithTag(PagedPool, 312 Length + sizeof(UNICODE_NULL), 313 TAG_CM); 314 if (FileNameInfo == NULL) 315 { 316 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n"); 317 return STATUS_INSUFFICIENT_RESOURCES; 318 } 319 320 /* Try to get the value */ 321 Status = ZwQueryObject(FileAttributes->RootDirectory, 322 ObjectNameInformation, 323 FileNameInfo, 324 Length, 325 &Length); 326 if (!NT_SUCCESS(Status)) 327 { 328 /* Fail */ 329 DPRINT1("CmpCmdHiveOpen(): Root directory handle object name query failed, Status = 0x%08lx\n", Status); 330 ExFreePoolWithTag(FileNameInfo, TAG_CM); 331 return Status; 332 } 333 334 /* Null-terminate and add the length of the terminator */ 335 Length -= sizeof(OBJECT_NAME_INFORMATION); 336 FilePath = FileNameInfo->Name.Buffer; 337 FilePath[Length / sizeof(WCHAR)] = UNICODE_NULL; 338 Length += sizeof(UNICODE_NULL); 339 340 /* Compute the size of the full path; Length already counts the terminating NULL */ 341 Length = Length + sizeof(WCHAR) + FileAttributes->ObjectName->Length; 342 if (Length > MAXUSHORT) 343 { 344 /* Name size too long, bail out */ 345 ExFreePoolWithTag(FileNameInfo, TAG_CM); 346 return STATUS_OBJECT_PATH_INVALID; 347 } 348 349 /* Build the full path */ 350 RtlInitEmptyUnicodeString(&FileName, NULL, 0); 351 FileName.Buffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM); 352 if (!FileName.Buffer) 353 { 354 /* Fail */ 355 DPRINT1("CmpCmdHiveOpen(): Unable to allocate memory\n"); 356 ExFreePoolWithTag(FileNameInfo, TAG_CM); 357 return STATUS_INSUFFICIENT_RESOURCES; 358 } 359 FileName.MaximumLength = Length; 360 RtlCopyUnicodeString(&FileName, &FileNameInfo->Name); 361 ExFreePoolWithTag(FileNameInfo, TAG_CM); 362 363 /* 364 * Append a path terminator if needed (we have already accounted 365 * for a possible extra one when allocating the buffer). 366 */ 367 if (/* FileAttributes->ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR && */ // We excluded ObjectName starting with a path separator above. 368 FileName.Length > 0 && FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] != OBJ_NAME_PATH_SEPARATOR) 369 { 370 /* ObjectName does not start with '\' and PathBuffer does not end with '\' */ 371 FileName.Buffer[FileName.Length / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; 372 FileName.Length += sizeof(WCHAR); 373 FileName.Buffer[FileName.Length / sizeof(WCHAR)] = UNICODE_NULL; 374 } 375 376 /* Append the object name */ 377 Status = RtlAppendUnicodeStringToString(&FileName, FileAttributes->ObjectName); 378 if (!NT_SUCCESS(Status)) 379 { 380 /* Fail */ 381 DPRINT1("CmpCmdHiveOpen(): RtlAppendUnicodeStringToString() failed, Status = 0x%08lx\n", Status); 382 ExFreePoolWithTag(FileName.Buffer, TAG_CM); 383 return Status; 384 } 385 } 386 else 387 { 388 FileName = *FileAttributes->ObjectName; 389 } 390 391 /* Open the file in the current security context */ 392 Status = CmpInitHiveFromFile(&FileName, 393 0, 394 NewHive, 395 Allocate, 396 CheckFlags); 397 if (((Status == STATUS_ACCESS_DENIED) || 398 (Status == STATUS_NO_SUCH_USER) || 399 (Status == STATUS_WRONG_PASSWORD) || 400 (Status == STATUS_ACCOUNT_EXPIRED) || 401 (Status == STATUS_ACCOUNT_DISABLED) || 402 (Status == STATUS_ACCOUNT_RESTRICTION)) && 403 (ImpersonationContext)) 404 { 405 /* We failed due to an account/security error, impersonate SYSTEM */ 406 Status = SeImpersonateClientEx(ImpersonationContext, NULL); 407 if (NT_SUCCESS(Status)) 408 { 409 /* Now try again */ 410 Status = CmpInitHiveFromFile(&FileName, 411 0, 412 NewHive, 413 Allocate, 414 CheckFlags); 415 416 /* Restore impersonation token */ 417 PsRevertToSelf(); 418 } 419 } 420 421 if (FileAttributes->RootDirectory) 422 { 423 ExFreePoolWithTag(FileName.Buffer, TAG_CM); 424 } 425 426 /* Return status of open attempt */ 427 return Status; 428 } 429 430 VOID 431 NTAPI 432 CmpShutdownWorkers(VOID) 433 { 434 /* Stop lazy flushing */ 435 PAGED_CODE(); 436 KeCancelTimer(&CmpLazyFlushTimer); 437 } 438 439 VOID 440 NTAPI 441 CmSetLazyFlushState(IN BOOLEAN Enable) 442 { 443 /* Set state for lazy flusher */ 444 CmpHoldLazyFlush = !Enable; 445 } 446 447 /* EOF */ 448