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)
9 /* INCLUDES ******************************************************************/
18 CmpIsValueCached(IN HCELL_INDEX CellIndex
)
20 /* Make sure that the cell is valid in the first place */
21 if (CellIndex
== HCELL_NIL
) return FALSE
;
23 /*Is this cell actually a pointer to the cached value data? */
24 if (CellIndex
& 1) return TRUE
;
26 /* This is a regular cell */
32 CmpSetValueCached(IN PHCELL_INDEX CellIndex
)
34 /* Set the cached bit */
38 #define ASSERT_VALUE_CACHE() \
39 ASSERTMSG("Cached Values Not Yet Supported!", FALSE);
41 /* FUNCTIONS *****************************************************************/
43 VALUE_SEARCH_RETURN_TYPE
45 CmpGetValueListFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
46 OUT PCELL_DATA
*CellData
,
47 OUT BOOLEAN
*IndexIsCached
,
48 OUT PHCELL_INDEX ValueListToRelease
)
51 PCACHED_CHILD_LIST ChildList
;
52 HCELL_INDEX CellToRelease
;
56 *ValueListToRelease
= HCELL_NIL
;
57 *IndexIsCached
= FALSE
;
61 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
63 /* Get the child value cache */
64 //ChildList = &Kcb->ValueCache;
65 ChildList
= (PCACHED_CHILD_LIST
)&KeyNode
->ValueList
;
67 /* Check if the value is cached */
68 if (CmpIsValueCached(ChildList
->ValueList
))
70 /* It is: we don't expect this yet! */
72 *IndexIsCached
= TRUE
;
77 /* Select the value list as our cell, and get the actual list array */
78 CellToRelease
= ChildList
->ValueList
;
79 *CellData
= (PCELL_DATA
)HvGetCell(Hive
, CellToRelease
);
80 if (!(*CellData
)) return SearchFail
;
82 /* Return the cell to be released */
83 *ValueListToRelease
= CellToRelease
;
86 /* If we got here, then the value list was found */
90 VALUE_SEARCH_RETURN_TYPE
92 CmpGetValueKeyFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
93 IN PCELL_DATA CellData
,
95 OUT PCM_CACHED_VALUE
**CachedValue
,
96 OUT PCM_KEY_VALUE
*Value
,
97 IN BOOLEAN IndexIsCached
,
98 OUT BOOLEAN
*ValueIsCached
,
99 OUT PHCELL_INDEX CellToRelease
)
102 PCM_KEY_VALUE KeyValue
;
106 *CellToRelease
= HCELL_NIL
;
108 *ValueIsCached
= FALSE
;
113 /* Check if the index was cached */
116 /* Not expected yet! */
117 ASSERT_VALUE_CACHE();
118 *ValueIsCached
= TRUE
;
122 /* Get the cell index and the key value associated to it */
123 Cell
= CellData
->u
.KeyList
[Index
];
124 KeyValue
= (PCM_KEY_VALUE
)HvGetCell(Hive
, Cell
);
125 if (!KeyValue
) return SearchFail
;
127 /* Return the cell and the actual key value */
128 *CellToRelease
= Cell
;
132 /* If we got here, then we found the key value */
133 return SearchSuccess
;
136 VALUE_SEARCH_RETURN_TYPE
138 CmpGetValueDataFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
139 IN PCM_CACHED_VALUE
*CachedValue
,
140 IN PCELL_DATA ValueKey
,
141 IN BOOLEAN ValueIsCached
,
142 OUT PVOID
*DataPointer
,
143 OUT PBOOLEAN Allocated
,
144 OUT PHCELL_INDEX CellToRelease
)
150 ASSERT(MAXIMUM_CACHED_DATA
< CM_KEY_VALUE_BIG
);
151 ASSERT((ValueKey
->u
.KeyValue
.DataLength
& CM_KEY_VALUE_SPECIAL_SIZE
) == 0);
156 *CellToRelease
= HCELL_NIL
;
161 /* Check it the value is cached */
164 /* This isn't expected! */
165 ASSERT_VALUE_CACHE();
169 /* It's not, get the value data using the typical routine */
170 if (!CmpGetValueData(Hive
,
171 &ValueKey
->u
.KeyValue
,
177 /* Nothing found: make sure no data was allocated */
178 ASSERT(*Allocated
== FALSE
);
179 ASSERT(*DataPointer
== NULL
);
184 /* We found the actual data, return success */
185 return SearchSuccess
;
188 VALUE_SEARCH_RETURN_TYPE
190 CmpFindValueByNameFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
191 IN PCUNICODE_STRING Name
,
192 OUT PCM_CACHED_VALUE
**CachedValue
,
194 OUT PCM_KEY_VALUE
*Value
,
195 OUT BOOLEAN
*ValueIsCached
,
196 OUT PHCELL_INDEX CellToRelease
)
199 VALUE_SEARCH_RETURN_TYPE SearchResult
= SearchFail
;
201 UNICODE_STRING SearchName
;
203 PCACHED_CHILD_LIST ChildList
;
204 PCM_KEY_VALUE KeyValue
;
205 BOOLEAN IndexIsCached
;
207 HCELL_INDEX Cell
= HCELL_NIL
;
208 PCM_KEY_NODE KeyNode
;
211 *CellToRelease
= HCELL_NIL
;
216 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
218 /* Get the child value cache */
219 //ChildList = &Kcb->ValueCache;
220 ChildList
= (PCACHED_CHILD_LIST
)&KeyNode
->ValueList
;
222 /* Check if the child list has any entries */
223 if (ChildList
->Count
!= 0)
225 /* Get the value list associated to this child list */
226 SearchResult
= CmpGetValueListFromCache(Kcb
,
230 if (SearchResult
!= SearchSuccess
) return SearchResult
;
232 /* The index shouldn't be cached right now */
233 if (IndexIsCached
) ASSERT_VALUE_CACHE();
235 /* Loop every value */
238 /* Check if there's any cell to release */
239 if (*CellToRelease
!= HCELL_NIL
)
242 HvReleaseCell(Hive
, *CellToRelease
);
243 *CellToRelease
= HCELL_NIL
;
246 /* Get the key value for this index */
247 SearchResult
= CmpGetValueKeyFromCache(Kcb
,
255 if (SearchResult
!= SearchSuccess
) return SearchResult
;
257 /* Check if the both the index and the value are cached */
258 if ((IndexIsCached
) && (*ValueIsCached
))
260 /* We don't expect this yet */
261 ASSERT_VALUE_CACHE();
266 /* No cache, so try to compare the name. Is it compressed? */
268 if (KeyValue
->Flags
& VALUE_COMP_NAME
)
270 /* It is, do a compressed name comparison */
271 Result
= CmpCompareCompressedName(Name
,
273 KeyValue
->NameLength
);
277 /* It's not compressed, so do a standard comparison */
278 SearchName
.Length
= KeyValue
->NameLength
;
279 SearchName
.MaximumLength
= SearchName
.Length
;
280 SearchName
.Buffer
= KeyValue
->Name
;
281 Result
= RtlCompareUnicodeString(Name
, &SearchName
, TRUE
);
285 /* Check if we found the value data */
288 /* We have, return the index of the value and success */
290 SearchResult
= SearchSuccess
;
294 /* We didn't find it, try the next entry */
295 if (++i
== ChildList
->Count
)
297 /* The entire list was parsed, fail */
299 SearchResult
= SearchFail
;
305 /* We should only get here if the child list is empty */
306 ASSERT(ChildList
->Count
== 0);
309 /* Release the value list cell if required, and return search result */
310 if (Cell
!= HCELL_NIL
) HvReleaseCell(Hive
, Cell
);
314 VALUE_SEARCH_RETURN_TYPE
316 CmpQueryKeyValueData(IN PCM_KEY_CONTROL_BLOCK Kcb
,
317 IN PCM_CACHED_VALUE
*CachedValue
,
318 IN PCM_KEY_VALUE ValueKey
,
319 IN BOOLEAN ValueIsCached
,
320 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
321 IN PVOID KeyValueInformation
,
323 OUT PULONG ResultLength
,
324 OUT PNTSTATUS Status
)
327 PKEY_VALUE_INFORMATION Info
= (PKEY_VALUE_INFORMATION
)KeyValueInformation
;
330 ULONG Size
, MinimumSize
, SizeLeft
, KeySize
, AlignedData
= 0, DataOffset
;
332 BOOLEAN IsSmall
, BufferAllocated
= FALSE
;
333 HCELL_INDEX CellToRelease
= HCELL_NIL
;
334 VALUE_SEARCH_RETURN_TYPE Result
= SearchSuccess
;
336 /* Get the hive and cell data */
338 CellData
= (PCELL_DATA
)ValueKey
;
340 /* Check if the value is compressed */
341 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
343 /* Get the compressed name size */
344 NameSize
= CmpCompressedNameSize(CellData
->u
.KeyValue
.Name
,
345 CellData
->u
.KeyValue
.NameLength
);
349 /* Get the real size */
350 NameSize
= CellData
->u
.KeyValue
.NameLength
;
353 /* Check what kind of information the caller is requesting */
354 switch (KeyValueInformationClass
)
356 /* Basic information */
357 case KeyValueBasicInformation
:
359 /* This is how much size we'll need */
360 Size
= FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION
, Name
) + NameSize
;
362 /* This is the minimum we can work with */
363 MinimumSize
= FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION
, Name
);
365 /* Return the size we'd like, and assume success */
366 *ResultLength
= Size
;
367 *Status
= STATUS_SUCCESS
;
369 /* Check if the caller gave us below our minimum */
370 if (Length
< MinimumSize
)
372 /* Then we must fail */
373 *Status
= STATUS_BUFFER_TOO_SMALL
;
377 /* Fill out the basic information */
378 Info
->KeyValueBasicInformation
.TitleIndex
= 0;
379 Info
->KeyValueBasicInformation
.Type
= CellData
->u
.KeyValue
.Type
;
380 Info
->KeyValueBasicInformation
.NameLength
= NameSize
;
382 /* Now only the name is left */
383 SizeLeft
= Length
- MinimumSize
;
386 /* Check if the remaining buffer is too small for the name */
389 /* Copy only as much as can fit, and tell the caller */
391 *Status
= STATUS_BUFFER_OVERFLOW
;
394 /* Check if this is a compressed name */
395 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
397 /* Copy as much as we can of the compressed name */
398 CmpCopyCompressedName(Info
->KeyValueBasicInformation
.Name
,
400 CellData
->u
.KeyValue
.Name
,
401 CellData
->u
.KeyValue
.NameLength
);
405 /* Copy as much as we can of the raw name */
406 RtlCopyMemory(Info
->KeyValueBasicInformation
.Name
,
407 CellData
->u
.KeyValue
.Name
,
414 /* Full key information */
415 case KeyValueFullInformation
:
417 /* Check if this is a small key and compute key size */
418 IsSmall
= CmpIsKeyValueSmall(&KeySize
,
419 CellData
->u
.KeyValue
.DataLength
);
421 /* Calculate the total size required */
422 Size
= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
) +
426 /* And this is the least we can work with */
427 MinimumSize
= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
);
429 /* Check if there's any key data */
432 /* Calculate the data offset */
433 DataOffset
= Size
- KeySize
;
435 /* Align the offset to 4 bytes */
436 AlignedData
= ALIGN_UP(DataOffset
, ULONG
);
438 /* If alignment was required, we'll need more space */
439 if (AlignedData
> DataOffset
) Size
+= (AlignedData
-DataOffset
);
442 /* Tell the caller the size we'll finally need, and set success */
443 *ResultLength
= Size
;
444 *Status
= STATUS_SUCCESS
;
446 /* Check if the caller is giving us too little */
447 if (Length
< MinimumSize
)
449 /* Then fail right now */
450 *Status
= STATUS_BUFFER_TOO_SMALL
;
454 /* Fill out the basic information */
455 Info
->KeyValueFullInformation
.TitleIndex
= 0;
456 Info
->KeyValueFullInformation
.Type
= CellData
->u
.KeyValue
.Type
;
457 Info
->KeyValueFullInformation
.DataLength
= KeySize
;
458 Info
->KeyValueFullInformation
.NameLength
= NameSize
;
460 /* Only the name is left now */
461 SizeLeft
= Length
- MinimumSize
;
464 /* Check if the name fits */
467 /* It doesn't, truncate what we'll copy, and tell the caller */
469 *Status
= STATUS_BUFFER_OVERFLOW
;
472 /* Check if this key value is compressed */
473 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
475 /* It is, copy the compressed name */
476 CmpCopyCompressedName(Info
->KeyValueFullInformation
.Name
,
478 CellData
->u
.KeyValue
.Name
,
479 CellData
->u
.KeyValue
.NameLength
);
483 /* It's not, copy the raw name */
484 RtlCopyMemory(Info
->KeyValueFullInformation
.Name
,
485 CellData
->u
.KeyValue
.Name
,
489 /* Now check if the key had any data */
492 /* Was it a small key? */
495 /* Then the data is directly into the cell */
496 Buffer
= &CellData
->u
.KeyValue
.Data
;
500 /* Otherwise, we must retrieve it from the value cache */
501 Result
= CmpGetValueDataFromCache(Kcb
,
508 if (Result
!= SearchSuccess
)
510 /* We failed, nothing should be allocated */
511 ASSERT(Buffer
== NULL
);
512 ASSERT(BufferAllocated
== FALSE
);
513 *Status
= STATUS_INSUFFICIENT_RESOURCES
;
517 /* Now that we know we truly have data, set its offset */
518 Info
->KeyValueFullInformation
.DataOffset
= AlignedData
;
520 /* Only the data remains to be copied */
521 SizeLeft
= (((LONG
)Length
- (LONG
)AlignedData
) < 0) ?
522 0 : (Length
- AlignedData
);
525 /* Check if the caller has no space for it */
528 /* Truncate what we'll copy, and tell the caller */
530 *Status
= STATUS_BUFFER_OVERFLOW
;
534 ASSERT((IsSmall
? (Size
<= CM_KEY_VALUE_SMALL
) : TRUE
));
536 /* Make sure we have a valid buffer */
539 /* Copy the data into the aligned offset */
540 RtlCopyMemory((PVOID
)((ULONG_PTR
)Info
+ AlignedData
),
547 /* We don't have any data, set the offset to -1, not 0! */
548 Info
->KeyValueFullInformation
.DataOffset
= 0xFFFFFFFF;
554 /* Partial information requested (no name or alignment!) */
555 case KeyValuePartialInformation
:
557 /* Check if this is a small key and compute key size */
558 IsSmall
= CmpIsKeyValueSmall(&KeySize
,
559 CellData
->u
.KeyValue
.DataLength
);
561 /* Calculate the total size required */
562 Size
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
) + KeySize
;
564 /* And this is the least we can work with */
565 MinimumSize
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
567 /* Tell the caller the size we'll finally need, and set success */
568 *ResultLength
= Size
;
569 *Status
= STATUS_SUCCESS
;
571 /* Check if the caller is giving us too little */
572 if (Length
< MinimumSize
)
574 /* Then fail right now */
575 *Status
= STATUS_BUFFER_TOO_SMALL
;
579 /* Fill out the basic information */
580 Info
->KeyValuePartialInformation
.TitleIndex
= 0;
581 Info
->KeyValuePartialInformation
.Type
= CellData
->u
.KeyValue
.Type
;
582 Info
->KeyValuePartialInformation
.DataLength
= KeySize
;
584 /* Now check if the key had any data */
587 /* Was it a small key? */
590 /* Then the data is directly into the cell */
591 Buffer
= &CellData
->u
.KeyValue
.Data
;
595 /* Otherwise, we must retrieve it from the value cache */
596 Result
= CmpGetValueDataFromCache(Kcb
,
603 if (Result
!= SearchSuccess
)
605 /* We failed, nothing should be allocated */
606 ASSERT(Buffer
== NULL
);
607 ASSERT(BufferAllocated
== FALSE
);
608 *Status
= STATUS_INSUFFICIENT_RESOURCES
;
612 /* Only the data remains to be copied */
613 SizeLeft
= Length
- MinimumSize
;
616 /* Check if the caller has no space for it */
619 /* Truncate what we'll copy, and tell the caller */
621 *Status
= STATUS_BUFFER_OVERFLOW
;
625 ASSERT((IsSmall
? (Size
<= CM_KEY_VALUE_SMALL
) : TRUE
));
627 /* Make sure we have a valid buffer */
630 /* Copy the data into the aligned offset */
631 RtlCopyMemory(Info
->KeyValuePartialInformation
.Data
,
640 /* Other information class */
643 /* We got some class that we don't support */
644 *Status
= STATUS_INVALID_PARAMETER
;
648 /* Return the search result as well */