1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/config/cmkcbncb.c 5 * PURPOSE: Routines for handling KCBs, NCBs, as well as key hashes. 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 ULONG CmpHashTableSize = 2048; 18 PCM_KEY_HASH_TABLE_ENTRY CmpCacheTable; 19 PCM_NAME_HASH_TABLE_ENTRY CmpNameCacheTable; 20 21 /* FUNCTIONS *****************************************************************/ 22 23 CODE_SEG("INIT") 24 VOID 25 NTAPI 26 CmpInitializeCache(VOID) 27 { 28 ULONG Length, i; 29 30 /* Calculate length for the table */ 31 Length = CmpHashTableSize * sizeof(CM_KEY_HASH_TABLE_ENTRY); 32 33 /* Allocate it */ 34 CmpCacheTable = CmpAllocate(Length, TRUE, TAG_CM); 35 if (!CmpCacheTable) 36 { 37 /* Take the system down */ 38 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 1, 0, 0); 39 } 40 41 /* Zero out the table */ 42 RtlZeroMemory(CmpCacheTable, Length); 43 44 /* Initialize the locks */ 45 for (i = 0;i < CmpHashTableSize; i++) 46 { 47 /* Setup the pushlock */ 48 ExInitializePushLock(&CmpCacheTable[i].Lock); 49 } 50 51 /* Calculate length for the name cache */ 52 Length = CmpHashTableSize * sizeof(CM_NAME_HASH_TABLE_ENTRY); 53 54 /* Now allocate the name cache table */ 55 CmpNameCacheTable = CmpAllocate(Length, TRUE, TAG_CM); 56 if (!CmpNameCacheTable) 57 { 58 /* Take the system down */ 59 KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 3, 0, 0); 60 } 61 62 /* Zero out the table */ 63 RtlZeroMemory(CmpNameCacheTable, Length); 64 65 /* Initialize the locks */ 66 for (i = 0;i < CmpHashTableSize; i++) 67 { 68 /* Setup the pushlock */ 69 ExInitializePushLock(&CmpNameCacheTable[i].Lock); 70 } 71 72 /* Setup the delayed close table */ 73 CmpInitializeDelayedCloseTable(); 74 } 75 76 VOID 77 NTAPI 78 CmpRemoveKeyHash(IN PCM_KEY_HASH KeyHash) 79 { 80 PCM_KEY_HASH *Prev; 81 PCM_KEY_HASH Current; 82 ASSERT_VALID_HASH(KeyHash); 83 84 /* Lookup all the keys in this index entry */ 85 Prev = &GET_HASH_ENTRY(CmpCacheTable, KeyHash->ConvKey)->Entry; 86 while (TRUE) 87 { 88 /* Save the current one and make sure it's valid */ 89 Current = *Prev; 90 ASSERT(Current != NULL); 91 ASSERT_VALID_HASH(Current); 92 93 /* Check if it matches */ 94 if (Current == KeyHash) 95 { 96 /* Then write the previous one */ 97 *Prev = Current->NextHash; 98 if (*Prev) ASSERT_VALID_HASH(*Prev); 99 break; 100 } 101 102 /* Otherwise, keep going */ 103 Prev = &Current->NextHash; 104 } 105 } 106 107 PCM_KEY_CONTROL_BLOCK 108 NTAPI 109 CmpInsertKeyHash(IN PCM_KEY_HASH KeyHash, 110 IN BOOLEAN IsFake) 111 { 112 ULONG i; 113 PCM_KEY_HASH Entry; 114 ASSERT_VALID_HASH(KeyHash); 115 116 /* Get the hash index */ 117 i = GET_HASH_INDEX(KeyHash->ConvKey); 118 119 /* If this is a fake key, increase the key cell to use the parent data */ 120 if (IsFake) KeyHash->KeyCell++; 121 122 /* Loop the hash table */ 123 Entry = CmpCacheTable[i].Entry; 124 while (Entry) 125 { 126 /* Check if this matches */ 127 ASSERT_VALID_HASH(Entry); 128 if ((KeyHash->ConvKey == Entry->ConvKey) && 129 (KeyHash->KeyCell == Entry->KeyCell) && 130 (KeyHash->KeyHive == Entry->KeyHive)) 131 { 132 /* Return it */ 133 return CONTAINING_RECORD(Entry, CM_KEY_CONTROL_BLOCK, KeyHash); 134 } 135 136 /* Keep looping */ 137 Entry = Entry->NextHash; 138 } 139 140 /* No entry found, add this one and return NULL since none existed */ 141 KeyHash->NextHash = CmpCacheTable[i].Entry; 142 CmpCacheTable[i].Entry = KeyHash; 143 return NULL; 144 } 145 146 PCM_NAME_CONTROL_BLOCK 147 NTAPI 148 CmpGetNameControlBlock(IN PUNICODE_STRING NodeName) 149 { 150 PCM_NAME_CONTROL_BLOCK Ncb = NULL; 151 ULONG ConvKey = 0; 152 PWCHAR p, pp; 153 ULONG i; 154 BOOLEAN IsCompressed = TRUE, Found = FALSE; 155 PCM_NAME_HASH HashEntry; 156 ULONG NcbSize; 157 USHORT Length; 158 159 /* Loop the name */ 160 p = NodeName->Buffer; 161 for (i = 0; i < NodeName->Length; i += sizeof(WCHAR)) 162 { 163 /* Make sure it's not a slash */ 164 if (*p != OBJ_NAME_PATH_SEPARATOR) 165 { 166 /* Add it to the hash */ 167 ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p); 168 } 169 170 /* Next character */ 171 p++; 172 } 173 174 /* Set assumed lengh and loop to check */ 175 Length = NodeName->Length / sizeof(WCHAR); 176 for (i = 0; i < (NodeName->Length / sizeof(WCHAR)); i++) 177 { 178 /* Check if this is a 16-bit character */ 179 if (NodeName->Buffer[i] > (UCHAR)-1) 180 { 181 /* This is the actual size, and we know we're not compressed */ 182 Length = NodeName->Length; 183 IsCompressed = FALSE; 184 break; 185 } 186 } 187 188 /* Lock the NCB entry */ 189 CmpAcquireNcbLockExclusiveByKey(ConvKey); 190 191 /* Get the hash entry */ 192 HashEntry = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey)->Entry; 193 while (HashEntry) 194 { 195 /* Get the current NCB */ 196 Ncb = CONTAINING_RECORD(HashEntry, CM_NAME_CONTROL_BLOCK, NameHash); 197 198 /* Check if the hash matches */ 199 if ((ConvKey == HashEntry->ConvKey) && (Length == Ncb->NameLength)) 200 { 201 /* Assume success */ 202 Found = TRUE; 203 204 /* If the NCB is compressed, do a compressed name compare */ 205 if (Ncb->Compressed) 206 { 207 /* Compare names */ 208 if (CmpCompareCompressedName(NodeName, Ncb->Name, Length)) 209 { 210 /* We failed */ 211 Found = FALSE; 212 } 213 } 214 else 215 { 216 /* Do a manual compare */ 217 p = NodeName->Buffer; 218 pp = Ncb->Name; 219 for (i = 0; i < Ncb->NameLength; i += sizeof(WCHAR)) 220 { 221 /* Compare the character */ 222 if (RtlUpcaseUnicodeChar(*p) != RtlUpcaseUnicodeChar(*pp)) 223 { 224 /* Failed */ 225 Found = FALSE; 226 break; 227 } 228 229 /* Next chars */ 230 p++; 231 pp++; 232 } 233 } 234 235 /* Check if we found a name */ 236 if (Found) 237 { 238 /* Reference it */ 239 ASSERT(Ncb->RefCount != 0xFFFF); 240 Ncb->RefCount++; 241 break; 242 } 243 } 244 245 /* Go to the next hash */ 246 HashEntry = HashEntry->NextHash; 247 } 248 249 /* Check if we didn't find it */ 250 if (!Found) 251 { 252 /* Allocate one */ 253 NcbSize = FIELD_OFFSET(CM_NAME_CONTROL_BLOCK, Name) + Length; 254 Ncb = CmpAllocate(NcbSize, TRUE, TAG_CM); 255 if (!Ncb) 256 { 257 /* Release the lock and fail */ 258 CmpReleaseNcbLockByKey(ConvKey); 259 return NULL; 260 } 261 262 /* Clear it out */ 263 RtlZeroMemory(Ncb, NcbSize); 264 265 /* Check if the name was compressed */ 266 if (IsCompressed) 267 { 268 /* Copy the compressed name */ 269 for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++) 270 { 271 /* Copy Unicode to ANSI */ 272 ((PCHAR)Ncb->Name)[i] = (CHAR)RtlUpcaseUnicodeChar(NodeName->Buffer[i]); 273 } 274 } 275 else 276 { 277 /* Copy the name directly */ 278 for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++) 279 { 280 /* Copy each unicode character */ 281 Ncb->Name[i] = RtlUpcaseUnicodeChar(NodeName->Buffer[i]); 282 } 283 } 284 285 /* Setup the rest of the NCB */ 286 Ncb->Compressed = IsCompressed; 287 Ncb->ConvKey = ConvKey; 288 Ncb->RefCount++; 289 Ncb->NameLength = Length; 290 291 /* Insert the name in the hash table */ 292 HashEntry = &Ncb->NameHash; 293 HashEntry->NextHash = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey)->Entry; 294 GET_HASH_ENTRY(CmpNameCacheTable, ConvKey)->Entry = HashEntry; 295 } 296 297 /* Release NCB lock */ 298 CmpReleaseNcbLockByKey(ConvKey); 299 300 /* Return the NCB found */ 301 return Ncb; 302 } 303 304 VOID 305 NTAPI 306 CmpRemoveKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb) 307 { 308 /* Make sure we have the exclusive lock */ 309 CMP_ASSERT_KCB_LOCK(Kcb); 310 311 /* Remove the key hash */ 312 CmpRemoveKeyHash(&Kcb->KeyHash); 313 } 314 315 VOID 316 NTAPI 317 CmpDereferenceNameControlBlockWithLock(IN PCM_NAME_CONTROL_BLOCK Ncb) 318 { 319 PCM_NAME_HASH Current, *Next; 320 ULONG ConvKey = Ncb->ConvKey; 321 322 /* Lock the NCB */ 323 CmpAcquireNcbLockExclusiveByKey(ConvKey); 324 325 /* Decrease the reference count */ 326 ASSERT(Ncb->RefCount >= 1); 327 if (!(--Ncb->RefCount)) 328 { 329 /* Find the NCB in the table */ 330 Next = &GET_HASH_ENTRY(CmpNameCacheTable, Ncb->ConvKey)->Entry; 331 while (TRUE) 332 { 333 /* Check the current entry */ 334 Current = *Next; 335 ASSERT(Current != NULL); 336 if (Current == &Ncb->NameHash) 337 { 338 /* Unlink it */ 339 *Next = Current->NextHash; 340 break; 341 } 342 343 /* Get to the next one */ 344 Next = &Current->NextHash; 345 } 346 347 /* Found it, now free it */ 348 CmpFree(Ncb, 0); 349 } 350 351 /* Release the lock */ 352 CmpReleaseNcbLockByKey(ConvKey); 353 } 354 355 BOOLEAN 356 NTAPI 357 CmpReferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb) 358 { 359 CMTRACE(CM_REFERENCE_DEBUG, 360 "%s - Referencing KCB: %p\n", __FUNCTION__, Kcb); 361 362 /* Check if this is the KCB's first reference */ 363 if (Kcb->RefCount == 0) 364 { 365 /* Check if the KCB is locked in shared mode */ 366 if (!CmpIsKcbLockedExclusive(Kcb)) 367 { 368 /* Convert it to exclusive */ 369 if (!CmpTryToConvertKcbSharedToExclusive(Kcb)) 370 { 371 /* Set the delayed close index so that we can be ignored */ 372 Kcb->DelayedCloseIndex = 1; 373 374 /* Increase the reference count while we release the lock */ 375 InterlockedIncrement((PLONG)&Kcb->RefCount); 376 377 /* Go from shared to exclusive */ 378 CmpConvertKcbSharedToExclusive(Kcb); 379 380 /* Decrement the reference count; the lock is now held again */ 381 InterlockedDecrement((PLONG)&Kcb->RefCount); 382 383 /* Check if we still control the index */ 384 if (Kcb->DelayedCloseIndex == 1) 385 { 386 /* Reset it */ 387 Kcb->DelayedCloseIndex = 0; 388 } 389 else 390 { 391 /* Sanity check */ 392 ASSERT((Kcb->DelayedCloseIndex == CmpDelayedCloseSize) || 393 (Kcb->DelayedCloseIndex == 0)); 394 } 395 } 396 } 397 } 398 399 /* Increase the reference count */ 400 if ((InterlockedIncrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0) 401 { 402 /* We've overflown to 64K references, bail out */ 403 InterlockedDecrement((PLONG)&Kcb->RefCount); 404 return FALSE; 405 } 406 407 /* Check if this was the last close index */ 408 if (!Kcb->DelayedCloseIndex) 409 { 410 /* Check if the KCB is locked in shared mode */ 411 if (!CmpIsKcbLockedExclusive(Kcb)) 412 { 413 /* Convert it to exclusive */ 414 if (!CmpTryToConvertKcbSharedToExclusive(Kcb)) 415 { 416 /* Go from shared to exclusive */ 417 CmpConvertKcbSharedToExclusive(Kcb); 418 } 419 } 420 421 /* If we're still the last entry, remove us */ 422 if (!Kcb->DelayedCloseIndex) CmpRemoveFromDelayedClose(Kcb); 423 } 424 425 /* Return success */ 426 return TRUE; 427 } 428 429 VOID 430 NTAPI 431 CmpCleanUpKcbValueCache(IN PCM_KEY_CONTROL_BLOCK Kcb) 432 { 433 PULONG_PTR CachedList; 434 ULONG i; 435 436 /* Make sure we have the exclusive lock */ 437 CMP_ASSERT_KCB_LOCK(Kcb); 438 439 /* Check if the value list is cached */ 440 if (CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)) 441 { 442 /* Get the cache list */ 443 CachedList = (PULONG_PTR)CMP_GET_CACHED_DATA(Kcb->ValueCache.ValueList); 444 for (i = 0; i < Kcb->ValueCache.Count; i++) 445 { 446 /* Check if this cell is cached */ 447 if (CMP_IS_CELL_CACHED(CachedList[i])) 448 { 449 /* Free it */ 450 CmpFree((PVOID)CMP_GET_CACHED_CELL(CachedList[i]), 0); 451 } 452 } 453 454 /* Now free the list */ 455 CmpFree((PVOID)CMP_GET_CACHED_CELL(Kcb->ValueCache.ValueList), 0); 456 Kcb->ValueCache.ValueList = HCELL_NIL; 457 } 458 else if (Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) 459 { 460 /* This is a sym link, check if there's only one reference left */ 461 if ((Kcb->ValueCache.RealKcb->RefCount == 1) && 462 !(Kcb->ValueCache.RealKcb->Delete)) 463 { 464 /* Disable delay close for the KCB */ 465 Kcb->ValueCache.RealKcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE; 466 } 467 468 /* Dereference the KCB */ 469 CmpDelayDerefKeyControlBlock(Kcb->ValueCache.RealKcb); 470 Kcb->ExtFlags &= ~CM_KCB_SYM_LINK_FOUND; 471 } 472 } 473 474 VOID 475 NTAPI 476 CmpCleanUpKcbCacheWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb, 477 IN BOOLEAN LockHeldExclusively) 478 { 479 PCM_KEY_CONTROL_BLOCK Parent; 480 PAGED_CODE(); 481 482 /* Sanity checks */ 483 CMP_ASSERT_KCB_LOCK(Kcb); 484 ASSERT(Kcb->RefCount == 0); 485 486 /* Cleanup the value cache */ 487 CmpCleanUpKcbValueCache(Kcb); 488 489 /* Dereference the NCB */ 490 CmpDereferenceNameControlBlockWithLock(Kcb->NameBlock); 491 492 /* Check if we have an index hint block and free it */ 493 if (Kcb->ExtFlags & CM_KCB_SUBKEY_HINT) CmpFree(Kcb->IndexHint, 0); 494 495 /* Check if we were already deleted */ 496 Parent = Kcb->ParentKcb; 497 if (!Kcb->Delete) CmpRemoveKeyControlBlock(Kcb); 498 499 /* Set invalid KCB signature */ 500 Kcb->Signature = CM_KCB_INVALID_SIGNATURE; 501 502 /* Free the KCB as well */ 503 CmpFreeKeyControlBlock(Kcb); 504 505 /* Check if we have a parent */ 506 if (Parent) 507 { 508 /* Dereference the parent */ 509 LockHeldExclusively ? 510 CmpDereferenceKeyControlBlockWithLock(Parent,LockHeldExclusively) : 511 CmpDelayDerefKeyControlBlock(Parent); 512 } 513 } 514 515 VOID 516 NTAPI 517 CmpCleanUpSubKeyInfo(IN PCM_KEY_CONTROL_BLOCK Kcb) 518 { 519 PCM_KEY_NODE KeyNode; 520 521 /* Make sure we have the exclusive lock */ 522 CMP_ASSERT_KCB_LOCK(Kcb); 523 524 /* Check if there's any cached subkey */ 525 if (Kcb->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT)) 526 { 527 /* Check if there's a hint */ 528 if (Kcb->ExtFlags & (CM_KCB_SUBKEY_HINT)) 529 { 530 /* Kill it */ 531 CmpFree(Kcb->IndexHint, 0); 532 } 533 534 /* Remove subkey flags */ 535 Kcb->ExtFlags &= ~(CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT); 536 } 537 538 /* Check if there's no linked cell */ 539 if (Kcb->KeyCell == HCELL_NIL) 540 { 541 /* Make sure it's a delete */ 542 ASSERT(Kcb->Delete); 543 KeyNode = NULL; 544 } 545 else 546 { 547 /* Get the key node */ 548 KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell); 549 } 550 551 /* Check if we got the node */ 552 if (!KeyNode) 553 { 554 /* We didn't, mark the cached data invalid */ 555 Kcb->ExtFlags |= CM_KCB_INVALID_CACHED_INFO; 556 } 557 else 558 { 559 /* We have a keynode, update subkey counts */ 560 Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO; 561 Kcb->SubKeyCount = KeyNode->SubKeyCounts[Stable] + 562 KeyNode->SubKeyCounts[Volatile]; 563 564 /* Release the cell */ 565 HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell); 566 } 567 } 568 569 VOID 570 NTAPI 571 CmpDereferenceKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb) 572 { 573 LONG OldRefCount, NewRefCount; 574 ULONG ConvKey; 575 CMTRACE(CM_REFERENCE_DEBUG, 576 "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb); 577 578 /* Get the ref count and update it */ 579 OldRefCount = *(PLONG)&Kcb->RefCount; 580 NewRefCount = OldRefCount - 1; 581 582 /* Check if we still have references */ 583 if ((NewRefCount & 0xFFFF) > 0) 584 { 585 /* Do the dereference */ 586 if (InterlockedCompareExchange((PLONG)&Kcb->RefCount, 587 NewRefCount, 588 OldRefCount) == OldRefCount) 589 { 590 /* We'de done */ 591 return; 592 } 593 } 594 595 /* Save the key */ 596 ConvKey = Kcb->ConvKey; 597 598 /* Do the dereference inside the lock */ 599 CmpAcquireKcbLockExclusive(Kcb); 600 CmpDereferenceKeyControlBlockWithLock(Kcb, FALSE); 601 CmpReleaseKcbLockByKey(ConvKey); 602 } 603 604 VOID 605 NTAPI 606 CmpDereferenceKeyControlBlockWithLock(IN PCM_KEY_CONTROL_BLOCK Kcb, 607 IN BOOLEAN LockHeldExclusively) 608 { 609 CMTRACE(CM_REFERENCE_DEBUG, 610 "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb); 611 612 /* Sanity check */ 613 ASSERT_KCB_VALID(Kcb); 614 615 /* Check if this is the last reference */ 616 if ((InterlockedDecrement((PLONG)&Kcb->RefCount) & 0xFFFF) == 0) 617 { 618 /* Make sure we have the exclusive lock */ 619 CMP_ASSERT_KCB_LOCK(Kcb); 620 621 /* Check if we should do a direct delete */ 622 if (((CmpHoldLazyFlush) && 623 !(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND) && 624 !(Kcb->Flags & KEY_SYM_LINK)) || 625 (Kcb->ExtFlags & CM_KCB_NO_DELAY_CLOSE) || 626 (Kcb->Delete)) 627 { 628 /* Clean up the KCB*/ 629 CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively); 630 } 631 else 632 { 633 /* Otherwise, use delayed close */ 634 CmpAddToDelayedClose(Kcb, LockHeldExclusively); 635 } 636 } 637 } 638 639 VOID 640 NTAPI 641 InitializeKCBKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb) 642 { 643 /* Initialize the list */ 644 InitializeListHead(&Kcb->KeyBodyListHead); 645 646 /* Clear the bodies */ 647 Kcb->KeyBodyArray[0] = 648 Kcb->KeyBodyArray[1] = 649 Kcb->KeyBodyArray[2] = 650 Kcb->KeyBodyArray[3] = NULL; 651 } 652 653 PCM_KEY_CONTROL_BLOCK 654 NTAPI 655 CmpCreateKeyControlBlock(IN PHHIVE Hive, 656 IN HCELL_INDEX Index, 657 IN PCM_KEY_NODE Node, 658 IN PCM_KEY_CONTROL_BLOCK Parent, 659 IN ULONG Flags, 660 IN PUNICODE_STRING KeyName) 661 { 662 PCM_KEY_CONTROL_BLOCK Kcb, FoundKcb = NULL; 663 UNICODE_STRING NodeName; 664 ULONG ConvKey = 0, i; 665 BOOLEAN IsFake, HashLock; 666 PWCHAR p; 667 668 /* Make sure we own this hive in case it's being unloaded */ 669 if ((Hive->HiveFlags & HIVE_IS_UNLOADING) && 670 (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread())) 671 { 672 /* Fail */ 673 return NULL; 674 } 675 676 /* Check if this is a fake KCB */ 677 IsFake = Flags & CMP_CREATE_FAKE_KCB ? TRUE : FALSE; 678 679 /* If we have a parent, use its ConvKey */ 680 if (Parent) ConvKey = Parent->ConvKey; 681 682 /* Make a copy of the name */ 683 NodeName = *KeyName; 684 685 /* Remove leading slash */ 686 while ((NodeName.Length) && (*NodeName.Buffer == OBJ_NAME_PATH_SEPARATOR)) 687 { 688 /* Move the buffer by one */ 689 NodeName.Buffer++; 690 NodeName.Length -= sizeof(WCHAR); 691 } 692 693 /* Make sure we didn't get just a slash or something */ 694 ASSERT(NodeName.Length > 0); 695 696 /* Now setup the hash */ 697 p = NodeName.Buffer; 698 for (i = 0; i < NodeName.Length; i += sizeof(WCHAR)) 699 { 700 /* Make sure it's a valid character */ 701 if (*p != OBJ_NAME_PATH_SEPARATOR) 702 { 703 /* Add this key to the hash */ 704 ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p); 705 } 706 707 /* Move on */ 708 p++; 709 } 710 711 /* Allocate the KCB */ 712 Kcb = CmpAllocateKeyControlBlock(); 713 if (!Kcb) return NULL; 714 715 /* Initailize the key list */ 716 InitializeKCBKeyBodyList(Kcb); 717 718 /* Set it up */ 719 Kcb->Signature = CM_KCB_SIGNATURE; 720 Kcb->Delete = FALSE; 721 Kcb->RefCount = 1; 722 Kcb->KeyHive = Hive; 723 Kcb->KeyCell = Index; 724 Kcb->ConvKey = ConvKey; 725 Kcb->DelayedCloseIndex = CmpDelayedCloseSize; 726 Kcb->InDelayClose = 0; 727 ASSERT_KCB_VALID(Kcb); 728 729 /* Check if we have two hash entires */ 730 HashLock = Flags & CMP_LOCK_HASHES_FOR_KCB ? TRUE : FALSE; 731 if (!HashLock) 732 { 733 /* It's not locked, do we have a parent? */ 734 if (Parent) 735 { 736 /* Lock the parent KCB and ourselves */ 737 CmpAcquireTwoKcbLocksExclusiveByKey(ConvKey, Parent->ConvKey); 738 } 739 else 740 { 741 /* Lock only ourselves */ 742 CmpAcquireKcbLockExclusive(Kcb); 743 } 744 } 745 746 /* Check if we already have a KCB */ 747 FoundKcb = CmpInsertKeyHash(&Kcb->KeyHash, IsFake); 748 if (FoundKcb) 749 { 750 /* Sanity check */ 751 ASSERT(!FoundKcb->Delete); 752 Kcb->Signature = CM_KCB_INVALID_SIGNATURE; 753 754 /* Free the one we allocated and reference this one */ 755 CmpFreeKeyControlBlock(Kcb); 756 ASSERT_KCB_VALID(FoundKcb); 757 Kcb = FoundKcb; 758 if (!CmpReferenceKeyControlBlock(Kcb)) 759 { 760 /* We got too many handles */ 761 ASSERT(Kcb->RefCount + 1 != 0); 762 Kcb = NULL; 763 } 764 else 765 { 766 /* Check if we're not creating a fake one, but it used to be fake */ 767 if ((Kcb->ExtFlags & CM_KCB_KEY_NON_EXIST) && !(IsFake)) 768 { 769 /* Set the hive and cell */ 770 Kcb->KeyHive = Hive; 771 Kcb->KeyCell = Index; 772 773 /* This means that our current information is invalid */ 774 Kcb->ExtFlags = CM_KCB_INVALID_CACHED_INFO; 775 } 776 777 /* Check if we didn't have any valid data */ 778 if (!(Kcb->ExtFlags & (CM_KCB_NO_SUBKEY | 779 CM_KCB_SUBKEY_ONE | 780 CM_KCB_SUBKEY_HINT))) 781 { 782 /* Calculate the index hint */ 783 Kcb->SubKeyCount = Node->SubKeyCounts[Stable] + 784 Node->SubKeyCounts[Volatile]; 785 786 /* Cached information is now valid */ 787 Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO; 788 } 789 790 /* Setup the other data */ 791 Kcb->KcbLastWriteTime = Node->LastWriteTime; 792 Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen; 793 Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen; 794 Kcb->KcbMaxValueDataLen = Node->MaxValueDataLen; 795 } 796 } 797 else 798 { 799 /* No KCB, do we have a parent? */ 800 if (Parent) 801 { 802 /* Reference the parent */ 803 if (((Parent->TotalLevels + 1) < 512) && 804 (CmpReferenceKeyControlBlock(Parent))) 805 { 806 /* Link it */ 807 Kcb->ParentKcb = Parent; 808 Kcb->TotalLevels = Parent->TotalLevels + 1; 809 } 810 else 811 { 812 /* Remove the KCB and free it */ 813 CmpRemoveKeyControlBlock(Kcb); 814 Kcb->Signature = CM_KCB_INVALID_SIGNATURE; 815 CmpFreeKeyControlBlock(Kcb); 816 Kcb = NULL; 817 } 818 } 819 else 820 { 821 /* No parent, this is the root node */ 822 Kcb->ParentKcb = NULL; 823 Kcb->TotalLevels = 1; 824 } 825 826 /* Check if we have a KCB */ 827 if (Kcb) 828 { 829 /* Get the NCB */ 830 Kcb->NameBlock = CmpGetNameControlBlock(&NodeName); 831 if (Kcb->NameBlock) 832 { 833 /* Fill it out */ 834 Kcb->ValueCache.Count = Node->ValueList.Count; 835 Kcb->ValueCache.ValueList = Node->ValueList.List; 836 Kcb->Flags = Node->Flags; 837 Kcb->ExtFlags = 0; 838 Kcb->DelayedCloseIndex = CmpDelayedCloseSize; 839 840 /* Remember if this is a fake key */ 841 if (IsFake) Kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST; 842 843 /* Setup the other data */ 844 Kcb->SubKeyCount = Node->SubKeyCounts[Stable] + 845 Node->SubKeyCounts[Volatile]; 846 Kcb->KcbLastWriteTime = Node->LastWriteTime; 847 Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen; 848 Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen; 849 Kcb->KcbMaxValueDataLen = (USHORT)Node->MaxValueDataLen; 850 } 851 else 852 { 853 /* Dereference the KCB */ 854 CmpDereferenceKeyControlBlockWithLock(Parent, FALSE); 855 856 /* Remove the KCB and free it */ 857 CmpRemoveKeyControlBlock(Kcb); 858 Kcb->Signature = CM_KCB_INVALID_SIGNATURE; 859 CmpFreeKeyControlBlock(Kcb); 860 Kcb = NULL; 861 } 862 } 863 } 864 865 /* Check if this is a KCB inside a frozen hive */ 866 if ((Kcb) && (((PCMHIVE)Hive)->Frozen) && (!(Kcb->Flags & KEY_SYM_LINK))) 867 { 868 /* Don't add these to the delay close */ 869 Kcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE; 870 } 871 872 /* Sanity check */ 873 ASSERT((!Kcb) || (Kcb->Delete == FALSE)); 874 875 /* Check if we had locked the hashes */ 876 if (!HashLock) 877 { 878 /* We locked them manually, do we have a parent? */ 879 if (Parent) 880 { 881 /* Unlock the parent KCB and ourselves */ 882 CmpReleaseTwoKcbLockByKey(ConvKey, Parent->ConvKey); 883 } 884 else 885 { 886 /* Unlock only ourselves */ 887 CmpReleaseKcbLockByKey(ConvKey); 888 } 889 } 890 891 /* Return the KCB */ 892 return Kcb; 893 } 894 895 PUNICODE_STRING 896 NTAPI 897 CmpConstructName(IN PCM_KEY_CONTROL_BLOCK Kcb) 898 { 899 PUNICODE_STRING KeyName; 900 ULONG i; 901 USHORT NameLength; 902 PCM_KEY_CONTROL_BLOCK MyKcb; 903 PCM_KEY_NODE KeyNode; 904 BOOLEAN DeletedKey = FALSE; 905 PWCHAR TargetBuffer, CurrentNameW; 906 PUCHAR CurrentName; 907 908 /* Calculate how much size our key name is going to occupy */ 909 NameLength = 0; 910 MyKcb = Kcb; 911 912 while (MyKcb) 913 { 914 /* Add length of the name */ 915 if (!MyKcb->NameBlock->Compressed) 916 { 917 NameLength += MyKcb->NameBlock->NameLength; 918 } 919 else 920 { 921 NameLength += CmpCompressedNameSize(MyKcb->NameBlock->Name, 922 MyKcb->NameBlock->NameLength); 923 } 924 925 /* Sum up the separator too */ 926 NameLength += sizeof(WCHAR); 927 928 /* Go to the parent KCB */ 929 MyKcb = MyKcb->ParentKcb; 930 } 931 932 /* Allocate the unicode string now */ 933 KeyName = CmpAllocate(NameLength + sizeof(UNICODE_STRING), 934 TRUE, 935 TAG_CM); 936 937 if (!KeyName) return NULL; 938 939 /* Set it up */ 940 KeyName->Buffer = (PWSTR)(KeyName + 1); 941 KeyName->Length = NameLength; 942 KeyName->MaximumLength = NameLength; 943 944 /* Loop the keys again, now adding names */ 945 NameLength = 0; 946 MyKcb = Kcb; 947 948 while (MyKcb) 949 { 950 /* Sanity checks for deleted and fake keys */ 951 if ((!MyKcb->KeyCell && !MyKcb->Delete) || 952 !MyKcb->KeyHive || 953 MyKcb->ExtFlags & CM_KCB_KEY_NON_EXIST) 954 { 955 /* Failure */ 956 CmpFree(KeyName, 0); 957 return NULL; 958 } 959 960 /* Try to get the name from the keynode, 961 if the key is not deleted */ 962 if (!DeletedKey && !MyKcb->Delete) 963 { 964 KeyNode = HvGetCell(MyKcb->KeyHive, MyKcb->KeyCell); 965 966 if (!KeyNode) 967 { 968 /* Failure */ 969 CmpFree(KeyName, 0); 970 return NULL; 971 } 972 } 973 else 974 { 975 /* The key was deleted */ 976 KeyNode = NULL; 977 DeletedKey = TRUE; 978 } 979 980 /* Get the pointer to the beginning of the current key name */ 981 NameLength += (MyKcb->NameBlock->NameLength + 1) * sizeof(WCHAR); 982 TargetBuffer = &KeyName->Buffer[(KeyName->Length - NameLength) / sizeof(WCHAR)]; 983 984 /* Add a separator */ 985 TargetBuffer[0] = OBJ_NAME_PATH_SEPARATOR; 986 987 /* Add the name, but remember to go from the end to the beginning */ 988 if (!MyKcb->NameBlock->Compressed) 989 { 990 /* Get the pointer to the name (from the keynode, if possible) */ 991 if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) || 992 !KeyNode) 993 { 994 CurrentNameW = MyKcb->NameBlock->Name; 995 } 996 else 997 { 998 CurrentNameW = KeyNode->Name; 999 } 1000 1001 /* Copy the name */ 1002 for (i=0; i < MyKcb->NameBlock->NameLength; i++) 1003 { 1004 TargetBuffer[i+1] = *CurrentNameW; 1005 CurrentNameW++; 1006 } 1007 } 1008 else 1009 { 1010 /* Get the pointer to the name (from the keynode, if possible) */ 1011 if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) || 1012 !KeyNode) 1013 { 1014 CurrentName = (PUCHAR)MyKcb->NameBlock->Name; 1015 } 1016 else 1017 { 1018 CurrentName = (PUCHAR)KeyNode->Name; 1019 } 1020 1021 /* Copy the name */ 1022 for (i=0; i < MyKcb->NameBlock->NameLength; i++) 1023 { 1024 TargetBuffer[i+1] = (WCHAR)*CurrentName; 1025 CurrentName++; 1026 } 1027 } 1028 1029 /* Release the cell, if needed */ 1030 if (KeyNode) HvReleaseCell(MyKcb->KeyHive, MyKcb->KeyCell); 1031 1032 /* Go to the parent KCB */ 1033 MyKcb = MyKcb->ParentKcb; 1034 } 1035 1036 /* Return resulting buffer (both UNICODE_STRING and 1037 its buffer following it) */ 1038 return KeyName; 1039 } 1040 1041 VOID 1042 NTAPI 1043 EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody, 1044 IN ULONG Flags) 1045 { 1046 ULONG i; 1047 1048 /* Sanity check */ 1049 ASSERT(KeyBody->KeyControlBlock != NULL); 1050 1051 /* Initialize the list entry */ 1052 InitializeListHead(&KeyBody->KeyBodyList); 1053 1054 /* Check if we can use the parent KCB array */ 1055 for (i = 0; i < 4; i++) 1056 { 1057 /* Add it into the list */ 1058 if (!InterlockedCompareExchangePointer((PVOID*)&KeyBody->KeyControlBlock-> 1059 KeyBodyArray[i], 1060 KeyBody, 1061 NULL)) 1062 { 1063 /* Added */ 1064 return; 1065 } 1066 } 1067 1068 /* Array full, check if we need to unlock the KCB */ 1069 if (Flags & CMP_ENLIST_KCB_LOCKED_SHARED) 1070 { 1071 /* It's shared, so release the KCB shared lock */ 1072 CmpReleaseKcbLock(KeyBody->KeyControlBlock); 1073 ASSERT(!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)); 1074 } 1075 1076 /* Check if we need to lock the KCB */ 1077 if (!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)) 1078 { 1079 /* Acquire the lock */ 1080 CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock); 1081 } 1082 1083 /* Make sure we have the exclusive lock */ 1084 CMP_ASSERT_KCB_LOCK(KeyBody->KeyControlBlock); 1085 1086 /* Do the insert */ 1087 InsertTailList(&KeyBody->KeyControlBlock->KeyBodyListHead, 1088 &KeyBody->KeyBodyList); 1089 1090 /* Check if we did a manual lock */ 1091 if (!(Flags & (CMP_ENLIST_KCB_LOCKED_SHARED | 1092 CMP_ENLIST_KCB_LOCKED_EXCLUSIVE))) 1093 { 1094 /* Release the lock */ 1095 CmpReleaseKcbLock(KeyBody->KeyControlBlock); 1096 } 1097 } 1098 1099 VOID 1100 NTAPI 1101 DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody, 1102 IN BOOLEAN LockHeld) 1103 { 1104 ULONG i; 1105 1106 /* Sanity check */ 1107 ASSERT(KeyBody->KeyControlBlock != NULL); 1108 1109 /* Check if we can use the parent KCB array */ 1110 for (i = 0; i < 4; i++) 1111 { 1112 /* Add it into the list */ 1113 if (InterlockedCompareExchangePointer((VOID*)&KeyBody->KeyControlBlock-> 1114 KeyBodyArray[i], 1115 NULL, 1116 KeyBody) == KeyBody) 1117 { 1118 /* Removed */ 1119 return; 1120 } 1121 } 1122 1123 /* Sanity checks */ 1124 ASSERT(IsListEmpty(&KeyBody->KeyControlBlock->KeyBodyListHead) == FALSE); 1125 ASSERT(IsListEmpty(&KeyBody->KeyBodyList) == FALSE); 1126 1127 /* Lock the KCB */ 1128 if (!LockHeld) CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock); 1129 CMP_ASSERT_KCB_LOCK(KeyBody->KeyControlBlock); 1130 1131 /* Remove the entry */ 1132 RemoveEntryList(&KeyBody->KeyBodyList); 1133 1134 /* Unlock it it if we did a manual lock */ 1135 if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock); 1136 } 1137 1138 VOID 1139 NTAPI 1140 CmpFlushNotifiesOnKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb, 1141 IN BOOLEAN LockHeld) 1142 { 1143 PLIST_ENTRY NextEntry, ListHead; 1144 PCM_KEY_BODY KeyBody; 1145 1146 /* Sanity check */ 1147 LockHeld ? CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK() : CmpIsKcbLockedExclusive(Kcb); 1148 while (TRUE) 1149 { 1150 /* Is the list empty? */ 1151 ListHead = &Kcb->KeyBodyListHead; 1152 if (!IsListEmpty(ListHead)) 1153 { 1154 /* Loop the list */ 1155 NextEntry = ListHead->Flink; 1156 while (NextEntry != ListHead) 1157 { 1158 /* Get the key body */ 1159 KeyBody = CONTAINING_RECORD(NextEntry, CM_KEY_BODY, KeyBodyList); 1160 ASSERT(KeyBody->Type == CM_KEY_BODY_TYPE); 1161 1162 /* Check for notifications */ 1163 if (KeyBody->NotifyBlock) 1164 { 1165 /* Is the lock held? */ 1166 if (LockHeld) 1167 { 1168 /* Flush it */ 1169 CmpFlushNotify(KeyBody, LockHeld); 1170 ASSERT(KeyBody->NotifyBlock == NULL); 1171 continue; 1172 } 1173 1174 /* Lock isn't held, so we need to take a reference */ 1175 if (ObReferenceObjectSafe(KeyBody)) 1176 { 1177 /* Now we can flush */ 1178 CmpFlushNotify(KeyBody, LockHeld); 1179 ASSERT(KeyBody->NotifyBlock == NULL); 1180 1181 /* Release the reference we took */ 1182 ObDereferenceObjectDeferDelete(KeyBody); 1183 continue; 1184 } 1185 } 1186 1187 /* Try the next entry */ 1188 NextEntry = NextEntry->Flink; 1189 } 1190 } 1191 1192 /* List has been parsed, exit */ 1193 break; 1194 } 1195 } 1196