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 = COMPUTE_HASH_CHAR(ConvKey, *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 = COMPUTE_HASH_CHAR(ConvKey, *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 = (PCM_KEY_NODE)HvGetCell(MyKcb->KeyHive, MyKcb->KeyCell); 965 if (!KeyNode) 966 { 967 /* Failure */ 968 CmpFree(KeyName, 0); 969 return NULL; 970 } 971 } 972 else 973 { 974 /* The key was deleted */ 975 KeyNode = NULL; 976 DeletedKey = TRUE; 977 } 978 979 /* Get the pointer to the beginning of the current key name */ 980 NameLength += (MyKcb->NameBlock->NameLength + 1) * sizeof(WCHAR); 981 TargetBuffer = &KeyName->Buffer[(KeyName->Length - NameLength) / sizeof(WCHAR)]; 982 983 /* Add a separator */ 984 TargetBuffer[0] = OBJ_NAME_PATH_SEPARATOR; 985 986 /* Add the name, but remember to go from the end to the beginning */ 987 if (!MyKcb->NameBlock->Compressed) 988 { 989 /* Get the pointer to the name (from the keynode, if possible) */ 990 if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) || 991 !KeyNode) 992 { 993 CurrentNameW = MyKcb->NameBlock->Name; 994 } 995 else 996 { 997 CurrentNameW = KeyNode->Name; 998 } 999 1000 /* Copy the name */ 1001 for (i=0; i < MyKcb->NameBlock->NameLength; i++) 1002 { 1003 TargetBuffer[i+1] = *CurrentNameW; 1004 CurrentNameW++; 1005 } 1006 } 1007 else 1008 { 1009 /* Get the pointer to the name (from the keynode, if possible) */ 1010 if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) || 1011 !KeyNode) 1012 { 1013 CurrentName = (PUCHAR)MyKcb->NameBlock->Name; 1014 } 1015 else 1016 { 1017 CurrentName = (PUCHAR)KeyNode->Name; 1018 } 1019 1020 /* Copy the name */ 1021 for (i=0; i < MyKcb->NameBlock->NameLength; i++) 1022 { 1023 TargetBuffer[i+1] = (WCHAR)*CurrentName; 1024 CurrentName++; 1025 } 1026 } 1027 1028 /* Release the cell, if needed */ 1029 if (KeyNode) HvReleaseCell(MyKcb->KeyHive, MyKcb->KeyCell); 1030 1031 /* Go to the parent KCB */ 1032 MyKcb = MyKcb->ParentKcb; 1033 } 1034 1035 /* Return resulting buffer (both UNICODE_STRING and 1036 its buffer following it) */ 1037 return KeyName; 1038 } 1039 1040 VOID 1041 NTAPI 1042 EnlistKeyBodyWithKCB(IN PCM_KEY_BODY KeyBody, 1043 IN ULONG Flags) 1044 { 1045 ULONG i; 1046 1047 /* Sanity check */ 1048 ASSERT(KeyBody->KeyControlBlock != NULL); 1049 1050 /* Initialize the list entry */ 1051 InitializeListHead(&KeyBody->KeyBodyList); 1052 1053 /* Check if we can use the parent KCB array */ 1054 for (i = 0; i < 4; i++) 1055 { 1056 /* Add it into the list */ 1057 if (!InterlockedCompareExchangePointer((PVOID*)&KeyBody->KeyControlBlock-> 1058 KeyBodyArray[i], 1059 KeyBody, 1060 NULL)) 1061 { 1062 /* Added */ 1063 return; 1064 } 1065 } 1066 1067 /* Array full, check if we need to unlock the KCB */ 1068 if (Flags & CMP_ENLIST_KCB_LOCKED_SHARED) 1069 { 1070 /* It's shared, so release the KCB shared lock */ 1071 CmpReleaseKcbLock(KeyBody->KeyControlBlock); 1072 ASSERT(!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)); 1073 } 1074 1075 /* Check if we need to lock the KCB */ 1076 if (!(Flags & CMP_ENLIST_KCB_LOCKED_EXCLUSIVE)) 1077 { 1078 /* Acquire the lock */ 1079 CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock); 1080 } 1081 1082 /* Make sure we have the exclusive lock */ 1083 CMP_ASSERT_KCB_LOCK(KeyBody->KeyControlBlock); 1084 1085 /* Do the insert */ 1086 InsertTailList(&KeyBody->KeyControlBlock->KeyBodyListHead, 1087 &KeyBody->KeyBodyList); 1088 1089 /* Check if we did a manual lock */ 1090 if (!(Flags & (CMP_ENLIST_KCB_LOCKED_SHARED | 1091 CMP_ENLIST_KCB_LOCKED_EXCLUSIVE))) 1092 { 1093 /* Release the lock */ 1094 CmpReleaseKcbLock(KeyBody->KeyControlBlock); 1095 } 1096 } 1097 1098 VOID 1099 NTAPI 1100 DelistKeyBodyFromKCB(IN PCM_KEY_BODY KeyBody, 1101 IN BOOLEAN LockHeld) 1102 { 1103 ULONG i; 1104 1105 /* Sanity check */ 1106 ASSERT(KeyBody->KeyControlBlock != NULL); 1107 1108 /* Check if we can use the parent KCB array */ 1109 for (i = 0; i < 4; i++) 1110 { 1111 /* Add it into the list */ 1112 if (InterlockedCompareExchangePointer((VOID*)&KeyBody->KeyControlBlock-> 1113 KeyBodyArray[i], 1114 NULL, 1115 KeyBody) == KeyBody) 1116 { 1117 /* Removed */ 1118 return; 1119 } 1120 } 1121 1122 /* Sanity checks */ 1123 ASSERT(IsListEmpty(&KeyBody->KeyControlBlock->KeyBodyListHead) == FALSE); 1124 ASSERT(IsListEmpty(&KeyBody->KeyBodyList) == FALSE); 1125 1126 /* Lock the KCB */ 1127 if (!LockHeld) CmpAcquireKcbLockExclusive(KeyBody->KeyControlBlock); 1128 CMP_ASSERT_KCB_LOCK(KeyBody->KeyControlBlock); 1129 1130 /* Remove the entry */ 1131 RemoveEntryList(&KeyBody->KeyBodyList); 1132 1133 /* Unlock it it if we did a manual lock */ 1134 if (!LockHeld) CmpReleaseKcbLock(KeyBody->KeyControlBlock); 1135 } 1136 1137 /** 1138 * @brief 1139 * Unlocks a number of KCBs provided by a KCB array. 1140 * 1141 * @param[in] KcbArray 1142 * A pointer to an array of KCBs to be unlocked. 1143 */ 1144 VOID 1145 CmpUnLockKcbArray( 1146 _In_ PULONG KcbArray) 1147 { 1148 ULONG i; 1149 1150 /* Release the locked KCBs in reverse order */ 1151 for (i = KcbArray[0]; i > 0; i--) 1152 { 1153 CmpReleaseKcbLockByIndex(KcbArray[i]); 1154 } 1155 } 1156 1157 /** 1158 * @brief 1159 * Locks a given number of KCBs. 1160 * 1161 * @param[in] KcbArray 1162 * A pointer to an array of KCBs to be locked. 1163 * The count of KCBs to be locked is defined by the 1164 * first element in the array. 1165 * 1166 * @param[in] KcbLockFlags 1167 * Define a lock flag to lock the KCBs. 1168 * 1169 * CMP_LOCK_KCB_ARRAY_EXCLUSIVE -- indicates the KCBs are locked 1170 * exclusively and owned by the calling thread. 1171 * 1172 * CMP_LOCK_KCB_ARRAY_SHARED -- indicates the KCBs are locked 1173 * in shared mode by the owning threads. 1174 */ 1175 static 1176 VOID 1177 CmpLockKcbArray( 1178 _In_ PULONG KcbArray, 1179 _In_ ULONG KcbLockFlags) 1180 { 1181 ULONG i; 1182 1183 /* Lock the KCBs */ 1184 for (i = 1; i <= KcbArray[0]; i++) 1185 { 1186 if (KcbLockFlags & CMP_LOCK_KCB_ARRAY_EXCLUSIVE) 1187 { 1188 CmpAcquireKcbLockExclusiveByIndex(KcbArray[i]); 1189 } 1190 else // CMP_LOCK_KCB_ARRAY_SHARED 1191 { 1192 CmpAcquireKcbLockSharedByIndex(KcbArray[i]); 1193 } 1194 } 1195 } 1196 1197 /** 1198 * @brief 1199 * Sorts an array of KCB hashes in ascending order 1200 * and removes any key indices that are duplicates. 1201 * The purpose of sorting the KCB elements is to 1202 * ensure consistent and proper locking order, so 1203 * that we can prevent a deadlock. 1204 * 1205 * @param[in,out] KcbArray 1206 * A pointer to an array of KCBs of which the key 1207 * indices are to be sorted. 1208 */ 1209 static 1210 VOID 1211 CmpSortKcbArray( 1212 _Inout_ PULONG KcbArray) 1213 { 1214 ULONG i, j, k, KcbCount; 1215 1216 /* Ensure we don't go above the limit of KCBs we can hold */ 1217 KcbCount = KcbArray[0]; 1218 ASSERT(KcbCount < CMP_KCBS_IN_ARRAY_LIMIT); 1219 1220 /* Exchange-Sort the array in ascending order. Complexity: O[n^2] */ 1221 for (i = 1; i <= KcbCount; i++) 1222 { 1223 for (j = i + 1; j <= KcbCount; j++) 1224 { 1225 if (KcbArray[i] > KcbArray[j]) 1226 { 1227 ULONG Temp = KcbArray[i]; 1228 KcbArray[i] = KcbArray[j]; 1229 KcbArray[j] = Temp; 1230 } 1231 } 1232 } 1233 1234 /* Now remove any duplicated indices on the sorted array if any */ 1235 for (i = 1; i <= KcbCount; i++) 1236 { 1237 for (j = i + 1; j <= KcbCount; j++) 1238 { 1239 if (KcbArray[i] == KcbArray[j]) 1240 { 1241 for (k = j; k <= KcbCount; k++) 1242 { 1243 KcbArray[k - 1] = KcbArray[k]; 1244 } 1245 1246 j--; 1247 KcbCount--; 1248 } 1249 } 1250 } 1251 1252 /* Update the KCB count */ 1253 KcbArray[0] = KcbCount; 1254 } 1255 1256 /** 1257 * @brief 1258 * Builds an array of KCBs and locks them. Whether these 1259 * KCBs are locked exclusively or in shared mode by the calling 1260 * thread, is specified by the KcbLockFlags parameter. The array 1261 * is sorted. 1262 * 1263 * @param[in] HashCacheStack 1264 * A pointer to a hash cache stack. This stack parameter 1265 * stores the convkey hashes of interested KCBs of a 1266 * key path name that need to be locked. 1267 * 1268 * @param[in] KcbLockFlags 1269 * Define a lock flag to lock the KCBs. Consult the 1270 * CmpLockKcbArray documentation for more information. 1271 * 1272 * @param[in] Kcb 1273 * A pointer to a key control block to be given. This 1274 * KCB is included in the array for locking, that is, 1275 * given by the CmpParseKey from the parser object. 1276 * 1277 * @param[in,out] OuterStackArray 1278 * A pointer to an array that lives on the caller's 1279 * stack. It acts like an auxiliary array used by 1280 * the function to store the KCB elements for locking. 1281 * The expected size of the array is up to 32 elements, 1282 * which is the imposed limit by CMP_HASH_STACK_LIMIT. 1283 * This limit also corresponds to the maximum depth of 1284 * subkey levels. 1285 * 1286 * @param[in] TotalRemainingSubkeys 1287 * The number of total remaining subkey levels. 1288 * 1289 * @param[in] MatchRemainSubkeyLevel 1290 * The number of remaining subkey levels that match. 1291 * 1292 * @return 1293 * Returns a pointer to an array of KCBs that have been 1294 * locked. 1295 * 1296 * @remarks 1297 * The caller HAS THE RESPONSIBILITY to unlock the KCBs 1298 * after the necessary operations are done! 1299 */ 1300 PULONG 1301 NTAPI 1302 CmpBuildAndLockKcbArray( 1303 _In_ PCM_HASH_CACHE_STACK HashCacheStack, 1304 _In_ ULONG KcbLockFlags, 1305 _In_ PCM_KEY_CONTROL_BLOCK Kcb, 1306 _Inout_ PULONG OuterStackArray, 1307 _In_ ULONG TotalRemainingSubkeys, 1308 _In_ ULONG MatchRemainSubkeyLevel) 1309 { 1310 ULONG KcbIndex = 1, HashStackIndex, TotalRemaining; 1311 PULONG LockedKcbs = NULL; 1312 PCM_KEY_CONTROL_BLOCK ParentKcb = Kcb->ParentKcb;; 1313 1314 /* These parameters are expected */ 1315 ASSERT(HashCacheStack != NULL); 1316 ASSERT(Kcb != NULL); 1317 ASSERT(OuterStackArray != NULL); 1318 1319 /* 1320 * Ensure when we build an array of KCBs to lock, that 1321 * we don't go beyond the boundary the limit allows us 1322 * to. 1 is the current KCB we would want to lock 1323 * alongside with the remaining key levels in the formula. 1324 */ 1325 TotalRemaining = (1 + TotalRemainingSubkeys) - MatchRemainSubkeyLevel; 1326 ASSERT(TotalRemaining <= CMP_KCBS_IN_ARRAY_LIMIT); 1327 1328 /* Count the parent if we have one */ 1329 if (ParentKcb) 1330 { 1331 /* Ensure we are still below the limit and add the parent to KCBs to lock */ 1332 if (TotalRemainingSubkeys == MatchRemainSubkeyLevel) 1333 { 1334 TotalRemaining++; 1335 ASSERT(TotalRemaining <= CMP_KCBS_IN_ARRAY_LIMIT); 1336 OuterStackArray[KcbIndex++] = GET_HASH_INDEX(ParentKcb->ConvKey); 1337 } 1338 } 1339 1340 /* Add the current KCB */ 1341 OuterStackArray[KcbIndex++] = GET_HASH_INDEX(Kcb->ConvKey); 1342 1343 /* Loop over the hash stack and grab the hashes for locking (they will be converted to indices) */ 1344 for (HashStackIndex = 0; 1345 HashStackIndex < TotalRemainingSubkeys; 1346 HashStackIndex++) 1347 { 1348 OuterStackArray[KcbIndex++] = GET_HASH_INDEX(HashCacheStack[HashStackIndex].ConvKey); 1349 } 1350 1351 /* 1352 * Store how many KCBs we need to lock and sort the array. 1353 * Remove any duplicated indices from the array if any. 1354 */ 1355 OuterStackArray[0] = KcbIndex - 1; 1356 CmpSortKcbArray(OuterStackArray); 1357 1358 /* Lock them */ 1359 CmpLockKcbArray(OuterStackArray, KcbLockFlags); 1360 1361 /* Give the locked KCBs to caller now */ 1362 LockedKcbs = OuterStackArray; 1363 return LockedKcbs; 1364 } 1365 1366 VOID 1367 NTAPI 1368 CmpFlushNotifiesOnKeyBodyList(IN PCM_KEY_CONTROL_BLOCK Kcb, 1369 IN BOOLEAN LockHeld) 1370 { 1371 PLIST_ENTRY NextEntry, ListHead; 1372 PCM_KEY_BODY KeyBody; 1373 1374 /* Sanity check */ 1375 LockHeld ? CMP_ASSERT_EXCLUSIVE_REGISTRY_LOCK() : CmpIsKcbLockedExclusive(Kcb); 1376 while (TRUE) 1377 { 1378 /* Is the list empty? */ 1379 ListHead = &Kcb->KeyBodyListHead; 1380 if (!IsListEmpty(ListHead)) 1381 { 1382 /* Loop the list */ 1383 NextEntry = ListHead->Flink; 1384 while (NextEntry != ListHead) 1385 { 1386 /* Get the key body */ 1387 KeyBody = CONTAINING_RECORD(NextEntry, CM_KEY_BODY, KeyBodyList); 1388 ASSERT(KeyBody->Type == CM_KEY_BODY_TYPE); 1389 1390 /* Check for notifications */ 1391 if (KeyBody->NotifyBlock) 1392 { 1393 /* Is the lock held? */ 1394 if (LockHeld) 1395 { 1396 /* Flush it */ 1397 CmpFlushNotify(KeyBody, LockHeld); 1398 ASSERT(KeyBody->NotifyBlock == NULL); 1399 continue; 1400 } 1401 1402 /* Lock isn't held, so we need to take a reference */ 1403 if (ObReferenceObjectSafe(KeyBody)) 1404 { 1405 /* Now we can flush */ 1406 CmpFlushNotify(KeyBody, LockHeld); 1407 ASSERT(KeyBody->NotifyBlock == NULL); 1408 1409 /* Release the reference we took */ 1410 ObDereferenceObjectDeferDelete(KeyBody); 1411 continue; 1412 } 1413 } 1414 1415 /* Try the next entry */ 1416 NextEntry = NextEntry->Flink; 1417 } 1418 } 1419 1420 /* List has been parsed, exit */ 1421 break; 1422 } 1423 } 1424