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