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 Quickie: 383 /* Check if we got here because of failure */ 384 if (!NT_SUCCESS(Status)) 385 { 386 /* Free any cells we might've allocated */ 387 if (ParseContext->Class.Length > 0) HvFreeCell(Hive, ClassCell); 388 HvFreeCell(Hive, *KeyCell); 389 } 390 391 /* Return status */ 392 return Status; 393 } 394 395 NTSTATUS 396 NTAPI 397 CmpDoCreate(IN PHHIVE Hive, 398 IN HCELL_INDEX Cell, 399 IN PACCESS_STATE AccessState, 400 IN PUNICODE_STRING Name, 401 IN KPROCESSOR_MODE AccessMode, 402 IN PCM_PARSE_CONTEXT ParseContext, 403 IN PCM_KEY_CONTROL_BLOCK ParentKcb, 404 OUT PVOID *Object) 405 { 406 NTSTATUS Status; 407 PCELL_DATA CellData; 408 HCELL_INDEX KeyCell; 409 ULONG ParentType; 410 PCM_KEY_BODY KeyBody; 411 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; 412 LARGE_INTEGER TimeStamp; 413 PCM_KEY_NODE KeyNode; 414 415 /* Check if the parent is being deleted */ 416 if (ParentKcb->Delete) 417 { 418 /* It has, quit */ 419 ASSERT(FALSE); 420 Status = STATUS_OBJECT_NAME_NOT_FOUND; 421 goto Exit; 422 } 423 424 /* Get the parent node */ 425 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 426 if (!KeyNode) 427 { 428 /* Fail */ 429 ASSERT(FALSE); 430 Status = STATUS_INSUFFICIENT_RESOURCES; 431 goto Exit; 432 } 433 434 /* Make sure nobody added us yet */ 435 if (CmpFindSubKeyByName(Hive, KeyNode, Name) != HCELL_NIL) 436 { 437 /* Fail */ 438 ASSERT(FALSE); 439 Status = STATUS_REPARSE; 440 goto Exit; 441 } 442 443 /* Sanity check */ 444 ASSERT(Cell == ParentKcb->KeyCell); 445 446 /* Get the parent type */ 447 ParentType = HvGetCellType(Cell); 448 if ((ParentType == Volatile) && 449 !(ParseContext->CreateOptions & REG_OPTION_VOLATILE)) 450 { 451 /* Children of volatile parents must also be volatile */ 452 //ASSERT(FALSE); 453 Status = STATUS_CHILD_MUST_BE_VOLATILE; 454 goto Exit; 455 } 456 457 /* Don't allow children under symlinks */ 458 if (ParentKcb->Flags & KEY_SYM_LINK) 459 { 460 /* Fail */ 461 ASSERT(FALSE); 462 Status = STATUS_ACCESS_DENIED; 463 goto Exit; 464 } 465 466 /* Make the cell dirty for now */ 467 HvMarkCellDirty(Hive, Cell, FALSE); 468 469 /* Do the actual create operation */ 470 Status = CmpDoCreateChild(Hive, 471 Cell, 472 SecurityDescriptor, 473 AccessState, 474 Name, 475 AccessMode, 476 ParseContext, 477 ParentKcb, 478 0, 479 &KeyCell, 480 Object); 481 if (NT_SUCCESS(Status)) 482 { 483 /* Get the key body */ 484 KeyBody = (PCM_KEY_BODY)(*Object); 485 486 /* Now add the subkey */ 487 if (!CmpAddSubKey(Hive, Cell, KeyCell)) 488 { 489 /* Failure! We don't handle this yet! */ 490 ASSERT(FALSE); 491 } 492 493 /* Get the key node */ 494 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 495 if (!KeyNode) 496 { 497 /* Fail, this shouldn't happen */ 498 ASSERT(FALSE); 499 } 500 501 /* Sanity checks */ 502 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell); 503 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive); 504 ASSERT(KeyBody->KeyControlBlock->ParentKcb == ParentKcb); 505 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen); 506 507 /* Update the timestamp */ 508 KeQuerySystemTime(&TimeStamp); 509 KeyNode->LastWriteTime = TimeStamp; 510 KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp; 511 512 /* Check if we need to update name maximum */ 513 if (KeyNode->MaxNameLen < Name->Length) 514 { 515 /* Do it */ 516 KeyNode->MaxNameLen = Name->Length; 517 KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name->Length; 518 } 519 520 /* Check if we need to update class length maximum */ 521 if (KeyNode->MaxClassLen < ParseContext->Class.Length) 522 { 523 /* Update it */ 524 KeyNode->MaxClassLen = ParseContext->Class.Length; 525 } 526 527 /* Check if we're creating a symbolic link */ 528 if (ParseContext->CreateOptions & REG_OPTION_CREATE_LINK) 529 { 530 /* Get the cell data */ 531 CellData = HvGetCell(Hive, KeyCell); 532 if (!CellData) 533 { 534 /* This shouldn't happen */ 535 ASSERT(FALSE); 536 } 537 538 /* Update the flags */ 539 CellData->u.KeyNode.Flags |= KEY_SYM_LINK; 540 KeyBody->KeyControlBlock->Flags = CellData->u.KeyNode.Flags; 541 HvReleaseCell(Hive, KeyCell); 542 } 543 } 544 545 Exit: 546 /* Release the flusher lock and return status */ 547 return Status; 548 } 549 550 NTSTATUS 551 NTAPI 552 CmpDoOpen(IN PHHIVE Hive, 553 IN HCELL_INDEX Cell, 554 IN PCM_KEY_NODE Node, 555 IN PACCESS_STATE AccessState, 556 IN KPROCESSOR_MODE AccessMode, 557 IN ULONG Attributes, 558 IN PCM_PARSE_CONTEXT Context OPTIONAL, 559 IN ULONG ControlFlags, 560 IN OUT PCM_KEY_CONTROL_BLOCK *CachedKcb, 561 IN PUNICODE_STRING KeyName, 562 OUT PVOID *Object) 563 { 564 NTSTATUS Status; 565 PCM_KEY_BODY KeyBody = NULL; 566 PCM_KEY_CONTROL_BLOCK Kcb = NULL; 567 568 /* Make sure the hive isn't locked */ 569 if ((Hive->HiveFlags & HIVE_IS_UNLOADING) && 570 (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread())) 571 { 572 /* It is, don't touch it */ 573 return STATUS_OBJECT_NAME_NOT_FOUND; 574 } 575 576 /* Check if we have a context */ 577 if (Context) 578 { 579 /* Check if this is a link create (which shouldn't be an open) */ 580 if (Context->CreateLink) 581 { 582 return STATUS_ACCESS_DENIED; 583 } 584 585 /* Check if this is symlink create attempt */ 586 if (Context->CreateOptions & REG_OPTION_CREATE_LINK) 587 { 588 /* Key already exists */ 589 return STATUS_OBJECT_NAME_COLLISION; 590 } 591 592 /* Set the disposition */ 593 Context->Disposition = REG_OPENED_EXISTING_KEY; 594 } 595 596 /* Do this in the registry lock */ 597 CmpLockRegistry(); 598 599 /* If we have a KCB, make sure it's locked */ 600 //ASSERT(CmpIsKcbLockedExclusive(*CachedKcb)); 601 602 /* Check if caller doesn't want to create a KCB */ 603 if (ControlFlags & CMP_OPEN_KCB_NO_CREATE) 604 { 605 /* Check if this is a symlink */ 606 if ((Node->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK)) 607 { 608 /* This case for a cached KCB is not implemented yet */ 609 ASSERT(FALSE); 610 } 611 612 /* The caller wants to open a cached KCB */ 613 if (!CmpReferenceKeyControlBlock(*CachedKcb)) 614 { 615 /* Release the registry lock */ 616 CmpUnlockRegistry(); 617 618 /* Return failure code */ 619 return STATUS_INSUFFICIENT_RESOURCES; 620 } 621 622 /* Our kcb is that one */ 623 Kcb = *CachedKcb; 624 } 625 else 626 { 627 /* Check if this is a symlink */ 628 if ((Node->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK)) 629 { 630 /* Create the KCB for the symlink */ 631 Kcb = CmpCreateKeyControlBlock(Hive, 632 Cell, 633 Node, 634 *CachedKcb, 635 0, 636 KeyName); 637 if (!Kcb) 638 { 639 /* Release registry lock and return failure */ 640 CmpUnlockRegistry(); 641 return STATUS_INSUFFICIENT_RESOURCES; 642 } 643 644 /* Make sure it's also locked, and set the pointer */ 645 //ASSERT(CmpIsKcbLockedExclusive(Kcb)); 646 *CachedKcb = Kcb; 647 648 /* Release the registry lock */ 649 CmpUnlockRegistry(); 650 651 /* Return reparse required */ 652 return STATUS_REPARSE; 653 } 654 655 /* Create the KCB. FIXME: Use lock flag */ 656 Kcb = CmpCreateKeyControlBlock(Hive, 657 Cell, 658 Node, 659 *CachedKcb, 660 0, 661 KeyName); 662 if (!Kcb) 663 { 664 /* Release registry lock and return failure */ 665 CmpUnlockRegistry(); 666 return STATUS_INSUFFICIENT_RESOURCES; 667 } 668 } 669 670 /* Make sure it's also locked, and set the pointer */ 671 //ASSERT(CmpIsKcbLockedExclusive(Kcb)); 672 *CachedKcb = Kcb; 673 674 /* Release the registry lock */ 675 CmpUnlockRegistry(); 676 677 /* Allocate the key object */ 678 Status = ObCreateObject(AccessMode, 679 CmpKeyObjectType, 680 NULL, 681 AccessMode, 682 NULL, 683 sizeof(CM_KEY_BODY), 684 0, 685 0, 686 Object); 687 if (NT_SUCCESS(Status)) 688 { 689 /* Get the key body and fill it out */ 690 KeyBody = (PCM_KEY_BODY)(*Object); 691 KeyBody->KeyControlBlock = Kcb; 692 KeyBody->Type = CM_KEY_BODY_TYPE; 693 KeyBody->ProcessID = PsGetCurrentProcessId(); 694 KeyBody->NotifyBlock = NULL; 695 696 /* Link to the KCB */ 697 EnlistKeyBodyWithKCB(KeyBody, 0); 698 699 if (!ObCheckObjectAccess(*Object, 700 AccessState, 701 FALSE, 702 AccessMode, 703 &Status)) 704 { 705 /* Access check failed */ 706 ObDereferenceObject(*Object); 707 } 708 } 709 else 710 { 711 /* Failed, dereference the KCB */ 712 CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE); 713 } 714 715 /* Return status */ 716 return Status; 717 } 718 719 NTSTATUS 720 NTAPI 721 CmpCreateLinkNode(IN PHHIVE Hive, 722 IN HCELL_INDEX Cell, 723 IN PACCESS_STATE AccessState, 724 IN UNICODE_STRING Name, 725 IN KPROCESSOR_MODE AccessMode, 726 IN ULONG CreateOptions, 727 IN PCM_PARSE_CONTEXT Context, 728 IN PCM_KEY_CONTROL_BLOCK ParentKcb, 729 OUT PVOID *Object) 730 { 731 NTSTATUS Status; 732 HCELL_INDEX KeyCell, LinkCell, ChildCell; 733 PCM_KEY_BODY KeyBody; 734 LARGE_INTEGER TimeStamp; 735 PCM_KEY_NODE KeyNode; 736 PCM_KEY_CONTROL_BLOCK Kcb = ParentKcb; 737 738 /* Link nodes only allowed on the master */ 739 if (Hive != &CmiVolatileHive->Hive) 740 { 741 /* Fail */ 742 DPRINT1("Invalid link node attempt\n"); 743 return STATUS_ACCESS_DENIED; 744 } 745 746 /* Check if the parent is being deleted */ 747 if (ParentKcb->Delete) 748 { 749 /* It is, quit */ 750 ASSERT(FALSE); 751 Status = STATUS_OBJECT_NAME_NOT_FOUND; 752 goto Exit; 753 } 754 755 /* Allocate a link node */ 756 LinkCell = HvAllocateCell(Hive, 757 FIELD_OFFSET(CM_KEY_NODE, Name) + 758 CmpNameSize(Hive, &Name), 759 Stable, 760 HCELL_NIL); 761 if (LinkCell == HCELL_NIL) 762 { 763 /* Fail */ 764 Status = STATUS_INSUFFICIENT_RESOURCES; 765 goto Exit; 766 } 767 768 /* Get the key cell */ 769 KeyCell = Context->ChildHive.KeyCell; 770 if (KeyCell != HCELL_NIL) 771 { 772 /* Hive exists! */ 773 ChildCell = KeyCell; 774 775 /* Get the node data */ 776 KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell); 777 if (!KeyNode) 778 { 779 /* Fail */ 780 ASSERT(FALSE); 781 Status = STATUS_INSUFFICIENT_RESOURCES; 782 goto Exit; 783 } 784 785 /* Fill out the data */ 786 KeyNode->Parent = LinkCell; 787 KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE; 788 HvReleaseCell(Context->ChildHive.KeyHive, ChildCell); 789 790 /* Now open the key cell */ 791 KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, KeyCell); 792 if (!KeyNode) 793 { 794 /* Fail */ 795 ASSERT(FALSE); 796 Status = STATUS_INSUFFICIENT_RESOURCES; 797 goto Exit; 798 } 799 800 /* Open the parent */ 801 Status = CmpDoOpen(Context->ChildHive.KeyHive, 802 KeyCell, 803 KeyNode, 804 AccessState, 805 AccessMode, 806 CreateOptions, 807 NULL, 808 0, 809 &Kcb, 810 &Name, 811 Object); 812 HvReleaseCell(Context->ChildHive.KeyHive, KeyCell); 813 } 814 else 815 { 816 /* Do the actual create operation */ 817 Status = CmpDoCreateChild(Context->ChildHive.KeyHive, 818 Cell, 819 NULL, 820 AccessState, 821 &Name, 822 AccessMode, 823 Context, 824 ParentKcb, 825 KEY_HIVE_ENTRY | KEY_NO_DELETE, 826 &ChildCell, 827 Object); 828 if (NT_SUCCESS(Status)) 829 { 830 /* Setup root pointer */ 831 Context->ChildHive.KeyHive->BaseBlock->RootCell = ChildCell; 832 } 833 } 834 835 /* Check if open or create suceeded */ 836 if (NT_SUCCESS(Status)) 837 { 838 /* Mark the cell dirty */ 839 HvMarkCellDirty(Context->ChildHive.KeyHive, ChildCell, FALSE); 840 841 /* Get the key node */ 842 KeyNode = HvGetCell(Context->ChildHive.KeyHive, ChildCell); 843 if (!KeyNode) 844 { 845 /* Fail */ 846 ASSERT(FALSE); 847 Status = STATUS_INSUFFICIENT_RESOURCES; 848 goto Exit; 849 } 850 851 /* Release it */ 852 HvReleaseCell(Context->ChildHive.KeyHive, ChildCell); 853 854 /* Set the parent and flags */ 855 KeyNode->Parent = LinkCell; 856 KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE; 857 858 /* Get the link node */ 859 KeyNode = HvGetCell(Hive, LinkCell); 860 if (!KeyNode) 861 { 862 /* Fail */ 863 ASSERT(FALSE); 864 Status = STATUS_INSUFFICIENT_RESOURCES; 865 goto Exit; 866 } 867 868 /* Set it up */ 869 KeyNode->Signature = CM_LINK_NODE_SIGNATURE; 870 KeyNode->Flags = KEY_HIVE_EXIT | KEY_NO_DELETE; 871 KeyNode->Parent = Cell; 872 KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, &Name); 873 if (KeyNode->NameLength < Name.Length) KeyNode->Flags |= KEY_COMP_NAME; 874 KeQuerySystemTime(&TimeStamp); 875 KeyNode->LastWriteTime = TimeStamp; 876 877 /* Clear out the rest */ 878 KeyNode->SubKeyCounts[Stable] = 0; 879 KeyNode->SubKeyCounts[Volatile] = 0; 880 KeyNode->SubKeyLists[Stable] = HCELL_NIL; 881 KeyNode->SubKeyLists[Volatile] = HCELL_NIL; 882 KeyNode->ValueList.Count = 0; 883 KeyNode->ValueList.List = HCELL_NIL; 884 KeyNode->ClassLength = 0; 885 886 /* Reference the root node */ 887 KeyNode->ChildHiveReference.KeyHive = Context->ChildHive.KeyHive; 888 KeyNode->ChildHiveReference.KeyCell = ChildCell; 889 HvReleaseCell(Hive, LinkCell); 890 891 /* Get the parent node */ 892 KeyNode = HvGetCell(Hive, Cell); 893 if (!KeyNode) 894 { 895 /* Fail */ 896 ASSERT(FALSE); 897 Status = STATUS_INSUFFICIENT_RESOURCES; 898 goto Exit; 899 } 900 901 /* Now add the subkey */ 902 if (!CmpAddSubKey(Hive, Cell, LinkCell)) 903 { 904 /* Failure! We don't handle this yet! */ 905 ASSERT(FALSE); 906 } 907 908 /* Get the key body */ 909 KeyBody = (PCM_KEY_BODY)*Object; 910 911 /* Sanity checks */ 912 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell); 913 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive); 914 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen); 915 916 /* Update the timestamp */ 917 KeQuerySystemTime(&TimeStamp); 918 KeyNode->LastWriteTime = TimeStamp; 919 KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp; 920 921 /* Check if we need to update name maximum */ 922 if (KeyNode->MaxNameLen < Name.Length) 923 { 924 /* Do it */ 925 KeyNode->MaxNameLen = Name.Length; 926 KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name.Length; 927 } 928 929 /* Check if we need to update class length maximum */ 930 if (KeyNode->MaxClassLen < Context->Class.Length) 931 { 932 /* Update it */ 933 KeyNode->MaxClassLen = Context->Class.Length; 934 } 935 936 /* Release the cell */ 937 HvReleaseCell(Hive, Cell); 938 } 939 else 940 { 941 /* Release the link cell */ 942 HvReleaseCell(Hive, LinkCell); 943 } 944 945 Exit: 946 /* Release the flusher locks and return status */ 947 return Status; 948 } 949 950 VOID 951 NTAPI 952 CmpHandleExitNode(IN OUT PHHIVE *Hive, 953 IN OUT HCELL_INDEX *Cell, 954 IN OUT PCM_KEY_NODE *KeyNode, 955 IN OUT PHHIVE *ReleaseHive, 956 IN OUT HCELL_INDEX *ReleaseCell) 957 { 958 /* Check if we have anything to release */ 959 if (*ReleaseCell != HCELL_NIL) 960 { 961 /* Release it */ 962 ASSERT(*ReleaseHive != NULL); 963 HvReleaseCell(*ReleaseHive, *ReleaseCell); 964 } 965 966 /* Get the link references */ 967 *Hive = (*KeyNode)->ChildHiveReference.KeyHive; 968 *Cell = (*KeyNode)->ChildHiveReference.KeyCell; 969 970 /* Get the new node */ 971 *KeyNode = (PCM_KEY_NODE)HvGetCell(*Hive, *Cell); 972 if (*KeyNode) 973 { 974 /* Set the new release values */ 975 *ReleaseCell = *Cell; 976 *ReleaseHive = *Hive; 977 } 978 else 979 { 980 /* Nothing to release */ 981 *ReleaseCell = HCELL_NIL; 982 *ReleaseHive = NULL; 983 } 984 } 985 986 NTSTATUS 987 NTAPI 988 CmpBuildHashStackAndLookupCache(IN PCM_KEY_BODY ParseObject, 989 IN OUT PCM_KEY_CONTROL_BLOCK *Kcb, 990 IN PUNICODE_STRING Current, 991 OUT PHHIVE *Hive, 992 OUT HCELL_INDEX *Cell, 993 OUT PULONG TotalRemainingSubkeys, 994 OUT PULONG MatchRemainSubkeyLevel, 995 OUT PULONG TotalSubkeys, 996 OUT PULONG OuterStackArray, 997 OUT PULONG *LockedKcbs) 998 { 999 /* We don't lock anything for now */ 1000 *LockedKcbs = NULL; 1001 1002 /* Calculate hash values */ 1003 *TotalRemainingSubkeys = 0xBAADF00D; 1004 1005 /* Lock the registry */ 1006 CmpLockRegistry(); 1007 1008 /* Return hive and cell data */ 1009 *Hive = (*Kcb)->KeyHive; 1010 *Cell = (*Kcb)->KeyCell; 1011 1012 /* Make sure it's not a dead KCB */ 1013 ASSERT((*Kcb)->RefCount > 0); 1014 1015 /* Reference it */ 1016 (VOID)CmpReferenceKeyControlBlock(*Kcb); 1017 1018 /* Return success for now */ 1019 return STATUS_SUCCESS; 1020 } 1021 1022 NTSTATUS 1023 NTAPI 1024 CmpParseKey(IN PVOID ParseObject, 1025 IN PVOID ObjectType, 1026 IN OUT PACCESS_STATE AccessState, 1027 IN KPROCESSOR_MODE AccessMode, 1028 IN ULONG Attributes, 1029 IN OUT PUNICODE_STRING CompleteName, 1030 IN OUT PUNICODE_STRING RemainingName, 1031 IN OUT PVOID Context OPTIONAL, 1032 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL, 1033 OUT PVOID *Object) 1034 { 1035 NTSTATUS Status; 1036 PCM_KEY_CONTROL_BLOCK Kcb, ParentKcb; 1037 PHHIVE Hive = NULL; 1038 PCM_KEY_NODE Node = NULL; 1039 HCELL_INDEX Cell = HCELL_NIL, NextCell; 1040 PHHIVE HiveToRelease = NULL; 1041 HCELL_INDEX CellToRelease = HCELL_NIL; 1042 UNICODE_STRING Current, NextName; 1043 PCM_PARSE_CONTEXT ParseContext = Context; 1044 ULONG TotalRemainingSubkeys = 0, MatchRemainSubkeyLevel = 0, TotalSubkeys = 0; 1045 PULONG LockedKcbs = NULL; 1046 BOOLEAN Result, Last; 1047 PAGED_CODE(); 1048 1049 /* Loop path separators at the end */ 1050 while ((RemainingName->Length) && 1051 (RemainingName->Buffer[(RemainingName->Length / sizeof(WCHAR)) - 1] == 1052 OBJ_NAME_PATH_SEPARATOR)) 1053 { 1054 /* Remove path separator */ 1055 RemainingName->Length -= sizeof(WCHAR); 1056 } 1057 1058 /* Fail if this isn't a key object */ 1059 if (ObjectType != CmpKeyObjectType) return STATUS_OBJECT_TYPE_MISMATCH; 1060 1061 /* Copy the remaining name */ 1062 Current = *RemainingName; 1063 1064 /* Check if this is a create */ 1065 if (!(ParseContext) || !(ParseContext->CreateOperation)) 1066 { 1067 /* It isn't, so no context */ 1068 ParseContext = NULL; 1069 } 1070 1071 /* Grab the KCB */ 1072 Kcb = ((PCM_KEY_BODY)ParseObject)->KeyControlBlock; 1073 1074 /* Sanity check */ 1075 ASSERT(Kcb != NULL); 1076 1077 /* Fail if the key was marked as deleted */ 1078 if (Kcb->Delete) 1079 return STATUS_KEY_DELETED; 1080 1081 /* Lookup in the cache */ 1082 Status = CmpBuildHashStackAndLookupCache(ParseObject, 1083 &Kcb, 1084 &Current, 1085 &Hive, 1086 &Cell, 1087 &TotalRemainingSubkeys, 1088 &MatchRemainSubkeyLevel, 1089 &TotalSubkeys, 1090 NULL, 1091 &LockedKcbs); 1092 1093 /* This is now the parent */ 1094 ParentKcb = Kcb; 1095 1096 /* Sanity check */ 1097 ASSERT(ParentKcb != NULL); 1098 1099 /* Check if everything was found cached */ 1100 if (!TotalRemainingSubkeys) 1101 ASSERTMSG("Caching not implemented\n", FALSE); 1102 1103 /* Don't do anything if we're being deleted */ 1104 if (Kcb->Delete) 1105 { 1106 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1107 goto Quickie; 1108 } 1109 1110 /* Check if this is a symlink */ 1111 if (Kcb->Flags & KEY_SYM_LINK) 1112 { 1113 /* Get the next name */ 1114 Result = CmpGetNextName(&Current, &NextName, &Last); 1115 Current.Buffer = NextName.Buffer; 1116 1117 /* Validate the current name string length */ 1118 if (Current.Length + NextName.Length > MAXUSHORT) 1119 { 1120 /* too long */ 1121 Status = STATUS_NAME_TOO_LONG; 1122 goto Quickie; 1123 } 1124 Current.Length += NextName.Length; 1125 1126 /* Validate the current name string maximum length */ 1127 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT) 1128 { 1129 /* too long */ 1130 Status = STATUS_NAME_TOO_LONG; 1131 goto Quickie; 1132 } 1133 Current.MaximumLength += NextName.MaximumLength; 1134 1135 /* Parse the symlink */ 1136 if (CmpGetSymbolicLink(Hive, 1137 CompleteName, 1138 Kcb, 1139 &Current)) 1140 { 1141 /* Symlink parse succeeded */ 1142 Status = STATUS_REPARSE; 1143 } 1144 else 1145 { 1146 /* Couldn't find symlink */ 1147 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1148 } 1149 1150 /* We're done */ 1151 goto Quickie; 1152 } 1153 1154 /* Get the key node */ 1155 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 1156 if (!Node) 1157 { 1158 Status = STATUS_INSUFFICIENT_RESOURCES; 1159 goto Quickie; 1160 } 1161 1162 /* Start parsing */ 1163 Status = STATUS_NOT_IMPLEMENTED; 1164 while (TRUE) 1165 { 1166 /* Get the next component */ 1167 Result = CmpGetNextName(&Current, &NextName, &Last); 1168 if ((Result) && (NextName.Length)) 1169 { 1170 /* See if this is a sym link */ 1171 if (!(Kcb->Flags & KEY_SYM_LINK)) 1172 { 1173 /* Find the subkey */ 1174 NextCell = CmpFindSubKeyByName(Hive, Node, &NextName); 1175 if (NextCell != HCELL_NIL) 1176 { 1177 /* Get the new node */ 1178 Cell = NextCell; 1179 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 1180 if (!Node) ASSERT(FALSE); 1181 1182 /* Check if this was the last key */ 1183 if (Last) 1184 { 1185 /* Is this an exit node */ 1186 if (Node->Flags & KEY_HIVE_EXIT) 1187 { 1188 /* Handle it */ 1189 CmpHandleExitNode(&Hive, 1190 &Cell, 1191 &Node, 1192 &HiveToRelease, 1193 &CellToRelease); 1194 if (!Node) 1195 { 1196 /* Fail */ 1197 Status = STATUS_INSUFFICIENT_RESOURCES; 1198 break; 1199 } 1200 } 1201 1202 /* Do the open */ 1203 Status = CmpDoOpen(Hive, 1204 Cell, 1205 Node, 1206 AccessState, 1207 AccessMode, 1208 Attributes, 1209 ParseContext, 1210 0, 1211 &Kcb, 1212 &NextName, 1213 Object); 1214 if (Status == STATUS_REPARSE) 1215 { 1216 /* Parse the symlink */ 1217 if (!CmpGetSymbolicLink(Hive, 1218 CompleteName, 1219 Kcb, 1220 NULL)) 1221 { 1222 /* Symlink parse failed */ 1223 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1224 } 1225 } 1226 1227 /* We are done */ 1228 break; 1229 } 1230 1231 /* Is this an exit node */ 1232 if (Node->Flags & KEY_HIVE_EXIT) 1233 { 1234 /* Handle it */ 1235 CmpHandleExitNode(&Hive, 1236 &Cell, 1237 &Node, 1238 &HiveToRelease, 1239 &CellToRelease); 1240 if (!Node) 1241 { 1242 /* Fail */ 1243 Status = STATUS_INSUFFICIENT_RESOURCES; 1244 break; 1245 } 1246 } 1247 1248 /* Create a KCB for this key */ 1249 Kcb = CmpCreateKeyControlBlock(Hive, 1250 Cell, 1251 Node, 1252 ParentKcb, 1253 0, 1254 &NextName); 1255 if (!Kcb) 1256 { 1257 /* Fail */ 1258 Status = STATUS_INSUFFICIENT_RESOURCES; 1259 break; 1260 } 1261 1262 /* Dereference the parent and set the new one */ 1263 CmpDereferenceKeyControlBlock(ParentKcb); 1264 ParentKcb = Kcb; 1265 } 1266 else 1267 { 1268 /* Check if this was the last key for a create */ 1269 if ((Last) && (ParseContext)) 1270 { 1271 /* Check if we're doing a link node */ 1272 if (ParseContext->CreateLink) 1273 { 1274 /* The only thing we should see */ 1275 Status = CmpCreateLinkNode(Hive, 1276 Cell, 1277 AccessState, 1278 NextName, 1279 AccessMode, 1280 Attributes, 1281 ParseContext, 1282 ParentKcb, 1283 Object); 1284 } 1285 else if (Hive == &CmiVolatileHive->Hive && CmpNoVolatileCreates) 1286 { 1287 /* Creating keys in the master hive is not allowed */ 1288 Status = STATUS_INVALID_PARAMETER; 1289 } 1290 else 1291 { 1292 /* Do the create */ 1293 Status = CmpDoCreate(Hive, 1294 Cell, 1295 AccessState, 1296 &NextName, 1297 AccessMode, 1298 ParseContext, 1299 ParentKcb, 1300 Object); 1301 } 1302 1303 /* Check for reparse (in this case, someone beat us) */ 1304 if (Status == STATUS_REPARSE) break; 1305 1306 /* Update disposition */ 1307 ParseContext->Disposition = REG_CREATED_NEW_KEY; 1308 break; 1309 } 1310 else 1311 { 1312 /* Key not found */ 1313 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1314 break; 1315 } 1316 } 1317 } 1318 else 1319 { 1320 /* Save the next name */ 1321 Current.Buffer = NextName.Buffer; 1322 1323 /* Validate the current name string length */ 1324 if (Current.Length + NextName.Length > MAXUSHORT) 1325 { 1326 /* too long */ 1327 Status = STATUS_NAME_TOO_LONG; 1328 break; 1329 } 1330 Current.Length += NextName.Length; 1331 1332 /* Validate the current name string maximum length */ 1333 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT) 1334 { 1335 /* too long */ 1336 Status = STATUS_NAME_TOO_LONG; 1337 break; 1338 } 1339 Current.MaximumLength += NextName.MaximumLength; 1340 1341 /* Parse the symlink */ 1342 if (CmpGetSymbolicLink(Hive, 1343 CompleteName, 1344 Kcb, 1345 &Current)) 1346 { 1347 /* Symlink parse succeeded */ 1348 Status = STATUS_REPARSE; 1349 } 1350 else 1351 { 1352 /* Couldn't find symlink */ 1353 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1354 } 1355 1356 /* We're done */ 1357 break; 1358 } 1359 } 1360 else if ((Result) && (Last)) 1361 { 1362 /* Opening the root. Is this an exit node? */ 1363 if (Node->Flags & KEY_HIVE_EXIT) 1364 { 1365 /* Handle it */ 1366 CmpHandleExitNode(&Hive, 1367 &Cell, 1368 &Node, 1369 &HiveToRelease, 1370 &CellToRelease); 1371 if (!Node) 1372 { 1373 /* Fail */ 1374 Status = STATUS_INSUFFICIENT_RESOURCES; 1375 break; 1376 } 1377 } 1378 1379 /* Do the open */ 1380 Status = CmpDoOpen(Hive, 1381 Cell, 1382 Node, 1383 AccessState, 1384 AccessMode, 1385 Attributes, 1386 ParseContext, 1387 CMP_OPEN_KCB_NO_CREATE /* | CMP_CREATE_KCB_KCB_LOCKED */, 1388 &Kcb, 1389 &NextName, 1390 Object); 1391 if (Status == STATUS_REPARSE) 1392 { 1393 /* Nothing to do */ 1394 } 1395 1396 /* We're done */ 1397 break; 1398 } 1399 else 1400 { 1401 /* Bogus */ 1402 Status = STATUS_INVALID_PARAMETER; 1403 break; 1404 } 1405 } 1406 1407 /* Dereference the parent if it exists */ 1408 Quickie: 1409 if (ParentKcb) 1410 CmpDereferenceKeyControlBlock(ParentKcb); 1411 1412 /* Unlock the registry */ 1413 CmpUnlockRegistry(); 1414 return Status; 1415 } 1416