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 PKEY_OBJECT KeyObject
,
46 OUT PCELL_DATA
*CellData
,
47 OUT BOOLEAN
*IndexIsCached
,
48 OUT PHCELL_INDEX ValueListToRelease
)
51 PCACHED_CHILD_LIST ChildList
;
52 HCELL_INDEX CellToRelease
;
55 *ValueListToRelease
= HCELL_NIL
;
56 *IndexIsCached
= FALSE
;
59 Hive
= &KeyObject
->RegistryHive
->Hive
;
61 /* Get the child value cache */
62 //ChildList = &KeyObject->ValueCache;
63 ChildList
= (PCACHED_CHILD_LIST
)&KeyObject
->KeyCell
->ValueList
;
65 /* Check if the value is cached */
66 if (CmpIsValueCached(ChildList
->ValueList
))
68 /* It is: we don't expect this yet! */
70 *IndexIsCached
= TRUE
;
75 /* Select the value list as our cell, and get the actual list array */
76 CellToRelease
= ChildList
->ValueList
;
77 *CellData
= (PCELL_DATA
)HvGetCell(Hive
, CellToRelease
);
78 if (!(*CellData
)) return SearchFail
;
80 /* Return the cell to be released */
81 *ValueListToRelease
= CellToRelease
;
84 /* If we got here, then the value list was found */
88 VALUE_SEARCH_RETURN_TYPE
90 CmpGetValueKeyFromCache(IN PKEY_OBJECT KeyObject
,
91 IN PCELL_DATA CellData
,
93 OUT PCM_CACHED_VALUE
**CachedValue
,
94 OUT PCM_KEY_VALUE
*Value
,
95 IN BOOLEAN IndexIsCached
,
96 OUT BOOLEAN
*ValueIsCached
,
97 OUT PHCELL_INDEX CellToRelease
)
100 PCM_KEY_VALUE KeyValue
;
104 *CellToRelease
= HCELL_NIL
;
106 *ValueIsCached
= FALSE
;
109 Hive
= &KeyObject
->RegistryHive
->Hive
;
111 /* Check if the index was cached */
114 /* Not expected yet! */
115 ASSERT_VALUE_CACHE();
116 *ValueIsCached
= TRUE
;
120 /* Get the cell index and the key value associated to it */
121 Cell
= CellData
->u
.KeyList
[Index
];
122 KeyValue
= (PCM_KEY_VALUE
)HvGetCell(Hive
, Cell
);
123 if (!KeyValue
) return SearchFail
;
125 /* Return the cell and the actual key value */
126 *CellToRelease
= Cell
;
130 /* If we got here, then we found the key value */
131 return SearchSuccess
;
134 VALUE_SEARCH_RETURN_TYPE
136 CmpGetValueDataFromCache(IN PKEY_OBJECT KeyObject
,
137 IN PCM_CACHED_VALUE
*CachedValue
,
138 IN PCELL_DATA ValueKey
,
139 IN BOOLEAN ValueIsCached
,
140 OUT PVOID
*DataPointer
,
141 OUT PBOOLEAN Allocated
,
142 OUT PHCELL_INDEX CellToRelease
)
148 ASSERT(MAXIMUM_CACHED_DATA
< CM_KEY_VALUE_BIG
);
149 ASSERT((ValueKey
->u
.KeyValue
.DataLength
& CM_KEY_VALUE_SPECIAL_SIZE
) == 0);
154 *CellToRelease
= HCELL_NIL
;
157 Hive
= &KeyObject
->RegistryHive
->Hive
;
159 /* Check it the value is cached */
162 /* This isn't expected! */
163 ASSERT_VALUE_CACHE();
167 /* It's not, get the value data using the typical routine */
168 if (!CmpGetValueData(Hive
,
169 &ValueKey
->u
.KeyValue
,
175 /* Nothing found: make sure no data was allocated */
176 ASSERT(*Allocated
== FALSE
);
177 ASSERT(*DataPointer
== NULL
);
182 /* We found the actual data, return success */
183 return SearchSuccess
;
186 VALUE_SEARCH_RETURN_TYPE
188 CmpFindValueByNameFromCache(IN PKEY_OBJECT KeyObject
,
189 IN PCUNICODE_STRING Name
,
190 OUT PCM_CACHED_VALUE
**CachedValue
,
192 OUT PCM_KEY_VALUE
*Value
,
193 OUT BOOLEAN
*ValueIsCached
,
194 OUT PHCELL_INDEX CellToRelease
)
197 VALUE_SEARCH_RETURN_TYPE SearchResult
= SearchFail
;
199 UNICODE_STRING SearchName
;
201 PCACHED_CHILD_LIST ChildList
;
202 PCM_KEY_VALUE KeyValue
;
203 BOOLEAN IndexIsCached
;
205 HCELL_INDEX Cell
= HCELL_NIL
;
208 *CellToRelease
= HCELL_NIL
;
211 /* Get the hive and child list */
212 Hive
= &KeyObject
->RegistryHive
->Hive
;
213 //ChildList = &KeyObject->ValueCache;
214 ChildList
= (PCACHED_CHILD_LIST
)&KeyObject
->KeyCell
->ValueList
;
216 /* Check if the child list has any entries */
217 if (ChildList
->Count
!= 0)
219 /* Get the value list associated to this child list */
220 SearchResult
= CmpGetValueListFromCache(KeyObject
,
224 if (SearchResult
!= SearchSuccess
) return SearchResult
;
226 /* The index shouldn't be cached right now */
227 if (IndexIsCached
) ASSERT_VALUE_CACHE();
229 /* Loop every value */
232 /* Check if there's any cell to release */
233 if (*CellToRelease
!= HCELL_NIL
)
236 HvReleaseCell(Hive
, *CellToRelease
);
237 *CellToRelease
= HCELL_NIL
;
240 /* Get the key value for this index */
241 SearchResult
= CmpGetValueKeyFromCache(KeyObject
,
249 if (SearchResult
!= SearchSuccess
) return SearchResult
;
251 /* Check if the both the index and the value are cached */
252 if ((IndexIsCached
) && (*ValueIsCached
))
254 /* We don't expect this yet */
255 ASSERT_VALUE_CACHE();
260 /* No cache, so try to compare the name. Is it compressed? */
262 if (KeyValue
->Flags
& VALUE_COMP_NAME
)
264 /* It is, do a compressed name comparison */
265 Result
= CmpCompareCompressedName(Name
,
267 KeyValue
->NameLength
);
271 /* It's not compressed, so do a standard comparison */
272 SearchName
.Length
= KeyValue
->NameLength
;
273 SearchName
.MaximumLength
= SearchName
.Length
;
274 SearchName
.Buffer
= KeyValue
->Name
;
275 Result
= RtlCompareUnicodeString(Name
, &SearchName
, TRUE
);
279 /* Check if we found the value data */
282 /* We have, return the index of the value and success */
284 SearchResult
= SearchSuccess
;
288 /* We didn't find it, try the next entry */
289 if (++i
== ChildList
->Count
)
291 /* The entire list was parsed, fail */
293 SearchResult
= SearchFail
;
299 /* We should only get here if the child list is empty */
300 ASSERT(ChildList
->Count
== 0);
303 /* Release the value list cell if required, and return search result */
304 if (Cell
!= HCELL_NIL
) HvReleaseCell(Hive
, Cell
);
308 VALUE_SEARCH_RETURN_TYPE
310 CmpQueryKeyValueData(IN PKEY_OBJECT KeyObject
,
311 IN PCM_CACHED_VALUE
*CachedValue
,
312 IN PCM_KEY_VALUE ValueKey
,
313 IN BOOLEAN ValueIsCached
,
314 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass
,
315 IN PVOID KeyValueInformation
,
317 OUT PULONG ResultLength
,
318 OUT PNTSTATUS Status
)
321 PKEY_VALUE_INFORMATION Info
= (PKEY_VALUE_INFORMATION
)KeyValueInformation
;
324 ULONG Size
, MinimumSize
, SizeLeft
, KeySize
, AlignedData
= 0, DataOffset
;
326 BOOLEAN IsSmall
, BufferAllocated
= FALSE
;
327 HCELL_INDEX CellToRelease
= HCELL_NIL
;
328 VALUE_SEARCH_RETURN_TYPE Result
= SearchSuccess
;
330 /* Get the hive and cell data */
331 Hive
= &KeyObject
->RegistryHive
->Hive
;
332 CellData
= (PCELL_DATA
)ValueKey
;
334 /* Check if the value is compressed */
335 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
337 /* Get the compressed name size */
338 NameSize
= CmpCompressedNameSize(CellData
->u
.KeyValue
.Name
,
339 CellData
->u
.KeyValue
.NameLength
);
343 /* Get the real size */
344 NameSize
= CellData
->u
.KeyValue
.NameLength
;
347 /* Check what kind of information the caller is requesting */
348 switch (KeyValueInformationClass
)
350 /* Basic information */
351 case KeyValueBasicInformation
:
353 /* This is how much size we'll need */
354 Size
= FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION
, Name
) + NameSize
;
356 /* This is the minimum we can work with */
357 MinimumSize
= FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION
, Name
);
359 /* Return the size we'd like, and assume success */
360 *ResultLength
= Size
;
361 *Status
= STATUS_SUCCESS
;
363 /* Check if the caller gave us below our minimum */
364 if (Length
< MinimumSize
)
366 /* Then we must fail */
367 *Status
= STATUS_BUFFER_TOO_SMALL
;
371 /* Fill out the basic information */
372 Info
->KeyValueBasicInformation
.TitleIndex
= 0;
373 Info
->KeyValueBasicInformation
.Type
= CellData
->u
.KeyValue
.Type
;
374 Info
->KeyValueBasicInformation
.NameLength
= NameSize
;
376 /* Now only the name is left */
377 SizeLeft
= Length
- MinimumSize
;
380 /* Check if the remaining buffer is too small for the name */
383 /* Copy only as much as can fit, and tell the caller */
385 *Status
= STATUS_BUFFER_OVERFLOW
;
388 /* Check if this is a compressed name */
389 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
391 /* Copy as much as we can of the compressed name */
392 CmpCopyCompressedName(Info
->KeyValueBasicInformation
.Name
,
394 CellData
->u
.KeyValue
.Name
,
395 CellData
->u
.KeyValue
.NameLength
);
399 /* Copy as much as we can of the raw name */
400 RtlCopyMemory(Info
->KeyValueBasicInformation
.Name
,
401 CellData
->u
.KeyValue
.Name
,
408 /* Full key information */
409 case KeyValueFullInformation
:
411 /* Check if this is a small key and compute key size */
412 IsSmall
= CmpIsKeyValueSmall(&KeySize
,
413 CellData
->u
.KeyValue
.DataLength
);
415 /* Calculate the total size required */
416 Size
= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
) +
420 /* And this is the least we can work with */
421 MinimumSize
= FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION
, Name
);
423 /* Check if there's any key data */
426 /* Calculate the data offset */
427 DataOffset
= Size
- KeySize
;
429 /* Align the offset to 4 bytes */
430 AlignedData
= ALIGN_UP(DataOffset
, ULONG
);
432 /* If alignment was required, we'll need more space */
433 if (AlignedData
> DataOffset
) Size
+= (AlignedData
-DataOffset
);
436 /* Tell the caller the size we'll finally need, and set success */
437 *ResultLength
= Size
;
438 *Status
= STATUS_SUCCESS
;
440 /* Check if the caller is giving us too little */
441 if (Length
< MinimumSize
)
443 /* Then fail right now */
444 *Status
= STATUS_BUFFER_TOO_SMALL
;
448 /* Fill out the basic information */
449 Info
->KeyValueFullInformation
.TitleIndex
= 0;
450 Info
->KeyValueFullInformation
.Type
= CellData
->u
.KeyValue
.Type
;
451 Info
->KeyValueFullInformation
.DataLength
= KeySize
;
452 Info
->KeyValueFullInformation
.NameLength
= NameSize
;
454 /* Only the name is left now */
455 SizeLeft
= Length
- MinimumSize
;
458 /* Check if the name fits */
461 /* It doesn't, truncate what we'll copy, and tell the caller */
463 *Status
= STATUS_BUFFER_OVERFLOW
;
466 /* Check if this key value is compressed */
467 if (CellData
->u
.KeyValue
.Flags
& VALUE_COMP_NAME
)
469 /* It is, copy the compressed name */
470 CmpCopyCompressedName(Info
->KeyValueFullInformation
.Name
,
472 CellData
->u
.KeyValue
.Name
,
473 CellData
->u
.KeyValue
.NameLength
);
477 /* It's not, copy the raw name */
478 RtlCopyMemory(Info
->KeyValueFullInformation
.Name
,
479 CellData
->u
.KeyValue
.Name
,
483 /* Now check if the key had any data */
486 /* Was it a small key? */
489 /* Then the data is directly into the cell */
490 Buffer
= &CellData
->u
.KeyValue
.Data
;
494 /* Otherwise, we must retrieve it from the value cache */
495 Result
= CmpGetValueDataFromCache(KeyObject
,
502 if (Result
!= SearchSuccess
)
504 /* We failed, nothing should be allocated */
505 ASSERT(Buffer
== NULL
);
506 ASSERT(BufferAllocated
== FALSE
);
507 *Status
= STATUS_INSUFFICIENT_RESOURCES
;
511 /* Now that we know we truly have data, set its offset */
512 Info
->KeyValueFullInformation
.DataOffset
= AlignedData
;
514 /* Only the data remains to be copied */
515 SizeLeft
= (((LONG
)Length
- (LONG
)AlignedData
) < 0) ?
516 0 : (Length
- AlignedData
);
519 /* Check if the caller has no space for it */
522 /* Truncate what we'll copy, and tell the caller */
524 *Status
= STATUS_BUFFER_OVERFLOW
;
528 ASSERT((IsSmall
? (Size
<= CM_KEY_VALUE_SMALL
) : TRUE
));
530 /* Make sure we have a valid buffer */
533 /* Copy the data into the aligned offset */
534 RtlCopyMemory((PVOID
)((ULONG_PTR
)Info
+ AlignedData
),
541 /* We don't have any data, set the offset to -1, not 0! */
542 Info
->KeyValueFullInformation
.DataOffset
= 0xFFFFFFFF;
548 /* Partial information requested (no name or alignment!) */
549 case KeyValuePartialInformation
:
551 /* Check if this is a small key and compute key size */
552 IsSmall
= CmpIsKeyValueSmall(&KeySize
,
553 CellData
->u
.KeyValue
.DataLength
);
555 /* Calculate the total size required */
556 Size
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
) + KeySize
;
558 /* And this is the least we can work with */
559 MinimumSize
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
561 /* Tell the caller the size we'll finally need, and set success */
562 *ResultLength
= Size
;
563 *Status
= STATUS_SUCCESS
;
565 /* Check if the caller is giving us too little */
566 if (Length
< MinimumSize
)
568 /* Then fail right now */
569 *Status
= STATUS_BUFFER_TOO_SMALL
;
573 /* Fill out the basic information */
574 Info
->KeyValuePartialInformation
.TitleIndex
= 0;
575 Info
->KeyValuePartialInformation
.Type
= CellData
->u
.KeyValue
.Type
;
576 Info
->KeyValuePartialInformation
.DataLength
= KeySize
;
578 /* Now check if the key had any data */
581 /* Was it a small key? */
584 /* Then the data is directly into the cell */
585 Buffer
= &CellData
->u
.KeyValue
.Data
;
589 /* Otherwise, we must retrieve it from the value cache */
590 Result
= CmpGetValueDataFromCache(KeyObject
,
597 if (Result
!= SearchSuccess
)
599 /* We failed, nothing should be allocated */
600 ASSERT(Buffer
== NULL
);
601 ASSERT(BufferAllocated
== FALSE
);
602 *Status
= STATUS_INSUFFICIENT_RESOURCES
;
606 /* Only the data remains to be copied */
607 SizeLeft
= Length
- MinimumSize
;
610 /* Check if the caller has no space for it */
613 /* Truncate what we'll copy, and tell the caller */
615 *Status
= STATUS_BUFFER_OVERFLOW
;
619 ASSERT((IsSmall
? (Size
<= CM_KEY_VALUE_SMALL
) : TRUE
));
621 /* Make sure we have a valid buffer */
624 /* Copy the data into the aligned offset */
625 RtlCopyMemory(Info
->KeyValuePartialInformation
.Data
,
634 /* Other information class */
637 /* We got some class that we don't support */
638 *Status
= STATUS_INVALID_PARAMETER
;
642 /* Return the search result as well */