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
CmpIsValueCached(IN HCELL_INDEX CellIndex)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
CmpSetValueCached(IN PHCELL_INDEX CellIndex)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!\n", FALSE);
39
40 /* FUNCTIONS *****************************************************************/
41
42 VALUE_SEARCH_RETURN_TYPE
43 NTAPI
CmpGetValueListFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb,OUT PCELL_DATA * CellData,OUT BOOLEAN * IndexIsCached,OUT PHCELL_INDEX ValueListToRelease)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
53 /* Set defaults */
54 *ValueListToRelease = HCELL_NIL;
55 *IndexIsCached = FALSE;
56
57 /* Get the hive and value cache */
58 Hive = Kcb->KeyHive;
59 ChildList = &Kcb->ValueCache;
60
61 /* Check if the value is cached */
62 if (CmpIsValueCached(ChildList->ValueList))
63 {
64 /* It is: we don't expect this yet! */
65 ASSERT_VALUE_CACHE();
66 *IndexIsCached = TRUE;
67 *CellData = NULL;
68 }
69 else
70 {
71 /* Make sure the KCB is locked exclusive */
72 if (!CmpIsKcbLockedExclusive(Kcb) &&
73 !CmpTryToConvertKcbSharedToExclusive(Kcb))
74 {
75 /* We need the exclusive lock */
76 return SearchNeedExclusiveLock;
77 }
78
79 /* Select the value list as our cell, and get the actual list array */
80 CellToRelease = ChildList->ValueList;
81 *CellData = (PCELL_DATA)HvGetCell(Hive, CellToRelease);
82 if (!*CellData) return SearchFail;
83
84 /* FIXME: Here we would cache the value */
85
86 /* Return the cell to be released */
87 *ValueListToRelease = CellToRelease;
88 }
89
90 /* If we got here, then the value list was found */
91 return SearchSuccess;
92 }
93
94 VALUE_SEARCH_RETURN_TYPE
95 NTAPI
CmpGetValueKeyFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb,IN PCELL_DATA CellData,IN ULONG Index,OUT PCM_CACHED_VALUE ** CachedValue,OUT PCM_KEY_VALUE * Value,IN BOOLEAN IndexIsCached,OUT BOOLEAN * ValueIsCached,OUT PHCELL_INDEX CellToRelease)96 CmpGetValueKeyFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb,
97 IN PCELL_DATA CellData,
98 IN ULONG Index,
99 OUT PCM_CACHED_VALUE **CachedValue,
100 OUT PCM_KEY_VALUE *Value,
101 IN BOOLEAN IndexIsCached,
102 OUT BOOLEAN *ValueIsCached,
103 OUT PHCELL_INDEX CellToRelease)
104 {
105 PHHIVE Hive;
106 PCM_KEY_VALUE KeyValue;
107 HCELL_INDEX Cell;
108
109 /* Set defaults */
110 *CellToRelease = HCELL_NIL;
111 *Value = NULL;
112 *ValueIsCached = FALSE;
113
114 /* Get the hive */
115 Hive = Kcb->KeyHive;
116
117 /* Check if the index was cached */
118 if (IndexIsCached)
119 {
120 /* Not expected yet! */
121 ASSERT_VALUE_CACHE();
122 *ValueIsCached = TRUE;
123 }
124 else
125 {
126 /* Get the cell index and the key value associated to it */
127 Cell = CellData->u.KeyList[Index];
128 KeyValue = (PCM_KEY_VALUE)HvGetCell(Hive, Cell);
129 if (!KeyValue) return SearchFail;
130
131 /* Return the cell and the actual key value */
132 *CellToRelease = Cell;
133 *Value = KeyValue;
134 }
135
136 /* If we got here, then we found the key value */
137 return SearchSuccess;
138 }
139
140 VALUE_SEARCH_RETURN_TYPE
141 NTAPI
CmpGetValueDataFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb,IN PCM_CACHED_VALUE * CachedValue,IN PCELL_DATA ValueKey,IN BOOLEAN ValueIsCached,OUT PVOID * DataPointer,OUT PBOOLEAN Allocated,OUT PHCELL_INDEX CellToRelease)142 CmpGetValueDataFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb,
143 IN PCM_CACHED_VALUE *CachedValue,
144 IN PCELL_DATA ValueKey,
145 IN BOOLEAN ValueIsCached,
146 OUT PVOID *DataPointer,
147 OUT PBOOLEAN Allocated,
148 OUT PHCELL_INDEX CellToRelease)
149 {
150 PHHIVE Hive;
151 ULONG Length;
152
153 /* Sanity checks */
154 ASSERT(MAXIMUM_CACHED_DATA < CM_KEY_VALUE_BIG);
155 ASSERT((ValueKey->u.KeyValue.DataLength & CM_KEY_VALUE_SPECIAL_SIZE) == 0);
156
157 /* Set defaults */
158 *DataPointer = NULL;
159 *Allocated = FALSE;
160 *CellToRelease = HCELL_NIL;
161
162 /* Get the hive */
163 Hive = Kcb->KeyHive;
164
165 /* Check it the value is cached */
166 if (ValueIsCached)
167 {
168 /* This isn't expected! */
169 ASSERT_VALUE_CACHE();
170 }
171 else
172 {
173 /* It's not, get the value data using the typical routine */
174 if (!CmpGetValueData(Hive,
175 &ValueKey->u.KeyValue,
176 &Length,
177 DataPointer,
178 Allocated,
179 CellToRelease))
180 {
181 /* Nothing found: make sure no data was allocated */
182 ASSERT(*Allocated == FALSE);
183 ASSERT(*DataPointer == NULL);
184 return SearchFail;
185 }
186 }
187
188 /* We found the actual data, return success */
189 return SearchSuccess;
190 }
191
192 VALUE_SEARCH_RETURN_TYPE
193 NTAPI
CmpFindValueByNameFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb,IN PCUNICODE_STRING Name,OUT PCM_CACHED_VALUE ** CachedValue,OUT ULONG * Index,OUT PCM_KEY_VALUE * Value,OUT BOOLEAN * ValueIsCached,OUT PHCELL_INDEX CellToRelease)194 CmpFindValueByNameFromCache(IN PCM_KEY_CONTROL_BLOCK Kcb,
195 IN PCUNICODE_STRING Name,
196 OUT PCM_CACHED_VALUE **CachedValue,
197 OUT ULONG *Index,
198 OUT PCM_KEY_VALUE *Value,
199 OUT BOOLEAN *ValueIsCached,
200 OUT PHCELL_INDEX CellToRelease)
201 {
202 PHHIVE Hive;
203 VALUE_SEARCH_RETURN_TYPE SearchResult = SearchFail;
204 LONG Result;
205 UNICODE_STRING SearchName;
206 PCELL_DATA CellData;
207 PCACHED_CHILD_LIST ChildList;
208 PCM_KEY_VALUE KeyValue;
209 BOOLEAN IndexIsCached;
210 ULONG i = 0;
211 HCELL_INDEX Cell = HCELL_NIL;
212
213 /* Set defaults */
214 *CellToRelease = HCELL_NIL;
215 *Value = NULL;
216
217 /* Get the hive and child list */
218 Hive = Kcb->KeyHive;
219 ChildList = &Kcb->ValueCache;
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)
230 {
231 /* We either failed or need the exclusive lock */
232 ASSERT((SearchResult == SearchFail) || !CmpIsKcbLockedExclusive(Kcb));
233 ASSERT(Cell == HCELL_NIL);
234 return SearchResult;
235 }
236
237 /* The index shouldn't be cached right now */
238 if (IndexIsCached) ASSERT_VALUE_CACHE();
239
240 /* Loop every value */
241 while (TRUE)
242 {
243 /* Check if there's any cell to release */
244 if (*CellToRelease != HCELL_NIL)
245 {
246 /* Release it now */
247 HvReleaseCell(Hive, *CellToRelease);
248 *CellToRelease = HCELL_NIL;
249 }
250
251 /* Get the key value for this index */
252 SearchResult = CmpGetValueKeyFromCache(Kcb,
253 CellData,
254 i,
255 CachedValue,
256 Value,
257 IndexIsCached,
258 ValueIsCached,
259 CellToRelease);
260 if (SearchResult != SearchSuccess)
261 {
262 /* We either failed or need the exclusive lock */
263 ASSERT((SearchResult == SearchFail) || !CmpIsKcbLockedExclusive(Kcb));
264 ASSERT(Cell == HCELL_NIL);
265 return SearchResult;
266 }
267
268 /* Check if the both the index and the value are cached */
269 if (IndexIsCached && *ValueIsCached)
270 {
271 /* We don't expect this yet */
272 ASSERT_VALUE_CACHE();
273 Result = -1;
274 }
275 else
276 {
277 /* No cache, so try to compare the name. Is it compressed? */
278 KeyValue = *Value;
279 if (KeyValue->Flags & VALUE_COMP_NAME)
280 {
281 /* It is, do a compressed name comparison */
282 Result = CmpCompareCompressedName(Name,
283 KeyValue->Name,
284 KeyValue->NameLength);
285 }
286 else
287 {
288 /* It's not compressed, so do a standard comparison */
289 SearchName.Length = KeyValue->NameLength;
290 SearchName.MaximumLength = SearchName.Length;
291 SearchName.Buffer = KeyValue->Name;
292 Result = RtlCompareUnicodeString(Name, &SearchName, TRUE);
293 }
294 }
295
296 /* Check if we found the value data */
297 if (!Result)
298 {
299 /* We have, return the index of the value and success */
300 *Index = i;
301 SearchResult = SearchSuccess;
302 goto Quickie;
303 }
304
305 /* We didn't find it, try the next entry */
306 if (++i == ChildList->Count)
307 {
308 /* The entire list was parsed, fail */
309 *Value = NULL;
310 SearchResult = SearchFail;
311 goto Quickie;
312 }
313 }
314 }
315
316 /* We should only get here if the child list is empty */
317 ASSERT(ChildList->Count == 0);
318
319 Quickie:
320 /* Release the value list cell if required, and return search result */
321 if (Cell != HCELL_NIL) HvReleaseCell(Hive, Cell);
322 return SearchResult;
323 }
324
325 VALUE_SEARCH_RETURN_TYPE
326 NTAPI
CmpQueryKeyValueData(IN PCM_KEY_CONTROL_BLOCK Kcb,IN PCM_CACHED_VALUE * CachedValue,IN PCM_KEY_VALUE ValueKey,IN BOOLEAN ValueIsCached,IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,IN PVOID KeyValueInformation,IN ULONG Length,OUT PULONG ResultLength,OUT PNTSTATUS Status)327 CmpQueryKeyValueData(IN PCM_KEY_CONTROL_BLOCK Kcb,
328 IN PCM_CACHED_VALUE *CachedValue,
329 IN PCM_KEY_VALUE ValueKey,
330 IN BOOLEAN ValueIsCached,
331 IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
332 IN PVOID KeyValueInformation,
333 IN ULONG Length,
334 OUT PULONG ResultLength,
335 OUT PNTSTATUS Status)
336 {
337 PKEY_VALUE_INFORMATION Info = (PKEY_VALUE_INFORMATION)KeyValueInformation;
338 PCELL_DATA CellData;
339 USHORT NameSize;
340 ULONG Size, MinimumSize, SizeLeft, KeySize, AlignedData = 0, DataOffset;
341 PVOID Buffer;
342 BOOLEAN IsSmall, BufferAllocated = FALSE;
343 HCELL_INDEX CellToRelease = HCELL_NIL;
344 VALUE_SEARCH_RETURN_TYPE Result = SearchSuccess;
345
346 /* Get the value data */
347 CellData = (PCELL_DATA)ValueKey;
348
349 /* Check if the value is compressed */
350 if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME)
351 {
352 /* Get the compressed name size */
353 NameSize = CmpCompressedNameSize(CellData->u.KeyValue.Name,
354 CellData->u.KeyValue.NameLength);
355 }
356 else
357 {
358 /* Get the real size */
359 NameSize = CellData->u.KeyValue.NameLength;
360 }
361
362 /* Check what kind of information the caller is requesting */
363 switch (KeyValueInformationClass)
364 {
365 /* Basic information */
366 case KeyValueBasicInformation:
367
368 /* This is how much size we'll need */
369 Size = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name) + NameSize;
370
371 /* This is the minimum we can work with */
372 MinimumSize = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name);
373
374 /* Return the size we'd like, and assume success */
375 *ResultLength = Size;
376 *Status = STATUS_SUCCESS;
377
378 /* Check if the caller gave us below our minimum */
379 if (Length < MinimumSize)
380 {
381 /* Then we must fail */
382 *Status = STATUS_BUFFER_TOO_SMALL;
383 break;
384 }
385
386 /* Fill out the basic information */
387 Info->KeyValueBasicInformation.TitleIndex = 0;
388 Info->KeyValueBasicInformation.Type = CellData->u.KeyValue.Type;
389 Info->KeyValueBasicInformation.NameLength = NameSize;
390
391 /* Now only the name is left */
392 SizeLeft = Length - MinimumSize;
393 Size = NameSize;
394
395 /* Check if the remaining buffer is too small for the name */
396 if (SizeLeft < Size)
397 {
398 /* Copy only as much as can fit, and tell the caller */
399 Size = SizeLeft;
400 *Status = STATUS_BUFFER_OVERFLOW;
401 }
402
403 /* Check if this is a compressed name */
404 if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME)
405 {
406 /* Copy as much as we can of the compressed name */
407 CmpCopyCompressedName(Info->KeyValueBasicInformation.Name,
408 Size,
409 CellData->u.KeyValue.Name,
410 CellData->u.KeyValue.NameLength);
411 }
412 else
413 {
414 /* Copy as much as we can of the raw name */
415 RtlCopyMemory(Info->KeyValueBasicInformation.Name,
416 CellData->u.KeyValue.Name,
417 Size);
418 }
419
420 /* We're all done */
421 break;
422
423 /* Full key information */
424 case KeyValueFullInformation:
425 case KeyValueFullInformationAlign64:
426
427 /* Check if this is a small key and compute key size */
428 IsSmall = CmpIsKeyValueSmall(&KeySize,
429 CellData->u.KeyValue.DataLength);
430
431 /* Calculate the total size required */
432 Size = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
433 NameSize +
434 KeySize;
435
436 /* And this is the least we can work with */
437 MinimumSize = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name);
438
439 /* Check if there's any key data */
440 if (KeySize > 0)
441 {
442 /* Calculate the data offset */
443 DataOffset = Size - KeySize;
444
445 #ifdef _WIN64
446 /* On 64-bit, always align to 8 bytes */
447 AlignedData = ALIGN_UP(DataOffset, ULONGLONG);
448 #else
449 /* On 32-bit, align the offset to 4 or 8 bytes */
450 if (KeyValueInformationClass == KeyValueFullInformationAlign64)
451 {
452 AlignedData = ALIGN_UP(DataOffset, ULONGLONG);
453 }
454 else
455 {
456 AlignedData = ALIGN_UP(DataOffset, ULONG);
457 }
458 #endif
459 /* If alignment was required, we'll need more space */
460 if (AlignedData > DataOffset) Size += (AlignedData-DataOffset);
461 }
462
463 /* Tell the caller the size we'll finally need, and set success */
464 *ResultLength = Size;
465 *Status = STATUS_SUCCESS;
466
467 /* Check if the caller is giving us too little */
468 if (Length < MinimumSize)
469 {
470 /* Then fail right now */
471 *Status = STATUS_BUFFER_TOO_SMALL;
472 break;
473 }
474
475 /* Fill out the basic information */
476 Info->KeyValueFullInformation.TitleIndex = 0;
477 Info->KeyValueFullInformation.Type = CellData->u.KeyValue.Type;
478 Info->KeyValueFullInformation.DataLength = KeySize;
479 Info->KeyValueFullInformation.NameLength = NameSize;
480
481 /* Only the name is left now */
482 SizeLeft = Length - MinimumSize;
483 Size = NameSize;
484
485 /* Check if the name fits */
486 if (SizeLeft < Size)
487 {
488 /* It doesn't, truncate what we'll copy, and tell the caller */
489 Size = SizeLeft;
490 *Status = STATUS_BUFFER_OVERFLOW;
491 }
492
493 /* Check if this key value is compressed */
494 if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME)
495 {
496 /* It is, copy the compressed name */
497 CmpCopyCompressedName(Info->KeyValueFullInformation.Name,
498 Size,
499 CellData->u.KeyValue.Name,
500 CellData->u.KeyValue.NameLength);
501 }
502 else
503 {
504 /* It's not, copy the raw name */
505 RtlCopyMemory(Info->KeyValueFullInformation.Name,
506 CellData->u.KeyValue.Name,
507 Size);
508 }
509
510 /* Now check if the key had any data */
511 if (KeySize > 0)
512 {
513 /* Was it a small key? */
514 if (IsSmall)
515 {
516 /* Then the data is directly into the cell */
517 Buffer = &CellData->u.KeyValue.Data;
518 }
519 else
520 {
521 /* Otherwise, we must retrieve it from the value cache */
522 Result = CmpGetValueDataFromCache(Kcb,
523 CachedValue,
524 CellData,
525 ValueIsCached,
526 &Buffer,
527 &BufferAllocated,
528 &CellToRelease);
529 if (Result != SearchSuccess)
530 {
531 /* We failed, nothing should be allocated */
532 ASSERT(Buffer == NULL);
533 ASSERT(BufferAllocated == FALSE);
534 *Status = STATUS_INSUFFICIENT_RESOURCES;
535 }
536 }
537
538 /* Now that we know we truly have data, set its offset */
539 Info->KeyValueFullInformation.DataOffset = AlignedData;
540
541 /* Only the data remains to be copied */
542 SizeLeft = (((LONG)Length - (LONG)AlignedData) < 0) ?
543 0 : (Length - AlignedData);
544 Size = KeySize;
545
546 /* Check if the caller has no space for it */
547 if (SizeLeft < Size)
548 {
549 /* Truncate what we'll copy, and tell the caller */
550 Size = SizeLeft;
551 *Status = STATUS_BUFFER_OVERFLOW;
552 }
553
554 /* Sanity check */
555 ASSERT(IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE);
556
557 /* Make sure we have a valid buffer */
558 if (Buffer)
559 {
560 /* Copy the data into the aligned offset */
561 RtlCopyMemory((PVOID)((ULONG_PTR)Info + AlignedData),
562 Buffer,
563 Size);
564 }
565 }
566 else
567 {
568 /* We don't have any data, set the offset to -1, not 0! */
569 Info->KeyValueFullInformation.DataOffset = 0xFFFFFFFF;
570 }
571
572 /* We're done! */
573 break;
574
575 /* Partial information requested (no name or alignment!) */
576 case KeyValuePartialInformation:
577 case KeyValuePartialInformationAlign64:
578
579 /* Check if this is a small key and compute key size */
580 IsSmall = CmpIsKeyValueSmall(&KeySize,
581 CellData->u.KeyValue.DataLength);
582
583 /* Calculate the total size required and the least we can work with */
584 if (KeyValueInformationClass == KeyValuePartialInformationAlign64)
585 {
586 Size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data) + KeySize;
587 MinimumSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION_ALIGN64, Data);
588 }
589 else
590 {
591 Size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + KeySize;
592 MinimumSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
593 }
594
595 /* Tell the caller the size we'll finally need, and set success */
596 *ResultLength = Size;
597 *Status = STATUS_SUCCESS;
598
599 /* Check if the caller is giving us too little */
600 if (Length < MinimumSize)
601 {
602 /* Then fail right now */
603 *Status = STATUS_BUFFER_TOO_SMALL;
604 break;
605 }
606
607 /* Fill out the basic information */
608 if (KeyValueInformationClass == KeyValuePartialInformationAlign64)
609 {
610 Info->KeyValuePartialInformationAlign64.Type = CellData->u.KeyValue.Type;
611 Info->KeyValuePartialInformationAlign64.DataLength = KeySize;
612 }
613 else
614 {
615 Info->KeyValuePartialInformation.TitleIndex = 0;
616 Info->KeyValuePartialInformation.Type = CellData->u.KeyValue.Type;
617 Info->KeyValuePartialInformation.DataLength = KeySize;
618 }
619
620 /* Now check if the key had any data */
621 if (KeySize > 0)
622 {
623 /* Was it a small key? */
624 if (IsSmall)
625 {
626 /* Then the data is directly into the cell */
627 Buffer = &CellData->u.KeyValue.Data;
628 }
629 else
630 {
631 /* Otherwise, we must retrieve it from the value cache */
632 Result = CmpGetValueDataFromCache(Kcb,
633 CachedValue,
634 CellData,
635 ValueIsCached,
636 &Buffer,
637 &BufferAllocated,
638 &CellToRelease);
639 if (Result != SearchSuccess)
640 {
641 /* We failed, nothing should be allocated */
642 ASSERT(Buffer == NULL);
643 ASSERT(BufferAllocated == FALSE);
644 *Status = STATUS_INSUFFICIENT_RESOURCES;
645 }
646 }
647
648 /* Only the data remains to be copied */
649 SizeLeft = Length - MinimumSize;
650 Size = KeySize;
651
652 /* Check if the caller has no space for it */
653 if (SizeLeft < Size)
654 {
655 /* Truncate what we'll copy, and tell the caller */
656 Size = SizeLeft;
657 *Status = STATUS_BUFFER_OVERFLOW;
658 }
659
660 /* Sanity check */
661 ASSERT(IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE);
662
663 /* Make sure we have a valid buffer */
664 if (Buffer)
665 {
666 /* Copy the data into the aligned offset */
667 if (KeyValueInformationClass == KeyValuePartialInformationAlign64)
668 {
669 RtlCopyMemory(Info->KeyValuePartialInformationAlign64.Data,
670 Buffer,
671 Size);
672 }
673 else
674 {
675 RtlCopyMemory(Info->KeyValuePartialInformation.Data,
676 Buffer,
677 Size);
678 }
679 }
680 }
681
682 /* We're done! */
683 break;
684
685 /* Other information class */
686 default:
687
688 /* We got some class that we don't support */
689 DPRINT1("Caller requested unknown class: %lx\n", KeyValueInformationClass);
690 *Status = STATUS_INVALID_PARAMETER;
691 break;
692 }
693
694 /* Return the search result as well */
695 return Result;
696 }
697
698 VALUE_SEARCH_RETURN_TYPE
699 NTAPI
CmpCompareNewValueDataAgainstKCBCache(IN PCM_KEY_CONTROL_BLOCK Kcb,IN PUNICODE_STRING ValueName,IN ULONG Type,IN PVOID Data,IN ULONG DataSize)700 CmpCompareNewValueDataAgainstKCBCache(IN PCM_KEY_CONTROL_BLOCK Kcb,
701 IN PUNICODE_STRING ValueName,
702 IN ULONG Type,
703 IN PVOID Data,
704 IN ULONG DataSize)
705 {
706 VALUE_SEARCH_RETURN_TYPE SearchResult;
707 PCM_KEY_NODE KeyNode;
708 PCM_CACHED_VALUE *CachedValue;
709 ULONG Index;
710 PCM_KEY_VALUE Value;
711 BOOLEAN ValueCached, BufferAllocated = FALSE;
712 PVOID Buffer;
713 HCELL_INDEX ValueCellToRelease = HCELL_NIL, CellToRelease = HCELL_NIL;
714 BOOLEAN IsSmall;
715 ULONG_PTR CompareResult;
716 PAGED_CODE();
717
718 /* Check if this is a symlink */
719 if (Kcb->Flags & KEY_SYM_LINK)
720 {
721 /* We need the exclusive lock */
722 if (!CmpIsKcbLockedExclusive(Kcb) &&
723 !CmpTryToConvertKcbSharedToExclusive(Kcb))
724 {
725 /* We need the exclusive lock */
726 return SearchNeedExclusiveLock;
727 }
728
729 /* Otherwise, get the key node */
730 KeyNode = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive, Kcb->KeyCell);
731 if (!KeyNode) return SearchFail;
732
733 /* Cleanup the KCB cache */
734 CmpCleanUpKcbValueCache(Kcb);
735
736 /* Sanity checks */
737 ASSERT(!CMP_IS_CELL_CACHED(Kcb->ValueCache.ValueList));
738 ASSERT(!(Kcb->ExtFlags & CM_KCB_SYM_LINK_FOUND));
739
740 /* Set the value cache */
741 Kcb->ValueCache.Count = KeyNode->ValueList.Count;
742 Kcb->ValueCache.ValueList = KeyNode->ValueList.List;
743
744 /* Release the cell */
745 HvReleaseCell(Kcb->KeyHive, Kcb->KeyCell);
746 }
747
748 /* Do the search */
749 SearchResult = CmpFindValueByNameFromCache(Kcb,
750 ValueName,
751 &CachedValue,
752 &Index,
753 &Value,
754 &ValueCached,
755 &ValueCellToRelease);
756 if (SearchResult == SearchNeedExclusiveLock)
757 {
758 /* We need the exclusive lock */
759 ASSERT(!CmpIsKcbLockedExclusive(Kcb));
760 ASSERT(ValueCellToRelease == HCELL_NIL);
761 ASSERT(Value == NULL);
762 goto Quickie;
763 }
764 else if (SearchResult == SearchSuccess)
765 {
766 /* Sanity check */
767 ASSERT(Value);
768
769 /* First of all, check if the key size and type matches */
770 if ((Type == Value->Type) &&
771 (DataSize == (Value->DataLength & ~CM_KEY_VALUE_SPECIAL_SIZE)))
772 {
773 /* Check if this is a small key */
774 IsSmall = (DataSize <= CM_KEY_VALUE_SMALL) ? TRUE: FALSE;
775 if (IsSmall)
776 {
777 /* Compare against the data directly */
778 Buffer = &Value->Data;
779 }
780 else
781 {
782 /* Do a search */
783 SearchResult = CmpGetValueDataFromCache(Kcb,
784 CachedValue,
785 (PCELL_DATA)Value,
786 ValueCached,
787 &Buffer,
788 &BufferAllocated,
789 &CellToRelease);
790 if (SearchResult != SearchSuccess)
791 {
792 /* Sanity checks */
793 ASSERT(Buffer == NULL);
794 ASSERT(BufferAllocated == FALSE);
795 goto Quickie;
796 }
797 }
798
799 /* Now check the data size */
800 if (DataSize)
801 {
802 /* Do the compare */
803 CompareResult = RtlCompareMemory(Buffer,
804 Data,
805 DataSize &
806 ~CM_KEY_VALUE_SPECIAL_SIZE);
807 }
808 else
809 {
810 /* It's equal */
811 CompareResult = 0;
812 }
813
814 /* Now check if the compare wasn't equal */
815 if (CompareResult != DataSize) SearchResult = SearchFail;
816 }
817 else
818 {
819 /* The length or type isn't equal */
820 SearchResult = SearchFail;
821 }
822 }
823
824 Quickie:
825 /* Release the value cell */
826 if (ValueCellToRelease) HvReleaseCell(Kcb->KeyHive, ValueCellToRelease);
827
828 /* Free the buffer */
829 if (BufferAllocated) CmpFree(Buffer, 0);
830
831 /* Free the cell */
832 if (CellToRelease) HvReleaseCell(Kcb->KeyHive, CellToRelease);
833
834 /* Return the search result */
835 return SearchResult;
836 }
837