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