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
;
55 *ValueListToRelease
= HCELL_NIL
;
56 *IndexIsCached
= FALSE
;
58 /* Get the hive and value cache */
60 ChildList
= &Kcb
->ValueCache
;
61 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
62 ChildList
= (PCACHED_CHILD_LIST
)&KeyNode
->ValueList
;
64 /* Check if the value is cached */
65 if (CmpIsValueCached(ChildList
->ValueList
))
67 /* It is: we don't expect this yet! */
69 *IndexIsCached
= TRUE
;
74 /* Make sure the KCB is locked exclusive */
75 if (!(CmpIsKcbLockedExclusive(Kcb
)) &&
76 !(CmpTryToConvertKcbSharedToExclusive(Kcb
)))
78 /* We need the exclusive lock */
79 return SearchNeedExclusiveLock
;
82 /* Select the value list as our cell, and get the actual list array */
83 CellToRelease
= ChildList
->ValueList
;
84 *CellData
= (PCELL_DATA
)HvGetCell(Hive
, CellToRelease
);
85 if (!(*CellData
)) return SearchFail
;
87 /* FIXME: Here we would cache the value */
89 /* Return the cell to be released */
90 *ValueListToRelease
= CellToRelease
;
93 /* If we got here, then the value list was found */
97 VALUE_SEARCH_RETURN_TYPE
99 CmpGetValueKeyFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
100 IN PCELL_DATA CellData
,
102 OUT PCM_CACHED_VALUE
**CachedValue
,
103 OUT PCM_KEY_VALUE
*Value
,
104 IN BOOLEAN IndexIsCached
,
105 OUT BOOLEAN
*ValueIsCached
,
106 OUT PHCELL_INDEX CellToRelease
)
109 PCM_KEY_VALUE KeyValue
;
113 *CellToRelease
= HCELL_NIL
;
115 *ValueIsCached
= FALSE
;
120 /* Check if the index was cached */
123 /* Not expected yet! */
124 ASSERT_VALUE_CACHE();
125 *ValueIsCached
= TRUE
;
129 /* Get the cell index and the key value associated to it */
130 Cell
= CellData
->u
.KeyList
[Index
];
131 KeyValue
= (PCM_KEY_VALUE
)HvGetCell(Hive
, Cell
);
132 if (!KeyValue
) return SearchFail
;
134 /* Return the cell and the actual key value */
135 *CellToRelease
= Cell
;
139 /* If we got here, then we found the key value */
140 return SearchSuccess
;
143 VALUE_SEARCH_RETURN_TYPE
145 CmpGetValueDataFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
146 IN PCM_CACHED_VALUE
*CachedValue
,
147 IN PCELL_DATA ValueKey
,
148 IN BOOLEAN ValueIsCached
,
149 OUT PVOID
*DataPointer
,
150 OUT PBOOLEAN Allocated
,
151 OUT PHCELL_INDEX CellToRelease
)
157 ASSERT(MAXIMUM_CACHED_DATA
< CM_KEY_VALUE_BIG
);
158 ASSERT((ValueKey
->u
.KeyValue
.DataLength
& CM_KEY_VALUE_SPECIAL_SIZE
) == 0);
163 *CellToRelease
= HCELL_NIL
;
168 /* Check it the value is cached */
171 /* This isn't expected! */
172 ASSERT_VALUE_CACHE();
176 /* It's not, get the value data using the typical routine */
177 if (!CmpGetValueData(Hive
,
178 &ValueKey
->u
.KeyValue
,
184 /* Nothing found: make sure no data was allocated */
185 ASSERT(*Allocated
== FALSE
);
186 ASSERT(*DataPointer
== NULL
);
191 /* We found the actual data, return success */
192 return SearchSuccess
;
195 VALUE_SEARCH_RETURN_TYPE
197 CmpFindValueByNameFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
198 IN PCUNICODE_STRING Name
,
199 OUT PCM_CACHED_VALUE
**CachedValue
,
201 OUT PCM_KEY_VALUE
*Value
,
202 OUT BOOLEAN
*ValueIsCached
,
203 OUT PHCELL_INDEX CellToRelease
)
206 VALUE_SEARCH_RETURN_TYPE SearchResult
= SearchFail
;
208 UNICODE_STRING SearchName
;
210 PCACHED_CHILD_LIST ChildList
;
211 PCM_KEY_VALUE KeyValue
;
212 BOOLEAN IndexIsCached
;
214 HCELL_INDEX Cell
= HCELL_NIL
;
215 PCM_KEY_NODE KeyNode
;
218 *CellToRelease
= HCELL_NIL
;
221 /* Get the hive and child list */
223 ChildList
= &Kcb
->ValueCache
;
224 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
225 ChildList
= (PCACHED_CHILD_LIST
)&KeyNode
->ValueList
;
227 /* Check if the child list has any entries */
228 if (ChildList
->Count
!= 0)
230 /* Get the value list associated to this child list */
231 SearchResult
= CmpGetValueListFromCache(Kcb
,
235 if (SearchResult
!= SearchSuccess
)
237 /* We either failed or need the exclusive lock */
238 ASSERT((SearchResult
== SearchFail
) || !(CmpIsKcbLockedExclusive(Kcb
)));
239 ASSERT(Cell
== HCELL_NIL
);
243 /* The index shouldn't be cached right now */
244 if (IndexIsCached
) ASSERT_VALUE_CACHE();
246 /* Loop every value */
249 /* Check if there's any cell to release */
250 if (*CellToRelease
!= HCELL_NIL
)
253 HvReleaseCell(Hive
, *CellToRelease
);
254 *CellToRelease
= HCELL_NIL
;
257 /* Get the key value for this index */
258 SearchResult
= CmpGetValueKeyFromCache(Kcb
,
266 if (SearchResult
!= SearchSuccess
)
268 /* We either failed or need the exclusive lock */
269 ASSERT((SearchResult
== SearchFail
) || !(CmpIsKcbLockedExclusive(Kcb
)));
270 ASSERT(Cell
== HCELL_NIL
);
274 /* Check if the both the index and the value are cached */
275 if ((IndexIsCached
) && (*ValueIsCached
))
277 /* We don't expect this yet */
278 ASSERT_VALUE_CACHE();
283 /* No cache, so try to compare the name. Is it compressed? */
285 if (KeyValue
->Flags
& VALUE_COMP_NAME
)
287 /* It is, do a compressed name comparison */
288 Result
= CmpCompareCompressedName(Name
,
290 KeyValue
->NameLength
);
294 /* It's not compressed, so do a standard comparison */
295 SearchName
.Length
= KeyValue
->NameLength
;
296 SearchName
.MaximumLength
= SearchName
.Length
;
297 SearchName
.Buffer
= KeyValue
->Name
;
298 Result
= RtlCompareUnicodeString(Name
, &SearchName
, TRUE
);
302 /* Check if we found the value data */
305 /* We have, return the index of the value and success */
307 SearchResult
= SearchSuccess
;
311 /* We didn't find it, try the next entry */
312 if (++i
== ChildList
->Count
)
314 /* The entire list was parsed, fail */
316 SearchResult
= SearchFail
;
322 /* We should only get here if the child list is empty */
323 ASSERT(ChildList
->Count
== 0);
326 /* Release the value list cell if required, and return search result */
327 if (Cell
!= HCELL_NIL
) HvReleaseCell(Hive
, Cell
);
331 VALUE_SEARCH_RETURN_TYPE
333 CmpQueryKeyValueData(IN PCM_KEY_CONTROL_BLOCK Kcb
,
334 IN PCM_CACHED_VALUE
*CachedValue
,
335 IN PCM_KEY_VALUE ValueKey
,
336 IN BOOLEAN ValueIsCached
,
337 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
338 IN PVOID KeyValueInformation
,
340 OUT PULONG ResultLength
,
341 OUT PNTSTATUS Status
)
343 PKEY_VALUE_INFORMATION Info
= (PKEY_VALUE_INFORMATION
)KeyValueInformation
;
346 ULONG Size
, MinimumSize
, SizeLeft
, KeySize
, AlignedData
= 0, DataOffset
;
348 BOOLEAN IsSmall
, BufferAllocated
= FALSE
;
349 HCELL_INDEX CellToRelease
= HCELL_NIL
;
350 VALUE_SEARCH_RETURN_TYPE Result
= SearchSuccess
;
352 /* Get the value data */
353 CellData
= (PCELL_DATA
)ValueKey
;
355 /* Check if the value is compressed */
356 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
358 /* Get the compressed name size */
359 NameSize
= CmpCompressedNameSize(CellData
->u
.KeyValue
.Name
,
360 CellData
->u
.KeyValue
.NameLength
);
364 /* Get the real size */
365 NameSize
= CellData
->u
.KeyValue
.NameLength
;
368 /* Check what kind of information the caller is requesting */
369 switch (KeyValueInformationClass
)
371 /* Basic information */
372 case KeyValueBasicInformation
:
374 /* This is how much size we'll need */
375 Size
= FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION
, Name
) + NameSize
;
377 /* This is the minimum we can work with */
378 MinimumSize
= FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION
, Name
);
380 /* Return the size we'd like, and assume success */
381 *ResultLength
= Size
;
382 *Status
= STATUS_SUCCESS
;
384 /* Check if the caller gave us below our minimum */
385 if (Length
< MinimumSize
)
387 /* Then we must fail */
388 *Status
= STATUS_BUFFER_TOO_SMALL
;
392 /* Fill out the basic information */
393 Info
->KeyValueBasicInformation
.TitleIndex
= 0;
394 Info
->KeyValueBasicInformation
.Type
= CellData
->u
.KeyValue
.Type
;
395 Info
->KeyValueBasicInformation
.NameLength
= NameSize
;
397 /* Now only the name is left */
398 SizeLeft
= Length
- MinimumSize
;
401 /* Check if the remaining buffer is too small for the name */
404 /* Copy only as much as can fit, and tell the caller */
406 *Status
= STATUS_BUFFER_OVERFLOW
;
409 /* Check if this is a compressed name */
410 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
412 /* Copy as much as we can of the compressed name */
413 CmpCopyCompressedName(Info
->KeyValueBasicInformation
.Name
,
415 CellData
->u
.KeyValue
.Name
,
416 CellData
->u
.KeyValue
.NameLength
);
420 /* Copy as much as we can of the raw name */
421 RtlCopyMemory(Info
->KeyValueBasicInformation
.Name
,
422 CellData
->u
.KeyValue
.Name
,
429 /* Full key information */
430 case KeyValueFullInformation
:
431 case KeyValueFullInformationAlign64
:
433 /* Check if this is a small key and compute key size */
434 IsSmall
= CmpIsKeyValueSmall(&KeySize
,
435 CellData
->u
.KeyValue
.DataLength
);
437 /* Calculate the total size required */
438 Size
= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
) +
442 /* And this is the least we can work with */
443 MinimumSize
= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
);
445 /* Check if there's any key data */
448 /* Calculate the data offset */
449 DataOffset
= Size
- KeySize
;
452 /* On 64-bit, always align to 8 bytes */
453 AlignedData
= ALIGN_UP(DataOffset
, ULONGLONG
);
455 /* On 32-bit, align the offset to 4 or 8 bytes */
456 if (KeyValueInformationClass
== KeyValueFullInformationAlign64
)
458 AlignedData
= ALIGN_UP(DataOffset
, ULONGLONG
);
462 AlignedData
= ALIGN_UP(DataOffset
, ULONG
);
465 /* If alignment was required, we'll need more space */
466 if (AlignedData
> DataOffset
) Size
+= (AlignedData
-DataOffset
);
469 /* Tell the caller the size we'll finally need, and set success */
470 *ResultLength
= Size
;
471 *Status
= STATUS_SUCCESS
;
473 /* Check if the caller is giving us too little */
474 if (Length
< MinimumSize
)
476 /* Then fail right now */
477 *Status
= STATUS_BUFFER_TOO_SMALL
;
481 /* Fill out the basic information */
482 Info
->KeyValueFullInformation
.TitleIndex
= 0;
483 Info
->KeyValueFullInformation
.Type
= CellData
->u
.KeyValue
.Type
;
484 Info
->KeyValueFullInformation
.DataLength
= KeySize
;
485 Info
->KeyValueFullInformation
.NameLength
= NameSize
;
487 /* Only the name is left now */
488 SizeLeft
= Length
- MinimumSize
;
491 /* Check if the name fits */
494 /* It doesn't, truncate what we'll copy, and tell the caller */
496 *Status
= STATUS_BUFFER_OVERFLOW
;
499 /* Check if this key value is compressed */
500 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
502 /* It is, copy the compressed name */
503 CmpCopyCompressedName(Info
->KeyValueFullInformation
.Name
,
505 CellData
->u
.KeyValue
.Name
,
506 CellData
->u
.KeyValue
.NameLength
);
510 /* It's not, copy the raw name */
511 RtlCopyMemory(Info
->KeyValueFullInformation
.Name
,
512 CellData
->u
.KeyValue
.Name
,
516 /* Now check if the key had any data */
519 /* Was it a small key? */
522 /* Then the data is directly into the cell */
523 Buffer
= &CellData
->u
.KeyValue
.Data
;
527 /* Otherwise, we must retrieve it from the value cache */
528 Result
= CmpGetValueDataFromCache(Kcb
,
535 if (Result
!= SearchSuccess
)
537 /* We failed, nothing should be allocated */
538 ASSERT(Buffer
== NULL
);
539 ASSERT(BufferAllocated
== FALSE
);
540 *Status
= STATUS_INSUFFICIENT_RESOURCES
;
544 /* Now that we know we truly have data, set its offset */
545 Info
->KeyValueFullInformation
.DataOffset
= AlignedData
;
547 /* Only the data remains to be copied */
548 SizeLeft
= (((LONG
)Length
- (LONG
)AlignedData
) < 0) ?
549 0 : (Length
- AlignedData
);
552 /* Check if the caller has no space for it */
555 /* Truncate what we'll copy, and tell the caller */
557 *Status
= STATUS_BUFFER_OVERFLOW
;
561 ASSERT((IsSmall
? (Size
<= CM_KEY_VALUE_SMALL
) : TRUE
));
563 /* Make sure we have a valid buffer */
566 /* Copy the data into the aligned offset */
567 RtlCopyMemory((PVOID
)((ULONG_PTR
)Info
+ AlignedData
),
574 /* We don't have any data, set the offset to -1, not 0! */
575 Info
->KeyValueFullInformation
.DataOffset
= 0xFFFFFFFF;
581 /* Partial information requested (no name or alignment!) */
582 case KeyValuePartialInformation
:
584 /* Check if this is a small key and compute key size */
585 IsSmall
= CmpIsKeyValueSmall(&KeySize
,
586 CellData
->u
.KeyValue
.DataLength
);
588 /* Calculate the total size required */
589 Size
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
) + KeySize
;
591 /* And this is the least we can work with */
592 MinimumSize
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
594 /* Tell the caller the size we'll finally need, and set success */
595 *ResultLength
= Size
;
596 *Status
= STATUS_SUCCESS
;
598 /* Check if the caller is giving us too little */
599 if (Length
< MinimumSize
)
601 /* Then fail right now */
602 *Status
= STATUS_BUFFER_TOO_SMALL
;
606 /* Fill out the basic information */
607 Info
->KeyValuePartialInformation
.TitleIndex
= 0;
608 Info
->KeyValuePartialInformation
.Type
= CellData
->u
.KeyValue
.Type
;
609 Info
->KeyValuePartialInformation
.DataLength
= KeySize
;
611 /* Now check if the key had any data */
614 /* Was it a small key? */
617 /* Then the data is directly into the cell */
618 Buffer
= &CellData
->u
.KeyValue
.Data
;
622 /* Otherwise, we must retrieve it from the value cache */
623 Result
= CmpGetValueDataFromCache(Kcb
,
630 if (Result
!= SearchSuccess
)
632 /* We failed, nothing should be allocated */
633 ASSERT(Buffer
== NULL
);
634 ASSERT(BufferAllocated
== FALSE
);
635 *Status
= STATUS_INSUFFICIENT_RESOURCES
;
639 /* Only the data remains to be copied */
640 SizeLeft
= Length
- MinimumSize
;
643 /* Check if the caller has no space for it */
646 /* Truncate what we'll copy, and tell the caller */
648 *Status
= STATUS_BUFFER_OVERFLOW
;
652 ASSERT((IsSmall
? (Size
<= CM_KEY_VALUE_SMALL
) : TRUE
));
654 /* Make sure we have a valid buffer */
657 /* Copy the data into the aligned offset */
658 RtlCopyMemory(Info
->KeyValuePartialInformation
.Data
,
667 /* Other information class */
670 /* We got some class that we don't support */
671 DPRINT1("Caller requested unknown class: %lx\n", KeyValueInformationClass
);
672 *Status
= STATUS_INVALID_PARAMETER
;
676 /* Return the search result as well */
680 VALUE_SEARCH_RETURN_TYPE
682 CmpCompareNewValueDataAgainstKCBCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
683 IN PUNICODE_STRING ValueName
,
688 VALUE_SEARCH_RETURN_TYPE SearchResult
;
689 PCM_KEY_NODE KeyNode
;
690 PCM_CACHED_VALUE
*CachedValue
;
693 BOOLEAN ValueCached
, BufferAllocated
= FALSE
;
695 HCELL_INDEX ValueCellToRelease
= HCELL_NIL
, CellToRelease
= HCELL_NIL
;
700 /* Check if this is a symlink */
701 if (Kcb
->Flags
& KEY_SYM_LINK
)
703 /* We need the exclusive lock */
704 if (!(CmpIsKcbLockedExclusive(Kcb
)) &&
705 !(CmpTryToConvertKcbSharedToExclusive(Kcb
)))
707 /* We need the exclusive lock */
708 return SearchNeedExclusiveLock
;
711 /* Otherwise, get the key node */
712 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Kcb
->KeyHive
, Kcb
->KeyCell
);
713 if (!KeyNode
) return SearchFail
;
715 /* Cleanup the KCB cache */
716 CmpCleanUpKcbValueCache(Kcb
);
719 ASSERT(!(CMP_IS_CELL_CACHED(Kcb
->ValueCache
.ValueList
)));
720 ASSERT(!(Kcb
->ExtFlags
& CM_KCB_SYM_LINK_FOUND
));
722 /* Set the value cache */
723 Kcb
->ValueCache
.Count
= KeyNode
->ValueList
.Count
;
724 Kcb
->ValueCache
.ValueList
= KeyNode
->ValueList
.List
;
726 /* Release the cell */
727 HvReleaseCell(Kcb
->KeyHive
, Kcb
->KeyCell
);
731 SearchResult
= CmpFindValueByNameFromCache(Kcb
,
737 &ValueCellToRelease
);
738 if (SearchResult
== SearchNeedExclusiveLock
)
740 /* We need the exclusive lock */
741 ASSERT(!CmpIsKcbLockedExclusive(Kcb
));
742 ASSERT(ValueCellToRelease
== HCELL_NIL
);
743 ASSERT(Value
== NULL
);
746 else if (SearchResult
== SearchSuccess
)
751 /* First of all, check if the key size and type matches */
752 if ((Type
== Value
->Type
) &&
753 (DataSize
== (Value
->DataLength
& ~CM_KEY_VALUE_SPECIAL_SIZE
)))
755 /* Check if this is a small key */
756 IsSmall
= (DataSize
<= CM_KEY_VALUE_SMALL
) ? TRUE
: FALSE
;
759 /* Compare against the data directly */
760 Buffer
= &Value
->Data
;
765 SearchResult
= CmpGetValueDataFromCache(Kcb
,
772 if (SearchResult
!= SearchSuccess
)
775 ASSERT(Buffer
== NULL
);
776 ASSERT(BufferAllocated
== FALSE
);
781 /* Now check the data size */
785 CompareResult
= RtlCompareMemory(Buffer
,
788 ~CM_KEY_VALUE_SPECIAL_SIZE
);
796 /* Now check if the compare wasn't equal */
797 if (CompareResult
!= DataSize
) SearchResult
= SearchFail
;
801 /* The length or type isn't equal */
802 SearchResult
= SearchFail
;
807 /* Release the value cell */
808 if (ValueCellToRelease
) HvReleaseCell(Kcb
->KeyHive
, ValueCellToRelease
);
810 /* Free the buffer */
811 if (BufferAllocated
) CmpFree(Buffer
, 0);
814 if (CellToRelease
) HvReleaseCell(Kcb
->KeyHive
, CellToRelease
);
816 /* Return the search result */