1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/config/cmvalche.c 5 * PURPOSE: Configuration Manager - Value Cell Cache 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 FORCEINLINE 16 BOOLEAN 17 CmpIsValueCached(IN HCELL_INDEX CellIndex) 18 { 19 /* Make sure that the cell is valid in the first place */ 20 if (CellIndex == HCELL_NIL) return FALSE; 21 22 /*Is this cell actually a pointer to the cached value data? */ 23 if (CellIndex & 1) return TRUE; 24 25 /* This is a regular cell */ 26 return FALSE; 27 } 28 29 FORCEINLINE 30 VOID 31 CmpSetValueCached(IN PHCELL_INDEX CellIndex) 32 { 33 /* Set the cached bit */ 34 *CellIndex |= 1; 35 } 36 37 #define ASSERT_VALUE_CACHE() \ 38 ASSERTMSG("Cached Values Not Yet Supported!\n", FALSE); 39 40 /* FUNCTIONS *****************************************************************/ 41 42 VALUE_SEARCH_RETURN_TYPE 43 NTAPI 44 CmpGetValueListFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb, 45 OUT PCELL_DATA *CellData, 46 OUT BOOLEAN *IndexIsCached, 47 OUT PHCELL_INDEX ValueListToRelease) 48 { 49 PHHIVE Hive; 50 PCACHED_CHILD_LIST ChildList; 51 HCELL_INDEX CellToRelease; 52 53 /* Set defaults */ 54 *ValueListToRelease = HCELL_NIL; 55 *IndexIsCached = FALSE; 56 57 /* Get the hive and value cache */ 58 Hive = Kcb->KeyHive; 59 ChildList = &Kcb->ValueCache; 60 61 /* Check if the value is cached */ 62 if (CmpIsValueCached(ChildList->ValueList)) 63 { 64 /* It is: we don't expect this yet! */ 65 ASSERT_VALUE_CACHE(); 66 *IndexIsCached = TRUE; 67 *CellData = NULL; 68 } 69 else 70 { 71 /* Make sure the KCB is locked exclusive */ 72 if (!CmpIsKcbLockedExclusive(Kcb) && 73 !CmpTryToConvertKcbSharedToExclusive(Kcb)) 74 { 75 /* We need the exclusive lock */ 76 return SearchNeedExclusiveLock; 77 } 78 79 /* Select the value list as our cell, and get the actual list array */ 80 CellToRelease = ChildList->ValueList; 81 *CellData = (PCELL_DATA)HvGetCell(Hive, CellToRelease); 82 if (!*CellData) return SearchFail; 83 84 /* FIXME: Here we would cache the value */ 85 86 /* Return the cell to be released */ 87 *ValueListToRelease = CellToRelease; 88 } 89 90 /* If we got here, then the value list was found */ 91 return SearchSuccess; 92 } 93 94 VALUE_SEARCH_RETURN_TYPE 95 NTAPI 96 CmpGetValueKeyFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb, 97 IN PCELL_DATA CellData, 98 IN ULONG Index, 99 OUT PCM_CACHED_VALUE **CachedValue, 100 OUT PCM_KEY_VALUE *Value, 101 IN BOOLEAN IndexIsCached, 102 OUT BOOLEAN *ValueIsCached, 103 OUT PHCELL_INDEX CellToRelease) 104 { 105 PHHIVE Hive; 106 PCM_KEY_VALUE KeyValue; 107 HCELL_INDEX Cell; 108 109 /* Set defaults */ 110 *CellToRelease = HCELL_NIL; 111 *Value = NULL; 112 *ValueIsCached = FALSE; 113 114 /* Get the hive */ 115 Hive = Kcb->KeyHive; 116 117 /* Check if the index was cached */ 118 if (IndexIsCached) 119 { 120 /* Not expected yet! */ 121 ASSERT_VALUE_CACHE(); 122 *ValueIsCached = TRUE; 123 } 124 else 125 { 126 /* Get the cell index and the key value associated to it */ 127 Cell = CellData->u.KeyList[Index]; 128 KeyValue = (PCM_KEY_VALUE)HvGetCell(Hive, Cell); 129 if (!KeyValue) return SearchFail; 130 131 /* Return the cell and the actual key value */ 132 *CellToRelease = Cell; 133 *Value = KeyValue; 134 } 135 136 /* If we got here, then we found the key value */ 137 return SearchSuccess; 138 } 139 140 VALUE_SEARCH_RETURN_TYPE 141 NTAPI 142 CmpGetValueDataFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb, 143 IN PCM_CACHED_VALUE *CachedValue, 144 IN PCELL_DATA ValueKey, 145 IN BOOLEAN ValueIsCached, 146 OUT PVOID *DataPointer, 147 OUT PBOOLEAN Allocated, 148 OUT PHCELL_INDEX CellToRelease) 149 { 150 PHHIVE Hive; 151 ULONG Length; 152 153 /* Sanity checks */ 154 ASSERT(MAXIMUM_CACHED_DATA < CM_KEY_VALUE_BIG); 155 ASSERT((ValueKey->u.KeyValue.DataLength & CM_KEY_VALUE_SPECIAL_SIZE) == 0); 156 157 /* Set defaults */ 158 *DataPointer = NULL; 159 *Allocated = FALSE; 160 *CellToRelease = HCELL_NIL; 161 162 /* Get the hive */ 163 Hive = Kcb->KeyHive; 164 165 /* Check it the value is cached */ 166 if (ValueIsCached) 167 { 168 /* This isn't expected! */ 169 ASSERT_VALUE_CACHE(); 170 } 171 else 172 { 173 /* It's not, get the value data using the typical routine */ 174 if (!CmpGetValueData(Hive, 175 &ValueKey->u.KeyValue, 176 &Length, 177 DataPointer, 178 Allocated, 179 CellToRelease)) 180 { 181 /* Nothing found: make sure no data was allocated */ 182 ASSERT(*Allocated == FALSE); 183 ASSERT(*DataPointer == NULL); 184 return SearchFail; 185 } 186 } 187 188 /* We found the actual data, return success */ 189 return SearchSuccess; 190 } 191 192 VALUE_SEARCH_RETURN_TYPE 193 NTAPI 194 CmpFindValueByNameFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb, 195 IN PCUNICODE_STRING Name, 196 OUT PCM_CACHED_VALUE **CachedValue, 197 OUT ULONG *Index, 198 OUT PCM_KEY_VALUE *Value, 199 OUT BOOLEAN *ValueIsCached, 200 OUT PHCELL_INDEX CellToRelease) 201 { 202 PHHIVE Hive; 203 VALUE_SEARCH_RETURN_TYPE SearchResult = SearchFail; 204 LONG Result; 205 UNICODE_STRING SearchName; 206 PCELL_DATA CellData; 207 PCACHED_CHILD_LIST ChildList; 208 PCM_KEY_VALUE KeyValue; 209 BOOLEAN IndexIsCached; 210 ULONG i = 0; 211 HCELL_INDEX Cell = HCELL_NIL; 212 213 /* Set defaults */ 214 *CellToRelease = HCELL_NIL; 215 *Value = NULL; 216 217 /* Get the hive and child list */ 218 Hive = Kcb->KeyHive; 219 ChildList = &Kcb->ValueCache; 220 221 /* Check if the child list has any entries */ 222 if (ChildList->Count != 0) 223 { 224 /* Get the value list associated to this child list */ 225 SearchResult = CmpGetValueListFromCache(Kcb, 226 &CellData, 227 &IndexIsCached, 228 &Cell); 229 if (SearchResult != SearchSuccess) 230 { 231 /* We either failed or need the exclusive lock */ 232 ASSERT((SearchResult == SearchFail) || !CmpIsKcbLockedExclusive(Kcb)); 233 ASSERT(Cell == HCELL_NIL); 234 return SearchResult; 235 } 236 237 /* The index shouldn't be cached right now */ 238 if (IndexIsCached) ASSERT_VALUE_CACHE(); 239 240 /* Loop every value */ 241 while (TRUE) 242 { 243 /* Check if there's any cell to release */ 244 if (*CellToRelease != HCELL_NIL) 245 { 246 /* Release it now */ 247 HvReleaseCell(Hive, *CellToRelease); 248 *CellToRelease = HCELL_NIL; 249 } 250 251 /* Get the key value for this index */ 252 SearchResult = CmpGetValueKeyFromCache(Kcb, 253 CellData, 254 i, 255 CachedValue, 256 Value, 257 IndexIsCached, 258 ValueIsCached, 259 CellToRelease); 260 if (SearchResult != SearchSuccess) 261 { 262 /* We either failed or need the exclusive lock */ 263 ASSERT((SearchResult == SearchFail) || !CmpIsKcbLockedExclusive(Kcb)); 264 ASSERT(Cell == HCELL_NIL); 265 return SearchResult; 266 } 267 268 /* Check if the both the index and the value are cached */ 269 if (IndexIsCached && *ValueIsCached) 270 { 271 /* We don't expect this yet */ 272 ASSERT_VALUE_CACHE(); 273 Result = -1; 274 } 275 else 276 { 277 /* No cache, so try to compare the name. Is it compressed? */ 278 KeyValue = *Value; 279 if (KeyValue->Flags & VALUE_COMP_NAME) 280 { 281 /* It is, do a compressed name comparison */ 282 Result = CmpCompareCompressedName(Name, 283 KeyValue->Name, 284 KeyValue->NameLength); 285 } 286 else 287 { 288 /* It's not compressed, so do a standard comparison */ 289 SearchName.Length = KeyValue->NameLength; 290 SearchName.MaximumLength = SearchName.Length; 291 SearchName.Buffer = KeyValue->Name; 292 Result = RtlCompareUnicodeString(Name, &SearchName, TRUE); 293 } 294 } 295 296 /* Check if we found the value data */ 297 if (!Result) 298 { 299 /* We have, return the index of the value and success */ 300 *Index = i; 301 SearchResult = SearchSuccess; 302 goto Quickie; 303 } 304 305 /* We didn't find it, try the next entry */ 306 if (++i == ChildList->Count) 307 { 308 /* The entire list was parsed, fail */ 309 *Value = NULL; 310 SearchResult = SearchFail; 311 goto Quickie; 312 } 313 } 314 } 315 316 /* We should only get here if the child list is empty */ 317 ASSERT(ChildList->Count == 0); 318 319 Quickie: 320 /* Release the value list cell if required, and return search result */ 321 if (Cell != HCELL_NIL) HvReleaseCell(Hive, Cell); 322 return SearchResult; 323 } 324 325 VALUE_SEARCH_RETURN_TYPE 326 NTAPI 327 CmpQueryKeyValueData(IN PCM_KEY_CONTROL_BLOCK Kcb, 328 IN PCM_CACHED_VALUE *CachedValue, 329 IN PCM_KEY_VALUE ValueKey, 330 IN BOOLEAN ValueIsCached, 331 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, 332 IN PVOID KeyValueInformation, 333 IN ULONG Length, 334 OUT PULONG ResultLength, 335 OUT PNTSTATUS Status) 336 { 337 PKEY_VALUE_INFORMATION Info = (PKEY_VALUE_INFORMATION)KeyValueInformation; 338 PCELL_DATA CellData; 339 USHORT NameSize; 340 ULONG Size, MinimumSize, SizeLeft, KeySize, AlignedData = 0, DataOffset; 341 PVOID Buffer; 342 BOOLEAN IsSmall, BufferAllocated = FALSE; 343 HCELL_INDEX CellToRelease = HCELL_NIL; 344 VALUE_SEARCH_RETURN_TYPE Result = SearchSuccess; 345 346 /* Get the value data */ 347 CellData = (PCELL_DATA)ValueKey; 348 349 /* Check if the value is compressed */ 350 if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME) 351 { 352 /* Get the compressed name size */ 353 NameSize = CmpCompressedNameSize(CellData->u.KeyValue.Name, 354 CellData->u.KeyValue.NameLength); 355 } 356 else 357 { 358 /* Get the real size */ 359 NameSize = CellData->u.KeyValue.NameLength; 360 } 361 362 /* Check what kind of information the caller is requesting */ 363 switch (KeyValueInformationClass) 364 { 365 /* Basic information */ 366 case KeyValueBasicInformation: 367 368 /* This is how much size we'll need */ 369 Size = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name) + NameSize; 370 371 /* This is the minimum we can work with */ 372 MinimumSize = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name); 373 374 /* Return the size we'd like, and assume success */ 375 *ResultLength = Size; 376 *Status = STATUS_SUCCESS; 377 378 /* Check if the caller gave us below our minimum */ 379 if (Length < MinimumSize) 380 { 381 /* Then we must fail */ 382 *Status = STATUS_BUFFER_TOO_SMALL; 383 break; 384 } 385 386 /* Fill out the basic information */ 387 Info->KeyValueBasicInformation.TitleIndex = 0; 388 Info->KeyValueBasicInformation.Type = CellData->u.KeyValue.Type; 389 Info->KeyValueBasicInformation.NameLength = NameSize; 390 391 /* Now only the name is left */ 392 SizeLeft = Length - MinimumSize; 393 Size = NameSize; 394 395 /* Check if the remaining buffer is too small for the name */ 396 if (SizeLeft < Size) 397 { 398 /* Copy only as much as can fit, and tell the caller */ 399 Size = SizeLeft; 400 *Status = STATUS_BUFFER_OVERFLOW; 401 } 402 403 /* Check if this is a compressed name */ 404 if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME) 405 { 406 /* Copy as much as we can of the compressed name */ 407 CmpCopyCompressedName(Info->KeyValueBasicInformation.Name, 408 Size, 409 CellData->u.KeyValue.Name, 410 CellData->u.KeyValue.NameLength); 411 } 412 else 413 { 414 /* Copy as much as we can of the raw name */ 415 RtlCopyMemory(Info->KeyValueBasicInformation.Name, 416 CellData->u.KeyValue.Name, 417 Size); 418 } 419 420 /* We're all done */ 421 break; 422 423 /* Full key information */ 424 case KeyValueFullInformation: 425 case KeyValueFullInformationAlign64: 426 427 /* Check if this is a small key and compute key size */ 428 IsSmall = CmpIsKeyValueSmall(&KeySize, 429 CellData->u.KeyValue.DataLength); 430 431 /* Calculate the total size required */ 432 Size = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) + 433 NameSize + 434 KeySize; 435 436 /* And this is the least we can work with */ 437 MinimumSize = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name); 438 439 /* Check if there's any key data */ 440 if (KeySize > 0) 441 { 442 /* Calculate the data offset */ 443 DataOffset = Size - KeySize; 444 445 #ifdef _WIN64 446 /* On 64-bit, always align to 8 bytes */ 447 AlignedData = ALIGN_UP(DataOffset, ULONGLONG); 448 #else 449 /* On 32-bit, align the offset to 4 or 8 bytes */ 450 if (KeyValueInformationClass == KeyValueFullInformationAlign64) 451 { 452 AlignedData = ALIGN_UP(DataOffset, ULONGLONG); 453 } 454 else 455 { 456 AlignedData = ALIGN_UP(DataOffset, ULONG); 457 } 458 #endif 459 /* If alignment was required, we'll need more space */ 460 if (AlignedData > DataOffset) Size += (AlignedData-DataOffset); 461 } 462 463 /* Tell the caller the size we'll finally need, and set success */ 464 *ResultLength = Size; 465 *Status = STATUS_SUCCESS; 466 467 /* Check if the caller is giving us too little */ 468 if (Length < MinimumSize) 469 { 470 /* Then fail right now */ 471 *Status = STATUS_BUFFER_TOO_SMALL; 472 break; 473 } 474 475 /* Fill out the basic information */ 476 Info->KeyValueFullInformation.TitleIndex = 0; 477 Info->KeyValueFullInformation.Type = CellData->u.KeyValue.Type; 478 Info->KeyValueFullInformation.DataLength = KeySize; 479 Info->KeyValueFullInformation.NameLength = NameSize; 480 481 /* Only the name is left now */ 482 SizeLeft = Length - MinimumSize; 483 Size = NameSize; 484 485 /* Check if the name fits */ 486 if (SizeLeft < Size) 487 { 488 /* It doesn't, truncate what we'll copy, and tell the caller */ 489 Size = SizeLeft; 490 *Status = STATUS_BUFFER_OVERFLOW; 491 } 492 493 /* Check if this key value is compressed */ 494 if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME) 495 { 496 /* It is, copy the compressed name */ 497 CmpCopyCompressedName(Info->KeyValueFullInformation.Name, 498 Size, 499 CellData->u.KeyValue.Name, 500 CellData->u.KeyValue.NameLength); 501 } 502 else 503 { 504 /* It's not, copy the raw name */ 505 RtlCopyMemory(Info->KeyValueFullInformation.Name, 506 CellData->u.KeyValue.Name, 507 Size); 508 } 509 510 /* Now check if the key had any data */ 511 if (KeySize > 0) 512 { 513 /* Was it a small key? */ 514 if (IsSmall) 515 { 516 /* Then the data is directly into the cell */ 517 Buffer = &CellData->u.KeyValue.Data; 518 } 519 else 520 { 521 /* Otherwise, we must retrieve it from the value cache */ 522 Result = CmpGetValueDataFromCache(Kcb, 523 CachedValue, 524 CellData, 525 ValueIsCached, 526 &Buffer, 527 &BufferAllocated, 528 &CellToRelease); 529 if (Result != SearchSuccess) 530 { 531 /* We failed, nothing should be allocated */ 532 ASSERT(Buffer == NULL); 533 ASSERT(BufferAllocated == FALSE); 534 *Status = STATUS_INSUFFICIENT_RESOURCES; 535 } 536 } 537 538 /* Now that we know we truly have data, set its offset */ 539 Info->KeyValueFullInformation.DataOffset = AlignedData; 540 541 /* Only the data remains to be copied */ 542 SizeLeft = (((LONG)Length - (LONG)AlignedData) < 0) ? 543 0 : (Length - AlignedData); 544 Size = KeySize; 545 546 /* Check if the caller has no space for it */ 547 if (SizeLeft < Size) 548 { 549 /* Truncate what we'll copy, and tell the caller */ 550 Size = SizeLeft; 551 *Status = STATUS_BUFFER_OVERFLOW; 552 } 553 554 /* Sanity check */ 555 ASSERT(IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE); 556 557 /* Make sure we have a valid buffer */ 558 if (Buffer) 559 { 560 /* Copy the data into the aligned offset */ 561 RtlCopyMemory((PVOID)((ULONG_PTR)Info + AlignedData), 562 Buffer, 563 Size); 564 } 565 } 566 else 567 { 568 /* We don't have any data, set the offset to -1, not 0! */ 569 Info->KeyValueFullInformation.DataOffset = 0xFFFFFFFF; 570 } 571 572 /* We're done! */ 573 break; 574 575 /* Partial information requested (no name or alignment!) */ 576 case KeyValuePartialInformation: 577 case KeyValuePartialInformationAlign64: 578 579 /* Check if this is a small key and compute key size */ 580 IsSmall = CmpIsKeyValueSmall(&KeySize, 581 CellData->u.KeyValue.DataLength); 582 583 /* Calculate the total size required and the least we can work with */ 584 if (KeyValueInformationClass == KeyValuePartialInformationAlign64) 585 { 586 Size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data) + KeySize; 587 MinimumSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data); 588 } 589 else 590 { 591 Size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + KeySize; 592 MinimumSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data); 593 } 594 595 /* Tell the caller the size we'll finally need, and set success */ 596 *ResultLength = Size; 597 *Status = STATUS_SUCCESS; 598 599 /* Check if the caller is giving us too little */ 600 if (Length < MinimumSize) 601 { 602 /* Then fail right now */ 603 *Status = STATUS_BUFFER_TOO_SMALL; 604 break; 605 } 606 607 /* Fill out the basic information */ 608 if (KeyValueInformationClass == KeyValuePartialInformationAlign64) 609 { 610 Info->KeyValuePartialInformationAlign64.Type = CellData->u.KeyValue.Type; 611 Info->KeyValuePartialInformationAlign64.DataLength = KeySize; 612 } 613 else 614 { 615 Info->KeyValuePartialInformation.TitleIndex = 0; 616 Info->KeyValuePartialInformation.Type = CellData->u.KeyValue.Type; 617 Info->KeyValuePartialInformation.DataLength = KeySize; 618 } 619 620 /* Now check if the key had any data */ 621 if (KeySize > 0) 622 { 623 /* Was it a small key? */ 624 if (IsSmall) 625 { 626 /* Then the data is directly into the cell */ 627 Buffer = &CellData->u.KeyValue.Data; 628 } 629 else 630 { 631 /* Otherwise, we must retrieve it from the value cache */ 632 Result = CmpGetValueDataFromCache(Kcb, 633 CachedValue, 634 CellData, 635 ValueIsCached, 636 &Buffer, 637 &BufferAllocated, 638 &CellToRelease); 639 if (Result != SearchSuccess) 640 { 641 /* We failed, nothing should be allocated */ 642 ASSERT(Buffer == NULL); 643 ASSERT(BufferAllocated == FALSE); 644 *Status = STATUS_INSUFFICIENT_RESOURCES; 645 } 646 } 647 648 /* Only the data remains to be copied */ 649 SizeLeft = Length - MinimumSize; 650 Size = KeySize; 651 652 /* Check if the caller has no space for it */ 653 if (SizeLeft < Size) 654 { 655 /* Truncate what we'll copy, and tell the caller */ 656 Size = SizeLeft; 657 *Status = STATUS_BUFFER_OVERFLOW; 658 } 659 660 /* Sanity check */ 661 ASSERT(IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE); 662 663 /* Make sure we have a valid buffer */ 664 if (Buffer) 665 { 666 /* Copy the data into the aligned offset */ 667 if (KeyValueInformationClass == KeyValuePartialInformationAlign64) 668 { 669 RtlCopyMemory(Info->KeyValuePartialInformationAlign64.Data, 670 Buffer, 671 Size); 672 } 673 else 674 { 675 RtlCopyMemory(Info->KeyValuePartialInformation.Data, 676 Buffer, 677 Size); 678 } 679 } 680 } 681 682 /* We're done! */ 683 break; 684 685 /* Other information class */ 686 default: 687 688 /* We got some class that we don't support */ 689 DPRINT1("Caller requested unknown class: %lx\n", KeyValueInformationClass); 690 *Status = STATUS_INVALID_PARAMETER; 691 break; 692 } 693 694 /* Return the search result as well */ 695 return Result; 696 } 697 698 VALUE_SEARCH_RETURN_TYPE 699 NTAPI 700 CmpCompareNewValueDataAgainstKCBCache(IN PCM_KEY_CONTROL_BLOCK Kcb, 701 IN PUNICODE_STRING ValueName, 702 IN ULONG Type, 703 IN PVOID Data, 704 IN ULONG DataSize) 705 { 706 VALUE_SEARCH_RETURN_TYPE SearchResult; 707 PCM_KEY_NODE KeyNode; 708 PCM_CACHED_VALUE *CachedValue; 709 ULONG Index; 710 PCM_KEY_VALUE Value; 711 BOOLEAN ValueCached, BufferAllocated = FALSE; 712 PVOID Buffer; 713 HCELL_INDEX ValueCellToRelease = HCELL_NIL, CellToRelease = HCELL_NIL; 714 BOOLEAN IsSmall; 715 ULONG_PTR CompareResult; 716 PAGED_CODE(); 717 718 /* Check if this is a symlink */ 719 if (Kcb->Flags & KEY_SYM_LINK) 720 { 721 /* We need the exclusive lock */ 722 if (!CmpIsKcbLockedExclusive(Kcb) && 723 !CmpTryToConvertKcbSharedToExclusive(Kcb)) 724 { 725 /* We need the exclusive lock */ 726 return SearchNeedExclusiveLock; 727 } 728 729 /* Otherwise, get the key node */ 730 KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell); 731 if (!KeyNode) return SearchFail; 732 733 /* Cleanup the KCB cache */ 734 CmpCleanUpKcbValueCache(Kcb); 735 736 /* Sanity checks */ 737 ASSERT(!CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)); 738 ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND)); 739 740 /* Set the value cache */ 741 Kcb->ValueCache.Count = KeyNode->ValueList.Count; 742 Kcb->ValueCache.ValueList = KeyNode->ValueList.List; 743 744 /* Release the cell */ 745 HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell); 746 } 747 748 /* Do the search */ 749 SearchResult = CmpFindValueByNameFromCache(Kcb, 750 ValueName, 751 &CachedValue, 752 &Index, 753 &Value, 754 &ValueCached, 755 &ValueCellToRelease); 756 if (SearchResult == SearchNeedExclusiveLock) 757 { 758 /* We need the exclusive lock */ 759 ASSERT(!CmpIsKcbLockedExclusive(Kcb)); 760 ASSERT(ValueCellToRelease == HCELL_NIL); 761 ASSERT(Value == NULL); 762 goto Quickie; 763 } 764 else if (SearchResult == SearchSuccess) 765 { 766 /* Sanity check */ 767 ASSERT(Value); 768 769 /* First of all, check if the key size and type matches */ 770 if ((Type == Value->Type) && 771 (DataSize == (Value->DataLength & ~CM_KEY_VALUE_SPECIAL_SIZE))) 772 { 773 /* Check if this is a small key */ 774 IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE; 775 if (IsSmall) 776 { 777 /* Compare against the data directly */ 778 Buffer = &Value->Data; 779 } 780 else 781 { 782 /* Do a search */ 783 SearchResult = CmpGetValueDataFromCache(Kcb, 784 CachedValue, 785 (PCELL_DATA)Value, 786 ValueCached, 787 &Buffer, 788 &BufferAllocated, 789 &CellToRelease); 790 if (SearchResult != SearchSuccess) 791 { 792 /* Sanity checks */ 793 ASSERT(Buffer == NULL); 794 ASSERT(BufferAllocated == FALSE); 795 goto Quickie; 796 } 797 } 798 799 /* Now check the data size */ 800 if (DataSize) 801 { 802 /* Do the compare */ 803 CompareResult = RtlCompareMemory(Buffer, 804 Data, 805 DataSize & 806 ~CM_KEY_VALUE_SPECIAL_SIZE); 807 } 808 else 809 { 810 /* It's equal */ 811 CompareResult = 0; 812 } 813 814 /* Now check if the compare wasn't equal */ 815 if (CompareResult != DataSize) SearchResult = SearchFail; 816 } 817 else 818 { 819 /* The length or type isn't equal */ 820 SearchResult = SearchFail; 821 } 822 } 823 824 Quickie: 825 /* Release the value cell */ 826 if (ValueCellToRelease) HvReleaseCell(Kcb->KeyHive, ValueCellToRelease); 827 828 /* Free the buffer */ 829 if (BufferAllocated) CmpFree(Buffer, 0); 830 831 /* Free the cell */ 832 if (CellToRelease) HvReleaseCell(Kcb->KeyHive, CellToRelease); 833 834 /* Return the search result */ 835 return SearchResult; 836 } 837