* Sync up to trunk head (r64894).
[reactos.git] / ntoskrnl / config / cmvalche.c
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!", 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
578 /* Check if this is a small key and compute key size */
579 IsSmall = CmpIsKeyValueSmall(&KeySize,
580 CellData->u.KeyValue.DataLength);
581
582 /* Calculate the total size required */
583 Size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + KeySize;
584
585 /* And this is the least we can work with */
586 MinimumSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
587
588 /* Tell the caller the size we'll finally need, and set success */
589 *ResultLength = Size;
590 *Status = STATUS_SUCCESS;
591
592 /* Check if the caller is giving us too little */
593 if (Length < MinimumSize)
594 {
595 /* Then fail right now */
596 *Status = STATUS_BUFFER_TOO_SMALL;
597 break;
598 }
599
600 /* Fill out the basic information */
601 Info->KeyValuePartialInformation.TitleIndex = 0;
602 Info->KeyValuePartialInformation.Type = CellData->u.KeyValue.Type;
603 Info->KeyValuePartialInformation.DataLength = KeySize;
604
605 /* Now check if the key had any data */
606 if (KeySize > 0)
607 {
608 /* Was it a small key? */
609 if (IsSmall)
610 {
611 /* Then the data is directly into the cell */
612 Buffer = &CellData->u.KeyValue.Data;
613 }
614 else
615 {
616 /* Otherwise, we must retrieve it from the value cache */
617 Result = CmpGetValueDataFromCache(Kcb,
618 CachedValue,
619 CellData,
620 ValueIsCached,
621 &Buffer,
622 &BufferAllocated,
623 &CellToRelease);
624 if (Result != SearchSuccess)
625 {
626 /* We failed, nothing should be allocated */
627 ASSERT(Buffer == NULL);
628 ASSERT(BufferAllocated == FALSE);
629 *Status = STATUS_INSUFFICIENT_RESOURCES;
630 }
631 }
632
633 /* Only the data remains to be copied */
634 SizeLeft = Length - MinimumSize;
635 Size = KeySize;
636
637 /* Check if the caller has no space for it */
638 if (SizeLeft < Size)
639 {
640 /* Truncate what we'll copy, and tell the caller */
641 Size = SizeLeft;
642 *Status = STATUS_BUFFER_OVERFLOW;
643 }
644
645 /* Sanity check */
646 ASSERT((IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE));
647
648 /* Make sure we have a valid buffer */
649 if (Buffer)
650 {
651 /* Copy the data into the aligned offset */
652 RtlCopyMemory(Info->KeyValuePartialInformation.Data,
653 Buffer,
654 Size);
655 }
656 }
657
658 /* We're done! */
659 break;
660
661 /* Other information class */
662 default:
663
664 /* We got some class that we don't support */
665 DPRINT1("Caller requested unknown class: %lx\n", KeyValueInformationClass);
666 *Status = STATUS_INVALID_PARAMETER;
667 break;
668 }
669
670 /* Return the search result as well */
671 return Result;
672 }
673
674 VALUE_SEARCH_RETURN_TYPE
675 NTAPI
676 CmpCompareNewValueDataAgainstKCBCache(IN PCM_KEY_CONTROL_BLOCK Kcb,
677 IN PUNICODE_STRING ValueName,
678 IN ULONG Type,
679 IN PVOID Data,
680 IN ULONG DataSize)
681 {
682 VALUE_SEARCH_RETURN_TYPE SearchResult;
683 PCM_KEY_NODE KeyNode;
684 PCM_CACHED_VALUE *CachedValue;
685 ULONG Index;
686 PCM_KEY_VALUE Value;
687 BOOLEAN ValueCached, BufferAllocated = FALSE;
688 PVOID Buffer;
689 HCELL_INDEX ValueCellToRelease = HCELL_NIL, CellToRelease = HCELL_NIL;
690 BOOLEAN IsSmall;
691 ULONG_PTR CompareResult;
692 PAGED_CODE();
693
694 /* Check if this is a symlink */
695 if (Kcb->Flags & KEY_SYM_LINK)
696 {
697 /* We need the exclusive lock */
698 if (!(CmpIsKcbLockedExclusive(Kcb)) &&
699 !(CmpTryToConvertKcbSharedToExclusive(Kcb)))
700 {
701 /* We need the exclusive lock */
702 return SearchNeedExclusiveLock;
703 }
704
705 /* Otherwise, get the key node */
706 KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell);
707 if (!KeyNode) return SearchFail;
708
709 /* Cleanup the KCB cache */
710 CmpCleanUpKcbValueCache(Kcb);
711
712 /* Sanity checks */
713 ASSERT(!(CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList)));
714 ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
715
716 /* Set the value cache */
717 Kcb->ValueCache.Count = KeyNode->ValueList.Count;
718 Kcb->ValueCache.ValueList = KeyNode->ValueList.List;
719
720 /* Release the cell */
721 HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell);
722 }
723
724 /* Do the search */
725 SearchResult = CmpFindValueByNameFromCache(Kcb,
726 ValueName,
727 &CachedValue,
728 &Index,
729 &Value,
730 &ValueCached,
731 &ValueCellToRelease);
732 if (SearchResult == SearchNeedExclusiveLock)
733 {
734 /* We need the exclusive lock */
735 ASSERT(!CmpIsKcbLockedExclusive(Kcb));
736 ASSERT(ValueCellToRelease == HCELL_NIL);
737 ASSERT(Value == NULL);
738 goto Quickie;
739 }
740 else if (SearchResult == SearchSuccess)
741 {
742 /* Sanity check */
743 ASSERT(Value);
744
745 /* First of all, check if the key size and type matches */
746 if ((Type == Value->Type) &&
747 (DataSize == (Value->DataLength & ~CM_KEY_VALUE_SPECIAL_SIZE)))
748 {
749 /* Check if this is a small key */
750 IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE;
751 if (IsSmall)
752 {
753 /* Compare against the data directly */
754 Buffer = &Value->Data;
755 }
756 else
757 {
758 /* Do a search */
759 SearchResult = CmpGetValueDataFromCache(Kcb,
760 CachedValue,
761 (PCELL_DATA)Value,
762 ValueCached,
763 &Buffer,
764 &BufferAllocated,
765 &CellToRelease);
766 if (SearchResult != SearchSuccess)
767 {
768 /* Sanity checks */
769 ASSERT(Buffer == NULL);
770 ASSERT(BufferAllocated == FALSE);
771 goto Quickie;
772 }
773 }
774
775 /* Now check the data size */
776 if (DataSize)
777 {
778 /* Do the compare */
779 CompareResult = RtlCompareMemory(Buffer,
780 Data,
781 DataSize &
782 ~CM_KEY_VALUE_SPECIAL_SIZE);
783 }
784 else
785 {
786 /* It's equal */
787 CompareResult = 0;
788 }
789
790 /* Now check if the compare wasn't equal */
791 if (CompareResult != DataSize) SearchResult = SearchFail;
792 }
793 else
794 {
795 /* The length or type isn't equal */
796 SearchResult = SearchFail;
797 }
798 }
799
800 Quickie:
801 /* Release the value cell */
802 if (ValueCellToRelease) HvReleaseCell(Kcb->KeyHive, ValueCellToRelease);
803
804 /* Free the buffer */
805 if (BufferAllocated) CmpFree(Buffer, 0);
806
807 /* Free the cell */
808 if (CellToRelease) HvReleaseCell(Kcb->KeyHive, CellToRelease);
809
810 /* Return the search result */
811 return SearchResult;
812 }