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 ******************************************************************/
17 CmpIsValueCached(IN HCELL_INDEX CellIndex
)
19 /* Make sure that the cell is valid in the first place */
20 if (CellIndex
== HCELL_NIL
) return FALSE
;
22 /*Is this cell actually a pointer to the cached value data? */
23 if (CellIndex
& 1) return TRUE
;
25 /* This is a regular cell */
31 CmpSetValueCached(IN PHCELL_INDEX CellIndex
)
33 /* Set the cached bit */
37 #define ASSERT_VALUE_CACHE() \
38 ASSERTMSG("Cached Values Not Yet Supported!", FALSE);
40 /* FUNCTIONS *****************************************************************/
42 VALUE_SEARCH_RETURN_TYPE
44 CmpGetValueListFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
45 OUT PCELL_DATA
*CellData
,
46 OUT BOOLEAN
*IndexIsCached
,
47 OUT PHCELL_INDEX ValueListToRelease
)
50 PCACHED_CHILD_LIST ChildList
;
51 HCELL_INDEX CellToRelease
;
54 *ValueListToRelease
= HCELL_NIL
;
55 *IndexIsCached
= FALSE
;
57 /* Get the hive and value cache */
59 ChildList
= &Kcb
->ValueCache
;
61 /* Check if the value is cached */
62 if (CmpIsValueCached(ChildList
->ValueList
))
64 /* It is: we don't expect this yet! */
66 *IndexIsCached
= TRUE
;
71 /* Make sure the KCB is locked exclusive */
72 if (!(CmpIsKcbLockedExclusive(Kcb
)) &&
73 !(CmpTryToConvertKcbSharedToExclusive(Kcb
)))
75 /* We need the exclusive lock */
76 return SearchNeedExclusiveLock
;
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
;
84 /* FIXME: Here we would cache the value */
86 /* Return the cell to be released */
87 *ValueListToRelease
= CellToRelease
;
90 /* If we got here, then the value list was found */
94 VALUE_SEARCH_RETURN_TYPE
96 CmpGetValueKeyFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
97 IN PCELL_DATA CellData
,
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
)
106 PCM_KEY_VALUE KeyValue
;
110 *CellToRelease
= HCELL_NIL
;
112 *ValueIsCached
= FALSE
;
117 /* Check if the index was cached */
120 /* Not expected yet! */
121 ASSERT_VALUE_CACHE();
122 *ValueIsCached
= TRUE
;
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
;
131 /* Return the cell and the actual key value */
132 *CellToRelease
= Cell
;
136 /* If we got here, then we found the key value */
137 return SearchSuccess
;
140 VALUE_SEARCH_RETURN_TYPE
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
)
154 ASSERT(MAXIMUM_CACHED_DATA
< CM_KEY_VALUE_BIG
);
155 ASSERT((ValueKey
->u
.KeyValue
.DataLength
& CM_KEY_VALUE_SPECIAL_SIZE
) == 0);
160 *CellToRelease
= HCELL_NIL
;
165 /* Check it the value is cached */
168 /* This isn't expected! */
169 ASSERT_VALUE_CACHE();
173 /* It's not, get the value data using the typical routine */
174 if (!CmpGetValueData(Hive
,
175 &ValueKey
->u
.KeyValue
,
181 /* Nothing found: make sure no data was allocated */
182 ASSERT(*Allocated
== FALSE
);
183 ASSERT(*DataPointer
== NULL
);
188 /* We found the actual data, return success */
189 return SearchSuccess
;
192 VALUE_SEARCH_RETURN_TYPE
194 CmpFindValueByNameFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
195 IN PCUNICODE_STRING Name
,
196 OUT PCM_CACHED_VALUE
**CachedValue
,
198 OUT PCM_KEY_VALUE
*Value
,
199 OUT BOOLEAN
*ValueIsCached
,
200 OUT PHCELL_INDEX CellToRelease
)
203 VALUE_SEARCH_RETURN_TYPE SearchResult
= SearchFail
;
205 UNICODE_STRING SearchName
;
207 PCACHED_CHILD_LIST ChildList
;
208 PCM_KEY_VALUE KeyValue
;
209 BOOLEAN IndexIsCached
;
211 HCELL_INDEX Cell
= HCELL_NIL
;
214 *CellToRelease
= HCELL_NIL
;
217 /* Get the hive and child list */
219 ChildList
= &Kcb
->ValueCache
;
221 /* Check if the child list has any entries */
222 if (ChildList
->Count
!= 0)
224 /* Get the value list associated to this child list */
225 SearchResult
= CmpGetValueListFromCache(Kcb
,
229 if (SearchResult
!= SearchSuccess
)
231 /* We either failed or need the exclusive lock */
232 ASSERT((SearchResult
== SearchFail
) || !(CmpIsKcbLockedExclusive(Kcb
)));
233 ASSERT(Cell
== HCELL_NIL
);
237 /* The index shouldn't be cached right now */
238 if (IndexIsCached
) ASSERT_VALUE_CACHE();
240 /* Loop every value */
243 /* Check if there's any cell to release */
244 if (*CellToRelease
!= HCELL_NIL
)
247 HvReleaseCell(Hive
, *CellToRelease
);
248 *CellToRelease
= HCELL_NIL
;
251 /* Get the key value for this index */
252 SearchResult
= CmpGetValueKeyFromCache(Kcb
,
260 if (SearchResult
!= SearchSuccess
)
262 /* We either failed or need the exclusive lock */
263 ASSERT((SearchResult
== SearchFail
) || !(CmpIsKcbLockedExclusive(Kcb
)));
264 ASSERT(Cell
== HCELL_NIL
);
268 /* Check if the both the index and the value are cached */
269 if ((IndexIsCached
) && (*ValueIsCached
))
271 /* We don't expect this yet */
272 ASSERT_VALUE_CACHE();
277 /* No cache, so try to compare the name. Is it compressed? */
279 if (KeyValue
->Flags
& VALUE_COMP_NAME
)
281 /* It is, do a compressed name comparison */
282 Result
= CmpCompareCompressedName(Name
,
284 KeyValue
->NameLength
);
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
);
296 /* Check if we found the value data */
299 /* We have, return the index of the value and success */
301 SearchResult
= SearchSuccess
;
305 /* We didn't find it, try the next entry */
306 if (++i
== ChildList
->Count
)
308 /* The entire list was parsed, fail */
310 SearchResult
= SearchFail
;
316 /* We should only get here if the child list is empty */
317 ASSERT(ChildList
->Count
== 0);
320 /* Release the value list cell if required, and return search result */
321 if (Cell
!= HCELL_NIL
) HvReleaseCell(Hive
, Cell
);
325 VALUE_SEARCH_RETURN_TYPE
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
,
334 OUT PULONG ResultLength
,
335 OUT PNTSTATUS Status
)
337 PKEY_VALUE_INFORMATION Info
= (PKEY_VALUE_INFORMATION
)KeyValueInformation
;
340 ULONG Size
, MinimumSize
, SizeLeft
, KeySize
, AlignedData
= 0, DataOffset
;
342 BOOLEAN IsSmall
, BufferAllocated
= FALSE
;
343 HCELL_INDEX CellToRelease
= HCELL_NIL
;
344 VALUE_SEARCH_RETURN_TYPE Result
= SearchSuccess
;
346 /* Get the value data */
347 CellData
= (PCELL_DATA
)ValueKey
;
349 /* Check if the value is compressed */
350 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
352 /* Get the compressed name size */
353 NameSize
= CmpCompressedNameSize(CellData
->u
.KeyValue
.Name
,
354 CellData
->u
.KeyValue
.NameLength
);
358 /* Get the real size */
359 NameSize
= CellData
->u
.KeyValue
.NameLength
;
362 /* Check what kind of information the caller is requesting */
363 switch (KeyValueInformationClass
)
365 /* Basic information */
366 case KeyValueBasicInformation
:
368 /* This is how much size we'll need */
369 Size
= FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION
, Name
) + NameSize
;
371 /* This is the minimum we can work with */
372 MinimumSize
= FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION
, Name
);
374 /* Return the size we'd like, and assume success */
375 *ResultLength
= Size
;
376 *Status
= STATUS_SUCCESS
;
378 /* Check if the caller gave us below our minimum */
379 if (Length
< MinimumSize
)
381 /* Then we must fail */
382 *Status
= STATUS_BUFFER_TOO_SMALL
;
386 /* Fill out the basic information */
387 Info
->KeyValueBasicInformation
.TitleIndex
= 0;
388 Info
->KeyValueBasicInformation
.Type
= CellData
->u
.KeyValue
.Type
;
389 Info
->KeyValueBasicInformation
.NameLength
= NameSize
;
391 /* Now only the name is left */
392 SizeLeft
= Length
- MinimumSize
;
395 /* Check if the remaining buffer is too small for the name */
398 /* Copy only as much as can fit, and tell the caller */
400 *Status
= STATUS_BUFFER_OVERFLOW
;
403 /* Check if this is a compressed name */
404 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
406 /* Copy as much as we can of the compressed name */
407 CmpCopyCompressedName(Info
->KeyValueBasicInformation
.Name
,
409 CellData
->u
.KeyValue
.Name
,
410 CellData
->u
.KeyValue
.NameLength
);
414 /* Copy as much as we can of the raw name */
415 RtlCopyMemory(Info
->KeyValueBasicInformation
.Name
,
416 CellData
->u
.KeyValue
.Name
,
423 /* Full key information */
424 case KeyValueFullInformation
:
425 case KeyValueFullInformationAlign64
:
427 /* Check if this is a small key and compute key size */
428 IsSmall
= CmpIsKeyValueSmall(&KeySize
,
429 CellData
->u
.KeyValue
.DataLength
);
431 /* Calculate the total size required */
432 Size
= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
) +
436 /* And this is the least we can work with */
437 MinimumSize
= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
);
439 /* Check if there's any key data */
442 /* Calculate the data offset */
443 DataOffset
= Size
- KeySize
;
446 /* On 64-bit, always align to 8 bytes */
447 AlignedData
= ALIGN_UP(DataOffset
, ULONGLONG
);
449 /* On 32-bit, align the offset to 4 or 8 bytes */
450 if (KeyValueInformationClass
== KeyValueFullInformationAlign64
)
452 AlignedData
= ALIGN_UP(DataOffset
, ULONGLONG
);
456 AlignedData
= ALIGN_UP(DataOffset
, ULONG
);
459 /* If alignment was required, we'll need more space */
460 if (AlignedData
> DataOffset
) Size
+= (AlignedData
-DataOffset
);
463 /* Tell the caller the size we'll finally need, and set success */
464 *ResultLength
= Size
;
465 *Status
= STATUS_SUCCESS
;
467 /* Check if the caller is giving us too little */
468 if (Length
< MinimumSize
)
470 /* Then fail right now */
471 *Status
= STATUS_BUFFER_TOO_SMALL
;
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
;
481 /* Only the name is left now */
482 SizeLeft
= Length
- MinimumSize
;
485 /* Check if the name fits */
488 /* It doesn't, truncate what we'll copy, and tell the caller */
490 *Status
= STATUS_BUFFER_OVERFLOW
;
493 /* Check if this key value is compressed */
494 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
496 /* It is, copy the compressed name */
497 CmpCopyCompressedName(Info
->KeyValueFullInformation
.Name
,
499 CellData
->u
.KeyValue
.Name
,
500 CellData
->u
.KeyValue
.NameLength
);
504 /* It's not, copy the raw name */
505 RtlCopyMemory(Info
->KeyValueFullInformation
.Name
,
506 CellData
->u
.KeyValue
.Name
,
510 /* Now check if the key had any data */
513 /* Was it a small key? */
516 /* Then the data is directly into the cell */
517 Buffer
= &CellData
->u
.KeyValue
.Data
;
521 /* Otherwise, we must retrieve it from the value cache */
522 Result
= CmpGetValueDataFromCache(Kcb
,
529 if (Result
!= SearchSuccess
)
531 /* We failed, nothing should be allocated */
532 ASSERT(Buffer
== NULL
);
533 ASSERT(BufferAllocated
== FALSE
);
534 *Status
= STATUS_INSUFFICIENT_RESOURCES
;
538 /* Now that we know we truly have data, set its offset */
539 Info
->KeyValueFullInformation
.DataOffset
= AlignedData
;
541 /* Only the data remains to be copied */
542 SizeLeft
= (((LONG
)Length
- (LONG
)AlignedData
) < 0) ?
543 0 : (Length
- AlignedData
);
546 /* Check if the caller has no space for it */
549 /* Truncate what we'll copy, and tell the caller */
551 *Status
= STATUS_BUFFER_OVERFLOW
;
555 ASSERT((IsSmall
? (Size
<= CM_KEY_VALUE_SMALL
) : TRUE
));
557 /* Make sure we have a valid buffer */
560 /* Copy the data into the aligned offset */
561 RtlCopyMemory((PVOID
)((ULONG_PTR
)Info
+ AlignedData
),
568 /* We don't have any data, set the offset to -1, not 0! */
569 Info
->KeyValueFullInformation
.DataOffset
= 0xFFFFFFFF;
575 /* Partial information requested (no name or alignment!) */
576 case KeyValuePartialInformation
:
578 /* Check if this is a small key and compute key size */
579 IsSmall
= CmpIsKeyValueSmall(&KeySize
,
580 CellData
->u
.KeyValue
.DataLength
);
582 /* Calculate the total size required */
583 Size
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
) + KeySize
;
585 /* And this is the least we can work with */
586 MinimumSize
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
588 /* Tell the caller the size we'll finally need, and set success */
589 *ResultLength
= Size
;
590 *Status
= STATUS_SUCCESS
;
592 /* Check if the caller is giving us too little */
593 if (Length
< MinimumSize
)
595 /* Then fail right now */
596 *Status
= STATUS_BUFFER_TOO_SMALL
;
600 /* Fill out the basic information */
601 Info
->KeyValuePartialInformation
.TitleIndex
= 0;
602 Info
->KeyValuePartialInformation
.Type
= CellData
->u
.KeyValue
.Type
;
603 Info
->KeyValuePartialInformation
.DataLength
= KeySize
;
605 /* Now check if the key had any data */
608 /* Was it a small key? */
611 /* Then the data is directly into the cell */
612 Buffer
= &CellData
->u
.KeyValue
.Data
;
616 /* Otherwise, we must retrieve it from the value cache */
617 Result
= CmpGetValueDataFromCache(Kcb
,
624 if (Result
!= SearchSuccess
)
626 /* We failed, nothing should be allocated */
627 ASSERT(Buffer
== NULL
);
628 ASSERT(BufferAllocated
== FALSE
);
629 *Status
= STATUS_INSUFFICIENT_RESOURCES
;
633 /* Only the data remains to be copied */
634 SizeLeft
= Length
- MinimumSize
;
637 /* Check if the caller has no space for it */
640 /* Truncate what we'll copy, and tell the caller */
642 *Status
= STATUS_BUFFER_OVERFLOW
;
646 ASSERT((IsSmall
? (Size
<= CM_KEY_VALUE_SMALL
) : TRUE
));
648 /* Make sure we have a valid buffer */
651 /* Copy the data into the aligned offset */
652 RtlCopyMemory(Info
->KeyValuePartialInformation
.Data
,
661 /* Other information class */
664 /* We got some class that we don't support */
665 DPRINT1("Caller requested unknown class: %lx\n", KeyValueInformationClass
);
666 *Status
= STATUS_INVALID_PARAMETER
;
670 /* Return the search result as well */
674 VALUE_SEARCH_RETURN_TYPE
676 CmpCompareNewValueDataAgainstKCBCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
677 IN PUNICODE_STRING ValueName
,
682 VALUE_SEARCH_RETURN_TYPE SearchResult
;
683 PCM_KEY_NODE KeyNode
;
684 PCM_CACHED_VALUE
*CachedValue
;
687 BOOLEAN ValueCached
, BufferAllocated
= FALSE
;
689 HCELL_INDEX ValueCellToRelease
= HCELL_NIL
, CellToRelease
= HCELL_NIL
;
691 ULONG_PTR CompareResult
;
694 /* Check if this is a symlink */
695 if (Kcb
->Flags
& KEY_SYM_LINK
)
697 /* We need the exclusive lock */
698 if (!(CmpIsKcbLockedExclusive(Kcb
)) &&
699 !(CmpTryToConvertKcbSharedToExclusive(Kcb
)))
701 /* We need the exclusive lock */
702 return SearchNeedExclusiveLock
;
705 /* Otherwise, get the key node */
706 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Kcb
->KeyHive
, Kcb
->KeyCell
);
707 if (!KeyNode
) return SearchFail
;
709 /* Cleanup the KCB cache */
710 CmpCleanUpKcbValueCache(Kcb
);
713 ASSERT(!(CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)));
714 ASSERT(!(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
));
716 /* Set the value cache */
717 Kcb
->ValueCache
.Count
= KeyNode
->ValueList
.Count
;
718 Kcb
->ValueCache
.ValueList
= KeyNode
->ValueList
.List
;
720 /* Release the cell */
721 HvReleaseCell(Kcb
->KeyHive
, Kcb
->KeyCell
);
725 SearchResult
= CmpFindValueByNameFromCache(Kcb
,
731 &ValueCellToRelease
);
732 if (SearchResult
== SearchNeedExclusiveLock
)
734 /* We need the exclusive lock */
735 ASSERT(!CmpIsKcbLockedExclusive(Kcb
));
736 ASSERT(ValueCellToRelease
== HCELL_NIL
);
737 ASSERT(Value
== NULL
);
740 else if (SearchResult
== SearchSuccess
)
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
)))
749 /* Check if this is a small key */
750 IsSmall
= (DataSize
<= CM_KEY_VALUE_SMALL
) ? TRUE
: FALSE
;
753 /* Compare against the data directly */
754 Buffer
= &Value
->Data
;
759 SearchResult
= CmpGetValueDataFromCache(Kcb
,
766 if (SearchResult
!= SearchSuccess
)
769 ASSERT(Buffer
== NULL
);
770 ASSERT(BufferAllocated
== FALSE
);
775 /* Now check the data size */
779 CompareResult
= RtlCompareMemory(Buffer
,
782 ~CM_KEY_VALUE_SPECIAL_SIZE
);
790 /* Now check if the compare wasn't equal */
791 if (CompareResult
!= DataSize
) SearchResult
= SearchFail
;
795 /* The length or type isn't equal */
796 SearchResult
= SearchFail
;
801 /* Release the value cell */
802 if (ValueCellToRelease
) HvReleaseCell(Kcb
->KeyHive
, ValueCellToRelease
);
804 /* Free the buffer */
805 if (BufferAllocated
) CmpFree(Buffer
, 0);
808 if (CellToRelease
) HvReleaseCell(Kcb
->KeyHive
, CellToRelease
);
810 /* Return the search result */