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 * Eric Kohl 8 * Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org> 9 */ 10 11 /* INCLUDES ******************************************************************/ 12 13 #include "ntoskrnl.h" 14 #define NDEBUG 15 #include "debug.h" 16 17 /* FUNCTIONS *****************************************************************/ 18 19 BOOLEAN 20 NTAPI 21 CmpIsHiveAlreadyLoaded(IN HANDLE KeyHandle, 22 IN POBJECT_ATTRIBUTES SourceFile, 23 OUT PCMHIVE *CmHive) 24 { 25 NTSTATUS Status; 26 PCM_KEY_BODY KeyBody; 27 PCMHIVE Hive; 28 BOOLEAN Loaded = FALSE; 29 PAGED_CODE(); 30 31 /* Sanity check */ 32 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK(); 33 34 /* Reference the handle */ 35 Status = ObReferenceObjectByHandle(KeyHandle, 36 0, 37 CmpKeyObjectType, 38 KernelMode, 39 (PVOID)&KeyBody, 40 NULL); 41 if (!NT_SUCCESS(Status)) return Loaded; 42 43 /* Don't touch deleted KCBs */ 44 if (KeyBody->KeyControlBlock->Delete) return Loaded; 45 46 Hive = CONTAINING_RECORD(KeyBody->KeyControlBlock->KeyHive, CMHIVE, Hive); 47 48 /* Must be the root key */ 49 if (!(KeyBody->KeyControlBlock->Flags & KEY_HIVE_ENTRY) || 50 !(Hive->FileUserName.Buffer)) 51 { 52 /* It isn't */ 53 ObDereferenceObject(KeyBody); 54 return Loaded; 55 } 56 57 /* Now compare the name of the file */ 58 if (!RtlCompareUnicodeString(&Hive->FileUserName, 59 SourceFile->ObjectName, 60 TRUE)) 61 { 62 /* Same file found */ 63 Loaded = TRUE; 64 *CmHive = Hive; 65 66 /* If the hive is frozen, not sure what to do */ 67 if (Hive->Frozen) 68 { 69 /* FIXME: TODO */ 70 UNIMPLEMENTED_DBGBREAK("ERROR: Hive is frozen\n"); 71 } 72 } 73 74 /* Dereference and return result */ 75 ObDereferenceObject(KeyBody); 76 return Loaded; 77 } 78 79 BOOLEAN 80 NTAPI 81 CmpDoFlushAll(IN BOOLEAN ForceFlush) 82 { 83 PLIST_ENTRY NextEntry; 84 PCMHIVE Hive; 85 NTSTATUS Status; 86 BOOLEAN Result = TRUE; 87 88 /* Make sure that the registry isn't read-only now */ 89 if (CmpNoWrite) return TRUE; 90 91 /* Otherwise, acquire the hive list lock and disable force flush */ 92 CmpForceForceFlush = FALSE; 93 ExAcquirePushLockShared(&CmpHiveListHeadLock); 94 95 /* Loop the hive list */ 96 NextEntry = CmpHiveListHead.Flink; 97 while (NextEntry != &CmpHiveListHead) 98 { 99 /* Get the hive */ 100 Hive = CONTAINING_RECORD(NextEntry, CMHIVE, HiveList); 101 if (!(Hive->Hive.HiveFlags & HIVE_NOLAZYFLUSH)) 102 { 103 /* Acquire the flusher lock */ 104 CmpLockHiveFlusherExclusive(Hive); 105 106 /* Check for illegal state */ 107 if (ForceFlush && Hive->UseCount) 108 { 109 /* Registry needs to be locked down */ 110 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK(); 111 UNIMPLEMENTED_DBGBREAK("FIXME: Hive is damaged and needs fixup\n"); 112 } 113 114 /* Only sync if we are forced to or if it won't cause a hive shrink */ 115 if (ForceFlush || !HvHiveWillShrink(&Hive->Hive)) 116 { 117 /* Do the sync */ 118 Status = HvSyncHive(&Hive->Hive); 119 120 /* If something failed - set the flag and continue looping */ 121 if (!NT_SUCCESS(Status)) 122 Result = FALSE; 123 } 124 else 125 { 126 /* We won't flush if the hive might shrink */ 127 Result = FALSE; 128 CmpForceForceFlush = TRUE; 129 } 130 131 /* Release the flusher lock */ 132 CmpUnlockHiveFlusher(Hive); 133 } 134 135 /* Try the next entry */ 136 NextEntry = NextEntry->Flink; 137 } 138 139 /* Release lock and return */ 140 ExReleasePushLock(&CmpHiveListHeadLock); 141 return Result; 142 } 143 144 NTSTATUS 145 NTAPI 146 CmpSetValueKeyNew(IN PHHIVE Hive, 147 IN PCM_KEY_NODE Parent, 148 IN PUNICODE_STRING ValueName, 149 IN ULONG Index, 150 IN ULONG Type, 151 IN PVOID Data, 152 IN ULONG DataSize, 153 IN ULONG StorageType, 154 IN ULONG SmallData) 155 { 156 PCELL_DATA CellData; 157 HCELL_INDEX ValueCell; 158 NTSTATUS Status; 159 160 /* Check if we already have a value list */ 161 if (Parent->ValueList.Count) 162 { 163 /* Then make sure it's valid and dirty it */ 164 ASSERT(Parent->ValueList.List != HCELL_NIL); 165 if (!HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE)) 166 { 167 /* Fail if we're out of space for log changes */ 168 return STATUS_NO_LOG_SPACE; 169 } 170 } 171 172 /* Allocate a value cell */ 173 ValueCell = HvAllocateCell(Hive, 174 FIELD_OFFSET(CM_KEY_VALUE, Name) + 175 CmpNameSize(Hive, ValueName), 176 StorageType, 177 HCELL_NIL); 178 if (ValueCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES; 179 180 /* Get the actual data for it */ 181 CellData = HvGetCell(Hive, ValueCell); 182 ASSERT(CellData); 183 184 /* Now we can release it, make sure it's also dirty */ 185 HvReleaseCell(Hive, ValueCell); 186 ASSERT(HvIsCellDirty(Hive, ValueCell)); 187 188 /* Set it up and copy the name */ 189 CellData->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE; 190 _SEH2_TRY 191 { 192 /* This can crash since the name is coming from user-mode */ 193 CellData->u.KeyValue.NameLength = CmpCopyName(Hive, 194 CellData->u.KeyValue.Name, 195 ValueName); 196 } 197 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 198 { 199 /* Fail */ 200 DPRINT1("Invalid user data!\n"); 201 HvFreeCell(Hive, ValueCell); 202 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 203 } 204 _SEH2_END; 205 206 /* Check for compressed name */ 207 if (CellData->u.KeyValue.NameLength < ValueName->Length) 208 { 209 /* This is a compressed name */ 210 CellData->u.KeyValue.Flags = VALUE_COMP_NAME; 211 } 212 else 213 { 214 /* No flags to set */ 215 CellData->u.KeyValue.Flags = 0; 216 } 217 218 /* Check if this is a normal key */ 219 if (DataSize > CM_KEY_VALUE_SMALL) 220 { 221 /* Build a data cell for it */ 222 Status = CmpSetValueDataNew(Hive, 223 Data, 224 DataSize, 225 StorageType, 226 ValueCell, 227 &CellData->u.KeyValue.Data); 228 if (!NT_SUCCESS(Status)) 229 { 230 /* We failed, free the cell */ 231 HvFreeCell(Hive, ValueCell); 232 return Status; 233 } 234 235 /* Otherwise, set the data length, and make sure the data is dirty */ 236 CellData->u.KeyValue.DataLength = DataSize; 237 ASSERT(HvIsCellDirty(Hive, CellData->u.KeyValue.Data)); 238 } 239 else 240 { 241 /* This is a small key, set the data directly inside */ 242 CellData->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE; 243 CellData->u.KeyValue.Data = SmallData; 244 } 245 246 /* Set the type now */ 247 CellData->u.KeyValue.Type = Type; 248 249 /* Add this value cell to the child list */ 250 Status = CmpAddValueToList(Hive, 251 ValueCell, 252 Index, 253 StorageType, 254 &Parent->ValueList); 255 256 /* If we failed, free the entire cell, including the data */ 257 if (!NT_SUCCESS(Status)) 258 { 259 /* Overwrite the status with a known one */ 260 CmpFreeValue(Hive, ValueCell); 261 Status = STATUS_INSUFFICIENT_RESOURCES; 262 } 263 264 /* Return Status */ 265 return Status; 266 } 267 268 NTSTATUS 269 NTAPI 270 CmpSetValueKeyExisting(IN PHHIVE Hive, 271 IN HCELL_INDEX OldChild, 272 IN PCM_KEY_VALUE Value, 273 IN ULONG Type, 274 IN PVOID Data, 275 IN ULONG DataSize, 276 IN ULONG StorageType, 277 IN ULONG TempData) 278 { 279 HCELL_INDEX DataCell, NewCell; 280 PCELL_DATA CellData; 281 ULONG Length; 282 BOOLEAN WasSmall, IsSmall; 283 284 /* Registry writes must be blocked */ 285 CMP_ASSERT_FLUSH_LOCK(Hive); 286 287 /* Mark the old child cell dirty */ 288 if (!HvMarkCellDirty(Hive, OldChild, FALSE)) return STATUS_NO_LOG_SPACE; 289 290 /* See if this is a small or normal key */ 291 WasSmall = CmpIsKeyValueSmall(&Length, Value->DataLength); 292 293 /* See if our new data can fit in a small key */ 294 IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE; 295 296 /* Big keys are unsupported */ 297 ASSERT_VALUE_BIG(Hive, Length); 298 ASSERT_VALUE_BIG(Hive, DataSize); 299 300 /* Mark the old value dirty */ 301 if (!CmpMarkValueDataDirty(Hive, Value)) return STATUS_NO_LOG_SPACE; 302 303 /* Check if we have a small key */ 304 if (IsSmall) 305 { 306 /* Check if we had a normal key with some data in it */ 307 if (!(WasSmall) && (Length > 0)) 308 { 309 /* Free the previous data */ 310 CmpFreeValueData(Hive, Value->Data, Length); 311 } 312 313 /* Write our data directly */ 314 Value->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE; 315 Value->Data = TempData; 316 Value->Type = Type; 317 return STATUS_SUCCESS; 318 } 319 320 /* We have a normal key. Was the old cell also normal and had data? */ 321 if (!(WasSmall) && (Length > 0)) 322 { 323 /* Get the current data cell and actual data inside it */ 324 DataCell = Value->Data; 325 ASSERT(DataCell != HCELL_NIL); 326 CellData = HvGetCell(Hive, DataCell); 327 if (!CellData) return STATUS_INSUFFICIENT_RESOURCES; 328 329 /* Immediately release the cell */ 330 HvReleaseCell(Hive, DataCell); 331 332 /* Make sure that the data cell actually has a size */ 333 ASSERT(HvGetCellSize(Hive, CellData) > 0); 334 335 /* Check if the previous data cell could fit our new data */ 336 if (DataSize <= (ULONG)(HvGetCellSize(Hive, CellData))) 337 { 338 /* Re-use it then */ 339 NewCell = DataCell; 340 } 341 else 342 { 343 /* Otherwise, re-allocate the current data cell */ 344 NewCell = HvReallocateCell(Hive, DataCell, DataSize); 345 if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES; 346 } 347 } 348 else 349 { 350 /* This was a small key, or a key with no data, allocate a cell */ 351 NewCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL); 352 if (NewCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES; 353 } 354 355 /* Now get the actual data for our data cell */ 356 CellData = HvGetCell(Hive, NewCell); 357 ASSERT(CellData); 358 359 /* Release it immediately */ 360 HvReleaseCell(Hive, NewCell); 361 362 /* Copy our data into the data cell's buffer, and set up the value */ 363 RtlCopyMemory(CellData, Data, DataSize); 364 Value->Data = NewCell; 365 Value->DataLength = DataSize; 366 Value->Type = Type; 367 368 /* Return success */ 369 ASSERT(HvIsCellDirty(Hive, NewCell)); 370 return STATUS_SUCCESS; 371 } 372 373 NTSTATUS 374 NTAPI 375 CmpQueryKeyData(IN PHHIVE Hive, 376 IN PCM_KEY_NODE Node, 377 IN KEY_INFORMATION_CLASS KeyInformationClass, 378 IN OUT PVOID KeyInformation, 379 IN ULONG Length, 380 IN OUT PULONG ResultLength) 381 { 382 NTSTATUS Status; 383 ULONG Size, SizeLeft, MinimumSize, Offset; 384 PKEY_INFORMATION Info = (PKEY_INFORMATION)KeyInformation; 385 USHORT NameLength; 386 PVOID ClassData; 387 388 /* Check if the value is compressed */ 389 if (Node->Flags & KEY_COMP_NAME) 390 { 391 /* Get the compressed name size */ 392 NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength); 393 } 394 else 395 { 396 /* Get the real size */ 397 NameLength = Node->NameLength; 398 } 399 400 /* Check what kind of information is being requested */ 401 switch (KeyInformationClass) 402 { 403 /* Basic information */ 404 case KeyBasicInformation: 405 406 /* This is the size we need */ 407 Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) + NameLength; 408 409 /* And this is the minimum we can work with */ 410 MinimumSize = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name); 411 412 /* Let the caller know and assume success */ 413 *ResultLength = Size; 414 Status = STATUS_SUCCESS; 415 416 /* Check if the bufer we got is too small */ 417 if (Length < MinimumSize) 418 { 419 /* Let the caller know and fail */ 420 Status = STATUS_BUFFER_TOO_SMALL; 421 break; 422 } 423 424 /* Copy the basic information */ 425 Info->KeyBasicInformation.LastWriteTime = Node->LastWriteTime; 426 Info->KeyBasicInformation.TitleIndex = 0; 427 Info->KeyBasicInformation.NameLength = NameLength; 428 429 /* Only the name is left */ 430 SizeLeft = Length - MinimumSize; 431 Size = NameLength; 432 433 /* Check if we don't have enough space for the name */ 434 if (SizeLeft < Size) 435 { 436 /* Truncate the name we'll return, and tell the caller */ 437 Size = SizeLeft; 438 Status = STATUS_BUFFER_OVERFLOW; 439 } 440 441 /* Check if this is a compressed key */ 442 if (Node->Flags & KEY_COMP_NAME) 443 { 444 /* Copy the compressed name */ 445 CmpCopyCompressedName(Info->KeyBasicInformation.Name, 446 SizeLeft, 447 Node->Name, 448 Node->NameLength); 449 } 450 else 451 { 452 /* Otherwise, copy the raw name */ 453 RtlCopyMemory(Info->KeyBasicInformation.Name, 454 Node->Name, 455 Size); 456 } 457 break; 458 459 /* Node information */ 460 case KeyNodeInformation: 461 462 /* Calculate the size we need */ 463 Size = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) + 464 NameLength + 465 Node->ClassLength; 466 467 /* And the minimum size we can support */ 468 MinimumSize = FIELD_OFFSET(KEY_NODE_INFORMATION, Name); 469 470 /* Return the size to the caller and assume succes */ 471 *ResultLength = Size; 472 Status = STATUS_SUCCESS; 473 474 /* Check if the caller's buffer is too small */ 475 if (Length < MinimumSize) 476 { 477 /* Let them know, and fail */ 478 Status = STATUS_BUFFER_TOO_SMALL; 479 break; 480 } 481 482 /* Copy the basic information */ 483 Info->KeyNodeInformation.LastWriteTime = Node->LastWriteTime; 484 Info->KeyNodeInformation.TitleIndex = 0; 485 Info->KeyNodeInformation.ClassLength = Node->ClassLength; 486 Info->KeyNodeInformation.NameLength = NameLength; 487 488 /* Now the name is left */ 489 SizeLeft = Length - MinimumSize; 490 Size = NameLength; 491 492 /* Check if the name can fit entirely */ 493 if (SizeLeft < Size) 494 { 495 /* It can't, we'll have to truncate. Tell the caller */ 496 Size = SizeLeft; 497 Status = STATUS_BUFFER_OVERFLOW; 498 } 499 500 /* Check if the key node name is compressed */ 501 if (Node->Flags & KEY_COMP_NAME) 502 { 503 /* Copy the compressed name */ 504 CmpCopyCompressedName(Info->KeyNodeInformation.Name, 505 SizeLeft, 506 Node->Name, 507 Node->NameLength); 508 } 509 else 510 { 511 /* It isn't, so copy the raw name */ 512 RtlCopyMemory(Info->KeyNodeInformation.Name, 513 Node->Name, 514 Size); 515 } 516 517 /* Check if the node has a class */ 518 if (Node->ClassLength > 0) 519 { 520 /* Set the class offset */ 521 Offset = FIELD_OFFSET(KEY_NODE_INFORMATION, Name) + NameLength; 522 Offset = ALIGN_UP_BY(Offset, sizeof(ULONG)); 523 Info->KeyNodeInformation.ClassOffset = Offset; 524 525 /* Get the class data */ 526 ClassData = HvGetCell(Hive, Node->Class); 527 if (ClassData == NULL) 528 { 529 Status = STATUS_INSUFFICIENT_RESOURCES; 530 break; 531 } 532 533 /* Check if we can copy anything */ 534 if (Length > Offset) 535 { 536 /* Copy the class data */ 537 RtlCopyMemory((PUCHAR)Info + Offset, 538 ClassData, 539 min(Node->ClassLength, Length - Offset)); 540 } 541 542 /* Check if the buffer was large enough */ 543 if (Length < Offset + Node->ClassLength) 544 { 545 Status = STATUS_BUFFER_OVERFLOW; 546 } 547 548 /* Release the class cell */ 549 HvReleaseCell(Hive, Node->Class); 550 } 551 else 552 { 553 /* It doesn't, so set offset to -1, not 0! */ 554 Info->KeyNodeInformation.ClassOffset = 0xFFFFFFFF; 555 } 556 break; 557 558 /* Full information requsted */ 559 case KeyFullInformation: 560 561 /* This is the size we need */ 562 Size = FIELD_OFFSET(KEY_FULL_INFORMATION, Class) + 563 Node->ClassLength; 564 565 /* This is what we can work with */ 566 MinimumSize = FIELD_OFFSET(KEY_FULL_INFORMATION, Class); 567 568 /* Return it to caller and assume success */ 569 *ResultLength = Size; 570 Status = STATUS_SUCCESS; 571 572 /* Check if the caller's buffer is to small */ 573 if (Length < MinimumSize) 574 { 575 /* Let them know and fail */ 576 Status = STATUS_BUFFER_TOO_SMALL; 577 break; 578 } 579 580 /* Now copy all the basic information */ 581 Info->KeyFullInformation.LastWriteTime = Node->LastWriteTime; 582 Info->KeyFullInformation.TitleIndex = 0; 583 Info->KeyFullInformation.ClassLength = Node->ClassLength; 584 Info->KeyFullInformation.SubKeys = Node->SubKeyCounts[Stable] + 585 Node->SubKeyCounts[Volatile]; 586 Info->KeyFullInformation.Values = Node->ValueList.Count; 587 Info->KeyFullInformation.MaxNameLen = Node->MaxNameLen; 588 Info->KeyFullInformation.MaxClassLen = Node->MaxClassLen; 589 Info->KeyFullInformation.MaxValueNameLen = Node->MaxValueNameLen; 590 Info->KeyFullInformation.MaxValueDataLen = Node->MaxValueDataLen; 591 592 /* Check if we have a class */ 593 if (Node->ClassLength > 0) 594 { 595 /* Set the class offset */ 596 Offset = FIELD_OFFSET(KEY_FULL_INFORMATION, Class); 597 Info->KeyFullInformation.ClassOffset = Offset; 598 599 /* Get the class data */ 600 ClassData = HvGetCell(Hive, Node->Class); 601 if (ClassData == NULL) 602 { 603 Status = STATUS_INSUFFICIENT_RESOURCES; 604 break; 605 } 606 607 /* Copy the class data */ 608 ASSERT(Length >= Offset); 609 RtlCopyMemory(Info->KeyFullInformation.Class, 610 ClassData, 611 min(Node->ClassLength, Length - Offset)); 612 613 /* Check if the buffer was large enough */ 614 if (Length < Offset + Node->ClassLength) 615 { 616 Status = STATUS_BUFFER_OVERFLOW; 617 } 618 619 /* Release the class cell */ 620 HvReleaseCell(Hive, Node->Class); 621 } 622 else 623 { 624 /* We don't have a class, so set offset to -1, not 0! */ 625 Info->KeyFullInformation.ClassOffset = 0xFFFFFFFF; 626 } 627 break; 628 629 /* Any other class that got sent here is invalid! */ 630 default: 631 632 /* Set failure code */ 633 Status = STATUS_INVALID_PARAMETER; 634 break; 635 } 636 637 /* Return status */ 638 return Status; 639 } 640 641 NTSTATUS 642 NTAPI 643 CmSetValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb, 644 IN PUNICODE_STRING ValueName, 645 IN ULONG Type, 646 IN PVOID Data, 647 IN ULONG DataLength) 648 { 649 PHHIVE Hive = NULL; 650 PCM_KEY_NODE Parent; 651 PCM_KEY_VALUE Value = NULL; 652 HCELL_INDEX CurrentChild, Cell; 653 NTSTATUS Status; 654 BOOLEAN Found, Result; 655 ULONG Count, ChildIndex, SmallData, Storage; 656 VALUE_SEARCH_RETURN_TYPE SearchResult; 657 BOOLEAN FirstTry = TRUE, FlusherLocked = FALSE; 658 HCELL_INDEX ParentCell = HCELL_NIL, ChildCell = HCELL_NIL; 659 660 /* Acquire hive and KCB lock */ 661 CmpLockRegistry(); 662 CmpAcquireKcbLockShared(Kcb); 663 664 /* Sanity check */ 665 ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL); 666 667 /* Don't touch deleted KCBs */ 668 DoAgain: 669 if (Kcb->Delete) 670 { 671 /* Fail */ 672 Status = STATUS_KEY_DELETED; 673 goto Quickie; 674 } 675 676 /* Don't let anyone mess with symlinks */ 677 if ((Kcb->Flags & KEY_SYM_LINK) && 678 ((Type != REG_LINK) || 679 !(ValueName) || 680 !(RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE)))) 681 { 682 /* Invalid modification of a symlink key */ 683 Status = STATUS_ACCESS_DENIED; 684 goto Quickie; 685 } 686 687 /* Check if this is the first attempt */ 688 if (FirstTry) 689 { 690 /* Search for the value in the cache */ 691 SearchResult = CmpCompareNewValueDataAgainstKCBCache(Kcb, 692 ValueName, 693 Type, 694 Data, 695 DataLength); 696 if (SearchResult == SearchNeedExclusiveLock) 697 { 698 /* Try again with the exclusive lock */ 699 CmpConvertKcbSharedToExclusive(Kcb); 700 goto DoAgain; 701 } 702 else if (SearchResult == SearchSuccess) 703 { 704 /* We don't actually need to do anything! */ 705 Status = STATUS_SUCCESS; 706 goto Quickie; 707 } 708 709 /* We need the exclusive KCB lock now */ 710 if (!CmpIsKcbLockedExclusive(Kcb) && 711 !CmpTryToConvertKcbSharedToExclusive(Kcb)) 712 { 713 /* Acquire exclusive lock */ 714 CmpConvertKcbSharedToExclusive(Kcb); 715 } 716 717 /* Cache lookup failed, so don't try it next time */ 718 FirstTry = FALSE; 719 720 /* Now grab the flush lock since the key will be modified */ 721 ASSERT(FlusherLocked == FALSE); 722 CmpLockHiveFlusherShared((PCMHIVE)Kcb->KeyHive); 723 FlusherLocked = TRUE; 724 goto DoAgain; 725 } 726 else 727 { 728 /* Get pointer to key cell */ 729 Hive = Kcb->KeyHive; 730 Cell = Kcb->KeyCell; 731 732 /* Get the parent */ 733 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 734 ASSERT(Parent); 735 ParentCell = Cell; 736 737 /* Prepare to scan the key node */ 738 Count = Parent->ValueList.Count; 739 Found = FALSE; 740 if (Count > 0) 741 { 742 /* Try to find the existing name */ 743 Result = CmpFindNameInList(Hive, 744 &Parent->ValueList, 745 ValueName, 746 &ChildIndex, 747 &CurrentChild); 748 if (!Result) 749 { 750 /* Fail */ 751 Status = STATUS_INSUFFICIENT_RESOURCES; 752 goto Quickie; 753 } 754 755 /* Check if we found something */ 756 if (CurrentChild != HCELL_NIL) 757 { 758 /* Release existing child */ 759 if (ChildCell != HCELL_NIL) 760 { 761 HvReleaseCell(Hive, ChildCell); 762 ChildCell = HCELL_NIL; 763 } 764 765 /* Get its value */ 766 Value = (PCM_KEY_VALUE)HvGetCell(Hive, CurrentChild); 767 if (!Value) 768 { 769 /* Fail */ 770 Status = STATUS_INSUFFICIENT_RESOURCES; 771 goto Quickie; 772 } 773 774 /* Remember that we found it */ 775 ChildCell = CurrentChild; 776 Found = TRUE; 777 } 778 } 779 else 780 { 781 /* No child list, we'll need to add it */ 782 ChildIndex = 0; 783 } 784 } 785 786 /* Should only get here on the second pass */ 787 ASSERT(FirstTry == FALSE); 788 789 /* The KCB must be locked exclusive at this point */ 790 CMP_ASSERT_KCB_LOCK(Kcb); 791 792 /* Mark the cell dirty */ 793 if (!HvMarkCellDirty(Hive, Cell, FALSE)) 794 { 795 /* Not enough log space, fail */ 796 Status = STATUS_NO_LOG_SPACE; 797 goto Quickie; 798 } 799 800 /* Get the storage type */ 801 Storage = HvGetCellType(Cell); 802 803 /* Check if this is small data */ 804 SmallData = 0; 805 if ((DataLength <= CM_KEY_VALUE_SMALL) && (DataLength > 0)) 806 { 807 /* Need SEH because user data may be invalid */ 808 _SEH2_TRY 809 { 810 /* Copy it */ 811 RtlCopyMemory(&SmallData, Data, DataLength); 812 } 813 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 814 { 815 /* Return failure code */ 816 Status = _SEH2_GetExceptionCode(); 817 _SEH2_YIELD(goto Quickie); 818 } 819 _SEH2_END; 820 } 821 822 /* Check if we didn't find a matching key */ 823 if (!Found) 824 { 825 /* Call the internal routine */ 826 Status = CmpSetValueKeyNew(Hive, 827 Parent, 828 ValueName, 829 ChildIndex, 830 Type, 831 Data, 832 DataLength, 833 Storage, 834 SmallData); 835 } 836 else 837 { 838 /* Call the internal routine */ 839 Status = CmpSetValueKeyExisting(Hive, 840 CurrentChild, 841 Value, 842 Type, 843 Data, 844 DataLength, 845 Storage, 846 SmallData); 847 } 848 849 /* Check for success */ 850 if (NT_SUCCESS(Status)) 851 { 852 /* Check if the maximum value name length changed */ 853 ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen); 854 if (Parent->MaxValueNameLen < ValueName->Length) 855 { 856 /* Set the new values */ 857 Parent->MaxValueNameLen = ValueName->Length; 858 Kcb->KcbMaxValueNameLen = ValueName->Length; 859 } 860 861 /* Check if the maximum data length changed */ 862 ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen); 863 if (Parent->MaxValueDataLen < DataLength) 864 { 865 /* Update it */ 866 Parent->MaxValueDataLen = DataLength; 867 Kcb->KcbMaxValueDataLen = Parent->MaxValueDataLen; 868 } 869 870 /* Save the write time */ 871 KeQuerySystemTime(&Parent->LastWriteTime); 872 Kcb->KcbLastWriteTime = Parent->LastWriteTime; 873 874 /* Check if the cell is cached */ 875 if (Found && CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)) 876 { 877 /* Shouldn't happen */ 878 ASSERT(FALSE); 879 } 880 else 881 { 882 /* Cleanup the value cache */ 883 CmpCleanUpKcbValueCache(Kcb); 884 885 /* Sanity checks */ 886 ASSERT(!CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)); 887 ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)); 888 889 /* Set the value cache */ 890 Kcb->ValueCache.Count = Parent->ValueList.Count; 891 Kcb->ValueCache.ValueList = Parent->ValueList.List; 892 } 893 894 /* Notify registered callbacks */ 895 CmpReportNotify(Kcb, 896 Hive, 897 Kcb->KeyCell, 898 REG_NOTIFY_CHANGE_LAST_SET); 899 } 900 901 /* Release the cells */ 902 Quickie: 903 if ((ParentCell != HCELL_NIL) && Hive) HvReleaseCell(Hive, ParentCell); 904 if ((ChildCell != HCELL_NIL) && Hive) HvReleaseCell(Hive, ChildCell); 905 906 /* Release the locks */ 907 if (FlusherLocked) CmpUnlockHiveFlusher((PCMHIVE)Hive); 908 CmpReleaseKcbLock(Kcb); 909 CmpUnlockRegistry(); 910 return Status; 911 } 912 913 NTSTATUS 914 NTAPI 915 CmDeleteValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb, 916 IN UNICODE_STRING ValueName) 917 { 918 NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND; 919 PHHIVE Hive; 920 PCM_KEY_NODE Parent; 921 HCELL_INDEX ChildCell, Cell; 922 PCHILD_LIST ChildList; 923 PCM_KEY_VALUE Value = NULL; 924 ULONG ChildIndex; 925 BOOLEAN Result; 926 927 /* Acquire hive lock */ 928 CmpLockRegistry(); 929 930 /* Lock KCB exclusively */ 931 CmpAcquireKcbLockExclusive(Kcb); 932 933 /* Don't touch deleted keys */ 934 if (Kcb->Delete) 935 { 936 /* Undo everything */ 937 CmpReleaseKcbLock(Kcb); 938 CmpUnlockRegistry(); 939 return STATUS_KEY_DELETED; 940 } 941 942 /* Get the hive and the cell index */ 943 Hive = Kcb->KeyHive; 944 Cell = Kcb->KeyCell; 945 946 /* Lock flushes */ 947 CmpLockHiveFlusherShared((PCMHIVE)Hive); 948 949 /* Get the parent key node */ 950 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 951 ASSERT(Parent); 952 953 /* Get the value list and check if it has any entries */ 954 ChildList = &Parent->ValueList; 955 if (ChildList->Count) 956 { 957 /* Try to find this value */ 958 Result = CmpFindNameInList(Hive, 959 ChildList, 960 &ValueName, 961 &ChildIndex, 962 &ChildCell); 963 if (!Result) 964 { 965 /* Fail */ 966 Status = STATUS_INSUFFICIENT_RESOURCES; 967 goto Quickie; 968 } 969 970 /* Value not found, return error */ 971 if (ChildCell == HCELL_NIL) goto Quickie; 972 973 /* We found the value, mark all relevant cells dirty */ 974 if (!(HvMarkCellDirty(Hive, Cell, FALSE) && 975 HvMarkCellDirty(Hive, Parent->ValueList.List, FALSE) && 976 HvMarkCellDirty(Hive, ChildCell, FALSE))) 977 { 978 /* Not enough log space, fail */ 979 Status = STATUS_NO_LOG_SPACE; 980 goto Quickie; 981 } 982 983 /* Get the key value */ 984 Value = (PCM_KEY_VALUE)HvGetCell(Hive, ChildCell); 985 ASSERT(Value); 986 987 /* Mark it and all related data as dirty */ 988 if (!CmpMarkValueDataDirty(Hive, Value)) 989 { 990 /* Not enough log space, fail */ 991 Status = STATUS_NO_LOG_SPACE; 992 goto Quickie; 993 } 994 995 /* Sanity checks */ 996 ASSERT(HvIsCellDirty(Hive, Parent->ValueList.List)); 997 ASSERT(HvIsCellDirty(Hive, ChildCell)); 998 999 /* Remove the value from the child list */ 1000 Status = CmpRemoveValueFromList(Hive, ChildIndex, ChildList); 1001 if (!NT_SUCCESS(Status)) 1002 { 1003 /* Set known error */ 1004 Status = STATUS_INSUFFICIENT_RESOURCES; 1005 goto Quickie; 1006 } 1007 1008 /* Remove the value and its data itself */ 1009 if (!CmpFreeValue(Hive, ChildCell)) 1010 { 1011 /* Failed to free the value, fail */ 1012 Status = STATUS_INSUFFICIENT_RESOURCES; 1013 goto Quickie; 1014 } 1015 1016 /* Set the last write time */ 1017 KeQuerySystemTime(&Parent->LastWriteTime); 1018 Kcb->KcbLastWriteTime = Parent->LastWriteTime; 1019 1020 /* Sanity check */ 1021 ASSERT(Parent->MaxValueNameLen == Kcb->KcbMaxValueNameLen); 1022 ASSERT(Parent->MaxValueDataLen == Kcb->KcbMaxValueDataLen); 1023 ASSERT(HvIsCellDirty(Hive, Cell)); 1024 1025 /* Check if the value list is empty now */ 1026 if (!Parent->ValueList.Count) 1027 { 1028 /* Then clear key node data */ 1029 Parent->MaxValueNameLen = 0; 1030 Parent->MaxValueDataLen = 0; 1031 Kcb->KcbMaxValueNameLen = 0; 1032 Kcb->KcbMaxValueDataLen = 0; 1033 } 1034 1035 /* Cleanup the value cache */ 1036 CmpCleanUpKcbValueCache(Kcb); 1037 1038 /* Sanity checks */ 1039 ASSERT(!CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)); 1040 ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)); 1041 1042 /* Set the value cache */ 1043 Kcb->ValueCache.Count = ChildList->Count; 1044 Kcb->ValueCache.ValueList = ChildList->List; 1045 1046 /* Notify registered callbacks */ 1047 CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_LAST_SET); 1048 1049 /* Change default Status to success */ 1050 Status = STATUS_SUCCESS; 1051 } 1052 1053 Quickie: 1054 /* Release the parent cell, if any */ 1055 if (Parent) HvReleaseCell(Hive, Cell); 1056 1057 /* Check if we had a value */ 1058 if (Value) 1059 { 1060 /* Release the child cell */ 1061 ASSERT(ChildCell != HCELL_NIL); 1062 HvReleaseCell(Hive, ChildCell); 1063 } 1064 1065 /* Release locks */ 1066 CmpUnlockHiveFlusher((PCMHIVE)Hive); 1067 CmpReleaseKcbLock(Kcb); 1068 CmpUnlockRegistry(); 1069 return Status; 1070 } 1071 1072 NTSTATUS 1073 NTAPI 1074 CmQueryValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb, 1075 IN UNICODE_STRING ValueName, 1076 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, 1077 IN PVOID KeyValueInformation, 1078 IN ULONG Length, 1079 IN PULONG ResultLength) 1080 { 1081 NTSTATUS Status; 1082 PCM_KEY_VALUE ValueData; 1083 ULONG Index; 1084 BOOLEAN ValueCached = FALSE; 1085 PCM_CACHED_VALUE *CachedValue; 1086 HCELL_INDEX CellToRelease; 1087 VALUE_SEARCH_RETURN_TYPE Result; 1088 PHHIVE Hive; 1089 PAGED_CODE(); 1090 1091 /* Acquire hive lock */ 1092 CmpLockRegistry(); 1093 1094 /* Lock the KCB shared */ 1095 CmpAcquireKcbLockShared(Kcb); 1096 1097 /* Don't touch deleted keys */ 1098 DoAgain: 1099 if (Kcb->Delete) 1100 { 1101 /* Undo everything */ 1102 CmpReleaseKcbLock(Kcb); 1103 CmpUnlockRegistry(); 1104 return STATUS_KEY_DELETED; 1105 } 1106 1107 /* We don't deal with this yet */ 1108 if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) 1109 { 1110 /* Shouldn't happen */ 1111 ASSERT(FALSE); 1112 } 1113 1114 /* Get the hive */ 1115 Hive = Kcb->KeyHive; 1116 1117 /* Find the key value */ 1118 Result = CmpFindValueByNameFromCache(Kcb, 1119 &ValueName, 1120 &CachedValue, 1121 &Index, 1122 &ValueData, 1123 &ValueCached, 1124 &CellToRelease); 1125 if (Result == SearchNeedExclusiveLock) 1126 { 1127 /* Check if we need an exclusive lock */ 1128 ASSERT(CellToRelease == HCELL_NIL); 1129 ASSERT(ValueData == NULL); 1130 1131 /* Try with exclusive KCB lock */ 1132 CmpConvertKcbSharedToExclusive(Kcb); 1133 goto DoAgain; 1134 } 1135 1136 if (Result == SearchSuccess) 1137 { 1138 /* Sanity check */ 1139 ASSERT(ValueData != NULL); 1140 1141 /* User data, protect against exceptions */ 1142 _SEH2_TRY 1143 { 1144 /* Query the information requested */ 1145 Result = CmpQueryKeyValueData(Kcb, 1146 CachedValue, 1147 ValueData, 1148 ValueCached, 1149 KeyValueInformationClass, 1150 KeyValueInformation, 1151 Length, 1152 ResultLength, 1153 &Status); 1154 if (Result == SearchNeedExclusiveLock) 1155 { 1156 /* Release the value cell */ 1157 if (CellToRelease != HCELL_NIL) 1158 { 1159 HvReleaseCell(Hive, CellToRelease); 1160 CellToRelease = HCELL_NIL; 1161 } 1162 1163 /* Try with exclusive KCB lock */ 1164 CmpConvertKcbSharedToExclusive(Kcb); 1165 _SEH2_YIELD(goto DoAgain); 1166 } 1167 } 1168 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1169 { 1170 Status = _SEH2_GetExceptionCode(); 1171 } 1172 _SEH2_END; 1173 } 1174 else 1175 { 1176 /* Failed to find the value */ 1177 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1178 } 1179 1180 /* If we have a cell to release, do so */ 1181 if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease); 1182 1183 /* Release locks */ 1184 CmpReleaseKcbLock(Kcb); 1185 CmpUnlockRegistry(); 1186 return Status; 1187 } 1188 1189 NTSTATUS 1190 NTAPI 1191 CmEnumerateValueKey(IN PCM_KEY_CONTROL_BLOCK Kcb, 1192 IN ULONG Index, 1193 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, 1194 IN PVOID KeyValueInformation, 1195 IN ULONG Length, 1196 IN PULONG ResultLength) 1197 { 1198 NTSTATUS Status; 1199 PHHIVE Hive; 1200 PCM_KEY_NODE Parent; 1201 HCELL_INDEX CellToRelease = HCELL_NIL, CellToRelease2 = HCELL_NIL; 1202 VALUE_SEARCH_RETURN_TYPE Result; 1203 BOOLEAN IndexIsCached, ValueIsCached = FALSE; 1204 PCELL_DATA CellData; 1205 PCM_CACHED_VALUE *CachedValue; 1206 PCM_KEY_VALUE ValueData = NULL; 1207 PAGED_CODE(); 1208 1209 /* Acquire hive lock */ 1210 CmpLockRegistry(); 1211 1212 /* Lock the KCB shared */ 1213 CmpAcquireKcbLockShared(Kcb); 1214 1215 /* Don't touch deleted keys */ 1216 DoAgain: 1217 if (Kcb->Delete) 1218 { 1219 /* Undo everything */ 1220 CmpReleaseKcbLock(Kcb); 1221 CmpUnlockRegistry(); 1222 return STATUS_KEY_DELETED; 1223 } 1224 1225 /* Get the hive and parent */ 1226 Hive = Kcb->KeyHive; 1227 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell); 1228 ASSERT(Parent); 1229 1230 /* FIXME: Lack of cache? */ 1231 if (Kcb->ValueCache.Count != Parent->ValueList.Count) 1232 { 1233 DPRINT1("HACK: Overriding value cache count\n"); 1234 Kcb->ValueCache.Count = Parent->ValueList.Count; 1235 } 1236 1237 /* Make sure the index is valid */ 1238 if (Index >= Kcb->ValueCache.Count) 1239 { 1240 /* Release the cell and fail */ 1241 HvReleaseCell(Hive, Kcb->KeyCell); 1242 Status = STATUS_NO_MORE_ENTRIES; 1243 goto Quickie; 1244 } 1245 1246 /* We don't deal with this yet */ 1247 if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) 1248 { 1249 /* Shouldn't happen */ 1250 ASSERT(FALSE); 1251 } 1252 1253 /* Find the value list */ 1254 Result = CmpGetValueListFromCache(Kcb, 1255 &CellData, 1256 &IndexIsCached, 1257 &CellToRelease); 1258 if (Result == SearchNeedExclusiveLock) 1259 { 1260 /* Check if we need an exclusive lock */ 1261 ASSERT(CellToRelease == HCELL_NIL); 1262 HvReleaseCell(Hive, Kcb->KeyCell); 1263 1264 /* Try with exclusive KCB lock */ 1265 CmpConvertKcbSharedToExclusive(Kcb); 1266 goto DoAgain; 1267 } 1268 else if (Result != SearchSuccess) 1269 { 1270 /* Sanity check */ 1271 ASSERT(CellData == NULL); 1272 1273 /* Release the cell and fail */ 1274 Status = STATUS_INSUFFICIENT_RESOURCES; 1275 goto Quickie; 1276 } 1277 1278 /* Now get the key value */ 1279 Result = CmpGetValueKeyFromCache(Kcb, 1280 CellData, 1281 Index, 1282 &CachedValue, 1283 &ValueData, 1284 IndexIsCached, 1285 &ValueIsCached, 1286 &CellToRelease2); 1287 if (Result == SearchNeedExclusiveLock) 1288 { 1289 /* Cleanup state */ 1290 ASSERT(CellToRelease2 == HCELL_NIL); 1291 if (CellToRelease) 1292 { 1293 HvReleaseCell(Hive, CellToRelease); 1294 CellToRelease = HCELL_NIL; 1295 } 1296 HvReleaseCell(Hive, Kcb->KeyCell); 1297 1298 /* Try with exclusive KCB lock */ 1299 CmpConvertKcbSharedToExclusive(Kcb); 1300 goto DoAgain; 1301 } 1302 else if (Result != SearchSuccess) 1303 { 1304 /* Sanity check */ 1305 ASSERT(ValueData == NULL); 1306 1307 /* Release the cells and fail */ 1308 Status = STATUS_INSUFFICIENT_RESOURCES; 1309 goto Quickie; 1310 } 1311 1312 /* User data, need SEH */ 1313 _SEH2_TRY 1314 { 1315 /* Query the information requested */ 1316 Result = CmpQueryKeyValueData(Kcb, 1317 CachedValue, 1318 ValueData, 1319 ValueIsCached, 1320 KeyValueInformationClass, 1321 KeyValueInformation, 1322 Length, 1323 ResultLength, 1324 &Status); 1325 if (Result == SearchNeedExclusiveLock) 1326 { 1327 /* Cleanup state */ 1328 if (CellToRelease2) HvReleaseCell(Hive, CellToRelease2); 1329 HvReleaseCell(Hive, Kcb->KeyCell); 1330 if (CellToRelease) HvReleaseCell(Hive, CellToRelease); 1331 1332 /* Try with exclusive KCB lock */ 1333 CmpConvertKcbSharedToExclusive(Kcb); 1334 _SEH2_YIELD(goto DoAgain); 1335 } 1336 } 1337 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1338 { 1339 /* Get exception code */ 1340 Status = _SEH2_GetExceptionCode(); 1341 } 1342 _SEH2_END; 1343 1344 Quickie: 1345 /* If we have a cell to release, do so */ 1346 if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease); 1347 1348 /* Release the parent cell */ 1349 HvReleaseCell(Hive, Kcb->KeyCell); 1350 1351 /* If we have a cell to release, do so */ 1352 if (CellToRelease2 != HCELL_NIL) HvReleaseCell(Hive, CellToRelease2); 1353 1354 /* Release locks */ 1355 CmpReleaseKcbLock(Kcb); 1356 CmpUnlockRegistry(); 1357 return Status; 1358 } 1359 1360 static 1361 NTSTATUS 1362 CmpQueryKeyDataFromCache( 1363 _In_ PCM_KEY_CONTROL_BLOCK Kcb, 1364 _Out_ PKEY_CACHED_INFORMATION KeyCachedInfo, 1365 _In_ ULONG Length, 1366 _Out_ PULONG ResultLength) 1367 { 1368 PCM_KEY_NODE Node; 1369 PHHIVE KeyHive; 1370 HCELL_INDEX KeyCell; 1371 USHORT NameLength; 1372 PAGED_CODE(); 1373 1374 /* Get the hive and cell index */ 1375 KeyHive = Kcb->KeyHash.KeyHive; 1376 KeyCell = Kcb->KeyHash.KeyCell; 1377 1378 #if DBG 1379 /* Get the cell node */ 1380 Node = (PCM_KEY_NODE)HvGetCell(KeyHive, KeyCell); 1381 if (Node != NULL) 1382 { 1383 ULONG SubKeyCount; 1384 ASSERT(Node->ValueList.Count == Kcb->ValueCache.Count); 1385 1386 if (!(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO)) 1387 { 1388 SubKeyCount = Node->SubKeyCounts[0] + Node->SubKeyCounts[1]; 1389 if (Kcb->ExtFlags & CM_KCB_NO_SUBKEY) 1390 { 1391 ASSERT(SubKeyCount == 0); 1392 } 1393 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_ONE) 1394 { 1395 ASSERT(SubKeyCount == 1); 1396 } 1397 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) 1398 { 1399 ASSERT(SubKeyCount == Kcb->IndexHint->Count); 1400 } 1401 else 1402 { 1403 ASSERT(SubKeyCount == Kcb->SubKeyCount); 1404 } 1405 } 1406 1407 ASSERT(Node->LastWriteTime.QuadPart == Kcb->KcbLastWriteTime.QuadPart); 1408 ASSERT(Node->MaxNameLen == Kcb->KcbMaxNameLen); 1409 ASSERT(Node->MaxValueNameLen == Kcb->KcbMaxValueNameLen); 1410 ASSERT(Node->MaxValueDataLen == Kcb->KcbMaxValueDataLen); 1411 1412 /* Release the cell */ 1413 HvReleaseCell(KeyHive, KeyCell); 1414 } 1415 #endif // DBG 1416 1417 /* Make sure we have a name block */ 1418 if (Kcb->NameBlock == NULL) 1419 { 1420 return STATUS_INSUFFICIENT_RESOURCES; 1421 } 1422 1423 /* Check for compressed name */ 1424 if (Kcb->NameBlock->Compressed) 1425 { 1426 /* Calculate the name size */ 1427 NameLength = CmpCompressedNameSize(Kcb->NameBlock->NameHash.Name, 1428 Kcb->NameBlock->NameHash.NameLength); 1429 } 1430 else 1431 { 1432 /* Use the stored name size */ 1433 NameLength = Kcb->NameBlock->NameHash.NameLength; 1434 } 1435 1436 /* Validate buffer length (we do not copy the name!) */ 1437 *ResultLength = sizeof(*KeyCachedInfo); 1438 if (Length < *ResultLength) 1439 { 1440 return STATUS_BUFFER_TOO_SMALL; 1441 } 1442 1443 /* Fill the structure */ 1444 KeyCachedInfo->LastWriteTime = Kcb->KcbLastWriteTime; 1445 KeyCachedInfo->TitleIndex = 0; 1446 KeyCachedInfo->NameLength = NameLength; 1447 KeyCachedInfo->Values = Kcb->ValueCache.Count; 1448 KeyCachedInfo->MaxNameLen = Kcb->KcbMaxNameLen; 1449 KeyCachedInfo->MaxValueNameLen = Kcb->KcbMaxValueNameLen; 1450 KeyCachedInfo->MaxValueDataLen = Kcb->KcbMaxValueDataLen; 1451 1452 /* Check the ExtFlags for what we have */ 1453 if (Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO) 1454 { 1455 /* Cache is not valid, do a full lookup */ 1456 DPRINT1("Kcb cache incoherency detected, kcb = %p\n", Kcb); 1457 1458 /* Get the cell node */ 1459 Node = (PCM_KEY_NODE)HvGetCell(KeyHive, KeyCell); 1460 if (Node == NULL) 1461 { 1462 return STATUS_INSUFFICIENT_RESOURCES; 1463 } 1464 1465 /* Calculate number of subkeys */ 1466 KeyCachedInfo->SubKeys = Node->SubKeyCounts[0] + Node->SubKeyCounts[1]; 1467 1468 /* Release the cell */ 1469 HvReleaseCell(KeyHive, KeyCell); 1470 } 1471 else if (Kcb->ExtFlags & CM_KCB_NO_SUBKEY) 1472 { 1473 /* There are no subkeys */ 1474 KeyCachedInfo->SubKeys = 0; 1475 } 1476 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_ONE) 1477 { 1478 /* There is exactly one subley */ 1479 KeyCachedInfo->SubKeys = 1; 1480 } 1481 else if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) 1482 { 1483 /* Get the number of subkeys from the subkey hint */ 1484 KeyCachedInfo->SubKeys = Kcb->IndexHint->Count; 1485 } 1486 else 1487 { 1488 /* No subkey hint, use the key count field */ 1489 KeyCachedInfo->SubKeys = Kcb->SubKeyCount; 1490 } 1491 1492 return STATUS_SUCCESS; 1493 } 1494 1495 static 1496 NTSTATUS 1497 CmpQueryFlagsInformation( 1498 _In_ PCM_KEY_CONTROL_BLOCK Kcb, 1499 _Out_ PKEY_USER_FLAGS_INFORMATION KeyFlagsInfo, 1500 _In_ ULONG Length, 1501 _In_ PULONG ResultLength) 1502 { 1503 /* Validate the buffer size */ 1504 *ResultLength = sizeof(*KeyFlagsInfo); 1505 if (Length < *ResultLength) 1506 { 1507 return STATUS_BUFFER_TOO_SMALL; 1508 } 1509 1510 /* Copy the user flags */ 1511 KeyFlagsInfo->UserFlags = Kcb->KcbUserFlags; 1512 1513 return STATUS_SUCCESS; 1514 } 1515 1516 static 1517 NTSTATUS 1518 CmpQueryNameInformation( 1519 _In_ PCM_KEY_CONTROL_BLOCK Kcb, 1520 _Out_opt_ PKEY_NAME_INFORMATION KeyNameInfo, 1521 _In_ ULONG Length, 1522 _Out_ PULONG ResultLength) 1523 { 1524 ULONG NeededLength; 1525 PCM_KEY_CONTROL_BLOCK CurrentKcb; 1526 1527 NeededLength = 0; 1528 CurrentKcb = Kcb; 1529 1530 /* Count the needed buffer size */ 1531 while (CurrentKcb) 1532 { 1533 if (CurrentKcb->NameBlock->Compressed) 1534 NeededLength += CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength); 1535 else 1536 NeededLength += CurrentKcb->NameBlock->NameLength; 1537 1538 NeededLength += sizeof(OBJ_NAME_PATH_SEPARATOR); 1539 1540 CurrentKcb = CurrentKcb->ParentKcb; 1541 } 1542 1543 _SEH2_TRY 1544 { 1545 *ResultLength = FIELD_OFFSET(KEY_NAME_INFORMATION, Name) + NeededLength; 1546 if (Length < RTL_SIZEOF_THROUGH_FIELD(KEY_NAME_INFORMATION, NameLength)) 1547 _SEH2_YIELD(return STATUS_BUFFER_TOO_SMALL); 1548 if (Length < *ResultLength) 1549 { 1550 KeyNameInfo->NameLength = NeededLength; 1551 _SEH2_YIELD(return STATUS_BUFFER_OVERFLOW); 1552 } 1553 } 1554 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1555 { 1556 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 1557 } 1558 _SEH2_END; 1559 1560 /* Do the real copy */ 1561 KeyNameInfo->NameLength = 0; 1562 CurrentKcb = Kcb; 1563 1564 _SEH2_TRY 1565 { 1566 while (CurrentKcb) 1567 { 1568 ULONG NameLength; 1569 1570 if (CurrentKcb->NameBlock->Compressed) 1571 { 1572 NameLength = CmpCompressedNameSize(CurrentKcb->NameBlock->Name, CurrentKcb->NameBlock->NameLength); 1573 /* Copy the compressed name */ 1574 CmpCopyCompressedName(&KeyNameInfo->Name[(NeededLength - NameLength)/sizeof(WCHAR)], 1575 NameLength, 1576 CurrentKcb->NameBlock->Name, 1577 CurrentKcb->NameBlock->NameLength); 1578 } 1579 else 1580 { 1581 NameLength = CurrentKcb->NameBlock->NameLength; 1582 /* Otherwise, copy the raw name */ 1583 RtlCopyMemory(&KeyNameInfo->Name[(NeededLength - NameLength)/sizeof(WCHAR)], 1584 CurrentKcb->NameBlock->Name, 1585 NameLength); 1586 } 1587 1588 NeededLength -= NameLength; 1589 NeededLength -= sizeof(OBJ_NAME_PATH_SEPARATOR); 1590 /* Add path separator */ 1591 KeyNameInfo->Name[NeededLength/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; 1592 KeyNameInfo->NameLength += NameLength + sizeof(OBJ_NAME_PATH_SEPARATOR); 1593 1594 CurrentKcb = CurrentKcb->ParentKcb; 1595 } 1596 } 1597 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1598 { 1599 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 1600 } 1601 _SEH2_END; 1602 1603 /* Make sure we copied everything */ 1604 ASSERT(NeededLength == 0); 1605 ASSERT(KeyNameInfo->Name[0] == OBJ_NAME_PATH_SEPARATOR); 1606 1607 /* We're done */ 1608 return STATUS_SUCCESS; 1609 } 1610 1611 1612 NTSTATUS 1613 NTAPI 1614 CmQueryKey(_In_ PCM_KEY_CONTROL_BLOCK Kcb, 1615 _In_ KEY_INFORMATION_CLASS KeyInformationClass, 1616 _Out_opt_ PVOID KeyInformation, 1617 _In_ ULONG Length, 1618 _Out_ PULONG ResultLength) 1619 { 1620 NTSTATUS Status; 1621 PHHIVE Hive; 1622 PCM_KEY_NODE Parent; 1623 HV_TRACK_CELL_REF CellReferences = {0}; 1624 1625 /* Acquire hive lock */ 1626 CmpLockRegistry(); 1627 1628 /* Lock KCB shared */ 1629 CmpAcquireKcbLockShared(Kcb); 1630 1631 /* Don't touch deleted keys */ 1632 if (Kcb->Delete) 1633 { 1634 /* Fail */ 1635 Status = STATUS_KEY_DELETED; 1636 goto Quickie; 1637 } 1638 1639 /* Data can be user-mode, use SEH */ 1640 _SEH2_TRY 1641 { 1642 /* Check what class we got */ 1643 switch (KeyInformationClass) 1644 { 1645 /* Typical information */ 1646 case KeyFullInformation: 1647 case KeyBasicInformation: 1648 case KeyNodeInformation: 1649 { 1650 /* Get the hive and parent */ 1651 Hive = Kcb->KeyHive; 1652 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell); 1653 ASSERT(Parent); 1654 1655 /* Track cell references */ 1656 if (!HvTrackCellRef(&CellReferences, Hive, Kcb->KeyCell)) 1657 { 1658 /* Not enough memory to track references */ 1659 Status = STATUS_INSUFFICIENT_RESOURCES; 1660 } 1661 else 1662 { 1663 /* Call the internal API */ 1664 Status = CmpQueryKeyData(Hive, 1665 Parent, 1666 KeyInformationClass, 1667 KeyInformation, 1668 Length, 1669 ResultLength); 1670 } 1671 break; 1672 } 1673 1674 case KeyCachedInformation: 1675 { 1676 /* Call the internal API */ 1677 Status = CmpQueryKeyDataFromCache(Kcb, 1678 KeyInformation, 1679 Length, 1680 ResultLength); 1681 break; 1682 } 1683 1684 case KeyFlagsInformation: 1685 { 1686 /* Call the internal API */ 1687 Status = CmpQueryFlagsInformation(Kcb, 1688 KeyInformation, 1689 Length, 1690 ResultLength); 1691 break; 1692 } 1693 1694 case KeyNameInformation: 1695 { 1696 /* Call the internal API */ 1697 Status = CmpQueryNameInformation(Kcb, 1698 KeyInformation, 1699 Length, 1700 ResultLength); 1701 break; 1702 } 1703 1704 /* Illegal classes */ 1705 default: 1706 { 1707 /* Print message and fail */ 1708 DPRINT1("Unsupported class: %d!\n", KeyInformationClass); 1709 Status = STATUS_INVALID_INFO_CLASS; 1710 break; 1711 } 1712 } 1713 } 1714 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1715 { 1716 /* Fail with exception code */ 1717 Status = _SEH2_GetExceptionCode(); 1718 _SEH2_YIELD(goto Quickie); 1719 } 1720 _SEH2_END; 1721 1722 Quickie: 1723 /* Release references */ 1724 HvReleaseFreeCellRefArray(&CellReferences); 1725 1726 /* Release locks */ 1727 CmpReleaseKcbLock(Kcb); 1728 CmpUnlockRegistry(); 1729 return Status; 1730 } 1731 1732 NTSTATUS 1733 NTAPI 1734 CmEnumerateKey(IN PCM_KEY_CONTROL_BLOCK Kcb, 1735 IN ULONG Index, 1736 IN KEY_INFORMATION_CLASS KeyInformationClass, 1737 IN PVOID KeyInformation, 1738 IN ULONG Length, 1739 IN PULONG ResultLength) 1740 { 1741 NTSTATUS Status; 1742 PHHIVE Hive; 1743 PCM_KEY_NODE Parent, Child; 1744 HCELL_INDEX ChildCell; 1745 HV_TRACK_CELL_REF CellReferences = {0}; 1746 1747 /* Acquire hive lock */ 1748 CmpLockRegistry(); 1749 1750 /* Lock the KCB shared */ 1751 CmpAcquireKcbLockShared(Kcb); 1752 1753 /* Don't touch deleted keys */ 1754 if (Kcb->Delete) 1755 { 1756 /* Undo everything */ 1757 Status = STATUS_KEY_DELETED; 1758 goto Quickie; 1759 } 1760 1761 /* Get the hive and parent */ 1762 Hive = Kcb->KeyHive; 1763 Parent = (PCM_KEY_NODE)HvGetCell(Hive, Kcb->KeyCell); 1764 ASSERT(Parent); 1765 1766 /* Get the child cell */ 1767 ChildCell = CmpFindSubKeyByNumber(Hive, Parent, Index); 1768 1769 /* Release the parent cell */ 1770 HvReleaseCell(Hive, Kcb->KeyCell); 1771 1772 /* Check if we found the child */ 1773 if (ChildCell == HCELL_NIL) 1774 { 1775 /* We didn't, fail */ 1776 Status = STATUS_NO_MORE_ENTRIES; 1777 goto Quickie; 1778 } 1779 1780 /* Now get the actual child node */ 1781 Child = (PCM_KEY_NODE)HvGetCell(Hive, ChildCell); 1782 ASSERT(Child); 1783 1784 /* Track references */ 1785 if (!HvTrackCellRef(&CellReferences, Hive, ChildCell)) 1786 { 1787 /* Can't allocate memory for tracking */ 1788 Status = STATUS_INSUFFICIENT_RESOURCES; 1789 goto Quickie; 1790 } 1791 1792 /* Data can be user-mode, use SEH */ 1793 _SEH2_TRY 1794 { 1795 /* Query the data requested */ 1796 Status = CmpQueryKeyData(Hive, 1797 Child, 1798 KeyInformationClass, 1799 KeyInformation, 1800 Length, 1801 ResultLength); 1802 } 1803 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1804 { 1805 /* Fail with exception code */ 1806 Status = _SEH2_GetExceptionCode(); 1807 _SEH2_YIELD(goto Quickie); 1808 } 1809 _SEH2_END; 1810 1811 Quickie: 1812 /* Release references */ 1813 HvReleaseFreeCellRefArray(&CellReferences); 1814 1815 /* Release locks */ 1816 CmpReleaseKcbLock(Kcb); 1817 CmpUnlockRegistry(); 1818 return Status; 1819 } 1820 1821 NTSTATUS 1822 NTAPI 1823 CmDeleteKey(IN PCM_KEY_BODY KeyBody) 1824 { 1825 NTSTATUS Status; 1826 PHHIVE Hive; 1827 PCM_KEY_NODE Node, Parent; 1828 HCELL_INDEX Cell, ParentCell; 1829 PCM_KEY_CONTROL_BLOCK Kcb; 1830 1831 /* Acquire hive lock */ 1832 CmpLockRegistry(); 1833 1834 /* Get the kcb */ 1835 Kcb = KeyBody->KeyControlBlock; 1836 1837 /* Don't allow deleting the root */ 1838 if (!Kcb->ParentKcb) 1839 { 1840 /* Fail */ 1841 CmpUnlockRegistry(); 1842 return STATUS_CANNOT_DELETE; 1843 } 1844 1845 /* Lock parent and child */ 1846 CmpAcquireTwoKcbLocksExclusiveByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey); 1847 1848 /* Check if we're already being deleted */ 1849 if (Kcb->Delete) 1850 { 1851 /* Don't do it twice */ 1852 Status = STATUS_SUCCESS; 1853 goto Quickie; 1854 } 1855 1856 /* Get the hive and node */ 1857 Hive = Kcb->KeyHive; 1858 Cell = Kcb->KeyCell; 1859 1860 /* Lock flushes */ 1861 CmpLockHiveFlusherShared((PCMHIVE)Hive); 1862 1863 /* Get the key node */ 1864 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 1865 ASSERT(Node); 1866 1867 /* Sanity check */ 1868 ASSERT(Node->Flags == Kcb->Flags); 1869 1870 /* Check if we don't have any children */ 1871 if (!(Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]) && 1872 !(Node->Flags & KEY_NO_DELETE)) 1873 { 1874 /* Send notification to registered callbacks */ 1875 CmpReportNotify(Kcb, Hive, Cell, REG_NOTIFY_CHANGE_NAME); 1876 1877 /* Get the parent and free the cell */ 1878 ParentCell = Node->Parent; 1879 Status = CmpFreeKeyByCell(Hive, Cell, TRUE); 1880 if (NT_SUCCESS(Status)) 1881 { 1882 /* Flush any notifications */ 1883 CmpFlushNotifiesOnKeyBodyList(Kcb, FALSE); 1884 1885 /* Clean up information we have on the subkey */ 1886 CmpCleanUpSubKeyInfo(Kcb->ParentKcb); 1887 1888 /* Get the parent node */ 1889 Parent = (PCM_KEY_NODE)HvGetCell(Hive, ParentCell); 1890 if (Parent) 1891 { 1892 /* Update the maximum name length */ 1893 Kcb->ParentKcb->KcbMaxNameLen = (USHORT)Parent->MaxNameLen; 1894 1895 /* Make sure we're dirty */ 1896 ASSERT(HvIsCellDirty(Hive, ParentCell)); 1897 1898 /* Update the write time */ 1899 KeQuerySystemTime(&Parent->LastWriteTime); 1900 Kcb->ParentKcb->KcbLastWriteTime = Parent->LastWriteTime; 1901 1902 /* Release the cell */ 1903 HvReleaseCell(Hive, ParentCell); 1904 } 1905 1906 /* Set the KCB in delete mode and remove it */ 1907 Kcb->Delete = TRUE; 1908 CmpRemoveKeyControlBlock(Kcb); 1909 1910 /* Clear the cell */ 1911 Kcb->KeyCell = HCELL_NIL; 1912 } 1913 } 1914 else 1915 { 1916 /* Fail */ 1917 Status = STATUS_CANNOT_DELETE; 1918 } 1919 1920 /* Release the cell */ 1921 HvReleaseCell(Hive, Cell); 1922 1923 /* Release flush lock */ 1924 CmpUnlockHiveFlusher((PCMHIVE)Hive); 1925 1926 /* Release the KCB locks */ 1927 Quickie: 1928 CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey); 1929 1930 /* Release hive lock */ 1931 CmpUnlockRegistry(); 1932 return Status; 1933 } 1934 1935 NTSTATUS 1936 NTAPI 1937 CmFlushKey(IN PCM_KEY_CONTROL_BLOCK Kcb, 1938 IN BOOLEAN ExclusiveLock) 1939 { 1940 PCMHIVE CmHive; 1941 #if DBG 1942 CM_CHECK_REGISTRY_STATUS CheckStatus; 1943 #endif 1944 NTSTATUS Status = STATUS_SUCCESS; 1945 PHHIVE Hive; 1946 1947 /* Ignore flushes until we're ready */ 1948 if (CmpNoWrite) return STATUS_SUCCESS; 1949 1950 /* Get the hives */ 1951 Hive = Kcb->KeyHive; 1952 CmHive = (PCMHIVE)Hive; 1953 1954 /* Check if this is the master hive */ 1955 if (CmHive == CmiVolatileHive) 1956 { 1957 /* Flush all the hives instead */ 1958 CmpDoFlushAll(FALSE); 1959 } 1960 else 1961 { 1962 #if DBG 1963 /* Make sure the registry hive we're going to flush is OK */ 1964 CheckStatus = CmCheckRegistry(CmHive, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 1965 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 1966 #endif 1967 1968 /* Don't touch the hive */ 1969 CmpLockHiveFlusherExclusive(CmHive); 1970 1971 ASSERT(CmHive->ViewLock); 1972 KeAcquireGuardedMutex(CmHive->ViewLock); 1973 CmHive->ViewLockOwner = KeGetCurrentThread(); 1974 1975 /* Will the hive shrink? */ 1976 if (HvHiveWillShrink(Hive)) 1977 { 1978 /* I don't believe the current Hv does shrinking */ 1979 ASSERT(FALSE); 1980 // CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive); 1981 } 1982 else 1983 { 1984 /* Now we can release views */ 1985 ASSERT(CmHive->ViewLock); 1986 // CMP_ASSERT_VIEW_LOCK_OWNED(CmHive); 1987 ASSERT((CmpSpecialBootCondition == TRUE) || 1988 (CmHive->HiveIsLoading == TRUE) || 1989 (CmHive->ViewLockOwner == KeGetCurrentThread()) || 1990 (CmpTestRegistryLockExclusive() == TRUE)); 1991 CmHive->ViewLockOwner = NULL; 1992 KeReleaseGuardedMutex(CmHive->ViewLock); 1993 } 1994 1995 /* Flush only this hive */ 1996 if (!HvSyncHive(Hive)) 1997 { 1998 /* Fail */ 1999 Status = STATUS_REGISTRY_IO_FAILED; 2000 } 2001 2002 /* Release the flush lock */ 2003 CmpUnlockHiveFlusher(CmHive); 2004 } 2005 2006 /* Return the status */ 2007 return Status; 2008 } 2009 2010 NTSTATUS 2011 NTAPI 2012 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey, 2013 IN POBJECT_ATTRIBUTES SourceFile, 2014 IN ULONG Flags, 2015 IN PCM_KEY_BODY KeyBody) 2016 { 2017 SECURITY_QUALITY_OF_SERVICE ServiceQos; 2018 SECURITY_CLIENT_CONTEXT ClientSecurityContext; 2019 HANDLE KeyHandle; 2020 BOOLEAN Allocate = TRUE; 2021 PCMHIVE CmHive, LoadedHive; 2022 NTSTATUS Status; 2023 CM_PARSE_CONTEXT ParseContext; 2024 2025 /* Check if we have a trust key */ 2026 if (KeyBody) 2027 { 2028 /* Fail */ 2029 DPRINT("Trusted classes not yet supported\n"); 2030 } 2031 2032 /* Build a service QoS for a security context */ 2033 ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); 2034 ServiceQos.ImpersonationLevel = SecurityImpersonation; 2035 ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; 2036 ServiceQos.EffectiveOnly = TRUE; 2037 Status = SeCreateClientSecurity(PsGetCurrentThread(), 2038 &ServiceQos, 2039 FALSE, 2040 &ClientSecurityContext); 2041 if (!NT_SUCCESS(Status)) 2042 { 2043 /* Fail */ 2044 DPRINT1("Security context failed\n"); 2045 return Status; 2046 } 2047 2048 /* Open the target key */ 2049 RtlZeroMemory(&ParseContext, sizeof(ParseContext)); 2050 ParseContext.CreateOperation = FALSE; 2051 Status = ObOpenObjectByName(TargetKey, 2052 CmpKeyObjectType, 2053 KernelMode, 2054 NULL, 2055 KEY_READ, 2056 &ParseContext, 2057 &KeyHandle); 2058 if (!NT_SUCCESS(Status)) KeyHandle = NULL; 2059 2060 /* Open the hive */ 2061 Status = CmpCmdHiveOpen(SourceFile, 2062 &ClientSecurityContext, 2063 &Allocate, 2064 &CmHive, 2065 CM_CHECK_REGISTRY_PURGE_VOLATILES); 2066 2067 /* Get rid of the security context */ 2068 SeDeleteClientSecurity(&ClientSecurityContext); 2069 2070 /* See if we failed */ 2071 if (!NT_SUCCESS(Status)) 2072 { 2073 /* See if the target already existed */ 2074 if (KeyHandle) 2075 { 2076 /* Lock the registry */ 2077 CmpLockRegistryExclusive(); 2078 2079 /* Check if we are already loaded */ 2080 if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive)) 2081 { 2082 /* That's okay then */ 2083 ASSERT(LoadedHive); 2084 Status = STATUS_SUCCESS; 2085 } 2086 2087 /* Release the registry */ 2088 CmpUnlockRegistry(); 2089 } 2090 2091 /* Close the key handle if we had one */ 2092 if (KeyHandle) ZwClose(KeyHandle); 2093 return Status; 2094 } 2095 2096 /* Lock the registry shared */ 2097 CmpLockRegistry(); 2098 2099 /* Lock loading */ 2100 ExAcquirePushLockExclusive(&CmpLoadHiveLock); 2101 2102 /* Lock the hive to this thread */ 2103 CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING; 2104 CmHive->CreatorOwner = KeGetCurrentThread(); 2105 2106 /* Set flag */ 2107 if (Flags & REG_NO_LAZY_FLUSH) CmHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH; 2108 2109 /* Link the hive */ 2110 Status = CmpLinkHiveToMaster(TargetKey->ObjectName, 2111 TargetKey->RootDirectory, 2112 CmHive, 2113 Allocate, 2114 TargetKey->SecurityDescriptor); 2115 if (NT_SUCCESS(Status)) 2116 { 2117 /* Add to HiveList key */ 2118 CmpAddToHiveFileList(CmHive); 2119 2120 /* Sync the hive if necessary */ 2121 if (Allocate) 2122 { 2123 /* Sync it under the flusher lock */ 2124 CmpLockHiveFlusherExclusive(CmHive); 2125 HvSyncHive(&CmHive->Hive); 2126 CmpUnlockHiveFlusher(CmHive); 2127 } 2128 2129 /* Release the hive */ 2130 CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING; 2131 CmHive->CreatorOwner = NULL; 2132 } 2133 else 2134 { 2135 DPRINT1("CmpLinkHiveToMaster failed, Status %lx\n", Status); 2136 2137 /* We're touching this hive, set the loading flag */ 2138 CmHive->HiveIsLoading = TRUE; 2139 2140 /* Close associated file handles */ 2141 CmpCloseHiveFiles(CmHive); 2142 2143 /* Cleanup its resources */ 2144 CmpDestroyHive(CmHive); 2145 } 2146 2147 /* Allow loads */ 2148 ExReleasePushLock(&CmpLoadHiveLock); 2149 2150 /* Is this first profile load? */ 2151 if (!CmpProfileLoaded && !CmpWasSetupBoot) 2152 { 2153 /* User is now logged on, set quotas */ 2154 CmpProfileLoaded = TRUE; 2155 CmpSetGlobalQuotaAllowed(); 2156 } 2157 2158 /* Unlock the registry */ 2159 CmpUnlockRegistry(); 2160 2161 /* Close handle and return */ 2162 if (KeyHandle) ZwClose(KeyHandle); 2163 return Status; 2164 } 2165 2166 static 2167 BOOLEAN 2168 NTAPI 2169 CmpUnlinkHiveFromMaster(IN PCMHIVE CmHive, 2170 IN HCELL_INDEX Cell) 2171 { 2172 PCELL_DATA CellData; 2173 HCELL_INDEX LinkCell; 2174 NTSTATUS Status; 2175 2176 DPRINT("CmpUnlinkHiveFromMaster()\n"); 2177 2178 /* Get the cell data */ 2179 CellData = HvGetCell(&CmHive->Hive, Cell); 2180 if (CellData == NULL) 2181 return FALSE; 2182 2183 /* Get the link cell and release the current cell */ 2184 LinkCell = CellData->u.KeyNode.Parent; 2185 HvReleaseCell(&CmHive->Hive, Cell); 2186 2187 /* Remove the link cell from the master hive */ 2188 CmpLockHiveFlusherExclusive(CmiVolatileHive); 2189 Status = CmpFreeKeyByCell((PHHIVE)CmiVolatileHive, 2190 LinkCell, 2191 TRUE); 2192 CmpUnlockHiveFlusher(CmiVolatileHive); 2193 if (!NT_SUCCESS(Status)) 2194 { 2195 DPRINT1("CmpFreeKeyByCell() failed (Status 0x%08lx)\n", Status); 2196 return FALSE; 2197 } 2198 2199 /* Remove the hive from the list */ 2200 ExAcquirePushLockExclusive(&CmpHiveListHeadLock); 2201 RemoveEntryList(&CmHive->HiveList); 2202 ExReleasePushLock(&CmpHiveListHeadLock); 2203 2204 return TRUE; 2205 } 2206 2207 NTSTATUS 2208 NTAPI 2209 CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb, 2210 IN ULONG Flags) 2211 { 2212 PHHIVE Hive; 2213 PCMHIVE CmHive; 2214 HCELL_INDEX Cell; 2215 2216 DPRINT("CmUnloadKey(%p, %lx)\n", Kcb, Flags); 2217 2218 /* Get the hive */ 2219 Hive = Kcb->KeyHive; 2220 Cell = Kcb->KeyCell; 2221 CmHive = (PCMHIVE)Hive; 2222 2223 /* Fail if the key is not a hive root key */ 2224 if (Cell != Hive->BaseBlock->RootCell) 2225 { 2226 DPRINT1("Key is not a hive root key!\n"); 2227 return STATUS_INVALID_PARAMETER; 2228 } 2229 2230 /* Fail if we try to unload the master hive */ 2231 if (CmHive == CmiVolatileHive) 2232 { 2233 DPRINT1("Do not try to unload the master hive!\n"); 2234 return STATUS_INVALID_PARAMETER; 2235 } 2236 2237 /* Mark this hive as being unloaded */ 2238 Hive->HiveFlags |= HIVE_IS_UNLOADING; 2239 2240 /* Search for any opened keys in this hive, and take an appropriate action */ 2241 if (Kcb->RefCount > 1) 2242 { 2243 if (Flags != REG_FORCE_UNLOAD) 2244 { 2245 if (CmpEnumerateOpenSubKeys(Kcb, FALSE, FALSE) != 0) 2246 { 2247 /* There are open subkeys but we don't force hive unloading, fail */ 2248 Hive->HiveFlags &= ~HIVE_IS_UNLOADING; 2249 return STATUS_CANNOT_DELETE; 2250 } 2251 } 2252 else 2253 { 2254 DPRINT1("CmUnloadKey: Force unloading is HALF-IMPLEMENTED, expect dangling KCBs problems!\n"); 2255 if (CmpEnumerateOpenSubKeys(Kcb, TRUE, TRUE) != 0) 2256 { 2257 /* There are open subkeys that we cannot force to unload, fail */ 2258 Hive->HiveFlags &= ~HIVE_IS_UNLOADING; 2259 return STATUS_CANNOT_DELETE; 2260 } 2261 } 2262 } 2263 2264 /* Set the loading flag */ 2265 CmHive->HiveIsLoading = TRUE; 2266 2267 /* Flush the hive */ 2268 CmFlushKey(Kcb, TRUE); 2269 2270 /* Unlink the hive from the master hive */ 2271 if (!CmpUnlinkHiveFromMaster(CmHive, Cell)) 2272 { 2273 DPRINT("CmpUnlinkHiveFromMaster() failed!\n"); 2274 2275 /* Remove the unloading flag */ 2276 Hive->HiveFlags &= ~HIVE_IS_UNLOADING; 2277 2278 /* Reset the loading flag */ 2279 CmHive->HiveIsLoading = FALSE; 2280 2281 /* Return failure */ 2282 return STATUS_INSUFFICIENT_RESOURCES; 2283 } 2284 2285 /* Flush any notifications if we force hive unloading */ 2286 if (Flags == REG_FORCE_UNLOAD) 2287 CmpFlushNotifiesOnKeyBodyList(Kcb, TRUE); // Lock is already held 2288 2289 /* Clean up information we have on the subkey */ 2290 CmpCleanUpSubKeyInfo(Kcb->ParentKcb); 2291 2292 /* Set the KCB in delete mode and remove it */ 2293 Kcb->Delete = TRUE; 2294 CmpRemoveKeyControlBlock(Kcb); 2295 2296 if (Flags != REG_FORCE_UNLOAD) 2297 { 2298 /* Release the KCB locks */ 2299 CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey); 2300 2301 /* Release the hive loading lock */ 2302 ExReleasePushLockExclusive(&CmpLoadHiveLock); 2303 } 2304 2305 /* Release hive lock */ 2306 CmpUnlockRegistry(); 2307 2308 /* Close file handles */ 2309 CmpCloseHiveFiles(CmHive); 2310 2311 /* Remove the hive from the hive file list */ 2312 CmpRemoveFromHiveFileList(CmHive); 2313 2314 /** 2315 ** NOTE: 2316 ** The following code is mostly equivalent to what we "call" CmpDestroyHive() 2317 **/ 2318 /* Destroy the security descriptor cache */ 2319 CmpDestroySecurityCache(CmHive); 2320 2321 /* Destroy the view list */ 2322 CmpDestroyHiveViewList(CmHive); 2323 2324 /* Delete the flusher lock */ 2325 ExDeleteResourceLite(CmHive->FlusherLock); 2326 ExFreePoolWithTag(CmHive->FlusherLock, TAG_CMHIVE); 2327 2328 /* Delete the view lock */ 2329 ExFreePoolWithTag(CmHive->ViewLock, TAG_CMHIVE); 2330 2331 /* Free the hive storage */ 2332 HvFree(Hive); 2333 2334 /* Free the hive */ 2335 CmpFree(CmHive, TAG_CM); 2336 2337 return STATUS_SUCCESS; 2338 } 2339 2340 ULONG 2341 NTAPI 2342 CmpEnumerateOpenSubKeys( 2343 IN PCM_KEY_CONTROL_BLOCK RootKcb, 2344 IN BOOLEAN RemoveEmptyCacheEntries, 2345 IN BOOLEAN DereferenceOpenedEntries) 2346 { 2347 PCM_KEY_HASH Entry; 2348 PCM_KEY_CONTROL_BLOCK CachedKcb; 2349 PCM_KEY_CONTROL_BLOCK ParentKcb; 2350 ULONG ParentKeyCount; 2351 ULONG i, j; 2352 ULONG SubKeys = 0; 2353 2354 DPRINT("CmpEnumerateOpenSubKeys() called\n"); 2355 2356 /* The root key is the only referenced key. There are no referenced sub keys. */ 2357 if (RootKcb->RefCount == 1) 2358 { 2359 DPRINT("Open sub keys: 0\n"); 2360 return 0; 2361 } 2362 2363 /* Enumerate all hash lists */ 2364 for (i = 0; i < CmpHashTableSize; i++) 2365 { 2366 /* Get the first cache entry */ 2367 Entry = CmpCacheTable[i].Entry; 2368 2369 /* Enumerate all cache entries */ 2370 while (Entry) 2371 { 2372 /* Get the KCB of the current cache entry */ 2373 CachedKcb = CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash); 2374 2375 /* Check keys only that are subkeys to our root key */ 2376 if (CachedKcb->TotalLevels > RootKcb->TotalLevels) 2377 { 2378 /* Calculate the number of parent keys to the root key */ 2379 ParentKeyCount = CachedKcb->TotalLevels - RootKcb->TotalLevels; 2380 2381 /* Find a parent key that could be the root key */ 2382 ParentKcb = CachedKcb; 2383 for (j = 0; j < ParentKeyCount; j++) 2384 { 2385 ParentKcb = ParentKcb->ParentKcb; 2386 } 2387 2388 /* Check whether the parent is the root key */ 2389 if (ParentKcb == RootKcb) 2390 { 2391 DPRINT("Found a sub key, RefCount = %u\n", CachedKcb->RefCount); 2392 2393 if (CachedKcb->RefCount > 0) 2394 { 2395 DPRINT("Found a sub key pointing to '%.*s', RefCount = %u\n", 2396 CachedKcb->NameBlock->NameLength, CachedKcb->NameBlock->Name, 2397 CachedKcb->RefCount); 2398 2399 /* If we dereference opened KCBs, don't touch read-only keys */ 2400 if (DereferenceOpenedEntries && 2401 !(CachedKcb->ExtFlags & CM_KCB_READ_ONLY_KEY)) 2402 { 2403 /* Registry needs to be locked down */ 2404 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK(); 2405 2406 /* Flush any notifications */ 2407 CmpFlushNotifiesOnKeyBodyList(CachedKcb, TRUE); // Lock is already held 2408 2409 /* Clean up information we have on the subkey */ 2410 CmpCleanUpSubKeyInfo(CachedKcb->ParentKcb); 2411 2412 /* Get and cache the next cache entry */ 2413 // Entry = Entry->NextHash; 2414 Entry = CachedKcb->NextHash; 2415 2416 /* Set the KCB in delete mode and remove it */ 2417 CachedKcb->Delete = TRUE; 2418 CmpRemoveKeyControlBlock(CachedKcb); 2419 2420 /* Clear the cell */ 2421 CachedKcb->KeyCell = HCELL_NIL; 2422 2423 /* Restart with the next cache entry */ 2424 continue; 2425 } 2426 /* Else, the key cannot be dereferenced, and we count it as in use */ 2427 2428 /* Count the current hash entry if it is in use */ 2429 SubKeys++; 2430 } 2431 else if ((CachedKcb->RefCount == 0) && RemoveEmptyCacheEntries) 2432 { 2433 /* Remove the current key from the delayed close list */ 2434 CmpRemoveFromDelayedClose(CachedKcb); 2435 2436 /* Remove the current cache entry */ 2437 CmpCleanUpKcbCacheWithLock(CachedKcb, TRUE); 2438 2439 /* Restart, because the hash list has changed */ 2440 Entry = CmpCacheTable[i].Entry; 2441 continue; 2442 } 2443 } 2444 } 2445 2446 /* Get the next cache entry */ 2447 Entry = Entry->NextHash; 2448 } 2449 } 2450 2451 if (SubKeys > 0) 2452 DPRINT1("Open sub keys: %u\n", SubKeys); 2453 2454 return SubKeys; 2455 } 2456 2457 static 2458 NTSTATUS 2459 CmpDeepCopyKeyInternal(IN PHHIVE SourceHive, 2460 IN HCELL_INDEX SrcKeyCell, 2461 IN PHHIVE DestinationHive, 2462 IN HCELL_INDEX Parent, 2463 IN HSTORAGE_TYPE StorageType, 2464 OUT PHCELL_INDEX DestKeyCell OPTIONAL) 2465 { 2466 NTSTATUS Status; 2467 PCM_KEY_NODE SrcNode; 2468 PCM_KEY_NODE DestNode = NULL; 2469 HCELL_INDEX NewKeyCell = HCELL_NIL; 2470 HCELL_INDEX NewClassCell = HCELL_NIL, NewSecCell = HCELL_NIL; 2471 HCELL_INDEX SubKey, NewSubKey; 2472 ULONG Index, SubKeyCount; 2473 2474 PAGED_CODE(); 2475 2476 DPRINT("CmpDeepCopyKeyInternal(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)\n", 2477 SourceHive, 2478 SrcKeyCell, 2479 DestinationHive, 2480 Parent, 2481 StorageType, 2482 DestKeyCell); 2483 2484 /* Get the source cell node */ 2485 SrcNode = (PCM_KEY_NODE)HvGetCell(SourceHive, SrcKeyCell); 2486 ASSERT(SrcNode); 2487 2488 /* Sanity check */ 2489 ASSERT(SrcNode->Signature == CM_KEY_NODE_SIGNATURE); 2490 2491 /* Create a simple copy of the source key */ 2492 NewKeyCell = CmpCopyCell(SourceHive, 2493 SrcKeyCell, 2494 DestinationHive, 2495 StorageType); 2496 if (NewKeyCell == HCELL_NIL) 2497 { 2498 /* Not enough storage space */ 2499 Status = STATUS_INSUFFICIENT_RESOURCES; 2500 goto Cleanup; 2501 } 2502 2503 /* Get the destination cell node */ 2504 DestNode = (PCM_KEY_NODE)HvGetCell(DestinationHive, NewKeyCell); 2505 ASSERT(DestNode); 2506 2507 /* Set the parent and copy the flags */ 2508 DestNode->Parent = Parent; 2509 DestNode->Flags = (SrcNode->Flags & KEY_COMP_NAME); // Keep only the single permanent flag 2510 if (Parent == HCELL_NIL) 2511 { 2512 /* This is the new root node */ 2513 DestNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE; 2514 } 2515 2516 /* Copy the class cell */ 2517 if (SrcNode->ClassLength > 0) 2518 { 2519 NewClassCell = CmpCopyCell(SourceHive, 2520 SrcNode->Class, 2521 DestinationHive, 2522 StorageType); 2523 if (NewClassCell == HCELL_NIL) 2524 { 2525 /* Not enough storage space */ 2526 Status = STATUS_INSUFFICIENT_RESOURCES; 2527 goto Cleanup; 2528 } 2529 2530 DestNode->Class = NewClassCell; 2531 DestNode->ClassLength = SrcNode->ClassLength; 2532 } 2533 else 2534 { 2535 DestNode->Class = HCELL_NIL; 2536 DestNode->ClassLength = 0; 2537 } 2538 2539 /* Copy the security cell (FIXME: HACKish poor-man version) */ 2540 if (SrcNode->Security != HCELL_NIL) 2541 { 2542 NewSecCell = CmpCopyCell(SourceHive, 2543 SrcNode->Security, 2544 DestinationHive, 2545 StorageType); 2546 if (NewSecCell == HCELL_NIL) 2547 { 2548 /* Not enough storage space */ 2549 Status = STATUS_INSUFFICIENT_RESOURCES; 2550 goto Cleanup; 2551 } 2552 } 2553 DestNode->Security = NewSecCell; 2554 2555 /* Copy the value list */ 2556 Status = CmpCopyKeyValueList(SourceHive, 2557 &SrcNode->ValueList, 2558 DestinationHive, 2559 &DestNode->ValueList, 2560 StorageType); 2561 if (!NT_SUCCESS(Status)) 2562 goto Cleanup; 2563 2564 /* Clear the invalid subkey index */ 2565 DestNode->SubKeyCounts[Stable] = DestNode->SubKeyCounts[Volatile] = 0; 2566 DestNode->SubKeyLists[Stable] = DestNode->SubKeyLists[Volatile] = HCELL_NIL; 2567 2568 /* Calculate the total number of subkeys */ 2569 SubKeyCount = SrcNode->SubKeyCounts[Stable] + SrcNode->SubKeyCounts[Volatile]; 2570 2571 /* Loop through all the subkeys */ 2572 for (Index = 0; Index < SubKeyCount; Index++) 2573 { 2574 /* Get the subkey */ 2575 SubKey = CmpFindSubKeyByNumber(SourceHive, SrcNode, Index); 2576 ASSERT(SubKey != HCELL_NIL); 2577 2578 /* Call the function recursively for the subkey */ 2579 // 2580 // FIXME: Danger!! Kernel stack exhaustion!! 2581 // 2582 Status = CmpDeepCopyKeyInternal(SourceHive, 2583 SubKey, 2584 DestinationHive, 2585 NewKeyCell, 2586 StorageType, 2587 &NewSubKey); 2588 if (!NT_SUCCESS(Status)) 2589 goto Cleanup; 2590 2591 /* Add the copy of the subkey to the new key */ 2592 if (!CmpAddSubKey(DestinationHive, 2593 NewKeyCell, 2594 NewSubKey)) 2595 { 2596 /* Cleanup allocated cell */ 2597 HvFreeCell(DestinationHive, NewSubKey); 2598 2599 Status = STATUS_INSUFFICIENT_RESOURCES; 2600 goto Cleanup; 2601 } 2602 } 2603 2604 /* Set success */ 2605 Status = STATUS_SUCCESS; 2606 2607 Cleanup: 2608 2609 /* Release the cells */ 2610 if (DestNode) HvReleaseCell(DestinationHive, NewKeyCell); 2611 if (SrcNode) HvReleaseCell(SourceHive, SrcKeyCell); 2612 2613 /* Cleanup allocated cells in case of failure */ 2614 if (!NT_SUCCESS(Status)) 2615 { 2616 if (NewSecCell != HCELL_NIL) 2617 HvFreeCell(DestinationHive, NewSecCell); 2618 2619 if (NewClassCell != HCELL_NIL) 2620 HvFreeCell(DestinationHive, NewClassCell); 2621 2622 if (NewKeyCell != HCELL_NIL) 2623 HvFreeCell(DestinationHive, NewKeyCell); 2624 2625 NewKeyCell = HCELL_NIL; 2626 } 2627 2628 /* Set the cell index if requested and return status */ 2629 if (DestKeyCell) *DestKeyCell = NewKeyCell; 2630 return Status; 2631 } 2632 2633 NTSTATUS 2634 NTAPI 2635 CmpDeepCopyKey(IN PHHIVE SourceHive, 2636 IN HCELL_INDEX SrcKeyCell, 2637 IN PHHIVE DestinationHive, 2638 IN HSTORAGE_TYPE StorageType, 2639 OUT PHCELL_INDEX DestKeyCell OPTIONAL) 2640 { 2641 /* Call the internal function */ 2642 return CmpDeepCopyKeyInternal(SourceHive, 2643 SrcKeyCell, 2644 DestinationHive, 2645 HCELL_NIL, 2646 StorageType, 2647 DestKeyCell); 2648 } 2649 2650 NTSTATUS 2651 NTAPI 2652 CmSaveKey(IN PCM_KEY_CONTROL_BLOCK Kcb, 2653 IN HANDLE FileHandle, 2654 IN ULONG Flags) 2655 { 2656 #if DBG 2657 CM_CHECK_REGISTRY_STATUS CheckStatus; 2658 PCMHIVE HiveToValidate = NULL; 2659 #endif 2660 NTSTATUS Status = STATUS_SUCCESS; 2661 PCMHIVE KeyHive = NULL; 2662 PAGED_CODE(); 2663 2664 DPRINT("CmSaveKey(0x%08X, 0x%08X, %lu)\n", Kcb, FileHandle, Flags); 2665 2666 /* Lock the registry and KCB */ 2667 CmpLockRegistry(); 2668 CmpAcquireKcbLockShared(Kcb); 2669 2670 #if DBG 2671 /* Get the hive for validation */ 2672 HiveToValidate = (PCMHIVE)Kcb->KeyHive; 2673 #endif 2674 2675 if (Kcb->Delete) 2676 { 2677 /* The source key has been deleted, do nothing */ 2678 Status = STATUS_KEY_DELETED; 2679 goto Cleanup; 2680 } 2681 2682 if (Kcb->KeyHive == &CmiVolatileHive->Hive) 2683 { 2684 /* Keys that are directly in the master hive can't be saved */ 2685 Status = STATUS_ACCESS_DENIED; 2686 goto Cleanup; 2687 } 2688 2689 #if DBG 2690 /* Make sure this control block has a sane hive */ 2691 CheckStatus = CmCheckRegistry(HiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2692 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2693 #endif 2694 2695 /* Create a new hive that will hold the key */ 2696 Status = CmpInitializeHive(&KeyHive, 2697 HINIT_CREATE, 2698 HIVE_VOLATILE, 2699 HFILE_TYPE_PRIMARY, 2700 NULL, 2701 NULL, 2702 NULL, 2703 NULL, 2704 NULL, 2705 NULL, 2706 CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES); 2707 if (!NT_SUCCESS(Status)) goto Cleanup; 2708 2709 /* Copy the key recursively into the new hive */ 2710 Status = CmpDeepCopyKey(Kcb->KeyHive, 2711 Kcb->KeyCell, 2712 &KeyHive->Hive, 2713 Stable, 2714 &KeyHive->Hive.BaseBlock->RootCell); 2715 if (!NT_SUCCESS(Status)) goto Cleanup; 2716 2717 /* Set the primary handle of the hive */ 2718 KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle; 2719 2720 /* Dump the hive into the file */ 2721 HvWriteHive(&KeyHive->Hive); 2722 2723 Cleanup: 2724 2725 /* Free the hive */ 2726 if (KeyHive) CmpDestroyHive(KeyHive); 2727 2728 #if DBG 2729 if (NT_SUCCESS(Status)) 2730 { 2731 /* Before we say goodbye, make sure the hive is still OK */ 2732 CheckStatus = CmCheckRegistry(HiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2733 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2734 } 2735 #endif 2736 2737 /* Release the locks */ 2738 CmpReleaseKcbLock(Kcb); 2739 CmpUnlockRegistry(); 2740 2741 return Status; 2742 } 2743 2744 NTSTATUS 2745 NTAPI 2746 CmSaveMergedKeys(IN PCM_KEY_CONTROL_BLOCK HighKcb, 2747 IN PCM_KEY_CONTROL_BLOCK LowKcb, 2748 IN HANDLE FileHandle) 2749 { 2750 #if DBG 2751 CM_CHECK_REGISTRY_STATUS CheckStatus; 2752 PCMHIVE LowHiveToValidate = NULL; 2753 PCMHIVE HighHiveToValidate = NULL; 2754 #endif 2755 PCMHIVE KeyHive = NULL; 2756 NTSTATUS Status = STATUS_SUCCESS; 2757 2758 PAGED_CODE(); 2759 2760 DPRINT("CmSaveKey(%p, %p, %p)\n", HighKcb, LowKcb, FileHandle); 2761 2762 /* Lock the registry and the KCBs */ 2763 CmpLockRegistry(); 2764 CmpAcquireKcbLockShared(HighKcb); 2765 CmpAcquireKcbLockShared(LowKcb); 2766 2767 #if DBG 2768 /* Get the high and low hives for validation */ 2769 HighHiveToValidate = (PCMHIVE)HighKcb->KeyHive; 2770 LowHiveToValidate = (PCMHIVE)LowKcb->KeyHive; 2771 #endif 2772 2773 if (LowKcb->Delete || HighKcb->Delete) 2774 { 2775 /* The source key has been deleted, do nothing */ 2776 Status = STATUS_KEY_DELETED; 2777 goto done; 2778 } 2779 2780 #if DBG 2781 /* Make sure that both the high and low precedence hives are OK */ 2782 CheckStatus = CmCheckRegistry(HighHiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2783 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2784 CheckStatus = CmCheckRegistry(LowHiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2785 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2786 #endif 2787 2788 /* Create a new hive that will hold the key */ 2789 Status = CmpInitializeHive(&KeyHive, 2790 HINIT_CREATE, 2791 HIVE_VOLATILE, 2792 HFILE_TYPE_PRIMARY, 2793 NULL, 2794 NULL, 2795 NULL, 2796 NULL, 2797 NULL, 2798 NULL, 2799 CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES); 2800 if (!NT_SUCCESS(Status)) 2801 goto done; 2802 2803 /* Copy the low precedence key recursively into the new hive */ 2804 Status = CmpDeepCopyKey(LowKcb->KeyHive, 2805 LowKcb->KeyCell, 2806 &KeyHive->Hive, 2807 Stable, 2808 &KeyHive->Hive.BaseBlock->RootCell); 2809 if (!NT_SUCCESS(Status)) 2810 goto done; 2811 2812 /* Copy the high precedence key recursively into the new hive */ 2813 Status = CmpDeepCopyKey(HighKcb->KeyHive, 2814 HighKcb->KeyCell, 2815 &KeyHive->Hive, 2816 Stable, 2817 &KeyHive->Hive.BaseBlock->RootCell); 2818 if (!NT_SUCCESS(Status)) 2819 goto done; 2820 2821 /* Set the primary handle of the hive */ 2822 KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle; 2823 2824 /* Dump the hive into the file */ 2825 HvWriteHive(&KeyHive->Hive); 2826 2827 done: 2828 /* Free the hive */ 2829 if (KeyHive) 2830 CmpDestroyHive(KeyHive); 2831 2832 #if DBG 2833 if (NT_SUCCESS(Status)) 2834 { 2835 /* Check those hives again before we say goodbye */ 2836 CheckStatus = CmCheckRegistry(HighHiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2837 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2838 CheckStatus = CmCheckRegistry(LowHiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2839 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2840 } 2841 #endif 2842 2843 /* Release the locks */ 2844 CmpReleaseKcbLock(LowKcb); 2845 CmpReleaseKcbLock(HighKcb); 2846 CmpUnlockRegistry(); 2847 2848 return Status; 2849 } 2850