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