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