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
;
60 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
62 /* Get the child value cache */
63 //ChildList = &Kcb->ValueCache;
64 ChildList
= (PCACHED_CHILD_LIST
)&KeyNode
->ValueList
;
66 /* Check if the value is cached */
67 if (CmpIsValueCached(ChildList
->ValueList
))
69 /* It is: we don't expect this yet! */
71 *IndexIsCached
= TRUE
;
76 /* Select the value list as our cell, and get the actual list array */
77 CellToRelease
= ChildList
->ValueList
;
78 *CellData
= (PCELL_DATA
)HvGetCell(Hive
, CellToRelease
);
79 if (!(*CellData
)) return SearchFail
;
81 /* Return the cell to be released */
82 *ValueListToRelease
= CellToRelease
;
85 /* If we got here, then the value list was found */
89 VALUE_SEARCH_RETURN_TYPE
91 CmpGetValueKeyFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
92 IN PCELL_DATA CellData
,
94 OUT PCM_CACHED_VALUE
**CachedValue
,
95 OUT PCM_KEY_VALUE
*Value
,
96 IN BOOLEAN IndexIsCached
,
97 OUT BOOLEAN
*ValueIsCached
,
98 OUT PHCELL_INDEX CellToRelease
)
101 PCM_KEY_VALUE KeyValue
;
105 *CellToRelease
= HCELL_NIL
;
107 *ValueIsCached
= FALSE
;
112 /* Check if the index was cached */
115 /* Not expected yet! */
116 ASSERT_VALUE_CACHE();
117 *ValueIsCached
= TRUE
;
121 /* Get the cell index and the key value associated to it */
122 Cell
= CellData
->u
.KeyList
[Index
];
123 KeyValue
= (PCM_KEY_VALUE
)HvGetCell(Hive
, Cell
);
124 if (!KeyValue
) return SearchFail
;
126 /* Return the cell and the actual key value */
127 *CellToRelease
= Cell
;
131 /* If we got here, then we found the key value */
132 return SearchSuccess
;
135 VALUE_SEARCH_RETURN_TYPE
137 CmpGetValueDataFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
138 IN PCM_CACHED_VALUE
*CachedValue
,
139 IN PCELL_DATA ValueKey
,
140 IN BOOLEAN ValueIsCached
,
141 OUT PVOID
*DataPointer
,
142 OUT PBOOLEAN Allocated
,
143 OUT PHCELL_INDEX CellToRelease
)
149 ASSERT(MAXIMUM_CACHED_DATA
< CM_KEY_VALUE_BIG
);
150 ASSERT((ValueKey
->u
.KeyValue
.DataLength
& CM_KEY_VALUE_SPECIAL_SIZE
) == 0);
155 *CellToRelease
= HCELL_NIL
;
160 /* Check it the value is cached */
163 /* This isn't expected! */
164 ASSERT_VALUE_CACHE();
168 /* It's not, get the value data using the typical routine */
169 if (!CmpGetValueData(Hive
,
170 &ValueKey
->u
.KeyValue
,
176 /* Nothing found: make sure no data was allocated */
177 ASSERT(*Allocated
== FALSE
);
178 ASSERT(*DataPointer
== NULL
);
183 /* We found the actual data, return success */
184 return SearchSuccess
;
187 VALUE_SEARCH_RETURN_TYPE
189 CmpFindValueByNameFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb
,
190 IN PCUNICODE_STRING Name
,
191 OUT PCM_CACHED_VALUE
**CachedValue
,
193 OUT PCM_KEY_VALUE
*Value
,
194 OUT BOOLEAN
*ValueIsCached
,
195 OUT PHCELL_INDEX CellToRelease
)
198 VALUE_SEARCH_RETURN_TYPE SearchResult
= SearchFail
;
200 UNICODE_STRING SearchName
;
202 PCACHED_CHILD_LIST ChildList
;
203 PCM_KEY_VALUE KeyValue
;
204 BOOLEAN IndexIsCached
;
206 HCELL_INDEX Cell
= HCELL_NIL
;
207 PCM_KEY_NODE KeyNode
;
210 *CellToRelease
= HCELL_NIL
;
215 KeyNode
= (PCM_KEY_NODE
)HvGetCell(Hive
, Kcb
->KeyCell
);
217 /* Get the child value cache */
218 //ChildList = &Kcb->ValueCache;
219 ChildList
= (PCACHED_CHILD_LIST
)&KeyNode
->ValueList
;
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
) return SearchResult
;
231 /* The index shouldn't be cached right now */
232 if (IndexIsCached
) ASSERT_VALUE_CACHE();
234 /* Loop every value */
237 /* Check if there's any cell to release */
238 if (*CellToRelease
!= HCELL_NIL
)
241 HvReleaseCell(Hive
, *CellToRelease
);
242 *CellToRelease
= HCELL_NIL
;
245 /* Get the key value for this index */
246 SearchResult
= CmpGetValueKeyFromCache(Kcb
,
254 if (SearchResult
!= SearchSuccess
) return SearchResult
;
256 /* Check if the both the index and the value are cached */
257 if ((IndexIsCached
) && (*ValueIsCached
))
259 /* We don't expect this yet */
260 ASSERT_VALUE_CACHE();
265 /* No cache, so try to compare the name. Is it compressed? */
267 if (KeyValue
->Flags
& VALUE_COMP_NAME
)
269 /* It is, do a compressed name comparison */
270 Result
= CmpCompareCompressedName(Name
,
272 KeyValue
->NameLength
);
276 /* It's not compressed, so do a standard comparison */
277 SearchName
.Length
= KeyValue
->NameLength
;
278 SearchName
.MaximumLength
= SearchName
.Length
;
279 SearchName
.Buffer
= KeyValue
->Name
;
280 Result
= RtlCompareUnicodeString(Name
, &SearchName
, TRUE
);
284 /* Check if we found the value data */
287 /* We have, return the index of the value and success */
289 SearchResult
= SearchSuccess
;
293 /* We didn't find it, try the next entry */
294 if (++i
== ChildList
->Count
)
296 /* The entire list was parsed, fail */
298 SearchResult
= SearchFail
;
304 /* We should only get here if the child list is empty */
305 ASSERT(ChildList
->Count
== 0);
308 /* Release the value list cell if required, and return search result */
309 if (Cell
!= HCELL_NIL
) HvReleaseCell(Hive
, Cell
);
313 VALUE_SEARCH_RETURN_TYPE
315 CmpQueryKeyValueData(IN PCM_KEY_CONTROL_BLOCK Kcb
,
316 IN PCM_CACHED_VALUE
*CachedValue
,
317 IN PCM_KEY_VALUE ValueKey
,
318 IN BOOLEAN ValueIsCached
,
319 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
320 IN PVOID KeyValueInformation
,
322 OUT PULONG ResultLength
,
323 OUT PNTSTATUS Status
)
326 PKEY_VALUE_INFORMATION Info
= (PKEY_VALUE_INFORMATION
)KeyValueInformation
;
329 ULONG Size
, MinimumSize
, SizeLeft
, KeySize
, AlignedData
= 0, DataOffset
;
331 BOOLEAN IsSmall
, BufferAllocated
= FALSE
;
332 HCELL_INDEX CellToRelease
= HCELL_NIL
;
333 VALUE_SEARCH_RETURN_TYPE Result
= SearchSuccess
;
335 /* Get the hive and cell data */
337 CellData
= (PCELL_DATA
)ValueKey
;
339 /* Check if the value is compressed */
340 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
342 /* Get the compressed name size */
343 NameSize
= CmpCompressedNameSize(CellData
->u
.KeyValue
.Name
,
344 CellData
->u
.KeyValue
.NameLength
);
348 /* Get the real size */
349 NameSize
= CellData
->u
.KeyValue
.NameLength
;
352 /* Check what kind of information the caller is requesting */
353 switch (KeyValueInformationClass
)
355 /* Basic information */
356 case KeyValueBasicInformation
:
358 /* This is how much size we'll need */
359 Size
= FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION
, Name
) + NameSize
;
361 /* This is the minimum we can work with */
362 MinimumSize
= FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION
, Name
);
364 /* Return the size we'd like, and assume success */
365 *ResultLength
= Size
;
366 *Status
= STATUS_SUCCESS
;
368 /* Check if the caller gave us below our minimum */
369 if (Length
< MinimumSize
)
371 /* Then we must fail */
372 *Status
= STATUS_BUFFER_TOO_SMALL
;
376 /* Fill out the basic information */
377 Info
->KeyValueBasicInformation
.TitleIndex
= 0;
378 Info
->KeyValueBasicInformation
.Type
= CellData
->u
.KeyValue
.Type
;
379 Info
->KeyValueBasicInformation
.NameLength
= NameSize
;
381 /* Now only the name is left */
382 SizeLeft
= Length
- MinimumSize
;
385 /* Check if the remaining buffer is too small for the name */
388 /* Copy only as much as can fit, and tell the caller */
390 *Status
= STATUS_BUFFER_OVERFLOW
;
393 /* Check if this is a compressed name */
394 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
396 /* Copy as much as we can of the compressed name */
397 CmpCopyCompressedName(Info
->KeyValueBasicInformation
.Name
,
399 CellData
->u
.KeyValue
.Name
,
400 CellData
->u
.KeyValue
.NameLength
);
404 /* Copy as much as we can of the raw name */
405 RtlCopyMemory(Info
->KeyValueBasicInformation
.Name
,
406 CellData
->u
.KeyValue
.Name
,
413 /* Full key information */
414 case KeyValueFullInformation
:
416 /* Check if this is a small key and compute key size */
417 IsSmall
= CmpIsKeyValueSmall(&KeySize
,
418 CellData
->u
.KeyValue
.DataLength
);
420 /* Calculate the total size required */
421 Size
= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
) +
425 /* And this is the least we can work with */
426 MinimumSize
= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
);
428 /* Check if there's any key data */
431 /* Calculate the data offset */
432 DataOffset
= Size
- KeySize
;
434 /* Align the offset to 4 bytes */
435 AlignedData
= ALIGN_UP(DataOffset
, ULONG
);
437 /* If alignment was required, we'll need more space */
438 if (AlignedData
> DataOffset
) Size
+= (AlignedData
-DataOffset
);
441 /* Tell the caller the size we'll finally need, and set success */
442 *ResultLength
= Size
;
443 *Status
= STATUS_SUCCESS
;
445 /* Check if the caller is giving us too little */
446 if (Length
< MinimumSize
)
448 /* Then fail right now */
449 *Status
= STATUS_BUFFER_TOO_SMALL
;
453 /* Fill out the basic information */
454 Info
->KeyValueFullInformation
.TitleIndex
= 0;
455 Info
->KeyValueFullInformation
.Type
= CellData
->u
.KeyValue
.Type
;
456 Info
->KeyValueFullInformation
.DataLength
= KeySize
;
457 Info
->KeyValueFullInformation
.NameLength
= NameSize
;
459 /* Only the name is left now */
460 SizeLeft
= Length
- MinimumSize
;
463 /* Check if the name fits */
466 /* It doesn't, truncate what we'll copy, and tell the caller */
468 *Status
= STATUS_BUFFER_OVERFLOW
;
471 /* Check if this key value is compressed */
472 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
474 /* It is, copy the compressed name */
475 CmpCopyCompressedName(Info
->KeyValueFullInformation
.Name
,
477 CellData
->u
.KeyValue
.Name
,
478 CellData
->u
.KeyValue
.NameLength
);
482 /* It's not, copy the raw name */
483 RtlCopyMemory(Info
->KeyValueFullInformation
.Name
,
484 CellData
->u
.KeyValue
.Name
,
488 /* Now check if the key had any data */
491 /* Was it a small key? */
494 /* Then the data is directly into the cell */
495 Buffer
= &CellData
->u
.KeyValue
.Data
;
499 /* Otherwise, we must retrieve it from the value cache */
500 Result
= CmpGetValueDataFromCache(Kcb
,
507 if (Result
!= SearchSuccess
)
509 /* We failed, nothing should be allocated */
510 ASSERT(Buffer
== NULL
);
511 ASSERT(BufferAllocated
== FALSE
);
512 *Status
= STATUS_INSUFFICIENT_RESOURCES
;
516 /* Now that we know we truly have data, set its offset */
517 Info
->KeyValueFullInformation
.DataOffset
= AlignedData
;
519 /* Only the data remains to be copied */
520 SizeLeft
= (((LONG
)Length
- (LONG
)AlignedData
) < 0) ?
521 0 : (Length
- AlignedData
);
524 /* Check if the caller has no space for it */
527 /* Truncate what we'll copy, and tell the caller */
529 *Status
= STATUS_BUFFER_OVERFLOW
;
533 ASSERT((IsSmall
? (Size
<= CM_KEY_VALUE_SMALL
) : TRUE
));
535 /* Make sure we have a valid buffer */
538 /* Copy the data into the aligned offset */
539 RtlCopyMemory((PVOID
)((ULONG_PTR
)Info
+ AlignedData
),
546 /* We don't have any data, set the offset to -1, not 0! */
547 Info
->KeyValueFullInformation
.DataOffset
= 0xFFFFFFFF;
553 /* Partial information requested (no name or alignment!) */
554 case KeyValuePartialInformation
:
556 /* Check if this is a small key and compute key size */
557 IsSmall
= CmpIsKeyValueSmall(&KeySize
,
558 CellData
->u
.KeyValue
.DataLength
);
560 /* Calculate the total size required */
561 Size
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
) + KeySize
;
563 /* And this is the least we can work with */
564 MinimumSize
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
566 /* Tell the caller the size we'll finally need, and set success */
567 *ResultLength
= Size
;
568 *Status
= STATUS_SUCCESS
;
570 /* Check if the caller is giving us too little */
571 if (Length
< MinimumSize
)
573 /* Then fail right now */
574 *Status
= STATUS_BUFFER_TOO_SMALL
;
578 /* Fill out the basic information */
579 Info
->KeyValuePartialInformation
.TitleIndex
= 0;
580 Info
->KeyValuePartialInformation
.Type
= CellData
->u
.KeyValue
.Type
;
581 Info
->KeyValuePartialInformation
.DataLength
= KeySize
;
583 /* Now check if the key had any data */
586 /* Was it a small key? */
589 /* Then the data is directly into the cell */
590 Buffer
= &CellData
->u
.KeyValue
.Data
;
594 /* Otherwise, we must retrieve it from the value cache */
595 Result
= CmpGetValueDataFromCache(Kcb
,
602 if (Result
!= SearchSuccess
)
604 /* We failed, nothing should be allocated */
605 ASSERT(Buffer
== NULL
);
606 ASSERT(BufferAllocated
== FALSE
);
607 *Status
= STATUS_INSUFFICIENT_RESOURCES
;
611 /* Only the data remains to be copied */
612 SizeLeft
= Length
- MinimumSize
;
615 /* Check if the caller has no space for it */
618 /* Truncate what we'll copy, and tell the caller */
620 *Status
= STATUS_BUFFER_OVERFLOW
;
624 ASSERT((IsSmall
? (Size
<= CM_KEY_VALUE_SMALL
) : TRUE
));
626 /* Make sure we have a valid buffer */
629 /* Copy the data into the aligned offset */
630 RtlCopyMemory(Info
->KeyValuePartialInformation
.Data
,
639 /* Other information class */
642 /* We got some class that we don't support */
643 *Status
= STATUS_INVALID_PARAMETER
;
647 /* Return the search result as well */