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( 2210 _In_ PCM_KEY_CONTROL_BLOCK Kcb, 2211 _In_ ULONG Flags) 2212 { 2213 PHHIVE Hive; 2214 PCMHIVE CmHive; 2215 HCELL_INDEX Cell; 2216 2217 DPRINT("CmUnloadKey(%p, %lx)\n", Kcb, Flags); 2218 2219 /* Ensure the registry is locked exclusively for the calling thread */ 2220 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK(); 2221 2222 /* Get the hive */ 2223 Hive = Kcb->KeyHive; 2224 Cell = Kcb->KeyCell; 2225 CmHive = (PCMHIVE)Hive; 2226 2227 /* Fail if the key is not a hive root key */ 2228 if (Cell != Hive->BaseBlock->RootCell) 2229 { 2230 DPRINT1("Key is not a hive root key!\n"); 2231 return STATUS_INVALID_PARAMETER; 2232 } 2233 2234 /* Fail if we try to unload the master hive */ 2235 if (CmHive == CmiVolatileHive) 2236 { 2237 DPRINT1("Do not try to unload the master hive!\n"); 2238 return STATUS_INVALID_PARAMETER; 2239 } 2240 2241 /* Mark this hive as being unloaded */ 2242 Hive->HiveFlags |= HIVE_IS_UNLOADING; 2243 2244 /* Search for any opened keys in this hive, and take an appropriate action */ 2245 if (Kcb->RefCount > 1) 2246 { 2247 if (Flags != REG_FORCE_UNLOAD) 2248 { 2249 if (CmpEnumerateOpenSubKeys(Kcb, TRUE, FALSE) != 0) 2250 { 2251 /* There are open subkeys but we don't force hive unloading, fail */ 2252 Hive->HiveFlags &= ~HIVE_IS_UNLOADING; 2253 return STATUS_CANNOT_DELETE; 2254 } 2255 } 2256 else 2257 { 2258 if (CmpEnumerateOpenSubKeys(Kcb, TRUE, TRUE) != 0) 2259 { 2260 /* There are open subkeys that we cannot force to unload, fail */ 2261 Hive->HiveFlags &= ~HIVE_IS_UNLOADING; 2262 return STATUS_CANNOT_DELETE; 2263 } 2264 } 2265 } 2266 2267 /* Set the loading flag */ 2268 CmHive->HiveIsLoading = TRUE; 2269 2270 /* Flush the hive */ 2271 CmFlushKey(Kcb, TRUE); 2272 2273 /* Unlink the hive from the master hive */ 2274 if (!CmpUnlinkHiveFromMaster(CmHive, Cell)) 2275 { 2276 DPRINT("CmpUnlinkHiveFromMaster() failed!\n"); 2277 2278 /* Remove the unloading flag */ 2279 Hive->HiveFlags &= ~HIVE_IS_UNLOADING; 2280 2281 /* Reset the loading flag */ 2282 CmHive->HiveIsLoading = FALSE; 2283 2284 /* Return failure */ 2285 return STATUS_INSUFFICIENT_RESOURCES; 2286 } 2287 2288 /* Flush any notifications if we force hive unloading */ 2289 if (Flags == REG_FORCE_UNLOAD) 2290 CmpFlushNotifiesOnKeyBodyList(Kcb, TRUE); // Lock is already held 2291 2292 /* Clean up information we have on the subkey */ 2293 CmpCleanUpSubKeyInfo(Kcb->ParentKcb); 2294 2295 /* Set the KCB in delete mode and remove it */ 2296 Kcb->Delete = TRUE; 2297 CmpRemoveKeyControlBlock(Kcb); 2298 2299 /* Release the hive loading lock */ 2300 ExReleasePushLockExclusive(&CmpLoadHiveLock); 2301 2302 /* Release hive lock */ 2303 CmpUnlockRegistry(); 2304 2305 /* Close file handles */ 2306 CmpCloseHiveFiles(CmHive); 2307 2308 /* Remove the hive from the hive file list */ 2309 CmpRemoveFromHiveFileList(CmHive); 2310 2311 /** 2312 ** NOTE: 2313 ** The following code is mostly equivalent to what we "call" CmpDestroyHive() 2314 **/ 2315 /* Destroy the security descriptor cache */ 2316 CmpDestroySecurityCache(CmHive); 2317 2318 /* Destroy the view list */ 2319 CmpDestroyHiveViewList(CmHive); 2320 2321 /* Delete the flusher lock */ 2322 ExDeleteResourceLite(CmHive->FlusherLock); 2323 ExFreePoolWithTag(CmHive->FlusherLock, TAG_CMHIVE); 2324 2325 /* Delete the view lock */ 2326 ExFreePoolWithTag(CmHive->ViewLock, TAG_CMHIVE); 2327 2328 /* Free the hive storage */ 2329 HvFree(Hive); 2330 2331 /* Free the hive */ 2332 CmpFree(CmHive, TAG_CM); 2333 2334 return STATUS_SUCCESS; 2335 } 2336 2337 ULONG 2338 NTAPI 2339 CmpEnumerateOpenSubKeys( 2340 _In_ PCM_KEY_CONTROL_BLOCK RootKcb, 2341 _In_ BOOLEAN RemoveEmptyCacheEntries, 2342 _In_ BOOLEAN DereferenceOpenedEntries) 2343 { 2344 PCM_KEY_HASH Entry; 2345 PCM_KEY_CONTROL_BLOCK CachedKcb; 2346 PCM_KEY_CONTROL_BLOCK ParentKcb; 2347 ULONG ParentKeyCount; 2348 ULONG i, j; 2349 ULONG SubKeys = 0; 2350 2351 DPRINT("CmpEnumerateOpenSubKeys() called\n"); 2352 2353 /* Ensure the registry is locked exclusively for the calling thread */ 2354 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK(); 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 /* Flush any notifications */ 2404 CmpFlushNotifiesOnKeyBodyList(CachedKcb, TRUE); // Lock is already held 2405 2406 /* Clean up information we have on the subkey */ 2407 CmpCleanUpSubKeyInfo(CachedKcb->ParentKcb); 2408 2409 /* Get and cache the next cache entry */ 2410 // Entry = Entry->NextHash; 2411 Entry = CachedKcb->NextHash; 2412 2413 /* Set the KCB in delete mode and remove it */ 2414 CachedKcb->Delete = TRUE; 2415 CmpRemoveKeyControlBlock(CachedKcb); 2416 2417 /* Clear the cell */ 2418 CachedKcb->KeyCell = HCELL_NIL; 2419 2420 /* Restart with the next cache entry */ 2421 continue; 2422 } 2423 /* Else, the key cannot be dereferenced, and we count it as in use */ 2424 2425 /* Count the current hash entry if it is in use */ 2426 SubKeys++; 2427 } 2428 else if ((CachedKcb->RefCount == 0) && RemoveEmptyCacheEntries) 2429 { 2430 /* Remove the current key from the delayed close list */ 2431 CmpRemoveFromDelayedClose(CachedKcb); 2432 2433 /* Remove the current cache entry */ 2434 CmpCleanUpKcbCacheWithLock(CachedKcb, TRUE); 2435 2436 /* Restart, because the hash list has changed */ 2437 Entry = CmpCacheTable[i].Entry; 2438 continue; 2439 } 2440 } 2441 } 2442 2443 /* Get the next cache entry */ 2444 Entry = Entry->NextHash; 2445 } 2446 } 2447 2448 if (SubKeys > 0) 2449 DPRINT1("Open sub keys: %u\n", SubKeys); 2450 2451 return SubKeys; 2452 } 2453 2454 static 2455 NTSTATUS 2456 CmpDeepCopyKeyInternal(IN PHHIVE SourceHive, 2457 IN HCELL_INDEX SrcKeyCell, 2458 IN PHHIVE DestinationHive, 2459 IN HCELL_INDEX Parent, 2460 IN HSTORAGE_TYPE StorageType, 2461 OUT PHCELL_INDEX DestKeyCell OPTIONAL) 2462 { 2463 NTSTATUS Status; 2464 PCM_KEY_NODE SrcNode; 2465 PCM_KEY_NODE DestNode = NULL; 2466 HCELL_INDEX NewKeyCell = HCELL_NIL; 2467 HCELL_INDEX NewClassCell = HCELL_NIL, NewSecCell = HCELL_NIL; 2468 HCELL_INDEX SubKey, NewSubKey; 2469 ULONG Index, SubKeyCount; 2470 2471 PAGED_CODE(); 2472 2473 DPRINT("CmpDeepCopyKeyInternal(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)\n", 2474 SourceHive, 2475 SrcKeyCell, 2476 DestinationHive, 2477 Parent, 2478 StorageType, 2479 DestKeyCell); 2480 2481 /* Get the source cell node */ 2482 SrcNode = (PCM_KEY_NODE)HvGetCell(SourceHive, SrcKeyCell); 2483 ASSERT(SrcNode); 2484 2485 /* Sanity check */ 2486 ASSERT(SrcNode->Signature == CM_KEY_NODE_SIGNATURE); 2487 2488 /* Create a simple copy of the source key */ 2489 NewKeyCell = CmpCopyCell(SourceHive, 2490 SrcKeyCell, 2491 DestinationHive, 2492 StorageType); 2493 if (NewKeyCell == HCELL_NIL) 2494 { 2495 /* Not enough storage space */ 2496 Status = STATUS_INSUFFICIENT_RESOURCES; 2497 goto Cleanup; 2498 } 2499 2500 /* Get the destination cell node */ 2501 DestNode = (PCM_KEY_NODE)HvGetCell(DestinationHive, NewKeyCell); 2502 ASSERT(DestNode); 2503 2504 /* Set the parent and copy the flags */ 2505 DestNode->Parent = Parent; 2506 DestNode->Flags = (SrcNode->Flags & KEY_COMP_NAME); // Keep only the single permanent flag 2507 if (Parent == HCELL_NIL) 2508 { 2509 /* This is the new root node */ 2510 DestNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE; 2511 } 2512 2513 /* Copy the class cell */ 2514 if (SrcNode->ClassLength > 0) 2515 { 2516 NewClassCell = CmpCopyCell(SourceHive, 2517 SrcNode->Class, 2518 DestinationHive, 2519 StorageType); 2520 if (NewClassCell == HCELL_NIL) 2521 { 2522 /* Not enough storage space */ 2523 Status = STATUS_INSUFFICIENT_RESOURCES; 2524 goto Cleanup; 2525 } 2526 2527 DestNode->Class = NewClassCell; 2528 DestNode->ClassLength = SrcNode->ClassLength; 2529 } 2530 else 2531 { 2532 DestNode->Class = HCELL_NIL; 2533 DestNode->ClassLength = 0; 2534 } 2535 2536 /* Copy the security cell (FIXME: HACKish poor-man version) */ 2537 if (SrcNode->Security != HCELL_NIL) 2538 { 2539 NewSecCell = CmpCopyCell(SourceHive, 2540 SrcNode->Security, 2541 DestinationHive, 2542 StorageType); 2543 if (NewSecCell == HCELL_NIL) 2544 { 2545 /* Not enough storage space */ 2546 Status = STATUS_INSUFFICIENT_RESOURCES; 2547 goto Cleanup; 2548 } 2549 } 2550 DestNode->Security = NewSecCell; 2551 2552 /* Copy the value list */ 2553 Status = CmpCopyKeyValueList(SourceHive, 2554 &SrcNode->ValueList, 2555 DestinationHive, 2556 &DestNode->ValueList, 2557 StorageType); 2558 if (!NT_SUCCESS(Status)) 2559 goto Cleanup; 2560 2561 /* Clear the invalid subkey index */ 2562 DestNode->SubKeyCounts[Stable] = DestNode->SubKeyCounts[Volatile] = 0; 2563 DestNode->SubKeyLists[Stable] = DestNode->SubKeyLists[Volatile] = HCELL_NIL; 2564 2565 /* Calculate the total number of subkeys */ 2566 SubKeyCount = SrcNode->SubKeyCounts[Stable] + SrcNode->SubKeyCounts[Volatile]; 2567 2568 /* Loop through all the subkeys */ 2569 for (Index = 0; Index < SubKeyCount; Index++) 2570 { 2571 /* Get the subkey */ 2572 SubKey = CmpFindSubKeyByNumber(SourceHive, SrcNode, Index); 2573 ASSERT(SubKey != HCELL_NIL); 2574 2575 /* Call the function recursively for the subkey */ 2576 // 2577 // FIXME: Danger!! Kernel stack exhaustion!! 2578 // 2579 Status = CmpDeepCopyKeyInternal(SourceHive, 2580 SubKey, 2581 DestinationHive, 2582 NewKeyCell, 2583 StorageType, 2584 &NewSubKey); 2585 if (!NT_SUCCESS(Status)) 2586 goto Cleanup; 2587 2588 /* Add the copy of the subkey to the new key */ 2589 if (!CmpAddSubKey(DestinationHive, 2590 NewKeyCell, 2591 NewSubKey)) 2592 { 2593 /* Cleanup allocated cell */ 2594 HvFreeCell(DestinationHive, NewSubKey); 2595 2596 Status = STATUS_INSUFFICIENT_RESOURCES; 2597 goto Cleanup; 2598 } 2599 } 2600 2601 /* Set success */ 2602 Status = STATUS_SUCCESS; 2603 2604 Cleanup: 2605 2606 /* Release the cells */ 2607 if (DestNode) HvReleaseCell(DestinationHive, NewKeyCell); 2608 if (SrcNode) HvReleaseCell(SourceHive, SrcKeyCell); 2609 2610 /* Cleanup allocated cells in case of failure */ 2611 if (!NT_SUCCESS(Status)) 2612 { 2613 if (NewSecCell != HCELL_NIL) 2614 HvFreeCell(DestinationHive, NewSecCell); 2615 2616 if (NewClassCell != HCELL_NIL) 2617 HvFreeCell(DestinationHive, NewClassCell); 2618 2619 if (NewKeyCell != HCELL_NIL) 2620 HvFreeCell(DestinationHive, NewKeyCell); 2621 2622 NewKeyCell = HCELL_NIL; 2623 } 2624 2625 /* Set the cell index if requested and return status */ 2626 if (DestKeyCell) *DestKeyCell = NewKeyCell; 2627 return Status; 2628 } 2629 2630 NTSTATUS 2631 NTAPI 2632 CmpDeepCopyKey(IN PHHIVE SourceHive, 2633 IN HCELL_INDEX SrcKeyCell, 2634 IN PHHIVE DestinationHive, 2635 IN HSTORAGE_TYPE StorageType, 2636 OUT PHCELL_INDEX DestKeyCell OPTIONAL) 2637 { 2638 /* Call the internal function */ 2639 return CmpDeepCopyKeyInternal(SourceHive, 2640 SrcKeyCell, 2641 DestinationHive, 2642 HCELL_NIL, 2643 StorageType, 2644 DestKeyCell); 2645 } 2646 2647 NTSTATUS 2648 NTAPI 2649 CmSaveKey(IN PCM_KEY_CONTROL_BLOCK Kcb, 2650 IN HANDLE FileHandle, 2651 IN ULONG Flags) 2652 { 2653 #if DBG 2654 CM_CHECK_REGISTRY_STATUS CheckStatus; 2655 PCMHIVE HiveToValidate = NULL; 2656 #endif 2657 NTSTATUS Status = STATUS_SUCCESS; 2658 PCMHIVE KeyHive = NULL; 2659 PAGED_CODE(); 2660 2661 DPRINT("CmSaveKey(0x%08X, 0x%08X, %lu)\n", Kcb, FileHandle, Flags); 2662 2663 /* Lock the registry and KCB */ 2664 CmpLockRegistry(); 2665 CmpAcquireKcbLockShared(Kcb); 2666 2667 #if DBG 2668 /* Get the hive for validation */ 2669 HiveToValidate = (PCMHIVE)Kcb->KeyHive; 2670 #endif 2671 2672 if (Kcb->Delete) 2673 { 2674 /* The source key has been deleted, do nothing */ 2675 Status = STATUS_KEY_DELETED; 2676 goto Cleanup; 2677 } 2678 2679 if (Kcb->KeyHive == &CmiVolatileHive->Hive) 2680 { 2681 /* Keys that are directly in the master hive can't be saved */ 2682 Status = STATUS_ACCESS_DENIED; 2683 goto Cleanup; 2684 } 2685 2686 #if DBG 2687 /* Make sure this control block has a sane hive */ 2688 CheckStatus = CmCheckRegistry(HiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2689 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2690 #endif 2691 2692 /* Create a new hive that will hold the key */ 2693 Status = CmpInitializeHive(&KeyHive, 2694 HINIT_CREATE, 2695 HIVE_VOLATILE, 2696 HFILE_TYPE_PRIMARY, 2697 NULL, 2698 NULL, 2699 NULL, 2700 NULL, 2701 NULL, 2702 NULL, 2703 CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES); 2704 if (!NT_SUCCESS(Status)) goto Cleanup; 2705 2706 /* Copy the key recursively into the new hive */ 2707 Status = CmpDeepCopyKey(Kcb->KeyHive, 2708 Kcb->KeyCell, 2709 &KeyHive->Hive, 2710 Stable, 2711 &KeyHive->Hive.BaseBlock->RootCell); 2712 if (!NT_SUCCESS(Status)) goto Cleanup; 2713 2714 /* Set the primary handle of the hive */ 2715 KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle; 2716 2717 /* Dump the hive into the file */ 2718 HvWriteHive(&KeyHive->Hive); 2719 2720 Cleanup: 2721 2722 /* Free the hive */ 2723 if (KeyHive) CmpDestroyHive(KeyHive); 2724 2725 #if DBG 2726 if (NT_SUCCESS(Status)) 2727 { 2728 /* Before we say goodbye, make sure the hive is still OK */ 2729 CheckStatus = CmCheckRegistry(HiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2730 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2731 } 2732 #endif 2733 2734 /* Release the locks */ 2735 CmpReleaseKcbLock(Kcb); 2736 CmpUnlockRegistry(); 2737 2738 return Status; 2739 } 2740 2741 NTSTATUS 2742 NTAPI 2743 CmSaveMergedKeys(IN PCM_KEY_CONTROL_BLOCK HighKcb, 2744 IN PCM_KEY_CONTROL_BLOCK LowKcb, 2745 IN HANDLE FileHandle) 2746 { 2747 #if DBG 2748 CM_CHECK_REGISTRY_STATUS CheckStatus; 2749 PCMHIVE LowHiveToValidate = NULL; 2750 PCMHIVE HighHiveToValidate = NULL; 2751 #endif 2752 PCMHIVE KeyHive = NULL; 2753 NTSTATUS Status = STATUS_SUCCESS; 2754 2755 PAGED_CODE(); 2756 2757 DPRINT("CmSaveKey(%p, %p, %p)\n", HighKcb, LowKcb, FileHandle); 2758 2759 /* Lock the registry and the KCBs */ 2760 CmpLockRegistry(); 2761 CmpAcquireKcbLockShared(HighKcb); 2762 CmpAcquireKcbLockShared(LowKcb); 2763 2764 #if DBG 2765 /* Get the high and low hives for validation */ 2766 HighHiveToValidate = (PCMHIVE)HighKcb->KeyHive; 2767 LowHiveToValidate = (PCMHIVE)LowKcb->KeyHive; 2768 #endif 2769 2770 if (LowKcb->Delete || HighKcb->Delete) 2771 { 2772 /* The source key has been deleted, do nothing */ 2773 Status = STATUS_KEY_DELETED; 2774 goto done; 2775 } 2776 2777 #if DBG 2778 /* Make sure that both the high and low precedence hives are OK */ 2779 CheckStatus = CmCheckRegistry(HighHiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2780 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2781 CheckStatus = CmCheckRegistry(LowHiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2782 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2783 #endif 2784 2785 /* Create a new hive that will hold the key */ 2786 Status = CmpInitializeHive(&KeyHive, 2787 HINIT_CREATE, 2788 HIVE_VOLATILE, 2789 HFILE_TYPE_PRIMARY, 2790 NULL, 2791 NULL, 2792 NULL, 2793 NULL, 2794 NULL, 2795 NULL, 2796 CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES); 2797 if (!NT_SUCCESS(Status)) 2798 goto done; 2799 2800 /* Copy the low precedence key recursively into the new hive */ 2801 Status = CmpDeepCopyKey(LowKcb->KeyHive, 2802 LowKcb->KeyCell, 2803 &KeyHive->Hive, 2804 Stable, 2805 &KeyHive->Hive.BaseBlock->RootCell); 2806 if (!NT_SUCCESS(Status)) 2807 goto done; 2808 2809 /* Copy the high precedence key recursively into the new hive */ 2810 Status = CmpDeepCopyKey(HighKcb->KeyHive, 2811 HighKcb->KeyCell, 2812 &KeyHive->Hive, 2813 Stable, 2814 &KeyHive->Hive.BaseBlock->RootCell); 2815 if (!NT_SUCCESS(Status)) 2816 goto done; 2817 2818 /* Set the primary handle of the hive */ 2819 KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle; 2820 2821 /* Dump the hive into the file */ 2822 HvWriteHive(&KeyHive->Hive); 2823 2824 done: 2825 /* Free the hive */ 2826 if (KeyHive) 2827 CmpDestroyHive(KeyHive); 2828 2829 #if DBG 2830 if (NT_SUCCESS(Status)) 2831 { 2832 /* Check those hives again before we say goodbye */ 2833 CheckStatus = CmCheckRegistry(HighHiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2834 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2835 CheckStatus = CmCheckRegistry(LowHiveToValidate, CM_CHECK_REGISTRY_DONT_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE); 2836 ASSERT(CM_CHECK_REGISTRY_SUCCESS(CheckStatus)); 2837 } 2838 #endif 2839 2840 /* Release the locks */ 2841 CmpReleaseKcbLock(LowKcb); 2842 CmpReleaseKcbLock(HighKcb); 2843 CmpUnlockRegistry(); 2844 2845 return Status; 2846 } 2847