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 KeyBody->KcbLocked = FALSE; 288 289 /* Check if we had a class */ 290 if (ParseContext->Class.Length > 0) 291 { 292 /* Get the class cell */ 293 CellData = HvGetCell(Hive, ClassCell); 294 if (!CellData) 295 { 296 /* Fail, this should never happen */ 297 ASSERT(FALSE); 298 Status = STATUS_INSUFFICIENT_RESOURCES; 299 ObDereferenceObject(*Object); 300 goto Quickie; 301 } 302 303 /* Release the cell */ 304 HvReleaseCell(Hive, ClassCell); 305 306 /* Copy the class data */ 307 RtlCopyMemory(&CellData->u.KeyString[0], 308 ParseContext->Class.Buffer, 309 ParseContext->Class.Length); 310 } 311 312 /* Fill out the key node */ 313 KeyNode->Signature = CM_KEY_NODE_SIGNATURE; 314 KeyNode->Flags = Flags; 315 KeQuerySystemTime(&KeyNode->LastWriteTime); 316 KeyNode->Spare = 0; 317 KeyNode->Parent = ParentCell; 318 KeyNode->SubKeyCounts[Stable] = 0; 319 KeyNode->SubKeyCounts[Volatile] = 0; 320 KeyNode->SubKeyLists[Stable] = HCELL_NIL; 321 KeyNode->SubKeyLists[Volatile] = HCELL_NIL; 322 KeyNode->ValueList.Count = 0; 323 KeyNode->ValueList.List = HCELL_NIL; 324 KeyNode->Security = HCELL_NIL; 325 KeyNode->Class = ClassCell; 326 KeyNode->ClassLength = ParseContext->Class.Length; 327 KeyNode->MaxValueDataLen = 0; 328 KeyNode->MaxNameLen = 0; 329 KeyNode->MaxValueNameLen = 0; 330 KeyNode->MaxClassLen = 0; 331 KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, Name); 332 if (KeyNode->NameLength < Name->Length) KeyNode->Flags |= KEY_COMP_NAME; 333 334 /* Create the KCB */ 335 Kcb = CmpCreateKeyControlBlock(Hive, 336 *KeyCell, 337 KeyNode, 338 ParentKcb, 339 CMP_LOCK_HASHES_FOR_KCB, 340 Name); 341 if (!Kcb) 342 { 343 /* Fail */ 344 ObDereferenceObjectDeferDelete(*Object); 345 Status = STATUS_INSUFFICIENT_RESOURCES; 346 goto Quickie; 347 } 348 349 /* Sanity check */ 350 ASSERT(Kcb->RefCount == 1); 351 352 /* Now fill out the Cm object */ 353 KeyBody->NotifyBlock = NULL; 354 KeyBody->ProcessID = PsGetCurrentProcessId(); 355 KeyBody->KeyControlBlock = Kcb; 356 357 /* Link it with the KCB */ 358 EnlistKeyBodyWithKCB(KeyBody, CMP_ENLIST_KCB_LOCKED_EXCLUSIVE); 359 360 /* Assign security */ 361 Status = SeAssignSecurity(ParentDescriptor, 362 AccessState->SecurityDescriptor, 363 &NewDescriptor, 364 TRUE, 365 &AccessState->SubjectSecurityContext, 366 &CmpKeyObjectType->TypeInfo.GenericMapping, 367 CmpKeyObjectType->TypeInfo.PoolType); 368 if (NT_SUCCESS(Status)) 369 { 370 /* 371 * FIXME: We must acquire a security lock when assigning 372 * a security descriptor to this hive but since the 373 * CmpAssignSecurityDescriptor function does nothing 374 * (we lack the necessary security management implementations 375 * anyway), do not do anything for now. 376 */ 377 Status = CmpAssignSecurityDescriptor(Kcb, NewDescriptor); 378 } 379 380 /* Now that the security descriptor is copied in the hive, we can free the original */ 381 SeDeassignSecurity(&NewDescriptor); 382 383 if (NT_SUCCESS(Status)) 384 { 385 /* Send notification to registered callbacks */ 386 CmpReportNotify(Kcb, Hive, Kcb->KeyCell, REG_NOTIFY_CHANGE_NAME); 387 } 388 389 Quickie: 390 /* Check if we got here because of failure */ 391 if (!NT_SUCCESS(Status)) 392 { 393 /* Free any cells we might've allocated */ 394 if (ParseContext->Class.Length > 0) HvFreeCell(Hive, ClassCell); 395 HvFreeCell(Hive, *KeyCell); 396 } 397 398 /* Return status */ 399 return Status; 400 } 401 402 NTSTATUS 403 NTAPI 404 CmpDoCreate(IN PHHIVE Hive, 405 IN HCELL_INDEX Cell, 406 IN PACCESS_STATE AccessState, 407 IN PUNICODE_STRING Name, 408 IN KPROCESSOR_MODE AccessMode, 409 IN PCM_PARSE_CONTEXT ParseContext, 410 IN PCM_KEY_CONTROL_BLOCK ParentKcb, 411 OUT PVOID *Object) 412 { 413 NTSTATUS Status; 414 PCELL_DATA CellData; 415 HCELL_INDEX KeyCell; 416 ULONG ParentType; 417 PCM_KEY_BODY KeyBody; 418 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; 419 LARGE_INTEGER TimeStamp; 420 PCM_KEY_NODE KeyNode; 421 422 /* Make sure the KCB is locked and lock the flusher */ 423 CMP_ASSERT_KCB_LOCK(ParentKcb); 424 CmpLockHiveFlusherShared((PCMHIVE)Hive); 425 426 /* Bail out on read-only KCBs */ 427 if (ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY) 428 { 429 Status = STATUS_ACCESS_DENIED; 430 goto Exit; 431 } 432 433 /* Check if the parent is being deleted */ 434 if (ParentKcb->Delete) 435 { 436 /* It has, quit */ 437 ASSERT(FALSE); 438 Status = STATUS_OBJECT_NAME_NOT_FOUND; 439 goto Exit; 440 } 441 442 /* Get the parent node */ 443 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 444 if (!KeyNode) 445 { 446 /* Fail */ 447 ASSERT(FALSE); 448 Status = STATUS_INSUFFICIENT_RESOURCES; 449 goto Exit; 450 } 451 452 /* Make sure nobody added us yet */ 453 if (CmpFindSubKeyByName(Hive, KeyNode, Name) != HCELL_NIL) 454 { 455 /* Fail */ 456 ASSERT(FALSE); 457 Status = STATUS_REPARSE; 458 goto Exit; 459 } 460 461 /* Sanity check */ 462 ASSERT(Cell == ParentKcb->KeyCell); 463 464 /* Get the parent type */ 465 ParentType = HvGetCellType(Cell); 466 if ((ParentType == Volatile) && 467 !(ParseContext->CreateOptions & REG_OPTION_VOLATILE)) 468 { 469 /* Children of volatile parents must also be volatile */ 470 //ASSERT(FALSE); 471 Status = STATUS_CHILD_MUST_BE_VOLATILE; 472 goto Exit; 473 } 474 475 /* Don't allow children under symlinks */ 476 if (ParentKcb->Flags & KEY_SYM_LINK) 477 { 478 /* Fail */ 479 ASSERT(FALSE); 480 Status = STATUS_ACCESS_DENIED; 481 goto Exit; 482 } 483 484 /* Make the cell dirty for now */ 485 HvMarkCellDirty(Hive, Cell, FALSE); 486 487 /* Do the actual create operation */ 488 Status = CmpDoCreateChild(Hive, 489 Cell, 490 SecurityDescriptor, 491 AccessState, 492 Name, 493 AccessMode, 494 ParseContext, 495 ParentKcb, 496 0, 497 &KeyCell, 498 Object); 499 if (NT_SUCCESS(Status)) 500 { 501 /* Get the key body */ 502 KeyBody = (PCM_KEY_BODY)(*Object); 503 504 /* Now add the subkey */ 505 if (!CmpAddSubKey(Hive, Cell, KeyCell)) 506 { 507 /* Free the created child */ 508 CmpFreeKeyByCell(Hive, KeyCell, FALSE); 509 510 /* Purge out this KCB */ 511 KeyBody->KeyControlBlock->Delete = TRUE; 512 CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock); 513 514 /* And cleanup the key body object */ 515 ObDereferenceObjectDeferDelete(*Object); 516 Status = STATUS_INSUFFICIENT_RESOURCES; 517 goto Exit; 518 } 519 520 /* Get the key node */ 521 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 522 if (!KeyNode) 523 { 524 /* Fail, this shouldn't happen */ 525 CmpFreeKeyByCell(Hive, KeyCell, TRUE); // Subkey linked above 526 527 /* Purge out this KCB */ 528 KeyBody->KeyControlBlock->Delete = TRUE; 529 CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock); 530 531 /* And cleanup the key body object */ 532 ObDereferenceObjectDeferDelete(*Object); 533 Status = STATUS_INSUFFICIENT_RESOURCES; 534 goto Exit; 535 } 536 537 /* Clean up information on this subkey */ 538 CmpCleanUpSubKeyInfo(KeyBody->KeyControlBlock->ParentKcb); 539 540 /* Sanity checks */ 541 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell); 542 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive); 543 ASSERT(KeyBody->KeyControlBlock->ParentKcb == ParentKcb); 544 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen); 545 546 /* Update the timestamp */ 547 KeQuerySystemTime(&TimeStamp); 548 KeyNode->LastWriteTime = TimeStamp; 549 KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp; 550 551 /* Check if we need to update name maximum */ 552 if (KeyNode->MaxNameLen < Name->Length) 553 { 554 /* Do it */ 555 KeyNode->MaxNameLen = Name->Length; 556 KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name->Length; 557 } 558 559 /* Check if we need to update class length maximum */ 560 if (KeyNode->MaxClassLen < ParseContext->Class.Length) 561 { 562 /* Update it */ 563 KeyNode->MaxClassLen = ParseContext->Class.Length; 564 } 565 566 /* Check if we're creating a symbolic link */ 567 if (ParseContext->CreateOptions & REG_OPTION_CREATE_LINK) 568 { 569 /* Get the cell data */ 570 CellData = HvGetCell(Hive, KeyCell); 571 if (!CellData) 572 { 573 /* This shouldn't happen */ 574 CmpFreeKeyByCell(Hive, KeyCell, TRUE); // Subkey linked above 575 576 /* Purge out this KCB */ 577 KeyBody->KeyControlBlock->Delete = TRUE; 578 CmpRemoveKeyControlBlock(KeyBody->KeyControlBlock); 579 580 /* And cleanup the key body object */ 581 ObDereferenceObjectDeferDelete(*Object); 582 Status = STATUS_INSUFFICIENT_RESOURCES; 583 goto Exit; 584 } 585 586 /* Update the flags */ 587 CellData->u.KeyNode.Flags |= KEY_SYM_LINK; 588 KeyBody->KeyControlBlock->Flags = CellData->u.KeyNode.Flags; 589 HvReleaseCell(Hive, KeyCell); 590 } 591 } 592 593 Exit: 594 /* Release the flusher lock and return status */ 595 CmpUnlockHiveFlusher((PCMHIVE)Hive); 596 return Status; 597 } 598 599 NTSTATUS 600 NTAPI 601 CmpDoOpen(IN PHHIVE Hive, 602 IN HCELL_INDEX Cell, 603 IN PCM_KEY_NODE Node, 604 IN PACCESS_STATE AccessState, 605 IN KPROCESSOR_MODE AccessMode, 606 IN ULONG Attributes, 607 IN PCM_PARSE_CONTEXT Context OPTIONAL, 608 IN ULONG ControlFlags, 609 IN OUT PCM_KEY_CONTROL_BLOCK *CachedKcb, 610 IN PULONG KcbsLocked, 611 IN PUNICODE_STRING KeyName, 612 OUT PVOID *Object) 613 { 614 NTSTATUS Status; 615 BOOLEAN LockKcb = FALSE; 616 BOOLEAN IsLockShared = FALSE; 617 PCM_KEY_BODY KeyBody = NULL; 618 PCM_KEY_CONTROL_BLOCK Kcb = NULL; 619 620 /* Make sure the hive isn't locked */ 621 if ((Hive->HiveFlags & HIVE_IS_UNLOADING) && 622 (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread())) 623 { 624 /* It is, don't touch it */ 625 return STATUS_OBJECT_NAME_NOT_FOUND; 626 } 627 628 /* Check if we have a context */ 629 if (Context) 630 { 631 /* Check if this is a link create (which shouldn't be an open) */ 632 if (Context->CreateLink) 633 { 634 return STATUS_ACCESS_DENIED; 635 } 636 637 /* Check if this is symlink create attempt */ 638 if (Context->CreateOptions & REG_OPTION_CREATE_LINK) 639 { 640 /* Key already exists */ 641 return STATUS_OBJECT_NAME_COLLISION; 642 } 643 644 /* Set the disposition */ 645 Context->Disposition = REG_OPENED_EXISTING_KEY; 646 } 647 648 /* Lock the KCB on creation if asked */ 649 if (ControlFlags & CMP_CREATE_KCB_KCB_LOCKED) 650 { 651 LockKcb = TRUE; 652 } 653 654 /* Check if caller doesn't want to create a KCB */ 655 if (ControlFlags & CMP_OPEN_KCB_NO_CREATE) 656 { 657 /* 658 * The caller doesn't want to create a KCB. This means the KCB 659 * is already in cache and other threads may take use of it 660 * so it has to be locked in share mode. 661 */ 662 IsLockShared = TRUE; 663 664 /* Check if this is a symlink */ 665 if (((*CachedKcb)->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK)) 666 { 667 /* Is this symlink found? */ 668 if ((*CachedKcb)->ExtFlags & CM_KCB_SYM_LINK_FOUND) 669 { 670 /* Get the real KCB, is this deleted? */ 671 Kcb = (*CachedKcb)->ValueCache.RealKcb; 672 if (Kcb->Delete) 673 { 674 /* 675 * The real KCB is gone, do a reparse. We used to lock the KCB in 676 * shared mode as others may have taken use of it but since we 677 * must do a reparse of the key the only thing that matter is us. 678 * Lock the KCB exclusively so nobody is going to mess with the KCB. 679 */ 680 DPRINT1("The real KCB is deleted, attempt a reparse\n"); 681 CmpUnLockKcbArray(KcbsLocked); 682 CmpAcquireKcbLockExclusiveByIndex(GET_HASH_INDEX((*CachedKcb)->ConvKey)); 683 CmpCleanUpKcbValueCache(*CachedKcb); 684 KcbsLocked[0] = 1; 685 KcbsLocked[1] = GET_HASH_INDEX((*CachedKcb)->ConvKey); 686 return STATUS_REPARSE; 687 } 688 689 /* 690 * The symlink has been found. As in the similar case above, 691 * the KCB of the symlink exclusively, we don't want anybody 692 * to mess it up. 693 */ 694 CmpUnLockKcbArray(KcbsLocked); 695 CmpAcquireKcbLockExclusiveByIndex(GET_HASH_INDEX((*CachedKcb)->ConvKey)); 696 KcbsLocked[0] = 1; 697 KcbsLocked[1] = GET_HASH_INDEX((*CachedKcb)->ConvKey); 698 } 699 else 700 { 701 /* We must do a reparse */ 702 DPRINT("The symlink is not found, attempt a reparse\n"); 703 return STATUS_REPARSE; 704 } 705 } 706 else 707 { 708 /* This is not a symlink, just give the cached KCB already */ 709 Kcb = *CachedKcb; 710 } 711 712 /* The caller wants to open a cached KCB */ 713 if (!CmpReferenceKeyControlBlock(Kcb)) 714 { 715 /* Return failure code */ 716 return STATUS_INSUFFICIENT_RESOURCES; 717 } 718 } 719 else 720 { 721 /* 722 * The caller wants to create a new KCB. Unlike the code path above, here 723 * we must check if the lock is exclusively held because in the scenario 724 * where the caller doesn't want to create a KCB is because it is already 725 * in the cache and it must have a shared lock instead. 726 */ 727 ASSERT(CmpIsKcbLockedExclusive(*CachedKcb)); 728 729 /* Check if this is a symlink */ 730 if ((Node->Flags & KEY_SYM_LINK) && !(Attributes & OBJ_OPENLINK)) 731 { 732 /* Create the KCB for the symlink */ 733 Kcb = CmpCreateKeyControlBlock(Hive, 734 Cell, 735 Node, 736 *CachedKcb, 737 LockKcb ? CMP_LOCK_HASHES_FOR_KCB : 0, 738 KeyName); 739 if (!Kcb) 740 { 741 /* Return failure */ 742 return STATUS_INSUFFICIENT_RESOURCES; 743 } 744 745 /* Make sure it's also locked, and set the pointer */ 746 ASSERT(CmpIsKcbLockedExclusive(Kcb)); 747 *CachedKcb = Kcb; 748 749 /* Return reparse required */ 750 return STATUS_REPARSE; 751 } 752 753 /* Create the KCB */ 754 Kcb = CmpCreateKeyControlBlock(Hive, 755 Cell, 756 Node, 757 *CachedKcb, 758 LockKcb ? CMP_LOCK_HASHES_FOR_KCB : 0, 759 KeyName); 760 if (!Kcb) 761 { 762 /* Return failure */ 763 return STATUS_INSUFFICIENT_RESOURCES; 764 } 765 766 /* Make sure it's also locked, and set the pointer */ 767 ASSERT(CmpIsKcbLockedExclusive(Kcb)); 768 *CachedKcb = Kcb; 769 } 770 771 /* Allocate the key object */ 772 Status = ObCreateObject(AccessMode, 773 CmpKeyObjectType, 774 NULL, 775 AccessMode, 776 NULL, 777 sizeof(CM_KEY_BODY), 778 0, 779 0, 780 Object); 781 if (NT_SUCCESS(Status)) 782 { 783 /* Get the key body and fill it out */ 784 KeyBody = (PCM_KEY_BODY)(*Object); 785 KeyBody->KeyControlBlock = Kcb; 786 KeyBody->Type = CM_KEY_BODY_TYPE; 787 KeyBody->ProcessID = PsGetCurrentProcessId(); 788 KeyBody->NotifyBlock = NULL; 789 790 /* Link to the KCB */ 791 EnlistKeyBodyWithKCB(KeyBody, IsLockShared ? CMP_ENLIST_KCB_LOCKED_SHARED : CMP_ENLIST_KCB_LOCKED_EXCLUSIVE); 792 793 /* 794 * We are already holding a lock against the KCB that is assigned 795 * to this key body. This is to prevent a potential deadlock on 796 * CmpSecurityMethod as ObCheckObjectAccess will invoke the Object 797 * Manager to call that method, of which CmpSecurityMethod would 798 * attempt to acquire a lock again. 799 */ 800 KeyBody->KcbLocked = TRUE; 801 802 if (!ObCheckObjectAccess(*Object, 803 AccessState, 804 FALSE, 805 AccessMode, 806 &Status)) 807 { 808 /* Access check failed */ 809 ObDereferenceObject(*Object); 810 } 811 812 /* 813 * We are done, the lock we are holding will be released 814 * once the registry parsing is done. 815 */ 816 KeyBody->KcbLocked = FALSE; 817 } 818 else 819 { 820 /* Failed, dereference the KCB */ 821 CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE); 822 } 823 824 /* Return status */ 825 return Status; 826 } 827 828 NTSTATUS 829 NTAPI 830 CmpCreateLinkNode(IN PHHIVE Hive, 831 IN HCELL_INDEX Cell, 832 IN PACCESS_STATE AccessState, 833 IN UNICODE_STRING Name, 834 IN KPROCESSOR_MODE AccessMode, 835 IN ULONG CreateOptions, 836 IN PCM_PARSE_CONTEXT Context, 837 IN PCM_KEY_CONTROL_BLOCK ParentKcb, 838 IN PULONG KcbsLocked, 839 OUT PVOID *Object) 840 { 841 NTSTATUS Status; 842 HCELL_INDEX KeyCell, LinkCell, ChildCell; 843 PCM_KEY_BODY KeyBody; 844 LARGE_INTEGER TimeStamp; 845 PCM_KEY_NODE KeyNode; 846 PCM_KEY_CONTROL_BLOCK Kcb = ParentKcb; 847 848 /* Link nodes only allowed on the master */ 849 if (Hive != &CmiVolatileHive->Hive) 850 { 851 /* Fail */ 852 DPRINT1("Invalid link node attempt\n"); 853 return STATUS_ACCESS_DENIED; 854 } 855 856 /* Make sure the KCB is locked and lock the flusher */ 857 CMP_ASSERT_KCB_LOCK(ParentKcb); 858 CmpLockHiveFlusherShared((PCMHIVE)Hive); 859 CmpLockHiveFlusherShared((PCMHIVE)Context->ChildHive.KeyHive); 860 861 /* Bail out on read-only KCBs */ 862 if (ParentKcb->ExtFlags & CM_KCB_READ_ONLY_KEY) 863 { 864 Status = STATUS_ACCESS_DENIED; 865 goto Exit; 866 } 867 868 /* Check if the parent is being deleted */ 869 if (ParentKcb->Delete) 870 { 871 /* It is, quit */ 872 ASSERT(FALSE); 873 Status = STATUS_OBJECT_NAME_NOT_FOUND; 874 goto Exit; 875 } 876 877 /* Allocate a link node */ 878 LinkCell = HvAllocateCell(Hive, 879 FIELD_OFFSET(CM_KEY_NODE, Name) + 880 CmpNameSize(Hive, &Name), 881 Stable, 882 HCELL_NIL); 883 if (LinkCell == HCELL_NIL) 884 { 885 /* Fail */ 886 Status = STATUS_INSUFFICIENT_RESOURCES; 887 goto Exit; 888 } 889 890 /* Get the key cell */ 891 KeyCell = Context->ChildHive.KeyCell; 892 if (KeyCell != HCELL_NIL) 893 { 894 /* Hive exists! */ 895 ChildCell = KeyCell; 896 897 /* Get the node data */ 898 KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell); 899 if (!KeyNode) 900 { 901 /* Fail */ 902 ASSERT(FALSE); 903 Status = STATUS_INSUFFICIENT_RESOURCES; 904 goto Exit; 905 } 906 907 /* Fill out the data */ 908 KeyNode->Parent = LinkCell; 909 KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE; 910 HvReleaseCell(Context->ChildHive.KeyHive, ChildCell); 911 912 /* Now open the key cell */ 913 KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, KeyCell); 914 if (!KeyNode) 915 { 916 /* Fail */ 917 ASSERT(FALSE); 918 Status = STATUS_INSUFFICIENT_RESOURCES; 919 goto Exit; 920 } 921 922 /* Open the parent */ 923 Status = CmpDoOpen(Context->ChildHive.KeyHive, 924 KeyCell, 925 KeyNode, 926 AccessState, 927 AccessMode, 928 CreateOptions, 929 NULL, 930 CMP_CREATE_KCB_KCB_LOCKED, 931 &Kcb, 932 KcbsLocked, 933 &Name, 934 Object); 935 HvReleaseCell(Context->ChildHive.KeyHive, KeyCell); 936 } 937 else 938 { 939 /* Do the actual create operation */ 940 Status = CmpDoCreateChild(Context->ChildHive.KeyHive, 941 Cell, 942 NULL, 943 AccessState, 944 &Name, 945 AccessMode, 946 Context, 947 ParentKcb, 948 KEY_HIVE_ENTRY | KEY_NO_DELETE, 949 &ChildCell, 950 Object); 951 if (NT_SUCCESS(Status)) 952 { 953 /* Setup root pointer */ 954 Context->ChildHive.KeyHive->BaseBlock->RootCell = ChildCell; 955 } 956 } 957 958 /* Check if open or create suceeded */ 959 if (NT_SUCCESS(Status)) 960 { 961 /* Mark the cell dirty */ 962 HvMarkCellDirty(Context->ChildHive.KeyHive, ChildCell, FALSE); 963 964 /* Get the key node */ 965 KeyNode = (PCM_KEY_NODE)HvGetCell(Context->ChildHive.KeyHive, ChildCell); 966 if (!KeyNode) 967 { 968 /* Fail */ 969 ASSERT(FALSE); 970 Status = STATUS_INSUFFICIENT_RESOURCES; 971 goto Exit; 972 } 973 974 /* Release it */ 975 HvReleaseCell(Context->ChildHive.KeyHive, ChildCell); 976 977 /* Set the parent and flags */ 978 KeyNode->Parent = LinkCell; 979 KeyNode->Flags |= KEY_HIVE_ENTRY | KEY_NO_DELETE; 980 981 /* Get the link node */ 982 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, LinkCell); 983 if (!KeyNode) 984 { 985 /* Fail */ 986 ASSERT(FALSE); 987 Status = STATUS_INSUFFICIENT_RESOURCES; 988 goto Exit; 989 } 990 991 /* Set it up */ 992 KeyNode->Signature = CM_LINK_NODE_SIGNATURE; 993 KeyNode->Flags = KEY_HIVE_EXIT | KEY_NO_DELETE; 994 KeyNode->Parent = Cell; 995 KeyNode->NameLength = CmpCopyName(Hive, KeyNode->Name, &Name); 996 if (KeyNode->NameLength < Name.Length) KeyNode->Flags |= KEY_COMP_NAME; 997 KeQuerySystemTime(&TimeStamp); 998 KeyNode->LastWriteTime = TimeStamp; 999 1000 /* Clear out the rest */ 1001 KeyNode->SubKeyCounts[Stable] = 0; 1002 KeyNode->SubKeyCounts[Volatile] = 0; 1003 KeyNode->SubKeyLists[Stable] = HCELL_NIL; 1004 KeyNode->SubKeyLists[Volatile] = HCELL_NIL; 1005 KeyNode->ValueList.Count = 0; 1006 KeyNode->ValueList.List = HCELL_NIL; 1007 KeyNode->ClassLength = 0; 1008 1009 /* Reference the root node */ 1010 KeyNode->ChildHiveReference.KeyHive = Context->ChildHive.KeyHive; 1011 KeyNode->ChildHiveReference.KeyCell = ChildCell; 1012 HvReleaseCell(Hive, LinkCell); 1013 1014 /* Get the parent node */ 1015 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 1016 if (!KeyNode) 1017 { 1018 /* Fail */ 1019 ASSERT(FALSE); 1020 Status = STATUS_INSUFFICIENT_RESOURCES; 1021 goto Exit; 1022 } 1023 1024 /* Now add the subkey */ 1025 if (!CmpAddSubKey(Hive, Cell, LinkCell)) 1026 { 1027 /* Failure! We don't handle this yet! */ 1028 ASSERT(FALSE); 1029 } 1030 1031 /* Get the key body */ 1032 KeyBody = (PCM_KEY_BODY)*Object; 1033 1034 /* Clean up information on this subkey */ 1035 CmpCleanUpSubKeyInfo(KeyBody->KeyControlBlock->ParentKcb); 1036 1037 /* Sanity checks */ 1038 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyCell == Cell); 1039 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KeyHive == Hive); 1040 ASSERT(KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen == KeyNode->MaxNameLen); 1041 1042 /* Update the timestamp */ 1043 KeQuerySystemTime(&TimeStamp); 1044 KeyNode->LastWriteTime = TimeStamp; 1045 KeyBody->KeyControlBlock->ParentKcb->KcbLastWriteTime = TimeStamp; 1046 1047 /* Check if we need to update name maximum */ 1048 if (KeyNode->MaxNameLen < Name.Length) 1049 { 1050 /* Do it */ 1051 KeyNode->MaxNameLen = Name.Length; 1052 KeyBody->KeyControlBlock->ParentKcb->KcbMaxNameLen = Name.Length; 1053 } 1054 1055 /* Check if we need to update class length maximum */ 1056 if (KeyNode->MaxClassLen < Context->Class.Length) 1057 { 1058 /* Update it */ 1059 KeyNode->MaxClassLen = Context->Class.Length; 1060 } 1061 1062 /* Release the cell */ 1063 HvReleaseCell(Hive, Cell); 1064 } 1065 else 1066 { 1067 /* Release the link cell */ 1068 HvReleaseCell(Hive, LinkCell); 1069 } 1070 1071 Exit: 1072 /* Release the flusher locks and return status */ 1073 CmpUnlockHiveFlusher((PCMHIVE)Context->ChildHive.KeyHive); 1074 CmpUnlockHiveFlusher((PCMHIVE)Hive); 1075 return Status; 1076 } 1077 1078 VOID 1079 NTAPI 1080 CmpHandleExitNode(IN OUT PHHIVE *Hive, 1081 IN OUT HCELL_INDEX *Cell, 1082 IN OUT PCM_KEY_NODE *KeyNode, 1083 IN OUT PHHIVE *ReleaseHive, 1084 IN OUT HCELL_INDEX *ReleaseCell) 1085 { 1086 /* Check if we have anything to release */ 1087 if (*ReleaseCell != HCELL_NIL) 1088 { 1089 /* Release it */ 1090 ASSERT(*ReleaseHive != NULL); 1091 HvReleaseCell(*ReleaseHive, *ReleaseCell); 1092 } 1093 1094 /* Get the link references */ 1095 *Hive = (*KeyNode)->ChildHiveReference.KeyHive; 1096 *Cell = (*KeyNode)->ChildHiveReference.KeyCell; 1097 1098 /* Get the new node */ 1099 *KeyNode = (PCM_KEY_NODE)HvGetCell(*Hive, *Cell); 1100 if (*KeyNode) 1101 { 1102 /* Set the new release values */ 1103 *ReleaseCell = *Cell; 1104 *ReleaseHive = *Hive; 1105 } 1106 else 1107 { 1108 /* Nothing to release */ 1109 *ReleaseCell = HCELL_NIL; 1110 *ReleaseHive = NULL; 1111 } 1112 } 1113 1114 /** 1115 * @brief 1116 * Computes the hashes of each subkey in key path name 1117 * and stores them in a hash stack for cache lookup. 1118 * 1119 * @param[in] RemainingName 1120 * A Unicode string structure consisting of the remaining 1121 * registry key path name. 1122 * 1123 * @param[in] ConvKey 1124 * The hash convkey of the current KCB to be supplied. 1125 * 1126 * @param[in,out] HashCacheStack 1127 * An array stack. This function uses this array to store 1128 * all the computed hashes of a key pathname. 1129 * 1130 * @param[out] TotalSubKeys 1131 * The number of total subkeys that have been found, returned 1132 * by this function to the caller. If no subkey levels are found 1133 * the function returns 0. 1134 * 1135 * @return 1136 * Returns the number of remaining subkey levels to caller. 1137 * If no subkey levels are found then this function returns 0. 1138 */ 1139 static 1140 ULONG 1141 CmpComputeHashValue( 1142 _In_ PUNICODE_STRING RemainingName, 1143 _In_ ULONG ConvKey, 1144 _Inout_ PCM_HASH_CACHE_STACK HashCacheStack, 1145 _Out_ PULONG TotalSubKeys) 1146 { 1147 ULONG CopyConvKey; 1148 ULONG SubkeysInTotal; 1149 ULONG RemainingSubkeysInTotal; 1150 PWCHAR RemainingNameBuffer; 1151 USHORT RemainingNameLength; 1152 USHORT KeyNameLength; 1153 1154 /* Don't compute the hashes on a NULL remaining name */ 1155 RemainingNameBuffer = RemainingName->Buffer; 1156 RemainingNameLength = RemainingName->Length; 1157 if (RemainingNameLength == 0) 1158 { 1159 *TotalSubKeys = 0; 1160 return 0; 1161 } 1162 1163 /* Skip any leading separator */ 1164 while (RemainingNameLength >= sizeof(WCHAR) && 1165 *RemainingNameBuffer == OBJ_NAME_PATH_SEPARATOR) 1166 { 1167 RemainingNameBuffer++; 1168 RemainingNameLength -= sizeof(WCHAR); 1169 } 1170 1171 /* Now set up the hash stack entries and compute the hashes */ 1172 SubkeysInTotal = 0; 1173 RemainingSubkeysInTotal = 0; 1174 KeyNameLength = 0; 1175 CopyConvKey = ConvKey; 1176 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Buffer = RemainingNameBuffer; 1177 while (RemainingNameLength > 0) 1178 { 1179 /* Is this character a separator? */ 1180 if (*RemainingNameBuffer != OBJ_NAME_PATH_SEPARATOR) 1181 { 1182 /* It's not, add it to the hash */ 1183 CopyConvKey = COMPUTE_HASH_CHAR(CopyConvKey, *RemainingNameBuffer); 1184 1185 /* Go to the next character (add up the length of the character as well) */ 1186 RemainingNameBuffer++; 1187 KeyNameLength += sizeof(WCHAR); 1188 RemainingNameLength -= sizeof(WCHAR); 1189 1190 /* 1191 * We are at the end of the key name path. Take into account 1192 * the last character and if we still have space in the hash 1193 * stack, add it up in the remaining list. 1194 */ 1195 if (RemainingNameLength == 0) 1196 { 1197 if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT) 1198 { 1199 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Length = KeyNameLength; 1200 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.MaximumLength = KeyNameLength; 1201 HashCacheStack[RemainingSubkeysInTotal].ConvKey = CopyConvKey; 1202 RemainingSubkeysInTotal++; 1203 } 1204 1205 SubkeysInTotal++; 1206 } 1207 } 1208 else 1209 { 1210 /* Skip any leading separator */ 1211 while (RemainingNameLength >= sizeof(WCHAR) && 1212 *RemainingNameBuffer == OBJ_NAME_PATH_SEPARATOR) 1213 { 1214 RemainingNameBuffer++; 1215 RemainingNameLength -= sizeof(WCHAR); 1216 } 1217 1218 /* 1219 * It would be possible that a malformed key pathname may be passed 1220 * to the registry parser such as a path with only separators like 1221 * "\\\\" for example. This would trick the function into believing 1222 * the key path has subkeys albeit that is not the case. 1223 */ 1224 ASSERT(RemainingNameLength != 0); 1225 1226 /* Take into account this subkey */ 1227 SubkeysInTotal++; 1228 1229 /* And add it up to the hash stack */ 1230 if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT) 1231 { 1232 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Length = KeyNameLength; 1233 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.MaximumLength = KeyNameLength; 1234 HashCacheStack[RemainingSubkeysInTotal].ConvKey = CopyConvKey; 1235 1236 RemainingSubkeysInTotal++; 1237 KeyNameLength = 0; 1238 1239 /* 1240 * Precaution check -- we have added up a remaining 1241 * subkey above but we must ensure we still have space 1242 * to hold up the new subkey for which we will compute 1243 * the hashes, so that we don't blow up the hash stack. 1244 */ 1245 if (RemainingSubkeysInTotal < CMP_SUBKEY_LEVELS_DEPTH_LIMIT) 1246 { 1247 HashCacheStack[RemainingSubkeysInTotal].NameOfKey.Buffer = RemainingNameBuffer; 1248 } 1249 } 1250 } 1251 } 1252 1253 *TotalSubKeys = SubkeysInTotal; 1254 return RemainingSubkeysInTotal; 1255 } 1256 1257 /** 1258 * @brief 1259 * Compares each subkey's hash and name with those 1260 * captured in the hash cache stack. 1261 * 1262 * @param[in] HashCacheStack 1263 * A pointer to a hash cache stack array filled with 1264 * subkey hashes and names for comparison. 1265 * 1266 * @param[in] CurrentKcb 1267 * A pointer to the currently given KCB. 1268 * 1269 * @param[in] RemainingSubkeys 1270 * The remaining subkey levels to be supplied. 1271 * 1272 * @param[out] ParentKcb 1273 * A pointer to the parent KCB returned to the caller. 1274 * This parameter points to the parent of the current 1275 * KCB if all the subkeys match, otherwise it points 1276 * to the actual current KCB. 1277 * 1278 * @return 1279 * Returns TRUE if all the subkey levels match, otherwise 1280 * FALSE is returned. 1281 */ 1282 static 1283 BOOLEAN 1284 CmpCompareSubkeys( 1285 _In_ PCM_HASH_CACHE_STACK HashCacheStack, 1286 _In_ PCM_KEY_CONTROL_BLOCK CurrentKcb, 1287 _In_ ULONG RemainingSubkeys, 1288 _Out_ PCM_KEY_CONTROL_BLOCK *ParentKcb) 1289 { 1290 LONG HashStackIndex; 1291 LONG Result; 1292 PCM_NAME_CONTROL_BLOCK NameBlock; 1293 UNICODE_STRING CurrentNameBlock; 1294 1295 ASSERT(CurrentKcb != NULL); 1296 1297 /* Loop each hash and check that they match */ 1298 HashStackIndex = RemainingSubkeys; 1299 while (HashStackIndex >= 0) 1300 { 1301 /* Does the subkey hash match? */ 1302 if (CurrentKcb->ConvKey != HashCacheStack[HashStackIndex].ConvKey) 1303 { 1304 *ParentKcb = CurrentKcb; 1305 return FALSE; 1306 } 1307 1308 /* Compare the subkey string, is the name compressed? */ 1309 NameBlock = CurrentKcb->NameBlock; 1310 if (NameBlock->Compressed) 1311 { 1312 Result = CmpCompareCompressedName(&HashCacheStack[HashStackIndex].NameOfKey, 1313 NameBlock->Name, 1314 NameBlock->NameLength); 1315 } 1316 else 1317 { 1318 CurrentNameBlock.Buffer = NameBlock->Name; 1319 CurrentNameBlock.Length = NameBlock->NameLength; 1320 CurrentNameBlock.MaximumLength = NameBlock->NameLength; 1321 1322 Result = RtlCompareUnicodeString(&HashCacheStack[HashStackIndex].NameOfKey, 1323 &CurrentNameBlock, 1324 TRUE); 1325 } 1326 1327 /* Do the subkey names match? */ 1328 if (Result) 1329 { 1330 *ParentKcb = CurrentKcb; 1331 return FALSE; 1332 } 1333 1334 /* Go to the next subkey hash */ 1335 HashStackIndex--; 1336 } 1337 1338 /* All the subkeys match */ 1339 *ParentKcb = CurrentKcb->ParentKcb; 1340 return TRUE; 1341 } 1342 1343 /** 1344 * @brief 1345 * Removes the subkeys on a remaining key pathname. 1346 * 1347 * @param[in] HashCacheStack 1348 * A pointer to a hash cache stack array filled with 1349 * subkey hashes and names. 1350 * 1351 * @param[in] RemainingSubkeys 1352 * The remaining subkey levels to be supplied. 1353 * 1354 * @param[in,out] RemainingName 1355 * A Unicode string structure consisting of the remaining 1356 * registry key path name, where the subkeys of such path 1357 * are to be removed. 1358 */ 1359 static 1360 VOID 1361 CmpRemoveSubkeysInRemainingName( 1362 _In_ PCM_HASH_CACHE_STACK HashCacheStack, 1363 _In_ ULONG RemainingSubkeys, 1364 _Inout_ PUNICODE_STRING RemainingName) 1365 { 1366 ULONG HashStackIndex = 0; 1367 1368 /* Skip any leading separator on matching name */ 1369 while (RemainingName->Length >= sizeof(WCHAR) && 1370 RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) 1371 { 1372 RemainingName->Buffer++; 1373 RemainingName->Length -= sizeof(WCHAR); 1374 } 1375 1376 /* Skip the subkeys as well */ 1377 while (HashStackIndex <= RemainingSubkeys) 1378 { 1379 RemainingName->Buffer += HashCacheStack[HashStackIndex].NameOfKey.Length / sizeof(WCHAR); 1380 RemainingName->Length -= HashCacheStack[HashStackIndex].NameOfKey.Length; 1381 1382 /* Skip any leading separator */ 1383 while (RemainingName->Length >= sizeof(WCHAR) && 1384 RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) 1385 { 1386 RemainingName->Buffer++; 1387 RemainingName->Length -= sizeof(WCHAR); 1388 } 1389 1390 /* Go to the next hash */ 1391 HashStackIndex++; 1392 } 1393 } 1394 1395 /** 1396 * @brief 1397 * Looks up in the pool cache for key pathname that matches 1398 * with one in the said cache and returns a KCB pointing 1399 * to that name. This function performs locking of KCBs 1400 * during cache lookup. 1401 * 1402 * @param[in] HashCacheStack 1403 * A pointer to a hash cache stack array filled with 1404 * subkey hashes and names. 1405 * 1406 * @param[in] LockKcbsExclusive 1407 * If set to TRUE, the KCBs are locked exclusively by the 1408 * calling thread, otherwise they are locked in shared mode. 1409 * See Remarks for further information. 1410 * 1411 * @param[in] TotalRemainingSubkeys 1412 * The total remaining subkey levels to be supplied. 1413 * 1414 * @param[in,out] RemainingName 1415 * A Unicode string structure consisting of the remaining 1416 * registry key path name. The remaining name is updated 1417 * by the function if a key pathname is found in cache. 1418 * 1419 * @param[in,out] OuterStackArray 1420 * A pointer to an array that lives on the caller's stack. 1421 * The expected size of the array is up to 32 elements, 1422 * which is the imposed limit by CMP_HASH_STACK_LIMIT. 1423 * This limit also corresponds to the maximum depth of 1424 * subkey levels. 1425 * 1426 * @param[in,out] Kcb 1427 * A pointer to a KCB, this KCB is changed if the key pathname 1428 * is found in cache. 1429 * 1430 * @param[out] Hive 1431 * A pointer to a hive, this hive is changed if the key pathname 1432 * is found in cache. 1433 * 1434 * @param[out] Cell 1435 * A pointer to a cell, this cell is changed if the key pathname 1436 * is found in cache. 1437 * 1438 * @param[out] MatchRemainSubkeyLevel 1439 * A pointer to match subkey levels returned by the function. 1440 * If no match levels are found, this is 0. 1441 * 1442 * @return 1443 * Returns STATUS_SUCCESS if cache lookup has completed successfully. 1444 * STATUS_OBJECT_NAME_NOT_FOUND is returned if the current KCB of 1445 * the key pathname has been deleted. STATUS_RETRY is returned if 1446 * at least the current KCB or its parent have been deleted 1447 * and a cache lookup must be retried again. STATUS_UNSUCCESSFUL is 1448 * returned if a KCB is referenced too many times. 1449 * 1450 * @remarks 1451 * The function attempts to do a cache lookup with a shared lock 1452 * on KCBs so that other threads can simultaneously get access 1453 * to these KCBs. When the captured KCB is being deleted on us 1454 * we have to retry a lookup with exclusive look so that no other 1455 * threads will mess with the KCBs and perform appropriate actions 1456 * if a KCB is deleted. 1457 */ 1458 static 1459 NTSTATUS 1460 CmpLookInCache( 1461 _In_ PCM_HASH_CACHE_STACK HashCacheStack, 1462 _In_ BOOLEAN LockKcbsExclusive, 1463 _In_ ULONG TotalRemainingSubkeys, 1464 _Inout_ PUNICODE_STRING RemainingName, 1465 _Inout_ PULONG OuterStackArray, 1466 _Inout_ PCM_KEY_CONTROL_BLOCK *Kcb, 1467 _Out_ PHHIVE *Hive, 1468 _Out_ PHCELL_INDEX Cell, 1469 _Out_ PULONG MatchRemainSubkeyLevel) 1470 { 1471 LONG RemainingSubkeys; 1472 ULONG TotalLevels; 1473 BOOLEAN SubkeysMatch; 1474 PCM_KEY_CONTROL_BLOCK CurrentKcb, ParentKcb; 1475 PCM_KEY_HASH HashEntry = NULL; 1476 BOOLEAN KeyFoundInCache = FALSE; 1477 PULONG LockedKcbs = NULL; 1478 1479 /* Reference the KCB */ 1480 if (!CmpReferenceKeyControlBlock(*Kcb)) 1481 { 1482 /* This key is opened too many times, bail out */ 1483 DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb); 1484 return STATUS_UNSUCCESSFUL; 1485 } 1486 1487 /* Prepare to lock the KCBs */ 1488 LockedKcbs = CmpBuildAndLockKcbArray(HashCacheStack, 1489 LockKcbsExclusive ? CMP_LOCK_KCB_ARRAY_EXCLUSIVE : CMP_LOCK_KCB_ARRAY_SHARED, 1490 *Kcb, 1491 OuterStackArray, 1492 TotalRemainingSubkeys, 1493 0); 1494 NT_ASSERT(LockedKcbs); 1495 1496 /* Lookup in the cache */ 1497 RemainingSubkeys = TotalRemainingSubkeys - 1; 1498 TotalLevels = TotalRemainingSubkeys + (*Kcb)->TotalLevels + 1; 1499 while (RemainingSubkeys >= 0) 1500 { 1501 /* Get the hash entry from the cache */ 1502 HashEntry = GET_HASH_ENTRY(CmpCacheTable, HashCacheStack[RemainingSubkeys].ConvKey)->Entry; 1503 1504 /* Take one level down as we are processing this hash entry */ 1505 TotalLevels--; 1506 1507 while (HashEntry != NULL) 1508 { 1509 /* Validate this hash and obtain the current KCB */ 1510 ASSERT_VALID_HASH(HashEntry); 1511 CurrentKcb = CONTAINING_RECORD(HashEntry, CM_KEY_CONTROL_BLOCK, KeyHash); 1512 1513 /* Does this KCB have matching levels? */ 1514 if (TotalLevels == CurrentKcb->TotalLevels) 1515 { 1516 /* 1517 * We have matching subkey levels but don't directly assume we have 1518 * a matching key path in cache. Start comparing each subkey. 1519 */ 1520 SubkeysMatch = CmpCompareSubkeys(HashCacheStack, 1521 CurrentKcb, 1522 RemainingSubkeys, 1523 &ParentKcb); 1524 if (SubkeysMatch) 1525 { 1526 /* All subkeys match, now check if the base KCB matches with parent */ 1527 if (*Kcb == ParentKcb) 1528 { 1529 /* Is the KCB marked as deleted? */ 1530 if (CurrentKcb->Delete || 1531 CurrentKcb->ParentKcb->Delete) 1532 { 1533 /* 1534 * Either the current or its parent KCB is marked 1535 * but we had a shared lock so probably a naughty 1536 * thread was deleting it. Retry doing a cache 1537 * lookup again with exclusive lock. 1538 */ 1539 if (!LockKcbsExclusive) 1540 { 1541 CmpUnLockKcbArray(LockedKcbs); 1542 CmpDereferenceKeyControlBlock(*Kcb); 1543 DPRINT1("The current KCB or its parent is deleted, retrying looking in the cache\n"); 1544 return STATUS_RETRY; 1545 } 1546 1547 /* We're under an exclusive lock, is the KCB deleted yet? */ 1548 if (CurrentKcb->Delete) 1549 { 1550 /* The KCB is gone, the key should no longer belong in the cache */ 1551 CmpRemoveKeyControlBlock(CurrentKcb); 1552 CmpUnLockKcbArray(LockedKcbs); 1553 CmpDereferenceKeyControlBlock(*Kcb); 1554 DPRINT1("The current KCB is deleted (KCB 0x%p)\n", CurrentKcb); 1555 return STATUS_OBJECT_NAME_NOT_FOUND; 1556 } 1557 1558 /* 1559 * The parent is deleted so it must be that somebody created 1560 * a fake key. Assert ourselves that is the case. 1561 */ 1562 ASSERT(CurrentKcb->ExtFlags & CM_KCB_KEY_NON_EXIST); 1563 1564 /* Remove this KCB out of cache if someone still uses it */ 1565 if (CurrentKcb->RefCount != 0) 1566 { 1567 CurrentKcb->Delete = TRUE; 1568 CmpRemoveKeyControlBlock(CurrentKcb); 1569 } 1570 else 1571 { 1572 /* Otherwise expunge it */ 1573 CmpRemoveFromDelayedClose(CurrentKcb); 1574 CmpCleanUpKcbCacheWithLock(CurrentKcb, FALSE); 1575 } 1576 1577 /* Stop looking for next hashes as the KCB is kaput */ 1578 break; 1579 } 1580 1581 /* We finally found the key in cache, acknowledge it */ 1582 KeyFoundInCache = TRUE; 1583 1584 /* Remove the subkeys in the remaining name and stop looking in the cache */ 1585 CmpRemoveSubkeysInRemainingName(HashCacheStack, RemainingSubkeys, RemainingName); 1586 break; 1587 } 1588 } 1589 } 1590 1591 /* Go to the next hash */ 1592 HashEntry = HashEntry->NextHash; 1593 } 1594 1595 /* Stop looking in cache if we found the matching key */ 1596 if (KeyFoundInCache) 1597 { 1598 DPRINT("Key found in cache, stop looking\n"); 1599 break; 1600 } 1601 1602 /* Keep looking in the cache until we run out of remaining subkeys */ 1603 RemainingSubkeys--; 1604 } 1605 1606 /* Return the matching subkey levels */ 1607 *MatchRemainSubkeyLevel = RemainingSubkeys + 1; 1608 1609 /* We have to update the KCB if the key was found in cache */ 1610 if (KeyFoundInCache) 1611 { 1612 /* 1613 * Before we change the KCB we must dereference the prior 1614 * KCB that we no longer need it. 1615 */ 1616 CmpDereferenceKeyControlBlock(*Kcb); 1617 *Kcb = CurrentKcb; 1618 1619 /* Reference the new KCB now */ 1620 if (!CmpReferenceKeyControlBlock(*Kcb)) 1621 { 1622 /* This key is opened too many times, bail out */ 1623 DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb); 1624 return STATUS_UNSUCCESSFUL; 1625 } 1626 1627 /* Update hive and cell data from current KCB */ 1628 *Hive = CurrentKcb->KeyHive; 1629 *Cell = CurrentKcb->KeyCell; 1630 } 1631 1632 /* Unlock the KCBs */ 1633 CmpUnLockKcbArray(LockedKcbs); 1634 return STATUS_SUCCESS; 1635 } 1636 1637 /** 1638 * @brief 1639 * Builds a hash stack cache and looks up in the 1640 * pool cache for a matching key pathname. 1641 * 1642 * @param[in] ParseObject 1643 * A pointer to a parse object, acting as a key 1644 * body. This parameter is unused. 1645 * 1646 * @param[in,out] Kcb 1647 * A pointer to a KCB. This KCB is used by the 1648 * registry parser after hash stack and cache 1649 * lookup are done. This KCB might change if the 1650 * key is found to be cached in the cache pool. 1651 * 1652 * @param[in] Current 1653 * The current remaining key pathname. 1654 * 1655 * @param[out] Hive 1656 * A pointer to a registry hive, returned by the caller. 1657 * 1658 * @param[out] Cell 1659 * A pointer to a hive cell, returned by the caller. 1660 * 1661 * @param[out] TotalRemainingSubkeys 1662 * A pointer to a number of total remaining subkey levels, 1663 * returned by the caller. This can be 0 if no subkey levels 1664 * have been found. 1665 * 1666 * @param[out] MatchRemainSubkeyLevel 1667 * A pointer to a number of remaining subkey levels that match, 1668 * returned by the caller. This can be 0 if no matching levels 1669 * are found. 1670 * 1671 * @param[out] TotalSubkeys 1672 * A pointer to a number of total subkeys. This can be 0 if no 1673 * subkey levels are found. By definition, both MatchRemainSubkeyLevel 1674 * and TotalRemainingSubkeys are 0 as well. 1675 * 1676 * @param[in,out] OuterStackArray 1677 * A pointer to an array that lives on the caller's stack. 1678 * The expected size of the array is up to 32 elements, 1679 * which is the imposed limit by CMP_HASH_STACK_LIMIT. 1680 * This limit also corresponds to the maximum depth of 1681 * subkey levels. 1682 * 1683 * @param[out] LockedKcbs 1684 * A pointer to an array of locked KCBs, returned by the caller. 1685 * 1686 * @return 1687 * Returns STATUS_SUCCESS if all the operations have succeeded without 1688 * problems. STATUS_NAME_TOO_LONG is returned if the key pathname has 1689 * too many subkey levels (more than 32 levels deep). A failure NTSTATUS 1690 * code is returned otherwise. Refer to CmpLookInCache documentation 1691 * for more information about other returned status codes. 1692 * STATUS_UNSUCCESSFUL is returned if a KCB is referenced too many times. 1693 */ 1694 NTSTATUS 1695 NTAPI 1696 CmpBuildHashStackAndLookupCache( 1697 _In_ PCM_KEY_BODY ParseObject, 1698 _Inout_ PCM_KEY_CONTROL_BLOCK *Kcb, 1699 _In_ PUNICODE_STRING Current, 1700 _Out_ PHHIVE *Hive, 1701 _Out_ PHCELL_INDEX Cell, 1702 _Out_ PULONG TotalRemainingSubkeys, 1703 _Out_ PULONG MatchRemainSubkeyLevel, 1704 _Out_ PULONG TotalSubkeys, 1705 _Inout_ PULONG OuterStackArray, 1706 _Out_ PULONG *LockedKcbs) 1707 { 1708 NTSTATUS Status; 1709 ULONG ConvKey; 1710 ULONG SubkeysInTotal, RemainingSubkeysInTotal, MatchRemainingSubkeys; 1711 CM_HASH_CACHE_STACK HashCacheStack[CMP_SUBKEY_LEVELS_DEPTH_LIMIT]; 1712 1713 /* Make sure it's not a dead KCB */ 1714 ASSERT((*Kcb)->RefCount > 0); 1715 1716 /* Lock the registry */ 1717 CmpLockRegistry(); 1718 1719 /* Calculate hash values for every subkey this key path has */ 1720 ConvKey = (*Kcb)->ConvKey; 1721 RemainingSubkeysInTotal = CmpComputeHashValue(Current, 1722 ConvKey, 1723 HashCacheStack, 1724 &SubkeysInTotal); 1725 1726 /* This key path has too many subkeys */ 1727 if (SubkeysInTotal > CMP_SUBKEY_LEVELS_DEPTH_LIMIT) 1728 { 1729 DPRINT1("The key path has too many subkeys - %lu\n", SubkeysInTotal); 1730 *LockedKcbs = NULL; 1731 return STATUS_NAME_TOO_LONG; 1732 } 1733 1734 /* Return hive and cell data */ 1735 *Hive = (*Kcb)->KeyHive; 1736 *Cell = (*Kcb)->KeyCell; 1737 1738 /* Do we have any subkeys? */ 1739 if (!RemainingSubkeysInTotal && !SubkeysInTotal) 1740 { 1741 /* 1742 * We don't have any subkeys nor remaining levels, the 1743 * KCB points to the actual key. Lock it. 1744 */ 1745 if (!CmpReferenceKeyControlBlock(*Kcb)) 1746 { 1747 /* This key is opened too many times, bail out */ 1748 DPRINT1("Could not reference the KCB, too many references (KCB 0x%p)\n", Kcb); 1749 return STATUS_UNSUCCESSFUL; 1750 } 1751 1752 CmpAcquireKcbLockSharedByIndex(GET_HASH_INDEX(ConvKey)); 1753 1754 /* Add this KCB in the array of locked KCBs */ 1755 OuterStackArray[0] = 1; 1756 OuterStackArray[1] = GET_HASH_INDEX(ConvKey); 1757 *LockedKcbs = OuterStackArray; 1758 1759 /* And return all the subkey level counters */ 1760 *TotalRemainingSubkeys = RemainingSubkeysInTotal; 1761 *MatchRemainSubkeyLevel = 0; 1762 *TotalSubkeys = SubkeysInTotal; 1763 return STATUS_SUCCESS; 1764 } 1765 1766 /* Lookup in the cache */ 1767 Status = CmpLookInCache(HashCacheStack, 1768 FALSE, 1769 RemainingSubkeysInTotal, 1770 Current, 1771 OuterStackArray, 1772 Kcb, 1773 Hive, 1774 Cell, 1775 &MatchRemainingSubkeys); 1776 if (!NT_SUCCESS(Status)) 1777 { 1778 /* Bail out if cache lookup failed for other reasons */ 1779 if (Status != STATUS_RETRY) 1780 { 1781 DPRINT1("CmpLookInCache() failed (Status 0x%lx)\n", Status); 1782 *LockedKcbs = NULL; 1783 return Status; 1784 } 1785 1786 /* Retry looking in the cache but with KCBs locked exclusively */ 1787 Status = CmpLookInCache(HashCacheStack, 1788 TRUE, 1789 RemainingSubkeysInTotal, 1790 Current, 1791 OuterStackArray, 1792 Kcb, 1793 Hive, 1794 Cell, 1795 &MatchRemainingSubkeys); 1796 if (!NT_SUCCESS(Status)) 1797 { 1798 DPRINT1("CmpLookInCache() failed after retry (Status 0x%lx)\n", Status); 1799 *LockedKcbs = NULL; 1800 return Status; 1801 } 1802 } 1803 1804 /* 1805 * Check if we have a full match of remaining levels. 1806 * 1807 * FIXME: It is possible we can catch a fake key from the cache 1808 * when we did the lookup, in such case we should not do any 1809 * locking as such KCB does not point to any real information. 1810 * Currently ReactOS doesn't create fake KCBs so we are good 1811 * for now. 1812 */ 1813 if (RemainingSubkeysInTotal == MatchRemainingSubkeys) 1814 { 1815 /* 1816 * Just simply lock this KCB as it points to the full 1817 * subkey levels in cache. 1818 */ 1819 CmpAcquireKcbLockSharedByIndex(GET_HASH_INDEX((*Kcb)->ConvKey)); 1820 OuterStackArray[0] = 1; 1821 OuterStackArray[1] = GET_HASH_INDEX((*Kcb)->ConvKey); 1822 *LockedKcbs = OuterStackArray; 1823 } 1824 else 1825 { 1826 /* 1827 * We only have a partial match so other subkey levels 1828 * have each KCB. Simply just lock them. 1829 */ 1830 *LockedKcbs = CmpBuildAndLockKcbArray(HashCacheStack, 1831 CMP_LOCK_KCB_ARRAY_EXCLUSIVE, 1832 *Kcb, 1833 OuterStackArray, 1834 RemainingSubkeysInTotal, 1835 MatchRemainingSubkeys); 1836 NT_ASSERT(*LockedKcbs); 1837 } 1838 1839 /* Return all the subkey level counters */ 1840 *TotalRemainingSubkeys = RemainingSubkeysInTotal; 1841 *MatchRemainSubkeyLevel = MatchRemainingSubkeys; 1842 *TotalSubkeys = SubkeysInTotal; 1843 return Status; 1844 } 1845 1846 NTSTATUS 1847 NTAPI 1848 CmpParseKey(IN PVOID ParseObject, 1849 IN PVOID ObjectType, 1850 IN OUT PACCESS_STATE AccessState, 1851 IN KPROCESSOR_MODE AccessMode, 1852 IN ULONG Attributes, 1853 IN OUT PUNICODE_STRING CompleteName, 1854 IN OUT PUNICODE_STRING RemainingName, 1855 IN OUT PVOID Context OPTIONAL, 1856 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL, 1857 OUT PVOID *Object) 1858 { 1859 NTSTATUS Status; 1860 PCM_KEY_CONTROL_BLOCK Kcb, ParentKcb; 1861 PHHIVE Hive = NULL; 1862 PCM_KEY_NODE Node = NULL; 1863 HCELL_INDEX Cell = HCELL_NIL, NextCell; 1864 PHHIVE HiveToRelease = NULL; 1865 HCELL_INDEX CellToRelease = HCELL_NIL; 1866 UNICODE_STRING Current, NextName; 1867 PCM_PARSE_CONTEXT ParseContext = Context; 1868 ULONG TotalRemainingSubkeys = 0, MatchRemainSubkeyLevel = 0, TotalSubkeys = 0; 1869 ULONG LockedKcbArray[CMP_KCBS_IN_ARRAY_LIMIT]; 1870 PULONG LockedKcbs; 1871 BOOLEAN IsKeyCached = FALSE; 1872 BOOLEAN Result, Last; 1873 PAGED_CODE(); 1874 1875 /* Loop path separators at the end */ 1876 while (RemainingName->Length && 1877 (RemainingName->Buffer[(RemainingName->Length / sizeof(WCHAR)) - 1] == 1878 OBJ_NAME_PATH_SEPARATOR)) 1879 { 1880 /* Remove path separator */ 1881 RemainingName->Length -= sizeof(WCHAR); 1882 } 1883 1884 /* Fail if this isn't a key object */ 1885 if (ObjectType != CmpKeyObjectType) return STATUS_OBJECT_TYPE_MISMATCH; 1886 1887 /* Copy the remaining name */ 1888 Current = *RemainingName; 1889 1890 /* Check if this is a create */ 1891 if (!ParseContext || !ParseContext->CreateOperation) 1892 { 1893 /* It isn't, so no context */ 1894 ParseContext = NULL; 1895 } 1896 1897 /* Grab the KCB */ 1898 Kcb = ((PCM_KEY_BODY)ParseObject)->KeyControlBlock; 1899 1900 /* Sanity check */ 1901 ASSERT(Kcb != NULL); 1902 1903 /* Fail if the key was marked as deleted */ 1904 if (Kcb->Delete) 1905 return STATUS_KEY_DELETED; 1906 1907 /* Lookup in the cache */ 1908 Status = CmpBuildHashStackAndLookupCache(ParseObject, 1909 &Kcb, 1910 &Current, 1911 &Hive, 1912 &Cell, 1913 &TotalRemainingSubkeys, 1914 &MatchRemainSubkeyLevel, 1915 &TotalSubkeys, 1916 LockedKcbArray, 1917 &LockedKcbs); 1918 CMP_ASSERT_REGISTRY_LOCK(); 1919 if (!NT_SUCCESS(Status)) 1920 { 1921 DPRINT1("Failed to look in cache, stop parsing (Status 0x%lx)\n", Status); 1922 ParentKcb = NULL; 1923 goto Quickie; 1924 } 1925 1926 /* This is now the parent */ 1927 ParentKcb = Kcb; 1928 1929 /* Sanity check */ 1930 ASSERT(ParentKcb != NULL); 1931 1932 /* Don't do anything if we're being deleted */ 1933 if (Kcb->Delete) 1934 { 1935 Status = STATUS_OBJECT_NAME_NOT_FOUND; 1936 goto Quickie; 1937 } 1938 1939 /* Check if everything was found cached */ 1940 if (!TotalRemainingSubkeys) 1941 { 1942 /* 1943 * We don't have any remaining subkey levels so we're good 1944 * that we have an already perfect candidate for a KCB, just 1945 * do the open directly. 1946 */ 1947 DPRINT("No remaining subkeys, the KCB points to the actual key\n"); 1948 IsKeyCached = TRUE; 1949 goto KeyCachedOpenNow; 1950 } 1951 1952 /* Check if we have a matching level */ 1953 if (MatchRemainSubkeyLevel) 1954 { 1955 /* 1956 * We have a matching level, check if that matches 1957 * with the total levels of subkeys. Do the open directly 1958 * if that is the case, because the whole subkeys levels 1959 * is cached. 1960 */ 1961 if (MatchRemainSubkeyLevel == TotalSubkeys) 1962 { 1963 DPRINT("We have a full matching level, open the key now\n"); 1964 IsKeyCached = TRUE; 1965 goto KeyCachedOpenNow; 1966 } 1967 1968 /* 1969 * We only have a partial match, make sure we did not 1970 * get mismatched hive data. 1971 */ 1972 ASSERT(Hive == Kcb->KeyHive); 1973 ASSERT(Cell == Kcb->KeyCell); 1974 } 1975 1976 /* 1977 * FIXME: Currently the registry parser doesn't check for fake 1978 * KCBs. CmpCreateKeyControlBlock does have the necessary implementation 1979 * to create such fake keys but we don't create these fake keys anywhere. 1980 * When we will do, we must improve the registry parser routine to handle 1981 * fake keys a bit differently here. 1982 */ 1983 1984 /* Check if this is a symlink */ 1985 if (Kcb->Flags & KEY_SYM_LINK) 1986 { 1987 /* Get the next name */ 1988 Result = CmpGetNextName(&Current, &NextName, &Last); 1989 Current.Buffer = NextName.Buffer; 1990 1991 /* Validate the current name string length */ 1992 if (Current.Length + NextName.Length > MAXUSHORT) 1993 { 1994 /* too long */ 1995 Status = STATUS_NAME_TOO_LONG; 1996 goto Quickie; 1997 } 1998 Current.Length += NextName.Length; 1999 2000 /* Validate the current name string maximum length */ 2001 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT) 2002 { 2003 /* too long */ 2004 Status = STATUS_NAME_TOO_LONG; 2005 goto Quickie; 2006 } 2007 Current.MaximumLength += NextName.MaximumLength; 2008 2009 /* CmpGetSymbolicLink doesn't want a lock */ 2010 CmpUnLockKcbArray(LockedKcbs); 2011 LockedKcbs = NULL; 2012 2013 /* Parse the symlink */ 2014 if (CmpGetSymbolicLink(Hive, 2015 CompleteName, 2016 Kcb, 2017 &Current)) 2018 { 2019 /* Symlink parse succeeded */ 2020 Status = STATUS_REPARSE; 2021 } 2022 else 2023 { 2024 /* Couldn't find symlink */ 2025 Status = STATUS_OBJECT_NAME_NOT_FOUND; 2026 } 2027 2028 /* We're done */ 2029 goto Quickie; 2030 } 2031 2032 /* Get the key node */ 2033 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 2034 if (!Node) 2035 { 2036 Status = STATUS_INSUFFICIENT_RESOURCES; 2037 goto Quickie; 2038 } 2039 2040 /* Start parsing */ 2041 Status = STATUS_NOT_IMPLEMENTED; 2042 while (TRUE) 2043 { 2044 /* Get the next component */ 2045 Result = CmpGetNextName(&Current, &NextName, &Last); 2046 if (Result && NextName.Length) 2047 { 2048 /* See if this is a sym link */ 2049 if (!(Kcb->Flags & KEY_SYM_LINK)) 2050 { 2051 /* Find the subkey */ 2052 NextCell = CmpFindSubKeyByName(Hive, Node, &NextName); 2053 if (NextCell != HCELL_NIL) 2054 { 2055 /* Get the new node */ 2056 Cell = NextCell; 2057 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell); 2058 ASSERT(Node); 2059 2060 /* Check if this was the last key */ 2061 if (Last) 2062 { 2063 /* Is this an exit node */ 2064 if (Node->Flags & KEY_HIVE_EXIT) 2065 { 2066 /* Handle it */ 2067 CmpHandleExitNode(&Hive, 2068 &Cell, 2069 &Node, 2070 &HiveToRelease, 2071 &CellToRelease); 2072 if (!Node) 2073 { 2074 /* Fail */ 2075 Status = STATUS_INSUFFICIENT_RESOURCES; 2076 break; 2077 } 2078 } 2079 2080 KeyCachedOpenNow: 2081 /* Do the open */ 2082 Status = CmpDoOpen(Hive, 2083 Cell, 2084 Node, 2085 AccessState, 2086 AccessMode, 2087 Attributes, 2088 ParseContext, 2089 IsKeyCached ? CMP_OPEN_KCB_NO_CREATE : CMP_CREATE_KCB_KCB_LOCKED, 2090 &Kcb, 2091 LockedKcbs, 2092 &NextName, 2093 Object); 2094 if (Status == STATUS_REPARSE) 2095 { 2096 /* CmpGetSymbolicLink doesn't want a lock */ 2097 CmpUnLockKcbArray(LockedKcbs); 2098 LockedKcbs = NULL; 2099 2100 /* Parse the symlink */ 2101 if (!CmpGetSymbolicLink(Hive, 2102 CompleteName, 2103 Kcb, 2104 NULL)) 2105 { 2106 /* Symlink parse failed */ 2107 Status = STATUS_OBJECT_NAME_NOT_FOUND; 2108 } 2109 } 2110 2111 /* We are done */ 2112 break; 2113 } 2114 2115 /* Is this an exit node */ 2116 if (Node->Flags & KEY_HIVE_EXIT) 2117 { 2118 /* Handle it */ 2119 CmpHandleExitNode(&Hive, 2120 &Cell, 2121 &Node, 2122 &HiveToRelease, 2123 &CellToRelease); 2124 if (!Node) 2125 { 2126 /* Fail */ 2127 Status = STATUS_INSUFFICIENT_RESOURCES; 2128 break; 2129 } 2130 } 2131 2132 /* Create a KCB for this key */ 2133 Kcb = CmpCreateKeyControlBlock(Hive, 2134 Cell, 2135 Node, 2136 ParentKcb, 2137 CMP_LOCK_HASHES_FOR_KCB, 2138 &NextName); 2139 if (!Kcb) 2140 { 2141 /* Fail */ 2142 Status = STATUS_INSUFFICIENT_RESOURCES; 2143 break; 2144 } 2145 2146 /* Dereference the parent and set the new one */ 2147 CmpDereferenceKeyControlBlockWithLock(ParentKcb, FALSE); 2148 ParentKcb = Kcb; 2149 } 2150 else 2151 { 2152 /* Check if this was the last key for a create */ 2153 if (Last && ParseContext) 2154 { 2155 /* Check if we're doing a link node */ 2156 if (ParseContext->CreateLink) 2157 { 2158 /* The only thing we should see */ 2159 Status = CmpCreateLinkNode(Hive, 2160 Cell, 2161 AccessState, 2162 NextName, 2163 AccessMode, 2164 Attributes, 2165 ParseContext, 2166 ParentKcb, 2167 LockedKcbs, 2168 Object); 2169 } 2170 else if (Hive == &CmiVolatileHive->Hive && CmpNoVolatileCreates) 2171 { 2172 /* Creating keys in the master hive is not allowed */ 2173 Status = STATUS_INVALID_PARAMETER; 2174 } 2175 else 2176 { 2177 /* Do the create */ 2178 Status = CmpDoCreate(Hive, 2179 Cell, 2180 AccessState, 2181 &NextName, 2182 AccessMode, 2183 ParseContext, 2184 ParentKcb, 2185 Object); 2186 } 2187 2188 /* Check for reparse (in this case, someone beat us) */ 2189 if (Status == STATUS_REPARSE) break; 2190 2191 /* Update disposition */ 2192 ParseContext->Disposition = REG_CREATED_NEW_KEY; 2193 break; 2194 } 2195 else 2196 { 2197 /* Key not found */ 2198 Status = STATUS_OBJECT_NAME_NOT_FOUND; 2199 break; 2200 } 2201 } 2202 } 2203 else 2204 { 2205 /* Save the next name */ 2206 Current.Buffer = NextName.Buffer; 2207 2208 /* Validate the current name string length */ 2209 if (Current.Length + NextName.Length > MAXUSHORT) 2210 { 2211 /* too long */ 2212 Status = STATUS_NAME_TOO_LONG; 2213 break; 2214 } 2215 Current.Length += NextName.Length; 2216 2217 /* Validate the current name string maximum length */ 2218 if (Current.MaximumLength + NextName.MaximumLength > MAXUSHORT) 2219 { 2220 /* too long */ 2221 Status = STATUS_NAME_TOO_LONG; 2222 break; 2223 } 2224 Current.MaximumLength += NextName.MaximumLength; 2225 2226 /* CmpGetSymbolicLink doesn't want a lock */ 2227 CmpUnLockKcbArray(LockedKcbs); 2228 LockedKcbs = NULL; 2229 2230 /* Parse the symlink */ 2231 if (CmpGetSymbolicLink(Hive, 2232 CompleteName, 2233 Kcb, 2234 &Current)) 2235 { 2236 /* Symlink parse succeeded */ 2237 Status = STATUS_REPARSE; 2238 } 2239 else 2240 { 2241 /* Couldn't find symlink */ 2242 Status = STATUS_OBJECT_NAME_NOT_FOUND; 2243 } 2244 2245 /* We're done */ 2246 break; 2247 } 2248 } 2249 else if (Result && Last) 2250 { 2251 /* Opening the root. Is this an exit node? */ 2252 if (Node->Flags & KEY_HIVE_EXIT) 2253 { 2254 /* Handle it */ 2255 CmpHandleExitNode(&Hive, 2256 &Cell, 2257 &Node, 2258 &HiveToRelease, 2259 &CellToRelease); 2260 if (!Node) 2261 { 2262 /* Fail */ 2263 Status = STATUS_INSUFFICIENT_RESOURCES; 2264 break; 2265 } 2266 } 2267 2268 /* Do the open */ 2269 Status = CmpDoOpen(Hive, 2270 Cell, 2271 Node, 2272 AccessState, 2273 AccessMode, 2274 Attributes, 2275 ParseContext, 2276 CMP_OPEN_KCB_NO_CREATE, 2277 &Kcb, 2278 LockedKcbs, 2279 &NextName, 2280 Object); 2281 if (Status == STATUS_REPARSE) 2282 { 2283 /* Nothing to do */ 2284 } 2285 2286 /* We're done */ 2287 break; 2288 } 2289 else 2290 { 2291 /* Bogus */ 2292 Status = STATUS_INVALID_PARAMETER; 2293 break; 2294 } 2295 } 2296 2297 Quickie: 2298 /* Unlock all the KCBs */ 2299 if (LockedKcbs != NULL) 2300 { 2301 CmpUnLockKcbArray(LockedKcbs); 2302 } 2303 2304 /* Dereference the parent if it exists */ 2305 if (ParentKcb) 2306 CmpDereferenceKeyControlBlock(ParentKcb); 2307 2308 /* Unlock the registry */ 2309 CmpUnlockRegistry(); 2310 return Status; 2311 } 2312