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