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 if (!CellData) ASSERT(FALSE); 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 if (!CellData) ASSERT(FALSE); 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 = 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 = 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 Quickie2; 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 Quickie2: 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 NTSTATUS Status = STATUS_SUCCESS; 1943 PHHIVE Hive; 1944 1945 /* Ignore flushes until we're ready */ 1946 if (CmpNoWrite) return STATUS_SUCCESS; 1947 1948 /* Get the hives */ 1949 Hive = Kcb->KeyHive; 1950 CmHive = (PCMHIVE)Hive; 1951 1952 /* Check if this is the master hive */ 1953 if (CmHive == CmiVolatileHive) 1954 { 1955 /* Flush all the hives instead */ 1956 CmpDoFlushAll(FALSE); 1957 } 1958 else 1959 { 1960 /* Don't touch the hive */ 1961 CmpLockHiveFlusherExclusive(CmHive); 1962 ASSERT(CmHive->ViewLock); 1963 KeAcquireGuardedMutex(CmHive->ViewLock); 1964 CmHive->ViewLockOwner = KeGetCurrentThread(); 1965 1966 /* Will the hive shrink? */ 1967 if (HvHiveWillShrink(Hive)) 1968 { 1969 /* I don't believe the current Hv does shrinking */ 1970 ASSERT(FALSE); 1971 } 1972 else 1973 { 1974 /* Now we can release views */ 1975 ASSERT(CmHive->ViewLock); 1976 CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK_OR_LOADING(CmHive); 1977 ASSERT(KeGetCurrentThread() == CmHive->ViewLockOwner); 1978 KeReleaseGuardedMutex(CmHive->ViewLock); 1979 } 1980 1981 /* Flush only this hive */ 1982 if (!HvSyncHive(Hive)) 1983 { 1984 /* Fail */ 1985 Status = STATUS_REGISTRY_IO_FAILED; 1986 } 1987 1988 /* Release the flush lock */ 1989 CmpUnlockHiveFlusher(CmHive); 1990 } 1991 1992 /* Return the status */ 1993 return Status; 1994 } 1995 1996 NTSTATUS 1997 NTAPI 1998 CmLoadKey(IN POBJECT_ATTRIBUTES TargetKey, 1999 IN POBJECT_ATTRIBUTES SourceFile, 2000 IN ULONG Flags, 2001 IN PCM_KEY_BODY KeyBody) 2002 { 2003 SECURITY_QUALITY_OF_SERVICE ServiceQos; 2004 SECURITY_CLIENT_CONTEXT ClientSecurityContext; 2005 HANDLE KeyHandle; 2006 BOOLEAN Allocate = TRUE; 2007 PCMHIVE CmHive, LoadedHive; 2008 NTSTATUS Status; 2009 CM_PARSE_CONTEXT ParseContext; 2010 2011 /* Check if we have a trust key */ 2012 if (KeyBody) 2013 { 2014 /* Fail */ 2015 DPRINT("Trusted classes not yet supported\n"); 2016 } 2017 2018 /* Build a service QoS for a security context */ 2019 ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); 2020 ServiceQos.ImpersonationLevel = SecurityImpersonation; 2021 ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; 2022 ServiceQos.EffectiveOnly = TRUE; 2023 Status = SeCreateClientSecurity(PsGetCurrentThread(), 2024 &ServiceQos, 2025 FALSE, 2026 &ClientSecurityContext); 2027 if (!NT_SUCCESS(Status)) 2028 { 2029 /* Fail */ 2030 DPRINT1("Security context failed\n"); 2031 return Status; 2032 } 2033 2034 /* Open the target key */ 2035 RtlZeroMemory(&ParseContext, sizeof(ParseContext)); 2036 ParseContext.CreateOperation = FALSE; 2037 Status = ObOpenObjectByName(TargetKey, 2038 CmpKeyObjectType, 2039 KernelMode, 2040 NULL, 2041 KEY_READ, 2042 &ParseContext, 2043 &KeyHandle); 2044 if (!NT_SUCCESS(Status)) KeyHandle = NULL; 2045 2046 /* Open the hive */ 2047 Status = CmpCmdHiveOpen(SourceFile, 2048 &ClientSecurityContext, 2049 &Allocate, 2050 &CmHive, 2051 0); 2052 2053 /* Get rid of the security context */ 2054 SeDeleteClientSecurity(&ClientSecurityContext); 2055 2056 /* See if we failed */ 2057 if (!NT_SUCCESS(Status)) 2058 { 2059 /* See if the target already existed */ 2060 if (KeyHandle) 2061 { 2062 /* Lock the registry */ 2063 CmpLockRegistryExclusive(); 2064 2065 /* Check if we are already loaded */ 2066 if (CmpIsHiveAlreadyLoaded(KeyHandle, SourceFile, &LoadedHive)) 2067 { 2068 /* That's okay then */ 2069 ASSERT(LoadedHive); 2070 Status = STATUS_SUCCESS; 2071 } 2072 2073 /* Release the registry */ 2074 CmpUnlockRegistry(); 2075 } 2076 2077 /* Close the key handle if we had one */ 2078 if (KeyHandle) ZwClose(KeyHandle); 2079 return Status; 2080 } 2081 2082 /* Lock the registry shared */ 2083 CmpLockRegistry(); 2084 2085 /* Lock loading */ 2086 ExAcquirePushLockExclusive(&CmpLoadHiveLock); 2087 2088 /* Lock the hive to this thread */ 2089 CmHive->Hive.HiveFlags |= HIVE_IS_UNLOADING; 2090 CmHive->CreatorOwner = KeGetCurrentThread(); 2091 2092 /* Set flag */ 2093 if (Flags & REG_NO_LAZY_FLUSH) CmHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH; 2094 2095 /* Link the hive */ 2096 Status = CmpLinkHiveToMaster(TargetKey->ObjectName, 2097 TargetKey->RootDirectory, 2098 CmHive, 2099 Allocate, 2100 TargetKey->SecurityDescriptor); 2101 if (NT_SUCCESS(Status)) 2102 { 2103 /* Add to HiveList key */ 2104 CmpAddToHiveFileList(CmHive); 2105 2106 /* Sync the hive if necessary */ 2107 if (Allocate) 2108 { 2109 /* Sync it under the flusher lock */ 2110 CmpLockHiveFlusherExclusive(CmHive); 2111 HvSyncHive(&CmHive->Hive); 2112 CmpUnlockHiveFlusher(CmHive); 2113 } 2114 2115 /* Release the hive */ 2116 CmHive->Hive.HiveFlags &= ~HIVE_IS_UNLOADING; 2117 CmHive->CreatorOwner = NULL; 2118 2119 /* Allow loads */ 2120 ExReleasePushLock(&CmpLoadHiveLock); 2121 } 2122 else 2123 { 2124 DPRINT1("CmpLinkHiveToMaster failed, Status %lx\n", Status); 2125 /* FIXME: TODO */ 2126 ASSERT(FALSE); 2127 } 2128 2129 /* Is this first profile load? */ 2130 if (!CmpProfileLoaded && !CmpWasSetupBoot) 2131 { 2132 /* User is now logged on, set quotas */ 2133 CmpProfileLoaded = TRUE; 2134 CmpSetGlobalQuotaAllowed(); 2135 } 2136 2137 /* Unlock the registry */ 2138 CmpUnlockRegistry(); 2139 2140 /* Close handle and return */ 2141 if (KeyHandle) ZwClose(KeyHandle); 2142 return Status; 2143 } 2144 2145 static 2146 BOOLEAN 2147 NTAPI 2148 CmpUnlinkHiveFromMaster(IN PCMHIVE CmHive, 2149 IN HCELL_INDEX Cell) 2150 { 2151 PCELL_DATA CellData; 2152 HCELL_INDEX LinkCell; 2153 NTSTATUS Status; 2154 2155 DPRINT("CmpUnlinkHiveFromMaster()\n"); 2156 2157 /* Get the cell data */ 2158 CellData = HvGetCell(&CmHive->Hive, Cell); 2159 if (CellData == NULL) 2160 return FALSE; 2161 2162 /* Get the link cell and release the current cell */ 2163 LinkCell = CellData->u.KeyNode.Parent; 2164 HvReleaseCell(&CmHive->Hive, Cell); 2165 2166 /* Remove the link cell from the master hive */ 2167 CmpLockHiveFlusherExclusive(CmiVolatileHive); 2168 Status = CmpFreeKeyByCell((PHHIVE)CmiVolatileHive, 2169 LinkCell, 2170 TRUE); 2171 CmpUnlockHiveFlusher(CmiVolatileHive); 2172 if (!NT_SUCCESS(Status)) 2173 { 2174 DPRINT1("CmpFreeKeyByCell() failed (Status 0x%08lx)\n", Status); 2175 return FALSE; 2176 } 2177 2178 /* Remove the hive from the list */ 2179 ExAcquirePushLockExclusive(&CmpHiveListHeadLock); 2180 RemoveEntryList(&CmHive->HiveList); 2181 ExReleasePushLock(&CmpHiveListHeadLock); 2182 2183 return TRUE; 2184 } 2185 2186 NTSTATUS 2187 NTAPI 2188 CmUnloadKey(IN PCM_KEY_CONTROL_BLOCK Kcb, 2189 IN ULONG Flags) 2190 { 2191 PHHIVE Hive; 2192 PCMHIVE CmHive; 2193 HCELL_INDEX Cell; 2194 2195 DPRINT("CmUnloadKey(%p, %lx)\n", Kcb, Flags); 2196 2197 /* Get the hive */ 2198 Hive = Kcb->KeyHive; 2199 Cell = Kcb->KeyCell; 2200 CmHive = (PCMHIVE)Hive; 2201 2202 /* Fail if the key is not a hive root key */ 2203 if (Cell != Hive->BaseBlock->RootCell) 2204 { 2205 DPRINT1("Key is not a hive root key!\n"); 2206 return STATUS_INVALID_PARAMETER; 2207 } 2208 2209 /* Fail if we try to unload the master hive */ 2210 if (CmHive == CmiVolatileHive) 2211 { 2212 DPRINT1("Do not try to unload the master hive!\n"); 2213 return STATUS_INVALID_PARAMETER; 2214 } 2215 2216 /* Mark this hive as being unloaded */ 2217 Hive->HiveFlags |= HIVE_IS_UNLOADING; 2218 2219 /* Search for any opened keys in this hive, and take an appropriate action */ 2220 if (Kcb->RefCount > 1) 2221 { 2222 if (Flags != REG_FORCE_UNLOAD) 2223 { 2224 if (CmCountOpenSubKeys(Kcb, FALSE) != 0) 2225 { 2226 /* There are open subkeys but we don't force hive unloading, fail */ 2227 Hive->HiveFlags &= ~HIVE_IS_UNLOADING; 2228 return STATUS_CANNOT_DELETE; 2229 } 2230 } 2231 else 2232 { 2233 DPRINT1("CmUnloadKey: Force unloading is UNIMPLEMENTED, expect dangling KCBs problems!\n"); 2234 } 2235 } 2236 2237 /* Flush the hive */ 2238 CmFlushKey(Kcb, TRUE); 2239 2240 /* Unlink the hive from the master hive */ 2241 if (!CmpUnlinkHiveFromMaster(CmHive, Cell)) 2242 { 2243 DPRINT("CmpUnlinkHiveFromMaster() failed!\n"); 2244 2245 /* Remove the unloading flag and return failure */ 2246 Hive->HiveFlags &= ~HIVE_IS_UNLOADING; 2247 return STATUS_INSUFFICIENT_RESOURCES; 2248 } 2249 2250 /* Clean up information we have on the subkey */ 2251 CmpCleanUpSubKeyInfo(Kcb->ParentKcb); 2252 2253 /* Set the KCB in delete mode and remove it */ 2254 Kcb->Delete = TRUE; 2255 CmpRemoveKeyControlBlock(Kcb); 2256 2257 if (Flags != REG_FORCE_UNLOAD) 2258 { 2259 /* Release the KCB locks */ 2260 CmpReleaseTwoKcbLockByKey(Kcb->ConvKey, Kcb->ParentKcb->ConvKey); 2261 2262 /* Release the hive loading lock */ 2263 ExReleasePushLockExclusive(&CmpLoadHiveLock); 2264 } 2265 2266 /* Release hive lock */ 2267 CmpUnlockRegistry(); 2268 2269 /* Close file handles */ 2270 CmpCloseHiveFiles(CmHive); 2271 2272 /* Remove the hive from the hive file list */ 2273 CmpRemoveFromHiveFileList(CmHive); 2274 2275 /* Destroy the security descriptor cache */ 2276 CmpDestroySecurityCache(CmHive); 2277 2278 /* Destroy the view list */ 2279 CmpDestroyHiveViewList(CmHive); 2280 2281 /* Delete the flusher lock */ 2282 ExDeleteResourceLite(CmHive->FlusherLock); 2283 ExFreePoolWithTag(CmHive->FlusherLock, TAG_CMHIVE); 2284 2285 /* Delete the view lock */ 2286 ExFreePoolWithTag(CmHive->ViewLock, TAG_CMHIVE); 2287 2288 /* Free the hive storage */ 2289 HvFree(Hive); 2290 2291 /* Free the hive */ 2292 CmpFree(CmHive, TAG_CM); 2293 2294 return STATUS_SUCCESS; 2295 } 2296 2297 ULONG 2298 NTAPI 2299 CmCountOpenSubKeys(IN PCM_KEY_CONTROL_BLOCK RootKcb, 2300 IN BOOLEAN RemoveEmptyCacheEntries) 2301 { 2302 PCM_KEY_HASH Entry; 2303 PCM_KEY_CONTROL_BLOCK CachedKcb; 2304 PCM_KEY_CONTROL_BLOCK ParentKcb; 2305 ULONG ParentKeyCount; 2306 ULONG i, j; 2307 ULONG SubKeys = 0; 2308 2309 DPRINT("CmCountOpenSubKeys() called\n"); 2310 2311 /* The root key is the only referenced key. There are no refereced sub keys. */ 2312 if (RootKcb->RefCount == 1) 2313 { 2314 DPRINT("Open sub keys: 0\n"); 2315 return 0; 2316 } 2317 2318 /* Enumerate all hash lists */ 2319 for (i = 0; i < CmpHashTableSize; i++) 2320 { 2321 /* Get the first cache entry */ 2322 Entry = CmpCacheTable[i].Entry; 2323 2324 /* Enumerate all cache entries */ 2325 while (Entry) 2326 { 2327 /* Get the KCB of the current cache entry */ 2328 CachedKcb = CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash); 2329 2330 /* Check keys only that are subkeys to our root key */ 2331 if (CachedKcb->TotalLevels > RootKcb->TotalLevels) 2332 { 2333 /* Calculate the number of parent keys to the root key */ 2334 ParentKeyCount = CachedKcb->TotalLevels - RootKcb->TotalLevels; 2335 2336 /* Find a parent key that could be the root key */ 2337 ParentKcb = CachedKcb; 2338 for (j = 0; j < ParentKeyCount; j++) 2339 { 2340 ParentKcb = ParentKcb->ParentKcb; 2341 } 2342 2343 /* Check whether the parent is the root key */ 2344 if (ParentKcb == RootKcb) 2345 { 2346 DPRINT("Found a sub key, RefCount = %u\n", CachedKcb->RefCount); 2347 2348 if (CachedKcb->RefCount > 0) 2349 { 2350 /* Count the current hash entry if it is in use */ 2351 SubKeys++; 2352 } 2353 else if ((CachedKcb->RefCount == 0) && (RemoveEmptyCacheEntries != FALSE)) 2354 { 2355 /* Remove the current key from the delayed close list */ 2356 CmpRemoveFromDelayedClose(CachedKcb); 2357 2358 /* Remove the current cache entry */ 2359 CmpCleanUpKcbCacheWithLock(CachedKcb, TRUE); 2360 2361 /* Restart, because the hash list has changed */ 2362 Entry = CmpCacheTable[i].Entry; 2363 continue; 2364 } 2365 } 2366 } 2367 2368 /* Get the next cache entry */ 2369 Entry = Entry->NextHash; 2370 } 2371 } 2372 2373 DPRINT("Open sub keys: %u\n", SubKeys); 2374 return SubKeys; 2375 } 2376 2377 static 2378 NTSTATUS 2379 CmpDeepCopyKeyInternal(IN PHHIVE SourceHive, 2380 IN HCELL_INDEX SrcKeyCell, 2381 IN PHHIVE DestinationHive, 2382 IN HCELL_INDEX Parent, 2383 IN HSTORAGE_TYPE StorageType, 2384 OUT PHCELL_INDEX DestKeyCell OPTIONAL) 2385 { 2386 NTSTATUS Status; 2387 PCM_KEY_NODE SrcNode; 2388 PCM_KEY_NODE DestNode = NULL; 2389 HCELL_INDEX NewKeyCell = HCELL_NIL; 2390 HCELL_INDEX NewClassCell = HCELL_NIL, NewSecCell = HCELL_NIL; 2391 HCELL_INDEX SubKey, NewSubKey; 2392 ULONG Index, SubKeyCount; 2393 2394 PAGED_CODE(); 2395 2396 DPRINT("CmpDeepCopyKeyInternal(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)\n", 2397 SourceHive, 2398 SrcKeyCell, 2399 DestinationHive, 2400 Parent, 2401 StorageType, 2402 DestKeyCell); 2403 2404 /* Get the source cell node */ 2405 SrcNode = HvGetCell(SourceHive, SrcKeyCell); 2406 ASSERT(SrcNode); 2407 2408 /* Sanity check */ 2409 ASSERT(SrcNode->Signature == CM_KEY_NODE_SIGNATURE); 2410 2411 /* Create a simple copy of the source key */ 2412 NewKeyCell = CmpCopyCell(SourceHive, 2413 SrcKeyCell, 2414 DestinationHive, 2415 StorageType); 2416 if (NewKeyCell == HCELL_NIL) 2417 { 2418 /* Not enough storage space */ 2419 Status = STATUS_INSUFFICIENT_RESOURCES; 2420 goto Cleanup; 2421 } 2422 2423 /* Get the destination cell node */ 2424 DestNode = HvGetCell(DestinationHive, NewKeyCell); 2425 ASSERT(DestNode); 2426 2427 /* Set the parent and copy the flags */ 2428 DestNode->Parent = Parent; 2429 DestNode->Flags = (SrcNode->Flags & KEY_COMP_NAME); // Keep only the single permanent flag 2430 if (Parent == HCELL_NIL) 2431 { 2432 /* This is the new root node */ 2433 DestNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE; 2434 } 2435 2436 /* Copy the class cell */ 2437 if (SrcNode->ClassLength > 0) 2438 { 2439 NewClassCell = CmpCopyCell(SourceHive, 2440 SrcNode->Class, 2441 DestinationHive, 2442 StorageType); 2443 if (NewClassCell == HCELL_NIL) 2444 { 2445 /* Not enough storage space */ 2446 Status = STATUS_INSUFFICIENT_RESOURCES; 2447 goto Cleanup; 2448 } 2449 2450 DestNode->Class = NewClassCell; 2451 DestNode->ClassLength = SrcNode->ClassLength; 2452 } 2453 else 2454 { 2455 DestNode->Class = HCELL_NIL; 2456 DestNode->ClassLength = 0; 2457 } 2458 2459 /* Copy the security cell (FIXME: HACKish poor-man version) */ 2460 if (SrcNode->Security != HCELL_NIL) 2461 { 2462 NewSecCell = CmpCopyCell(SourceHive, 2463 SrcNode->Security, 2464 DestinationHive, 2465 StorageType); 2466 if (NewSecCell == HCELL_NIL) 2467 { 2468 /* Not enough storage space */ 2469 Status = STATUS_INSUFFICIENT_RESOURCES; 2470 goto Cleanup; 2471 } 2472 } 2473 DestNode->Security = NewSecCell; 2474 2475 /* Copy the value list */ 2476 Status = CmpCopyKeyValueList(SourceHive, 2477 &SrcNode->ValueList, 2478 DestinationHive, 2479 &DestNode->ValueList, 2480 StorageType); 2481 if (!NT_SUCCESS(Status)) 2482 goto Cleanup; 2483 2484 /* Clear the invalid subkey index */ 2485 DestNode->SubKeyCounts[Stable] = DestNode->SubKeyCounts[Volatile] = 0; 2486 DestNode->SubKeyLists[Stable] = DestNode->SubKeyLists[Volatile] = HCELL_NIL; 2487 2488 /* Calculate the total number of subkeys */ 2489 SubKeyCount = SrcNode->SubKeyCounts[Stable] + SrcNode->SubKeyCounts[Volatile]; 2490 2491 /* Loop through all the subkeys */ 2492 for (Index = 0; Index < SubKeyCount; Index++) 2493 { 2494 /* Get the subkey */ 2495 SubKey = CmpFindSubKeyByNumber(SourceHive, SrcNode, Index); 2496 ASSERT(SubKey != HCELL_NIL); 2497 2498 /* Call the function recursively for the subkey */ 2499 // 2500 // FIXME: Danger!! Kernel stack exhaustion!! 2501 // 2502 Status = CmpDeepCopyKeyInternal(SourceHive, 2503 SubKey, 2504 DestinationHive, 2505 NewKeyCell, 2506 StorageType, 2507 &NewSubKey); 2508 if (!NT_SUCCESS(Status)) 2509 goto Cleanup; 2510 2511 /* Add the copy of the subkey to the new key */ 2512 if (!CmpAddSubKey(DestinationHive, 2513 NewKeyCell, 2514 NewSubKey)) 2515 { 2516 /* Cleanup allocated cell */ 2517 HvFreeCell(DestinationHive, NewSubKey); 2518 2519 Status = STATUS_INSUFFICIENT_RESOURCES; 2520 goto Cleanup; 2521 } 2522 } 2523 2524 /* Set success */ 2525 Status = STATUS_SUCCESS; 2526 2527 Cleanup: 2528 2529 /* Release the cells */ 2530 if (DestNode) HvReleaseCell(DestinationHive, NewKeyCell); 2531 if (SrcNode) HvReleaseCell(SourceHive, SrcKeyCell); 2532 2533 /* Cleanup allocated cells in case of failure */ 2534 if (!NT_SUCCESS(Status)) 2535 { 2536 if (NewSecCell != HCELL_NIL) 2537 HvFreeCell(DestinationHive, NewSecCell); 2538 2539 if (NewClassCell != HCELL_NIL) 2540 HvFreeCell(DestinationHive, NewClassCell); 2541 2542 if (NewKeyCell != HCELL_NIL) 2543 HvFreeCell(DestinationHive, NewKeyCell); 2544 2545 NewKeyCell = HCELL_NIL; 2546 } 2547 2548 /* Set the cell index if requested and return status */ 2549 if (DestKeyCell) *DestKeyCell = NewKeyCell; 2550 return Status; 2551 } 2552 2553 NTSTATUS 2554 NTAPI 2555 CmpDeepCopyKey(IN PHHIVE SourceHive, 2556 IN HCELL_INDEX SrcKeyCell, 2557 IN PHHIVE DestinationHive, 2558 IN HSTORAGE_TYPE StorageType, 2559 OUT PHCELL_INDEX DestKeyCell OPTIONAL) 2560 { 2561 /* Call the internal function */ 2562 return CmpDeepCopyKeyInternal(SourceHive, 2563 SrcKeyCell, 2564 DestinationHive, 2565 HCELL_NIL, 2566 StorageType, 2567 DestKeyCell); 2568 } 2569 2570 NTSTATUS 2571 NTAPI 2572 CmSaveKey(IN PCM_KEY_CONTROL_BLOCK Kcb, 2573 IN HANDLE FileHandle, 2574 IN ULONG Flags) 2575 { 2576 NTSTATUS Status = STATUS_SUCCESS; 2577 PCMHIVE KeyHive = NULL; 2578 PAGED_CODE(); 2579 2580 DPRINT("CmSaveKey(0x%08X, 0x%08X, %lu)\n", Kcb, FileHandle, Flags); 2581 2582 /* Lock the registry and KCB */ 2583 CmpLockRegistry(); 2584 CmpAcquireKcbLockShared(Kcb); 2585 2586 if (Kcb->Delete) 2587 { 2588 /* The source key has been deleted, do nothing */ 2589 Status = STATUS_KEY_DELETED; 2590 goto Cleanup; 2591 } 2592 2593 if (Kcb->KeyHive == &CmiVolatileHive->Hive) 2594 { 2595 /* Keys that are directly in the master hive can't be saved */ 2596 Status = STATUS_ACCESS_DENIED; 2597 goto Cleanup; 2598 } 2599 2600 /* Create a new hive that will hold the key */ 2601 Status = CmpInitializeHive(&KeyHive, 2602 HINIT_CREATE, 2603 HIVE_VOLATILE, 2604 HFILE_TYPE_PRIMARY, 2605 NULL, 2606 NULL, 2607 NULL, 2608 NULL, 2609 NULL, 2610 0); 2611 if (!NT_SUCCESS(Status)) goto Cleanup; 2612 2613 /* Copy the key recursively into the new hive */ 2614 Status = CmpDeepCopyKey(Kcb->KeyHive, 2615 Kcb->KeyCell, 2616 &KeyHive->Hive, 2617 Stable, 2618 &KeyHive->Hive.BaseBlock->RootCell); 2619 if (!NT_SUCCESS(Status)) goto Cleanup; 2620 2621 /* Set the primary handle of the hive */ 2622 KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle; 2623 2624 /* Dump the hive into the file */ 2625 HvWriteHive(&KeyHive->Hive); 2626 2627 Cleanup: 2628 2629 /* Free the hive */ 2630 if (KeyHive) CmpDestroyHive(KeyHive); 2631 2632 /* Release the locks */ 2633 CmpReleaseKcbLock(Kcb); 2634 CmpUnlockRegistry(); 2635 2636 return Status; 2637 } 2638 2639 NTSTATUS 2640 NTAPI 2641 CmSaveMergedKeys(IN PCM_KEY_CONTROL_BLOCK HighKcb, 2642 IN PCM_KEY_CONTROL_BLOCK LowKcb, 2643 IN HANDLE FileHandle) 2644 { 2645 PCMHIVE KeyHive = NULL; 2646 NTSTATUS Status = STATUS_SUCCESS; 2647 2648 PAGED_CODE(); 2649 2650 DPRINT("CmSaveKey(%p, %p, %p)\n", HighKcb, LowKcb, FileHandle); 2651 2652 /* Lock the registry and the KCBs */ 2653 CmpLockRegistry(); 2654 CmpAcquireKcbLockShared(HighKcb); 2655 CmpAcquireKcbLockShared(LowKcb); 2656 2657 if (LowKcb->Delete || HighKcb->Delete) 2658 { 2659 /* The source key has been deleted, do nothing */ 2660 Status = STATUS_KEY_DELETED; 2661 goto done; 2662 } 2663 2664 /* Create a new hive that will hold the key */ 2665 Status = CmpInitializeHive(&KeyHive, 2666 HINIT_CREATE, 2667 HIVE_VOLATILE, 2668 HFILE_TYPE_PRIMARY, 2669 NULL, 2670 NULL, 2671 NULL, 2672 NULL, 2673 NULL, 2674 0); 2675 if (!NT_SUCCESS(Status)) 2676 goto done; 2677 2678 /* Copy the low precedence key recursively into the new hive */ 2679 Status = CmpDeepCopyKey(LowKcb->KeyHive, 2680 LowKcb->KeyCell, 2681 &KeyHive->Hive, 2682 Stable, 2683 &KeyHive->Hive.BaseBlock->RootCell); 2684 if (!NT_SUCCESS(Status)) 2685 goto done; 2686 2687 /* Copy the high precedence key recursively into the new hive */ 2688 Status = CmpDeepCopyKey(HighKcb->KeyHive, 2689 HighKcb->KeyCell, 2690 &KeyHive->Hive, 2691 Stable, 2692 &KeyHive->Hive.BaseBlock->RootCell); 2693 if (!NT_SUCCESS(Status)) 2694 goto done; 2695 2696 /* Set the primary handle of the hive */ 2697 KeyHive->FileHandles[HFILE_TYPE_PRIMARY] = FileHandle; 2698 2699 /* Dump the hive into the file */ 2700 HvWriteHive(&KeyHive->Hive); 2701 2702 done: 2703 /* Free the hive */ 2704 if (KeyHive) 2705 CmpDestroyHive(KeyHive); 2706 2707 /* Release the locks */ 2708 CmpReleaseKcbLock(LowKcb); 2709 CmpReleaseKcbLock(HighKcb); 2710 CmpUnlockRegistry(); 2711 2712 return Status; 2713 } 2714