xref: /reactos/ntoskrnl/config/cmvalche.c (revision 8a978a17)
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 
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 }
813