1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/config/cmparse.c 5 * PURPOSE: Configuration Manager - Object Manager Parse Interface 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include "ntoskrnl.h" 12 #define NDEBUG 13 #include "debug.h" 14 15 /* GLOBALS *******************************************************************/ 16 17 /* FUNCTIONS *****************************************************************/ 18 19 BOOLEAN 20 NTAPI 21 CmpGetNextName(IN OUT PUNICODE_STRING RemainingName, 22 OUT PUNICODE_STRING NextName, 23 OUT PBOOLEAN LastName) 24 { 25 BOOLEAN NameValid = TRUE; 26 27 ASSERT(RemainingName->Length % sizeof(WCHAR) == 0); 28 29 /* Check if there's nothing left in the name */ 30 if (!(RemainingName->Buffer) || 31 (!RemainingName->Length) || 32 !(*RemainingName->Buffer)) 33 { 34 /* Clear the next name and set this as last */ 35 *LastName = TRUE; 36 NextName->Buffer = NULL; 37 NextName->Length = 0; 38 return TRUE; 39 } 40 41 /* Check if we have a path separator */ 42 while ((RemainingName->Length) && 43 (*RemainingName->Buffer == OBJ_NAME_PATH_SEPARATOR)) 44 { 45 /* Skip it */ 46 RemainingName->Buffer++; 47 RemainingName->Length -= sizeof(WCHAR); 48 RemainingName->MaximumLength -= sizeof(WCHAR); 49 } 50 51 /* Start loop at where the current buffer is */ 52 NextName->Buffer = RemainingName->Buffer; 53 while ((RemainingName->Length) && 54 (*RemainingName->Buffer != OBJ_NAME_PATH_SEPARATOR)) 55 { 56 /* Move to the next character */ 57 RemainingName->Buffer++; 58 RemainingName->Length -= sizeof(WCHAR); 59 RemainingName->MaximumLength -= sizeof(WCHAR); 60 } 61 62 /* See how many chars we parsed and validate the length */ 63 NextName->Length = (USHORT)((ULONG_PTR)RemainingName->Buffer - 64 (ULONG_PTR)NextName->Buffer); 65 if (NextName->Length > 512) NameValid = FALSE; 66 NextName->MaximumLength = NextName->Length; 67 68 /* If there's nothing left, we're last */ 69 *LastName = !RemainingName->Length; 70 return NameValid; 71 } 72 73 BOOLEAN 74 NTAPI 75 CmpGetSymbolicLink(IN PHHIVE Hive, 76 IN OUT PUNICODE_STRING ObjectName, 77 IN OUT PCM_KEY_CONTROL_BLOCK SymbolicKcb, 78 IN PUNICODE_STRING RemainingName OPTIONAL) 79 { 80 HCELL_INDEX LinkCell = HCELL_NIL; 81 PCM_KEY_VALUE LinkValue = NULL; 82 PWSTR LinkName = NULL; 83 BOOLEAN LinkNameAllocated = FALSE; 84 PWSTR NewBuffer; 85 ULONG Length = 0; 86 ULONG ValueLength = 0; 87 BOOLEAN Result = FALSE; 88 HCELL_INDEX CellToRelease = HCELL_NIL; 89 PCM_KEY_NODE Node; 90 UNICODE_STRING NewObjectName; 91 92 /* Make sure we're not being deleted */ 93 if (SymbolicKcb->Delete) return FALSE; 94 95 /* Get the key node */ 96 Node = (PCM_KEY_NODE)HvGetCell(SymbolicKcb->KeyHive, SymbolicKcb->KeyCell); 97 if (!Node) goto Exit; 98 99 /* Find the symbolic link key */ 100 LinkCell = CmpFindValueByName(Hive, Node, &CmSymbolicLinkValueName); 101 HvReleaseCell(SymbolicKcb->KeyHive, SymbolicKcb->KeyCell); 102 if (LinkCell == HCELL_NIL) goto Exit; 103 104 /* Get the value cell */ 105 LinkValue = (PCM_KEY_VALUE)HvGetCell(Hive, LinkCell); 106 if (!LinkValue) goto Exit; 107 108 /* Make sure it's a registry link */ 109 if (LinkValue->Type != REG_LINK) goto Exit; 110 111 /* Now read the value data */ 112 if (!CmpGetValueData(Hive, 113 LinkValue, 114 &ValueLength, 115 (PVOID*)&LinkName, 116 &LinkNameAllocated, 117 &CellToRelease)) 118 { 119 /* Fail */ 120 goto Exit; 121 } 122 123 /* Get the length */ 124 Length = ValueLength + sizeof(WCHAR); 125 126 /* Make sure we start with a slash */ 127 if (*LinkName != OBJ_NAME_PATH_SEPARATOR) goto Exit; 128 129 /* Add the remaining name if needed */ 130 if (RemainingName) Length += RemainingName->Length + sizeof(WCHAR); 131 132 /* Check for overflow */ 133 if (Length > 0xFFFF) goto Exit; 134 135 /* Check if we need a new buffer */ 136 if (Length > ObjectName->MaximumLength) 137 { 138 /* We do -- allocate one */ 139 NewBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM); 140 if (!NewBuffer) goto Exit; 141 142 /* Setup the new string and copy the symbolic target */ 143 NewObjectName.Buffer = NewBuffer; 144 NewObjectName.MaximumLength = (USHORT)Length; 145 NewObjectName.Length = (USHORT)ValueLength; 146 RtlCopyMemory(NewBuffer, LinkName, ValueLength); 147 148 /* Check if we need to add anything else */ 149 if (RemainingName) 150 { 151 /* Add the remaining name */ 152 NewBuffer[ValueLength / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; 153 NewObjectName.Length += sizeof(WCHAR); 154 RtlAppendUnicodeStringToString(&NewObjectName, RemainingName); 155 } 156 157 /* Free the old buffer */ 158 ExFreePool(ObjectName->Buffer); 159 *ObjectName = NewObjectName; 160 } 161 else 162 { 163 /* The old name is large enough -- update the length */ 164 ObjectName->Length = (USHORT)ValueLength; 165 if (RemainingName) 166 { 167 /* Copy the remaining name inside */ 168 RtlMoveMemory(&ObjectName->Buffer[(ValueLength / sizeof(WCHAR)) + 1], 169 RemainingName->Buffer, 170 RemainingName->Length); 171 172 /* Add the slash and update the length */ 173 ObjectName->Buffer[ValueLength / sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR; 174 ObjectName->Length += RemainingName->Length + sizeof(WCHAR); 175 } 176 177 /* Copy the symbolic link target name */ 178 RtlCopyMemory(ObjectName->Buffer, LinkName, ValueLength); 179 } 180 181 /* Null-terminate the whole thing */ 182 ObjectName->Buffer[ObjectName->Length / sizeof(WCHAR)] = UNICODE_NULL; 183 Result = TRUE; 184 185 Exit: 186 /* Free the link name */ 187 if (LinkNameAllocated) ExFreePool(LinkName); 188 189 /* Check if we had a value cell */ 190 if (LinkValue) 191 { 192 /* Release it */ 193 ASSERT(LinkCell != HCELL_NIL); 194 HvReleaseCell(Hive, LinkCell); 195 } 196 197 /* Check if we had an active cell and release it, then return the result */ 198 if (CellToRelease != HCELL_NIL) HvReleaseCell(Hive, CellToRelease); 199 return Result; 200 } 201 202 NTSTATUS 203 NTAPI 204 CmpDoCreateChild(IN PHHIVE Hive, 205 IN HCELL_INDEX ParentCell, 206 IN PSECURITY_DESCRIPTOR ParentDescriptor OPTIONAL, 207 IN PACCESS_STATE AccessState, 208 IN PUNICODE_STRING Name, 209 IN KPROCESSOR_MODE AccessMode, 210 IN PCM_PARSE_CONTEXT ParseContext, 211 IN PCM_KEY_CONTROL_BLOCK ParentKcb, 212 IN ULONG Flags, 213 OUT PHCELL_INDEX KeyCell, 214 OUT PVOID *Object) 215 { 216 NTSTATUS Status = STATUS_SUCCESS; 217 PCM_KEY_BODY KeyBody; 218 HCELL_INDEX ClassCell = HCELL_NIL; 219 PCM_KEY_NODE KeyNode; 220 PCELL_DATA CellData; 221 ULONG StorageType; 222 PCM_KEY_CONTROL_BLOCK Kcb; 223 PSECURITY_DESCRIPTOR NewDescriptor; 224 225 /* Get the storage type */ 226 StorageType = Stable; 227 if (ParseContext->CreateOptions & REG_OPTION_VOLATILE) StorageType = Volatile; 228 229 /* Allocate the child */ 230 *KeyCell = HvAllocateCell(Hive, 231 FIELD_OFFSET(CM_KEY_NODE, Name) + 232 CmpNameSize(Hive, Name), 233 StorageType, 234 HCELL_NIL); 235 if (*KeyCell == HCELL_NIL) 236 { 237 /* Fail */ 238 Status = STATUS_INSUFFICIENT_RESOURCES; 239 goto Quickie; 240 } 241 242 /* Get the key node */ 243 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, *KeyCell); 244 if (!KeyNode) 245 { 246 /* Fail, this should never happen */ 247 ASSERT(FALSE); 248 Status = STATUS_INSUFFICIENT_RESOURCES; 249 goto Quickie; 250 } 251 252 /* Release the cell */ 253 HvReleaseCell(Hive, *KeyCell); 254 255 /* Check if we have a class name */ 256 if (ParseContext->Class.Length > 0) 257 { 258 /* Allocate a class cell */ 259 ClassCell = HvAllocateCell(Hive, 260 ParseContext->Class.Length, 261 StorageType, 262 HCELL_NIL); 263 if (ClassCell == HCELL_NIL) 264 { 265 /* Fail */ 266 Status = STATUS_INSUFFICIENT_RESOURCES; 267 goto Quickie; 268 } 269 } 270 271 /* Allocate the Cm Object */ 272 Status = ObCreateObject(AccessMode, 273 CmpKeyObjectType, 274 NULL, 275 AccessMode, 276 NULL, 277 sizeof(CM_KEY_BODY), 278 0, 279 0, 280 Object); 281 if (!NT_SUCCESS(Status)) goto Quickie; 282 283 /* Setup the key body */ 284 KeyBody = (PCM_KEY_BODY)(*Object); 285 KeyBody->Type = CM_KEY_BODY_TYPE; 286 KeyBody->KeyControlBlock = NULL; 287 288 /* Check if we had a class */ 289 if (ParseContext->Class.Length > 0) 290 { 291 /* Get the class cell */ 292 CellData = HvGetCell(Hive, ClassCell); 293 if (!CellData) 294 { 295 /* Fail, this should never happen */ 296 ASSERT(FALSE); 297 Status = STATUS_INSUFFICIENT_RESOURCES; 298 ObDereferenceObject(*Object); 299 goto Quickie; 300 } 301 302 /* Release the cell */ 303 HvReleaseCell(Hive, ClassCell); 304 305 /* Copy the class data */ 306 RtlCopyMemory(&CellData->u.KeyString[0], 307 ParseContext->Class.Buffer, 308 ParseContext->Class.Length); 309 } 310 311 /* Fill out the key node */ 312 KeyNode->Signature = CM_KEY_NODE_SIGNATURE; 313 KeyNode->Flags = Flags; 314 KeQuerySystemTime(&KeyNode->LastWriteTime); 315 KeyNode->Spare = 0; 316 KeyNode->Parent = ParentCell; 317 KeyNode->SubKeyCounts[Stable] = 0; 318 KeyNode->SubKeyCounts[Volatile] = 0; 319 KeyNode->SubKeyLists[Stable] = HCELL_NIL; 320 KeyNode->SubKeyLists[Volatile] = HCELL_NIL; 321 KeyNode->ValueList.Count = 0; 322 KeyNode->ValueList.List = HCELL_NIL; 323 KeyNode->Security = HCELL_NIL; 324 KeyNode->Class = ClassCell; 325 KeyNode->ClassLength = ParseContext->Class.Length; 326 KeyNode->MaxValueDataLen = 0; 327 KeyNode->MaxNameLen = 0; 328 KeyNode->MaxValueNameLen = 0; 329 KeyNode->MaxClassLen = 0; 330 KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, Name); 331 if (KeyNode->NameLength < Name->Length) KeyNode->Flags |= KEY_COMP_NAME; 332 333 /* Create the KCB */ 334 Kcb = CmpCreateKeyControlBlock(Hive, 335 *KeyCell, 336 KeyNode, 337 ParentKcb, 338 0, // CMP_LOCK_HASHES_FOR_KCB, 339 Name); 340 if (!Kcb) 341 { 342 /* Fail */ 343 ObDereferenceObjectDeferDelete(*Object); 344 Status = STATUS_INSUFFICIENT_RESOURCES; 345 goto Quickie; 346 } 347 348 /* Sanity check */ 349 ASSERT(Kcb->RefCount == 1); 350 351 /* Now fill out the Cm object */ 352 KeyBody->NotifyBlock = NULL; 353 KeyBody->ProcessID = PsGetCurrentProcessId(); 354 KeyBody->KeyControlBlock = Kcb; 355 356 /* Link it with the KCB */ 357 EnlistKeyBodyWithKCB(KeyBody, 0); 358 359 /* Assign security */ 360 Status = SeAssignSecurity(ParentDescriptor, 361 AccessState->SecurityDescriptor, 362 &NewDescriptor, 363 TRUE, 364 &AccessState->SubjectSecurityContext, 365 &CmpKeyObjectType->TypeInfo.GenericMapping, 366 CmpKeyObjectType->TypeInfo.PoolType); 367 if (NT_SUCCESS(Status)) 368 { 369 Status = CmpSecurityMethod(*Object, 370 AssignSecurityDescriptor, 371 NULL, 372 NewDescriptor, 373 NULL, 374 NULL, 375 CmpKeyObjectType->TypeInfo.PoolType, 376 &CmpKeyObjectType->TypeInfo.GenericMapping); 377 } 378 379 /* Now that the security descriptor is copied in the hive, we can free the original */ 380 SeDeassignSecurity(&NewDescriptor); 381 382 if (NT_SUCCESS(Status)) 383 { 384 /* Send notification to registered callbacks */ 385 CmpReportNotify(Kcb, Hive, Kcb->KeyCell, REG_NOTIFY_CHANGE_NAME); 386 } 387 388 Quickie: 389 /* Check if we got here because of failure */ 390 if (!NT_SUCCESS(Status)) 391 { 392 /* Free any cells we might've allocated */ 393 if (ParseContext->Class.Length > 0) HvFreeCell(Hive, ClassCell); 394 HvFreeCell(Hive, *KeyCell); 395 } 396 397 /* Return status */ 398 return Status; 399 } 400 401 NTSTATUS 402 NTAPI 403 CmpDoCreate(IN PHHIVE Hive, 404 IN HCELL_INDEX Cell, 405 IN PACCESS_STATE AccessState, 406 IN PUNICODE_STRING Name, 407 IN KPROCESSOR_MODE AccessMode, 408 IN PCM_PARSE_CONTEXT ParseContext, 409 IN PCM_KEY_CONTROL_BLOCK ParentKcb, 410 OUT PVOID *Object) 411 { 412 NTSTATUS Status; 413 PCELL_DATA CellData; 414 HCELL_INDEX KeyCell; 415 ULONG ParentType; 416 PCM_KEY_BODY KeyBody; 417 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; 418 LARGE_INTEGER TimeStamp; 419 PCM_KEY_NODE KeyNode; 420 421 /* Check if the parent is being deleted */ 422 if (ParentKcb->Delete) 423 { 424 /* It has, quit */ 425 ASSERT(FALSE); 426 Status = STATUS_OBJECT_NAME_NOT_FOUND; 427 goto Exit; 428 } 429 430 /* Get the parent node */ 431 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 432 if (!KeyNode) 433 { 434 /* Fail */ 435 ASSERT(FALSE); 436 Status = STATUS_INSUFFICIENT_RESOURCES; 437 goto Exit; 438 } 439 440 /* Make sure nobody added us yet */ 441 if (CmpFindSubKeyByName(Hive, KeyNode, Name) != HCELL_NIL) 442 { 443 /* Fail */ 444 ASSERT(FALSE); 445 Status = STATUS_REPARSE; 446 goto Exit; 447 } 448 449 /* Sanity check */ 450 ASSERT(Cell == ParentKcb->KeyCell); 451 452 /* Get the parent type */ 453 ParentType = HvGetCellType(Cell); 454 if ((ParentType == Volatile) && 455 !(ParseContext->CreateOptions & REG_OPTION_VOLATILE)) 456 { 457 /* Children of volatile parents must also be volatile */ 458 //ASSERT(FALSE); 459 Status = STATUS_CHILD_MUST_BE_VOLATILE; 460 goto Exit; 461 } 462 463 /* Don't allow children under symlinks */ 464 if (ParentKcb->Flags & KEY_SYM_LINK) 465 { 466 /* Fail */ 467 ASSERT(FALSE); 468 Status = STATUS_ACCESS_DENIED; 469 goto Exit; 470 } 471 472 /* Make the cell dirty for now */ 473 HvMarkCellDirty(Hive, Cell, FALSE); 474 475 /* Do the actual create operation */ 476 Status = CmpDoCreateChild(Hive, 477 Cell, 478 SecurityDescriptor, 479 AccessState, 480 Name, 481 AccessMode, 482 ParseContext, 483 ParentKcb, 484 0, 485 &KeyCell, 486 Object); 487 if (NT_SUCCESS(Status)) 488 { 489 /* Get the key body */ 490 KeyBody = (PCM_KEY_BODY)(*Object); 491 492 /* Now add the subkey */ 493 if (!CmpAddSubKey(Hive, Cell, KeyCell)) 494 { 495 /* Failure! We don't handle this yet! */ 496 ASSERT(FALSE); 497 } 498 499 /* Get the key node */ 500 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 501 if (!KeyNode) 502 { 503 /* Fail, this shouldn't happen */ 504 ASSERT(FALSE); 505 } 506 507 /* Sanity checks */ 508 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell); 509 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive); 510 ASSERT(KeyBody->KeyControlBlock->ParentKcb == ParentKcb); 511 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen); 512 513 /* Update the timestamp */ 514 KeQuerySystemTime(&TimeStamp); 515 KeyNode->LastWriteTime = TimeStamp; 516 KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp; 517 518 /* Check if we need to update name maximum */ 519 if (KeyNode->MaxNameLen < Name->Length) 520 { 521 /* Do it */ 522 KeyNode->MaxNameLen = Name->Length; 523 KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name->Length; 524 } 525 526 /* Check if we need to update class length maximum */ 527 if (KeyNode->MaxClassLen < ParseContext->Class.Length) 528 { 529 /* Update it */ 530 KeyNode->MaxClassLen = ParseContext->Class.Length; 531 } 532 533 /* Check if we're creating a symbolic link */ 534 if (ParseContext->CreateOptions & REG_OPTION_CREATE_LINK) 535 { 536 /* Get the cell data */ 537 CellData = HvGetCell(Hive, KeyCell); 538 if (!CellData) 539 { 540 /* This shouldn't happen */ 541 ASSERT(FALSE); 542 } 543 544 /* Update the flags */ 545 CellData->u.KeyNode.Flags |= KEY_SYM_LINK; 546 KeyBody->KeyControlBlock->Flags = CellData->u.KeyNode.Flags; 547 HvReleaseCell(Hive, KeyCell); 548 } 549 } 550 551 Exit: 552 /* Release the flusher lock and return status */ 553 return Status; 554 } 555 556 NTSTATUS 557 NTAPI 558 CmpDoOpen(IN PHHIVE Hive, 559 IN HCELL_INDEX Cell, 560 IN PCM_KEY_NODE Node, 561 IN PACCESS_STATE AccessState, 562 IN KPROCESSOR_MODE AccessMode, 563 IN ULONG Attributes, 564 IN PCM_PARSE_CONTEXT Context OPTIONAL, 565 IN ULONG ControlFlags, 566 IN OUT PCM_KEY_CONTROL_BLOCK *CachedKcb, 567 IN PUNICODE_STRING KeyName, 568 OUT PVOID *Object) 569 { 570 NTSTATUS Status; 571 PCM_KEY_BODY KeyBody = NULL; 572 PCM_KEY_CONTROL_BLOCK Kcb = NULL; 573 574 /* Make sure the hive isn't locked */ 575 if ((Hive->HiveFlags & HIVE_IS_UNLOADING) && 576 (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread())) 577 { 578 /* It is, don't touch it */ 579 return STATUS_OBJECT_NAME_NOT_FOUND; 580 } 581 582 /* Check if we have a context */ 583 if (Context) 584 { 585 /* Check if this is a link create (which shouldn't be an open) */ 586 if (Context->CreateLink) 587 { 588 return STATUS_ACCESS_DENIED; 589 } 590 591 /* Check if this is symlink create attempt */ 592 if (Context->CreateOptions & REG_OPTION_CREATE_LINK) 593 { 594 /* Key already exists */ 595 return STATUS_OBJECT_NAME_COLLISION; 596 } 597 598 /* Set the disposition */ 599 Context->Disposition = REG_OPENED_EXISTING_KEY; 600 } 601 602 /* Do this in the registry lock */ 603 CmpLockRegistry(); 604 605 /* If we have a KCB, make sure it's locked */ 606 //ASSERT(CmpIsKcbLockedExclusive(*CachedKcb)); 607 608 /* Check if caller doesn't want to create a KCB */ 609 if (ControlFlags & CMP_OPEN_KCB_NO_CREATE) 610 { 611 /* Check if this is a symlink */ 612 if ((Node->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK)) 613 { 614 /* This case for a cached KCB is not implemented yet */ 615 ASSERT(FALSE); 616 } 617 618 /* The caller wants to open a cached KCB */ 619 if (!CmpReferenceKeyControlBlock(*CachedKcb)) 620 { 621 /* Release the registry lock */ 622 CmpUnlockRegistry(); 623 624 /* Return failure code */ 625 return STATUS_INSUFFICIENT_RESOURCES; 626 } 627 628 /* Our kcb is that one */ 629 Kcb = *CachedKcb; 630 } 631 else 632 { 633 /* Check if this is a symlink */ 634 if ((Node->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK)) 635 { 636 /* Create the KCB for the symlink */ 637 Kcb = CmpCreateKeyControlBlock(Hive, 638 Cell, 639 Node, 640 *CachedKcb, 641 0, 642 KeyName); 643 if (!Kcb) 644 { 645 /* Release registry lock and return failure */ 646 CmpUnlockRegistry(); 647 return STATUS_INSUFFICIENT_RESOURCES; 648 } 649 650 /* Make sure it's also locked, and set the pointer */ 651 //ASSERT(CmpIsKcbLockedExclusive(Kcb)); 652 *CachedKcb = Kcb; 653 654 /* Release the registry lock */ 655 CmpUnlockRegistry(); 656 657 /* Return reparse required */ 658 return STATUS_REPARSE; 659 } 660 661 /* Create the KCB. FIXME: Use lock flag */ 662 Kcb = CmpCreateKeyControlBlock(Hive, 663 Cell, 664 Node, 665 *CachedKcb, 666 0, 667 KeyName); 668 if (!Kcb) 669 { 670 /* Release registry lock and return failure */ 671 CmpUnlockRegistry(); 672 return STATUS_INSUFFICIENT_RESOURCES; 673 } 674 } 675 676 /* Make sure it's also locked, and set the pointer */ 677 //ASSERT(CmpIsKcbLockedExclusive(Kcb)); 678 *CachedKcb = Kcb; 679 680 /* Release the registry lock */ 681 CmpUnlockRegistry(); 682 683 /* Allocate the key object */ 684 Status = ObCreateObject(AccessMode, 685 CmpKeyObjectType, 686 NULL, 687 AccessMode, 688 NULL, 689 sizeof(CM_KEY_BODY), 690 0, 691 0, 692 Object); 693 if (NT_SUCCESS(Status)) 694 { 695 /* Get the key body and fill it out */ 696 KeyBody = (PCM_KEY_BODY)(*Object); 697 KeyBody->KeyControlBlock = Kcb; 698 KeyBody->Type = CM_KEY_BODY_TYPE; 699 KeyBody->ProcessID = PsGetCurrentProcessId(); 700 KeyBody->NotifyBlock = NULL; 701 702 /* Link to the KCB */ 703 EnlistKeyBodyWithKCB(KeyBody, 0); 704 705 if (!ObCheckObjectAccess(*Object, 706 AccessState, 707 FALSE, 708 AccessMode, 709 &Status)) 710 { 711 /* Access check failed */ 712 ObDereferenceObject(*Object); 713 } 714 } 715 else 716 { 717 /* Failed, dereference the KCB */ 718 CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE); 719 } 720 721 /* Return status */ 722 return Status; 723 } 724 725 NTSTATUS 726 NTAPI 727 CmpCreateLinkNode(IN PHHIVE Hive, 728 IN HCELL_INDEX Cell, 729 IN PACCESS_STATE AccessState, 730 IN UNICODE_STRING Name, 731 IN KPROCESSOR_MODE AccessMode, 732 IN ULONG CreateOptions, 733 IN PCM_PARSE_CONTEXT Context, 734 IN PCM_KEY_CONTROL_BLOCK ParentKcb, 735 OUT PVOID *Object) 736 { 737 NTSTATUS Status; 738 HCELL_INDEX KeyCell, LinkCell, ChildCell; 739 PCM_KEY_BODY KeyBody; 740 LARGE_INTEGER TimeStamp; 741 PCM_KEY_NODE KeyNode; 742 PCM_KEY_CONTROL_BLOCK Kcb = ParentKcb; 743 744 /* Link nodes only allowed on the master */ 745 if (Hive != &CmiVolatileHive->Hive) 746 { 747 /* Fail */ 748 DPRINT1("Invalid link node attempt\n"); 749 return STATUS_ACCESS_DENIED; 750 } 751 752 /* Check if the parent is being deleted */ 753 if (ParentKcb->Delete) 754 { 755 /* It is, quit */ 756 ASSERT(FALSE); 757 Status = STATUS_OBJECT_NAME_NOT_FOUND; 758 goto Exit; 759 } 760 761 /* Allocate a link node */ 762 LinkCell = HvAllocateCell(Hive, 763 FIELD_OFFSET(CM_KEY_NODE, Name) + 764 CmpNameSize(Hive, &Name), 765 Stable, 766 HCELL_NIL); 767 if (LinkCell == HCELL_NIL) 768 { 769 /* Fail */ 770 Status = STATUS_INSUFFICIENT_RESOURCES; 771 goto Exit; 772 } 773 774 /* Get the key cell */ 775 KeyCell = Context->ChildHive.KeyCell; 776 if (KeyCell != HCELL_NIL) 777 { 778 /* Hive exists! */ 779 ChildCell = KeyCell; 780 781 /* Get the node data */ 782 KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell); 783 if (!KeyNode) 784 { 785 /* Fail */ 786 ASSERT(FALSE); 787 Status = STATUS_INSUFFICIENT_RESOURCES; 788 goto Exit; 789 } 790 791 /* Fill out the data */ 792 KeyNode->Parent = LinkCell; 793 KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE; 794 HvReleaseCell(Context->ChildHive.KeyHive, ChildCell); 795 796 /* Now open the key cell */ 797 KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, KeyCell); 798 if (!KeyNode) 799 { 800 /* Fail */ 801 ASSERT(FALSE); 802 Status = STATUS_INSUFFICIENT_RESOURCES; 803 goto Exit; 804 } 805 806 /* Open the parent */ 807 Status = CmpDoOpen(Context->ChildHive.KeyHive, 808 KeyCell, 809 KeyNode, 810 AccessState, 811 AccessMode, 812 CreateOptions, 813 NULL, 814 0, 815 &Kcb, 816 &Name, 817 Object); 818 HvReleaseCell(Context->ChildHive.KeyHive, KeyCell); 819 } 820 else 821 { 822 /* Do the actual create operation */ 823 Status = CmpDoCreateChild(Context->ChildHive.KeyHive, 824 Cell, 825 NULL, 826 AccessState, 827 &Name, 828 AccessMode, 829 Context, 830 ParentKcb, 831 KEY_HIVE_ENTRY | KEY_NO_DELETE, 832 &ChildCell, 833 Object); 834 if (NT_SUCCESS(Status)) 835 { 836 /* Setup root pointer */ 837 Context->ChildHive.KeyHive->BaseBlock->RootCell = ChildCell; 838 } 839 } 840 841 /* Check if open or create suceeded */ 842 if (NT_SUCCESS(Status)) 843 { 844 /* Mark the cell dirty */ 845 HvMarkCellDirty(Context->ChildHive.KeyHive, ChildCell, FALSE); 846 847 /* Get the key node */ 848 KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell); 849 if (!KeyNode) 850 { 851 /* Fail */ 852 ASSERT(FALSE); 853 Status = STATUS_INSUFFICIENT_RESOURCES; 854 goto Exit; 855 } 856 857 /* Release it */ 858 HvReleaseCell(Context->ChildHive.KeyHive, ChildCell); 859 860 /* Set the parent and flags */ 861 KeyNode->Parent = LinkCell; 862 KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE; 863 864 /* Get the link node */ 865 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, LinkCell); 866 if (!KeyNode) 867 { 868 /* Fail */ 869 ASSERT(FALSE); 870 Status = STATUS_INSUFFICIENT_RESOURCES; 871 goto Exit; 872 } 873 874 /* Set it up */ 875 KeyNode->Signature = CM_LINK_NODE_SIGNATURE; 876 KeyNode->Flags = KEY_HIVE_EXIT | KEY_NO_DELETE; 877 KeyNode->Parent = Cell; 878 KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, &Name); 879 if (KeyNode->NameLength < Name.Length) KeyNode->Flags |= KEY_COMP_NAME; 880 KeQuerySystemTime(&TimeStamp); 881 KeyNode->LastWriteTime = TimeStamp; 882 883 /* Clear out the rest */ 884 KeyNode->SubKeyCounts[Stable] = 0; 885 KeyNode->SubKeyCounts[Volatile] = 0; 886 KeyNode->SubKeyLists[Stable] = HCELL_NIL; 887 KeyNode->SubKeyLists[Volatile] = HCELL_NIL; 888 KeyNode->ValueList.Count = 0; 889 KeyNode->ValueList.List = HCELL_NIL; 890 KeyNode->ClassLength = 0; 891 892 /* Reference the root node */ 893 KeyNode->ChildHiveReference.KeyHive = Context->ChildHive.KeyHive; 894 KeyNode->ChildHiveReference.KeyCell = ChildCell; 895 HvReleaseCell(Hive, LinkCell); 896 897 /* Get the parent node */ 898 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 899 if (!KeyNode) 900 { 901 /* Fail */ 902 ASSERT(FALSE); 903 Status = STATUS_INSUFFICIENT_RESOURCES; 904 goto Exit; 905 } 906 907 /* Now add the subkey */ 908 if (!CmpAddSubKey(Hive, Cell, LinkCell)) 909 { 910 /* Failure! We don't handle this yet! */ 911 ASSERT(FALSE); 912 } 913 914 /* Get the key body */ 915 KeyBody = (PCM_KEY_BODY)*Object; 916 917 /* Sanity checks */ 918 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell); 919 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive); 920 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen); 921 922 /* Update the timestamp */ 923 KeQuerySystemTime(&TimeStamp); 924 KeyNode->LastWriteTime = TimeStamp; 925 KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp; 926 927 /* Check if we need to update name maximum */ 928 if (KeyNode->MaxNameLen < Name.Length) 929 { 930 /* Do it */ 931 KeyNode->MaxNameLen = Name.Length; 932 KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name.Length; 933 } 934 935 /* Check if we need to update class length maximum */ 936 if (KeyNode->MaxClassLen < Context->Class.Length) 937 { 938 /* Update it */ 939 KeyNode->MaxClassLen = Context->Class.Length; 940 } 941 942 /* Release the cell */ 943 HvReleaseCell(Hive, Cell); 944 } 945 else 946 { 947 /* Release the link cell */ 948 HvReleaseCell(Hive, LinkCell); 949 } 950 951 Exit: 952 /* Release the flusher locks and return status */ 953 return Status; 954 } 955 956 VOID 957 NTAPI 958 CmpHandleExitNode(IN OUT PHHIVE *Hive, 959 IN OUT HCELL_INDEX *Cell, 960 IN OUT PCM_KEY_NODE *KeyNode, 961 IN OUT PHHIVE *ReleaseHive, 962 IN OUT HCELL_INDEX *ReleaseCell) 963 { 964 /* Check if we have anything to release */ 965 if (*ReleaseCell != HCELL_NIL) 966 { 967 /* Release it */ 968 ASSERT(*ReleaseHive != NULL); 969 HvReleaseCell(*ReleaseHive, *ReleaseCell); 970 } 971 972 /* Get the link references */ 973 *Hive = (*KeyNode)->ChildHiveReference.KeyHive; 974 *Cell = (*KeyNode)->ChildHiveReference.KeyCell; 975 976 /* Get the new node */ 977 *KeyNode = (PCM_KEY_NODE)HvGetCell(*Hive, *Cell); 978 if (*KeyNode) 979 { 980 /* Set the new release values */ 981 *ReleaseCell = *Cell; 982 *ReleaseHive = *Hive; 983 } 984 else 985 { 986 /* Nothing to release */ 987 *ReleaseCell = HCELL_NIL; 988 *ReleaseHive = NULL; 989 } 990 } 991 992 NTSTATUS 993 NTAPI 994 CmpBuildHashStackAndLookupCache(IN PCM_KEY_BODY ParseObject, 995 IN OUT PCM_KEY_CONTROL_BLOCK *Kcb, 996 IN PUNICODE_STRING Current, 997 OUT PHHIVE *Hive, 998 OUT HCELL_INDEX *Cell, 999 OUT PULONG TotalRemainingSubkeys, 1000 OUT PULONG MatchRemainSubkeyLevel, 1001 OUT PULONG TotalSubkeys, 1002 OUT PULONG OuterStackArray, 1003 OUT PULONG *LockedKcbs) 1004 { 1005 /* We don't lock anything for now */ 1006 *LockedKcbs = NULL; 1007 1008 /* Calculate hash values */ 1009 *TotalRemainingSubkeys = 0xBAADF00D; 1010 1011 /* Lock the registry */ 1012 CmpLockRegistry(); 1013 1014 /* Return hive and cell data */ 1015 *Hive = (*Kcb)->KeyHive; 1016 *Cell = (*Kcb)->KeyCell; 1017 1018 /* Make sure it's not a dead KCB */ 1019 ASSERT((*Kcb)->RefCount > 0); 1020 1021 /* Reference it */ 1022 (VOID)CmpReferenceKeyControlBlock(*Kcb); 1023 1024 /* Return success for now */ 1025 return STATUS_SUCCESS; 1026 } 1027 1028 NTSTATUS 1029 NTAPI 1030 CmpParseKey(IN PVOID ParseObject, 1031 IN PVOID ObjectType, 1032 IN OUT PACCESS_STATE AccessState, 1033 IN KPROCESSOR_MODE AccessMode, 1034 IN ULONG Attributes, 1035 IN OUT PUNICODE_STRING CompleteName, 1036 IN OUT PUNICODE_STRING RemainingName, 1037 IN OUT PVOID Context OPTIONAL, 1038 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL, 1039 OUT PVOID *Object) 1040 { 1041 NTSTATUS Status; 1042 PCM_KEY_CONTROL_BLOCK Kcb, ParentKcb; 1043 PHHIVE Hive = NULL; 1044 PCM_KEY_NODE Node = NULL; 1045 HCELL_INDEX Cell = HCELL_NIL, NextCell; 1046 PHHIVE HiveToRelease = NULL; 1047 HCELL_INDEX CellToRelease = HCELL_NIL; 1048 UNICODE_STRING Current, NextName; 1049 PCM_PARSE_CONTEXT ParseContext = Context; 1050 ULONG TotalRemainingSubkeys = 0, MatchRemainSubkeyLevel = 0, TotalSubkeys = 0; 1051 PULONG LockedKcbs = NULL; 1052 BOOLEAN Result, Last; 1053 PAGED_CODE(); 1054 1055 /* Loop path separators at the end */ 1056 while ((RemainingName->Length) && 1057 (RemainingName->Buffer[(RemainingName->Length / sizeof(WCHAR)) - 1] == 1058 OBJ_NAME_PATH_SEPARATOR)) 1059 { 1060 /* Remove path separator */ 1061 RemainingName->Length -= sizeof(WCHAR); 1062 } 1063 1064 /* Fail if this isn't a key object */ 1065 if (ObjectType != CmpKeyObjectType) return STATUS_OBJECT_TYPE_MISMATCH; 1066 1067 /* Copy the remaining name */ 1068 Current = *RemainingName; 1069 1070 /* Check if this is a create */ 1071 if (!(ParseContext) || !(ParseContext->CreateOperation)) 1072 { 1073 /* It isn't, so no context */ 1074 ParseContext = NULL; 1075 } 1076 1077 /* Grab the KCB */ 1078 Kcb = ((PCM_KEY_BODY)ParseObject)->KeyControlBlock; 1079 1080 /* Sanity check */ 1081 ASSERT(Kcb != NULL); 1082 1083 /* Fail if the key was marked as deleted */ 1084 if (Kcb->Delete) 1085 return STATUS_KEY_DELETED; 1086 1087 /* Lookup in the cache */ 1088 Status = CmpBuildHashStackAndLookupCache(ParseObject, 1089 &Kcb, 1090 &Current, 1091 &Hive, 1092 &Cell, 1093 &TotalRemainingSubkeys, 1094 &MatchRemainSubkeyLevel, 1095 &TotalSubkeys, 1096 NULL, 1097 &LockedKcbs); 1098 1099 /* This is now the parent */ 1100 ParentKcb = Kcb; 1101 1102 /* Sanity check */ 1103 ASSERT(ParentKcb != NULL); 1104 1105 /* Check if everything was found cached */ 1106 if (!TotalRemainingSubkeys) 1107 ASSERTMSG("Caching not implemented\n", FALSE); 1108 1109 /* Don't do anything if we're being deleted */ 1110 if (Kcb->Delete) 1111 { 1112 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1113 goto Quickie; 1114 } 1115 1116 /* Check if this is a symlink */ 1117 if (Kcb->Flags & KEY_SYM_LINK) 1118 { 1119 /* Get the next name */ 1120 Result = CmpGetNextName(&Current, &NextName, &Last); 1121 Current.Buffer = NextName.Buffer; 1122 1123 /* Validate the current name string length */ 1124 if (Current.Length + NextName.Length > MAXUSHORT) 1125 { 1126 /* too long */ 1127 Status = STATUS_NAME_TOO_LONG; 1128 goto Quickie; 1129 } 1130 Current.Length += NextName.Length; 1131 1132 /* Validate the current name string maximum length */ 1133 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT) 1134 { 1135 /* too long */ 1136 Status = STATUS_NAME_TOO_LONG; 1137 goto Quickie; 1138 } 1139 Current.MaximumLength += NextName.MaximumLength; 1140 1141 /* Parse the symlink */ 1142 if (CmpGetSymbolicLink(Hive, 1143 CompleteName, 1144 Kcb, 1145 &Current)) 1146 { 1147 /* Symlink parse succeeded */ 1148 Status = STATUS_REPARSE; 1149 } 1150 else 1151 { 1152 /* Couldn't find symlink */ 1153 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1154 } 1155 1156 /* We're done */ 1157 goto Quickie; 1158 } 1159 1160 /* Get the key node */ 1161 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 1162 if (!Node) 1163 { 1164 Status = STATUS_INSUFFICIENT_RESOURCES; 1165 goto Quickie; 1166 } 1167 1168 /* Start parsing */ 1169 Status = STATUS_NOT_IMPLEMENTED; 1170 while (TRUE) 1171 { 1172 /* Get the next component */ 1173 Result = CmpGetNextName(&Current, &NextName, &Last); 1174 if ((Result) && (NextName.Length)) 1175 { 1176 /* See if this is a sym link */ 1177 if (!(Kcb->Flags & KEY_SYM_LINK)) 1178 { 1179 /* Find the subkey */ 1180 NextCell = CmpFindSubKeyByName(Hive, Node, &NextName); 1181 if (NextCell != HCELL_NIL) 1182 { 1183 /* Get the new node */ 1184 Cell = NextCell; 1185 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 1186 ASSERT(Node); 1187 1188 /* Check if this was the last key */ 1189 if (Last) 1190 { 1191 /* Is this an exit node */ 1192 if (Node->Flags & KEY_HIVE_EXIT) 1193 { 1194 /* Handle it */ 1195 CmpHandleExitNode(&Hive, 1196 &Cell, 1197 &Node, 1198 &HiveToRelease, 1199 &CellToRelease); 1200 if (!Node) 1201 { 1202 /* Fail */ 1203 Status = STATUS_INSUFFICIENT_RESOURCES; 1204 break; 1205 } 1206 } 1207 1208 /* Do the open */ 1209 Status = CmpDoOpen(Hive, 1210 Cell, 1211 Node, 1212 AccessState, 1213 AccessMode, 1214 Attributes, 1215 ParseContext, 1216 0, 1217 &Kcb, 1218 &NextName, 1219 Object); 1220 if (Status == STATUS_REPARSE) 1221 { 1222 /* Parse the symlink */ 1223 if (!CmpGetSymbolicLink(Hive, 1224 CompleteName, 1225 Kcb, 1226 NULL)) 1227 { 1228 /* Symlink parse failed */ 1229 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1230 } 1231 } 1232 1233 /* We are done */ 1234 break; 1235 } 1236 1237 /* Is this an exit node */ 1238 if (Node->Flags & KEY_HIVE_EXIT) 1239 { 1240 /* Handle it */ 1241 CmpHandleExitNode(&Hive, 1242 &Cell, 1243 &Node, 1244 &HiveToRelease, 1245 &CellToRelease); 1246 if (!Node) 1247 { 1248 /* Fail */ 1249 Status = STATUS_INSUFFICIENT_RESOURCES; 1250 break; 1251 } 1252 } 1253 1254 /* Create a KCB for this key */ 1255 Kcb = CmpCreateKeyControlBlock(Hive, 1256 Cell, 1257 Node, 1258 ParentKcb, 1259 0, 1260 &NextName); 1261 if (!Kcb) 1262 { 1263 /* Fail */ 1264 Status = STATUS_INSUFFICIENT_RESOURCES; 1265 break; 1266 } 1267 1268 /* Dereference the parent and set the new one */ 1269 CmpDereferenceKeyControlBlock(ParentKcb); 1270 ParentKcb = Kcb; 1271 } 1272 else 1273 { 1274 /* Check if this was the last key for a create */ 1275 if ((Last) && (ParseContext)) 1276 { 1277 /* Check if we're doing a link node */ 1278 if (ParseContext->CreateLink) 1279 { 1280 /* The only thing we should see */ 1281 Status = CmpCreateLinkNode(Hive, 1282 Cell, 1283 AccessState, 1284 NextName, 1285 AccessMode, 1286 Attributes, 1287 ParseContext, 1288 ParentKcb, 1289 Object); 1290 } 1291 else if (Hive == &CmiVolatileHive->Hive && CmpNoVolatileCreates) 1292 { 1293 /* Creating keys in the master hive is not allowed */ 1294 Status = STATUS_INVALID_PARAMETER; 1295 } 1296 else 1297 { 1298 /* Do the create */ 1299 Status = CmpDoCreate(Hive, 1300 Cell, 1301 AccessState, 1302 &NextName, 1303 AccessMode, 1304 ParseContext, 1305 ParentKcb, 1306 Object); 1307 } 1308 1309 /* Check for reparse (in this case, someone beat us) */ 1310 if (Status == STATUS_REPARSE) break; 1311 1312 /* Update disposition */ 1313 ParseContext->Disposition = REG_CREATED_NEW_KEY; 1314 break; 1315 } 1316 else 1317 { 1318 /* Key not found */ 1319 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1320 break; 1321 } 1322 } 1323 } 1324 else 1325 { 1326 /* Save the next name */ 1327 Current.Buffer = NextName.Buffer; 1328 1329 /* Validate the current name string length */ 1330 if (Current.Length + NextName.Length > MAXUSHORT) 1331 { 1332 /* too long */ 1333 Status = STATUS_NAME_TOO_LONG; 1334 break; 1335 } 1336 Current.Length += NextName.Length; 1337 1338 /* Validate the current name string maximum length */ 1339 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT) 1340 { 1341 /* too long */ 1342 Status = STATUS_NAME_TOO_LONG; 1343 break; 1344 } 1345 Current.MaximumLength += NextName.MaximumLength; 1346 1347 /* Parse the symlink */ 1348 if (CmpGetSymbolicLink(Hive, 1349 CompleteName, 1350 Kcb, 1351 &Current)) 1352 { 1353 /* Symlink parse succeeded */ 1354 Status = STATUS_REPARSE; 1355 } 1356 else 1357 { 1358 /* Couldn't find symlink */ 1359 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1360 } 1361 1362 /* We're done */ 1363 break; 1364 } 1365 } 1366 else if ((Result) && (Last)) 1367 { 1368 /* Opening the root. Is this an exit node? */ 1369 if (Node->Flags & KEY_HIVE_EXIT) 1370 { 1371 /* Handle it */ 1372 CmpHandleExitNode(&Hive, 1373 &Cell, 1374 &Node, 1375 &HiveToRelease, 1376 &CellToRelease); 1377 if (!Node) 1378 { 1379 /* Fail */ 1380 Status = STATUS_INSUFFICIENT_RESOURCES; 1381 break; 1382 } 1383 } 1384 1385 /* Do the open */ 1386 Status = CmpDoOpen(Hive, 1387 Cell, 1388 Node, 1389 AccessState, 1390 AccessMode, 1391 Attributes, 1392 ParseContext, 1393 CMP_OPEN_KCB_NO_CREATE /* | CMP_CREATE_KCB_KCB_LOCKED */, 1394 &Kcb, 1395 &NextName, 1396 Object); 1397 if (Status == STATUS_REPARSE) 1398 { 1399 /* Nothing to do */ 1400 } 1401 1402 /* We're done */ 1403 break; 1404 } 1405 else 1406 { 1407 /* Bogus */ 1408 Status = STATUS_INVALID_PARAMETER; 1409 break; 1410 } 1411 } 1412 1413 /* Dereference the parent if it exists */ 1414 Quickie: 1415 if (ParentKcb) 1416 CmpDereferenceKeyControlBlock(ParentKcb); 1417 1418 /* Unlock the registry */ 1419 CmpUnlockRegistry(); 1420 return Status; 1421 } 1422