const-correctness cleanup. 'CONST UNICODE_STRING*' -> PCUNICODE_STRING
[reactos.git] / reactos / ntoskrnl / config / cmvalche.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "ntoskrnl.h"
12 #include "cm.h"
13 #define NDEBUG
14 #include "debug.h"
15
16 FORCEINLINE
17 BOOLEAN
18 CmpIsValueCached(IN HCELL_INDEX CellIndex)
19 {
20 /* Make sure that the cell is valid in the first place */
21 if (CellIndex == HCELL_NIL) return FALSE;
22
23 /*Is this cell actually a pointer to the cached value data? */
24 if (CellIndex & 1) return TRUE;
25
26 /* This is a regular cell */
27 return FALSE;
28 }
29
30 FORCEINLINE
31 VOID
32 CmpSetValueCached(IN PHCELL_INDEX CellIndex)
33 {
34 /* Set the cached bit */
35 *CellIndex |= 1;
36 }
37
38 #define ASSERT_VALUE_CACHE() \
39 ASSERTMSG("Cached Values Not Yet Supported!", FALSE);
40
41 /* FUNCTIONS *****************************************************************/
42
43 VALUE_SEARCH_RETURN_TYPE
44 NTAPI
45 CmpGetValueListFromCache(IN PKEY_OBJECT KeyObject,
46 OUT PCELL_DATA *CellData,
47 OUT BOOLEAN *IndexIsCached,
48 OUT PHCELL_INDEX ValueListToRelease)
49 {
50 PHHIVE Hive;
51 PCACHED_CHILD_LIST ChildList;
52 HCELL_INDEX CellToRelease;
53
54 /* Set defaults */
55 *ValueListToRelease = HCELL_NIL;
56 *IndexIsCached = FALSE;
57
58 /* Get the hive */
59 Hive = &KeyObject->RegistryHive->Hive;
60
61 /* Get the child value cache */
62 //ChildList = &KeyObject->ValueCache;
63 ChildList = (PCACHED_CHILD_LIST)&KeyObject->KeyCell->ValueList;
64
65 /* Check if the value is cached */
66 if (CmpIsValueCached(ChildList->ValueList))
67 {
68 /* It is: we don't expect this yet! */
69 ASSERT_VALUE_CACHE();
70 *IndexIsCached = TRUE;
71 *CellData = NULL;
72 }
73 else
74 {
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;
79
80 /* Return the cell to be released */
81 *ValueListToRelease = CellToRelease;
82 }
83
84 /* If we got here, then the value list was found */
85 return SearchSuccess;
86 }
87
88 VALUE_SEARCH_RETURN_TYPE
89 NTAPI
90 CmpGetValueKeyFromCache(IN PKEY_OBJECT KeyObject,
91 IN PCELL_DATA CellData,
92 IN ULONG Index,
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)
98 {
99 PHHIVE Hive;
100 PCM_KEY_VALUE KeyValue;
101 HCELL_INDEX Cell;
102
103 /* Set defaults */
104 *CellToRelease = HCELL_NIL;
105 *Value = NULL;
106 *ValueIsCached = FALSE;
107
108 /* Get the hive */
109 Hive = &KeyObject->RegistryHive->Hive;
110
111 /* Check if the index was cached */
112 if (IndexIsCached)
113 {
114 /* Not expected yet! */
115 ASSERT_VALUE_CACHE();
116 *ValueIsCached = TRUE;
117 }
118 else
119 {
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;
124
125 /* Return the cell and the actual key value */
126 *CellToRelease = Cell;
127 *Value = KeyValue;
128 }
129
130 /* If we got here, then we found the key value */
131 return SearchSuccess;
132 }
133
134 VALUE_SEARCH_RETURN_TYPE
135 NTAPI
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)
143 {
144 PHHIVE Hive;
145 ULONG Length;
146
147 /* Sanity checks */
148 ASSERT(MAXIMUM_CACHED_DATA < CM_KEY_VALUE_BIG);
149 ASSERT((ValueKey->u.KeyValue.DataLength & CM_KEY_VALUE_SPECIAL_SIZE) == 0);
150
151 /* Set defaults */
152 *DataPointer = NULL;
153 *Allocated = FALSE;
154 *CellToRelease = HCELL_NIL;
155
156 /* Get the hive */
157 Hive = &KeyObject->RegistryHive->Hive;
158
159 /* Check it the value is cached */
160 if (ValueIsCached)
161 {
162 /* This isn't expected! */
163 ASSERT_VALUE_CACHE();
164 }
165 else
166 {
167 /* It's not, get the value data using the typical routine */
168 if (!CmpGetValueData(Hive,
169 &ValueKey->u.KeyValue,
170 &Length,
171 DataPointer,
172 Allocated,
173 CellToRelease))
174 {
175 /* Nothing found: make sure no data was allocated */
176 ASSERT(*Allocated == FALSE);
177 ASSERT(*DataPointer == NULL);
178 return SearchFail;
179 }
180 }
181
182 /* We found the actual data, return success */
183 return SearchSuccess;
184 }
185
186 VALUE_SEARCH_RETURN_TYPE
187 NTAPI
188 CmpFindValueByNameFromCache(IN PKEY_OBJECT KeyObject,
189 IN PCUNICODE_STRING Name,
190 OUT PCM_CACHED_VALUE **CachedValue,
191 OUT ULONG *Index,
192 OUT PCM_KEY_VALUE *Value,
193 OUT BOOLEAN *ValueIsCached,
194 OUT PHCELL_INDEX CellToRelease)
195 {
196 PHHIVE Hive;
197 VALUE_SEARCH_RETURN_TYPE SearchResult = SearchFail;
198 LONG Result;
199 UNICODE_STRING SearchName;
200 PCELL_DATA CellData;
201 PCACHED_CHILD_LIST ChildList;
202 PCM_KEY_VALUE KeyValue;
203 BOOLEAN IndexIsCached;
204 ULONG i = 0;
205 HCELL_INDEX Cell = HCELL_NIL;
206
207 /* Set defaults */
208 *CellToRelease = HCELL_NIL;
209 *Value = NULL;
210
211 /* Get the hive and child list */
212 Hive = &KeyObject->RegistryHive->Hive;
213 //ChildList = &KeyObject->ValueCache;
214 ChildList = (PCACHED_CHILD_LIST)&KeyObject->KeyCell->ValueList;
215
216 /* Check if the child list has any entries */
217 if (ChildList->Count != 0)
218 {
219 /* Get the value list associated to this child list */
220 SearchResult = CmpGetValueListFromCache(KeyObject,
221 &CellData,
222 &IndexIsCached,
223 &Cell);
224 if (SearchResult != SearchSuccess) return SearchResult;
225
226 /* The index shouldn't be cached right now */
227 if (IndexIsCached) ASSERT_VALUE_CACHE();
228
229 /* Loop every value */
230 while (TRUE)
231 {
232 /* Check if there's any cell to release */
233 if (*CellToRelease != HCELL_NIL)
234 {
235 /* Release it now */
236 HvReleaseCell(Hive, *CellToRelease);
237 *CellToRelease = HCELL_NIL;
238 }
239
240 /* Get the key value for this index */
241 SearchResult = CmpGetValueKeyFromCache(KeyObject,
242 CellData,
243 i,
244 CachedValue,
245 Value,
246 IndexIsCached,
247 ValueIsCached,
248 CellToRelease);
249 if (SearchResult != SearchSuccess) return SearchResult;
250
251 /* Check if the both the index and the value are cached */
252 if ((IndexIsCached) && (*ValueIsCached))
253 {
254 /* We don't expect this yet */
255 ASSERT_VALUE_CACHE();
256 Result = -1;
257 }
258 else
259 {
260 /* No cache, so try to compare the name. Is it compressed? */
261 KeyValue = *Value;
262 if (KeyValue->Flags & VALUE_COMP_NAME)
263 {
264 /* It is, do a compressed name comparison */
265 Result = CmpCompareCompressedName(Name,
266 KeyValue->Name,
267 KeyValue->NameLength);
268 }
269 else
270 {
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);
276 }
277 }
278
279 /* Check if we found the value data */
280 if (!Result)
281 {
282 /* We have, return the index of the value and success */
283 *Index = i;
284 SearchResult = SearchSuccess;
285 goto Quickie;
286 }
287
288 /* We didn't find it, try the next entry */
289 if (++i == ChildList->Count)
290 {
291 /* The entire list was parsed, fail */
292 *Value = NULL;
293 SearchResult = SearchFail;
294 goto Quickie;
295 }
296 }
297 }
298
299 /* We should only get here if the child list is empty */
300 ASSERT(ChildList->Count == 0);
301
302 Quickie:
303 /* Release the value list cell if required, and return search result */
304 if (Cell != HCELL_NIL) HvReleaseCell(Hive, Cell);
305 return SearchResult;
306 }
307
308 VALUE_SEARCH_RETURN_TYPE
309 NTAPI
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,
316 IN ULONG Length,
317 OUT PULONG ResultLength,
318 OUT PNTSTATUS Status)
319 {
320 PHHIVE Hive;
321 PKEY_VALUE_INFORMATION Info = (PKEY_VALUE_INFORMATION)KeyValueInformation;
322 PCELL_DATA CellData;
323 USHORT NameSize;
324 ULONG Size, MinimumSize, SizeLeft, KeySize, AlignedData = 0, DataOffset;
325 PVOID Buffer;
326 BOOLEAN IsSmall, BufferAllocated = FALSE;
327 HCELL_INDEX CellToRelease = HCELL_NIL;
328 VALUE_SEARCH_RETURN_TYPE Result = SearchSuccess;
329
330 /* Get the hive and cell data */
331 Hive = &KeyObject->RegistryHive->Hive;
332 CellData = (PCELL_DATA)ValueKey;
333
334 /* Check if the value is compressed */
335 if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME)
336 {
337 /* Get the compressed name size */
338 NameSize = CmpCompressedNameSize(CellData->u.KeyValue.Name,
339 CellData->u.KeyValue.NameLength);
340 }
341 else
342 {
343 /* Get the real size */
344 NameSize = CellData->u.KeyValue.NameLength;
345 }
346
347 /* Check what kind of information the caller is requesting */
348 switch (KeyValueInformationClass)
349 {
350 /* Basic information */
351 case KeyValueBasicInformation:
352
353 /* This is how much size we'll need */
354 Size = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name) + NameSize;
355
356 /* This is the minimum we can work with */
357 MinimumSize = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name);
358
359 /* Return the size we'd like, and assume success */
360 *ResultLength = Size;
361 *Status = STATUS_SUCCESS;
362
363 /* Check if the caller gave us below our minimum */
364 if (Length < MinimumSize)
365 {
366 /* Then we must fail */
367 *Status = STATUS_BUFFER_TOO_SMALL;
368 break;
369 }
370
371 /* Fill out the basic information */
372 Info->KeyValueBasicInformation.TitleIndex = 0;
373 Info->KeyValueBasicInformation.Type = CellData->u.KeyValue.Type;
374 Info->KeyValueBasicInformation.NameLength = NameSize;
375
376 /* Now only the name is left */
377 SizeLeft = Length - MinimumSize;
378 Size = NameSize;
379
380 /* Check if the remaining buffer is too small for the name */
381 if (SizeLeft < Size)
382 {
383 /* Copy only as much as can fit, and tell the caller */
384 Size = SizeLeft;
385 *Status = STATUS_BUFFER_OVERFLOW;
386 }
387
388 /* Check if this is a compressed name */
389 if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME)
390 {
391 /* Copy as much as we can of the compressed name */
392 CmpCopyCompressedName(Info->KeyValueBasicInformation.Name,
393 Size,
394 CellData->u.KeyValue.Name,
395 CellData->u.KeyValue.NameLength);
396 }
397 else
398 {
399 /* Copy as much as we can of the raw name */
400 RtlCopyMemory(Info->KeyValueBasicInformation.Name,
401 CellData->u.KeyValue.Name,
402 Size);
403 }
404
405 /* We're all done */
406 break;
407
408 /* Full key information */
409 case KeyValueFullInformation:
410
411 /* Check if this is a small key and compute key size */
412 IsSmall = CmpIsKeyValueSmall(&KeySize,
413 CellData->u.KeyValue.DataLength);
414
415 /* Calculate the total size required */
416 Size = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
417 NameSize +
418 KeySize;
419
420 /* And this is the least we can work with */
421 MinimumSize = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name);
422
423 /* Check if there's any key data */
424 if (KeySize > 0)
425 {
426 /* Calculate the data offset */
427 DataOffset = Size - KeySize;
428
429 /* Align the offset to 4 bytes */
430 AlignedData = ALIGN_UP(DataOffset, ULONG);
431
432 /* If alignment was required, we'll need more space */
433 if (AlignedData > DataOffset) Size += (AlignedData-DataOffset);
434 }
435
436 /* Tell the caller the size we'll finally need, and set success */
437 *ResultLength = Size;
438 *Status = STATUS_SUCCESS;
439
440 /* Check if the caller is giving us too little */
441 if (Length < MinimumSize)
442 {
443 /* Then fail right now */
444 *Status = STATUS_BUFFER_TOO_SMALL;
445 break;
446 }
447
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;
453
454 /* Only the name is left now */
455 SizeLeft = Length - MinimumSize;
456 Size = NameSize;
457
458 /* Check if the name fits */
459 if (SizeLeft < Size)
460 {
461 /* It doesn't, truncate what we'll copy, and tell the caller */
462 Size = SizeLeft;
463 *Status = STATUS_BUFFER_OVERFLOW;
464 }
465
466 /* Check if this key value is compressed */
467 if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME)
468 {
469 /* It is, copy the compressed name */
470 CmpCopyCompressedName(Info->KeyValueFullInformation.Name,
471 Size,
472 CellData->u.KeyValue.Name,
473 CellData->u.KeyValue.NameLength);
474 }
475 else
476 {
477 /* It's not, copy the raw name */
478 RtlCopyMemory(Info->KeyValueFullInformation.Name,
479 CellData->u.KeyValue.Name,
480 Size);
481 }
482
483 /* Now check if the key had any data */
484 if (KeySize > 0)
485 {
486 /* Was it a small key? */
487 if (IsSmall)
488 {
489 /* Then the data is directly into the cell */
490 Buffer = &CellData->u.KeyValue.Data;
491 }
492 else
493 {
494 /* Otherwise, we must retrieve it from the value cache */
495 Result = CmpGetValueDataFromCache(KeyObject,
496 CachedValue,
497 CellData,
498 ValueIsCached,
499 &Buffer,
500 &BufferAllocated,
501 &CellToRelease);
502 if (Result != SearchSuccess)
503 {
504 /* We failed, nothing should be allocated */
505 ASSERT(Buffer == NULL);
506 ASSERT(BufferAllocated == FALSE);
507 *Status = STATUS_INSUFFICIENT_RESOURCES;
508 }
509 }
510
511 /* Now that we know we truly have data, set its offset */
512 Info->KeyValueFullInformation.DataOffset = AlignedData;
513
514 /* Only the data remains to be copied */
515 SizeLeft = (((LONG)Length - (LONG)AlignedData) < 0) ?
516 0 : (Length - AlignedData);
517 Size = KeySize;
518
519 /* Check if the caller has no space for it */
520 if (SizeLeft < Size)
521 {
522 /* Truncate what we'll copy, and tell the caller */
523 Size = SizeLeft;
524 *Status = STATUS_BUFFER_OVERFLOW;
525 }
526
527 /* Sanity check */
528 ASSERT((IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE));
529
530 /* Make sure we have a valid buffer */
531 if (Buffer)
532 {
533 /* Copy the data into the aligned offset */
534 RtlCopyMemory((PVOID)((ULONG_PTR)Info + AlignedData),
535 Buffer,
536 Size);
537 }
538 }
539 else
540 {
541 /* We don't have any data, set the offset to -1, not 0! */
542 Info->KeyValueFullInformation.DataOffset = 0xFFFFFFFF;
543 }
544
545 /* We're done! */
546 break;
547
548 /* Partial information requested (no name or alignment!) */
549 case KeyValuePartialInformation:
550
551 /* Check if this is a small key and compute key size */
552 IsSmall = CmpIsKeyValueSmall(&KeySize,
553 CellData->u.KeyValue.DataLength);
554
555 /* Calculate the total size required */
556 Size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + KeySize;
557
558 /* And this is the least we can work with */
559 MinimumSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
560
561 /* Tell the caller the size we'll finally need, and set success */
562 *ResultLength = Size;
563 *Status = STATUS_SUCCESS;
564
565 /* Check if the caller is giving us too little */
566 if (Length < MinimumSize)
567 {
568 /* Then fail right now */
569 *Status = STATUS_BUFFER_TOO_SMALL;
570 break;
571 }
572
573 /* Fill out the basic information */
574 Info->KeyValuePartialInformation.TitleIndex = 0;
575 Info->KeyValuePartialInformation.Type = CellData->u.KeyValue.Type;
576 Info->KeyValuePartialInformation.DataLength = KeySize;
577
578 /* Now check if the key had any data */
579 if (KeySize > 0)
580 {
581 /* Was it a small key? */
582 if (IsSmall)
583 {
584 /* Then the data is directly into the cell */
585 Buffer = &CellData->u.KeyValue.Data;
586 }
587 else
588 {
589 /* Otherwise, we must retrieve it from the value cache */
590 Result = CmpGetValueDataFromCache(KeyObject,
591 CachedValue,
592 CellData,
593 ValueIsCached,
594 &Buffer,
595 &BufferAllocated,
596 &CellToRelease);
597 if (Result != SearchSuccess)
598 {
599 /* We failed, nothing should be allocated */
600 ASSERT(Buffer == NULL);
601 ASSERT(BufferAllocated == FALSE);
602 *Status = STATUS_INSUFFICIENT_RESOURCES;
603 }
604 }
605
606 /* Only the data remains to be copied */
607 SizeLeft = Length - MinimumSize;
608 Size = KeySize;
609
610 /* Check if the caller has no space for it */
611 if (SizeLeft < Size)
612 {
613 /* Truncate what we'll copy, and tell the caller */
614 Size = SizeLeft;
615 *Status = STATUS_BUFFER_OVERFLOW;
616 }
617
618 /* Sanity check */
619 ASSERT((IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE));
620
621 /* Make sure we have a valid buffer */
622 if (Buffer)
623 {
624 /* Copy the data into the aligned offset */
625 RtlCopyMemory(Info->KeyValuePartialInformation.Data,
626 Buffer,
627 Size);
628 }
629 }
630
631 /* We're done! */
632 break;
633
634 /* Other information class */
635 default:
636
637 /* We got some class that we don't support */
638 *Status = STATUS_INVALID_PARAMETER;
639 break;
640 }
641
642 /* Return the search result as well */
643 return Result;
644 }