xref: /reactos/sdk/lib/cmlib/cmvalue.c (revision 84344399)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            lib/cmlib/cmvalue.c
5  * PURPOSE:         Configuration Manager Library - Cell Values
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "cmlib.h"
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* FUNCTIONS *****************************************************************/
16 
17 BOOLEAN
18 NTAPI
19 CmpMarkValueDataDirty(IN PHHIVE Hive,
20                       IN PCM_KEY_VALUE Value)
21 {
22     ULONG KeySize;
23     PAGED_CODE();
24 
25     /* Make sure there's actually any data */
26     if (Value->Data != HCELL_NIL)
27     {
28         /* If this is a small key, there's no need to have it dirty */
29         if (CmpIsKeyValueSmall(&KeySize, Value->DataLength)) return TRUE;
30 
31         /* Check if this is a big key */
32         ASSERT_VALUE_BIG(Hive, KeySize);
33 
34         /* Normal value, just mark it dirty */
35         HvMarkCellDirty(Hive, Value->Data, FALSE);
36     }
37 
38     /* Operation complete */
39     return TRUE;
40 }
41 
42 BOOLEAN
43 NTAPI
44 CmpFreeValueData(IN PHHIVE Hive,
45                  IN HCELL_INDEX DataCell,
46                  IN ULONG DataLength)
47 {
48     ULONG KeySize;
49     PAGED_CODE();
50 
51     /* If this is a small key, the data is built-in */
52     if (!CmpIsKeyValueSmall(&KeySize, DataLength))
53     {
54         /* If there's no data cell, there's nothing to do */
55         if (DataCell == HCELL_NIL) return TRUE;
56 
57         /* Make sure the data cell is allocated */
58         //ASSERT(HvIsCellAllocated(Hive, DataCell));
59 
60         /* Unsupported value type */
61         ASSERT_VALUE_BIG(Hive, KeySize);
62 
63         /* Normal value, just free the data cell */
64         HvFreeCell(Hive, DataCell);
65     }
66 
67     /* Operation complete */
68     return TRUE;
69 }
70 
71 BOOLEAN
72 NTAPI
73 CmpFreeValue(IN PHHIVE Hive,
74              IN HCELL_INDEX Cell)
75 {
76     PCM_KEY_VALUE Value;
77     PAGED_CODE();
78 
79     /* Get the cell data */
80     Value = (PCM_KEY_VALUE)HvGetCell(Hive, Cell);
81     if (!Value) ASSERT(FALSE);
82 
83     /* Free it */
84     if (!CmpFreeValueData(Hive, Value->Data, Value->DataLength))
85     {
86         /* We failed to free the data, return failure */
87         HvReleaseCell(Hive, Cell);
88         return FALSE;
89     }
90 
91     /* Release the cell and free it */
92     HvReleaseCell(Hive, Cell);
93     HvFreeCell(Hive, Cell);
94     return TRUE;
95 }
96 
97 HCELL_INDEX
98 NTAPI
99 CmpFindValueByName(IN PHHIVE Hive,
100                    IN PCM_KEY_NODE KeyNode,
101                    IN PCUNICODE_STRING Name)
102 {
103     HCELL_INDEX CellIndex;
104 
105     /* Call the main function */
106     if (!CmpFindNameInList(Hive,
107                            &KeyNode->ValueList,
108                            Name,
109                            NULL,
110                            &CellIndex))
111     {
112         /* Sanity check */
113         ASSERT(CellIndex == HCELL_NIL);
114     }
115 
116     /* Return the index */
117     return CellIndex;
118 }
119 
120 /*
121  * NOTE: This function should support big values, contrary to CmpValueToData.
122  */
123 BOOLEAN
124 NTAPI
125 CmpGetValueData(IN PHHIVE Hive,
126                 IN PCM_KEY_VALUE Value,
127                 OUT PULONG Length,
128                 OUT PVOID *Buffer,
129                 OUT PBOOLEAN BufferAllocated,
130                 OUT PHCELL_INDEX CellToRelease)
131 {
132     PAGED_CODE();
133 
134     /* Sanity check */
135     ASSERT(Value->Signature == CM_KEY_VALUE_SIGNATURE);
136 
137     /* Set failure defaults */
138     *BufferAllocated = FALSE;
139     *Buffer = NULL;
140     *CellToRelease = HCELL_NIL;
141 
142     /* Check if this is a small key */
143     if (CmpIsKeyValueSmall(Length, Value->DataLength))
144     {
145         /* Return the data immediately */
146         *Buffer = &Value->Data;
147         return TRUE;
148     }
149 
150     /* Unsupported at the moment */
151     ASSERT_VALUE_BIG(Hive, *Length);
152 
153     /* Get the data from the cell */
154     *Buffer = HvGetCell(Hive, Value->Data);
155     if (!(*Buffer)) return FALSE;
156 
157     /* Return success and the cell to be released */
158     *CellToRelease = Value->Data;
159     return TRUE;
160 }
161 
162 /*
163  * NOTE: This function doesn't support big values, contrary to CmpGetValueData.
164  */
165 PCELL_DATA
166 NTAPI
167 CmpValueToData(IN PHHIVE Hive,
168                IN PCM_KEY_VALUE Value,
169                OUT PULONG Length)
170 {
171     PCELL_DATA Buffer;
172     BOOLEAN BufferAllocated;
173     HCELL_INDEX CellToRelease;
174     PAGED_CODE();
175 
176     /* Sanity check */
177     ASSERT(Hive->ReleaseCellRoutine == NULL);
178 
179     /* Get the actual data */
180     if (!CmpGetValueData(Hive,
181                          Value,
182                          Length,
183                          (PVOID*)&Buffer,
184                          &BufferAllocated,
185                          &CellToRelease))
186     {
187         /* We failed */
188         ASSERT(BufferAllocated == FALSE);
189         ASSERT(Buffer == NULL);
190         return NULL;
191     }
192 
193     /* This should never happen! */
194     if (BufferAllocated)
195     {
196         /* Free the buffer and bugcheck */
197         Hive->Free(Buffer, 0);
198         KeBugCheckEx(REGISTRY_ERROR, 8, 0, (ULONG_PTR)Hive, (ULONG_PTR)Value);
199     }
200 
201     /* Otherwise, return the cell data */
202     return Buffer;
203 }
204 
205 NTSTATUS
206 NTAPI
207 CmpAddValueToList(IN PHHIVE Hive,
208                   IN HCELL_INDEX ValueCell,
209                   IN ULONG Index,
210                   IN HSTORAGE_TYPE StorageType,
211                   IN OUT PCHILD_LIST ChildList)
212 {
213     HCELL_INDEX ListCell;
214     ULONG ChildCount, Length, i;
215     PCELL_DATA CellData;
216     PAGED_CODE();
217 
218     /* Sanity check */
219     ASSERT((((LONG)Index) >= 0) && (Index <= ChildList->Count));
220 
221     /* Get the number of entries in the child list */
222     ChildCount = ChildList->Count;
223     ChildCount++;
224     if (ChildCount > 1)
225     {
226         ASSERT(ChildList->List != HCELL_NIL);
227 
228         /* The cell should be dirty at this point */
229         ASSERT(HvIsCellDirty(Hive, ChildList->List));
230 
231         /* Check if we have less then 100 children */
232         if (ChildCount < 100)
233         {
234             /* Allocate just enough as requested */
235             Length = ChildCount * sizeof(HCELL_INDEX);
236         }
237         else
238         {
239             /* Otherwise, we have quite a few, so allocate a batch */
240             Length = ROUND_UP(ChildCount, 100) * sizeof(HCELL_INDEX);
241             if (Length > HBLOCK_SIZE)
242             {
243                 /* But make sure we don't allocate beyond our block size */
244                 Length = ROUND_UP(Length, HBLOCK_SIZE);
245             }
246         }
247 
248         /* Perform the allocation */
249         ListCell = HvReallocateCell(Hive, ChildList->List, Length);
250     }
251     else
252     {
253         /* This is our first child, so allocate a single cell */
254         ASSERT(ChildList->List == HCELL_NIL);
255         ListCell = HvAllocateCell(Hive, sizeof(HCELL_INDEX), StorageType, HCELL_NIL);
256     }
257 
258     /* Fail if we couldn't get a cell */
259     if (ListCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
260 
261     /* Set this cell as the child list's list cell */
262     ChildList->List = ListCell;
263 
264     /* Get the actual key list memory */
265     CellData = HvGetCell(Hive, ListCell);
266     ASSERT(CellData != NULL);
267 
268     /* Loop all the children */
269     for (i = ChildCount - 1; i > Index; i--)
270     {
271         /* Move them all down */
272         CellData->u.KeyList[i] = CellData->u.KeyList[i - 1];
273     }
274 
275     /* Insert us on top now */
276     CellData->u.KeyList[Index] = ValueCell;
277     ChildList->Count = ChildCount;
278 
279     /* Release the list cell and make sure the value cell is dirty */
280     HvReleaseCell(Hive, ListCell);
281     ASSERT(HvIsCellDirty(Hive, ValueCell));
282 
283     /* We're done here */
284     return STATUS_SUCCESS;
285 }
286 
287 NTSTATUS
288 NTAPI
289 CmpSetValueDataNew(IN PHHIVE Hive,
290                    IN PVOID Data,
291                    IN ULONG DataSize,
292                    IN HSTORAGE_TYPE StorageType,
293                    IN HCELL_INDEX ValueCell,
294                    OUT PHCELL_INDEX DataCell)
295 {
296     PCELL_DATA CellData;
297     PAGED_CODE();
298     ASSERT(DataSize > CM_KEY_VALUE_SMALL);
299 
300     /* Check if this is a big key */
301     ASSERT_VALUE_BIG(Hive, DataSize);
302 
303     /* Allocate a data cell */
304     *DataCell = HvAllocateCell(Hive, DataSize, StorageType, HCELL_NIL);
305     if (*DataCell == HCELL_NIL) return STATUS_INSUFFICIENT_RESOURCES;
306 
307     /* Get the actual data */
308     CellData = HvGetCell(Hive, *DataCell);
309     if (!CellData) ASSERT(FALSE);
310 
311     /* Copy our buffer into it */
312     RtlCopyMemory(CellData, Data, DataSize);
313 
314     /* All done */
315     return STATUS_SUCCESS;
316 }
317 
318 NTSTATUS
319 NTAPI
320 CmpRemoveValueFromList(IN PHHIVE Hive,
321                        IN ULONG Index,
322                        IN OUT PCHILD_LIST ChildList)
323 {
324     ULONG Count;
325     PCELL_DATA CellData;
326     HCELL_INDEX NewCell;
327     PAGED_CODE();
328 
329     /* Sanity check */
330     ASSERT((((LONG)Index) >= 0) && (Index <= ChildList->Count));
331 
332     /* Get the new count after removal */
333     Count = ChildList->Count - 1;
334     if (Count > 0)
335     {
336         /* Get the actual list array */
337         CellData = HvGetCell(Hive, ChildList->List);
338         if (!CellData) return STATUS_INSUFFICIENT_RESOURCES;
339 
340         /* Make sure cells data have been made dirty */
341         ASSERT(HvIsCellDirty(Hive, ChildList->List));
342         ASSERT(HvIsCellDirty(Hive, CellData->u.KeyList[Index]));
343 
344         /* Loop the list */
345         while (Index < Count)
346         {
347             /* Move everything up */
348             CellData->u.KeyList[Index] = CellData->u.KeyList[Index + 1];
349             Index++;
350         }
351 
352         /* Re-allocate the cell for the list by decreasing the count */
353         NewCell = HvReallocateCell(Hive,
354                                    ChildList->List,
355                                    Count * sizeof(HCELL_INDEX));
356         ASSERT(NewCell != HCELL_NIL);
357         HvReleaseCell(Hive,ChildList->List);
358 
359         /* Update the list cell */
360         ChildList->List = NewCell;
361     }
362     else
363     {
364         /* Otherwise, we were the last entry, so free the list entirely */
365         HvFreeCell(Hive, ChildList->List);
366         ChildList->List = HCELL_NIL;
367     }
368 
369     /* Update the child list with the new count */
370     ChildList->Count = Count;
371     return STATUS_SUCCESS;
372 }
373 
374 HCELL_INDEX
375 NTAPI
376 CmpCopyCell(IN PHHIVE SourceHive,
377             IN HCELL_INDEX SourceCell,
378             IN PHHIVE DestinationHive,
379             IN HSTORAGE_TYPE StorageType)
380 {
381     PCELL_DATA SourceData;
382     PCELL_DATA DestinationData = NULL;
383     HCELL_INDEX DestinationCell = HCELL_NIL;
384     LONG DataSize;
385 
386     PAGED_CODE();
387 
388     /* Get the data and the size of the source cell */
389     SourceData = HvGetCell(SourceHive, SourceCell);
390     DataSize = HvGetCellSize(SourceHive, SourceData);
391 
392     /* Allocate a new cell in the destination hive */
393     DestinationCell = HvAllocateCell(DestinationHive,
394                                      DataSize,
395                                      StorageType,
396                                      HCELL_NIL);
397     if (DestinationCell == HCELL_NIL) goto Cleanup;
398 
399     /* Get the data of the destination cell */
400     DestinationData = HvGetCell(DestinationHive, DestinationCell);
401 
402     /* Copy the data from the source cell to the destination cell */
403     RtlMoveMemory(DestinationData, SourceData, DataSize);
404 
405 Cleanup:
406 
407     /* Release the cells */
408     if (DestinationData) HvReleaseCell(DestinationHive, DestinationCell);
409     if (SourceData) HvReleaseCell(SourceHive, SourceCell);
410 
411     /* Return the destination cell index */
412     return DestinationCell;
413 }
414 
415 HCELL_INDEX
416 NTAPI
417 CmpCopyValue(IN PHHIVE SourceHive,
418              IN HCELL_INDEX SourceValueCell,
419              IN PHHIVE DestinationHive,
420              IN HSTORAGE_TYPE StorageType)
421 {
422     PCM_KEY_VALUE Value, NewValue;
423     HCELL_INDEX NewValueCell, NewDataCell;
424     PCELL_DATA CellData;
425     ULONG SmallData;
426     ULONG DataSize;
427     BOOLEAN IsSmall;
428 
429     PAGED_CODE();
430 
431     /* Get the actual source data */
432     Value = (PCM_KEY_VALUE)HvGetCell(SourceHive, SourceValueCell);
433     if (!Value) ASSERT(FALSE);
434 
435     /* Copy the value cell body */
436     NewValueCell = CmpCopyCell(SourceHive,
437                                SourceValueCell,
438                                DestinationHive,
439                                StorageType);
440     if (NewValueCell == HCELL_NIL)
441     {
442         /* Not enough storage space */
443         goto Quit;
444     }
445 
446     /* Copy the value data */
447     IsSmall = CmpIsKeyValueSmall(&DataSize, Value->DataLength);
448     if (DataSize == 0)
449     {
450         /* Nothing to copy */
451 
452         NewValue = (PCM_KEY_VALUE)HvGetCell(DestinationHive, NewValueCell);
453         ASSERT(NewValue);
454         NewValue->DataLength = 0;
455         NewValue->Data = HCELL_NIL;
456         HvReleaseCell(DestinationHive, NewValueCell);
457 
458         goto Quit;
459     }
460 
461     if (DataSize <= CM_KEY_VALUE_SMALL)
462     {
463         if (IsSmall)
464         {
465             /* Small value, copy directly */
466             SmallData = Value->Data;
467         }
468         else
469         {
470             /* The value is small, but was stored in a regular cell. Get the data from it. */
471             CellData = HvGetCell(SourceHive, Value->Data);
472             ASSERT(CellData);
473             SmallData = *(PULONG)CellData;
474             HvReleaseCell(SourceHive, Value->Data);
475         }
476 
477         /* This is a small key, set the data directly inside */
478         NewValue = (PCM_KEY_VALUE)HvGetCell(DestinationHive, NewValueCell);
479         ASSERT(NewValue);
480         NewValue->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
481         NewValue->Data = SmallData;
482         HvReleaseCell(DestinationHive, NewValueCell);
483     }
484     else
485     {
486         /* Big keys are currently unsupported */
487         ASSERT_VALUE_BIG(SourceHive, DataSize);
488         // Should use CmpGetValueData and CmpSetValueDataNew for big values!
489 
490         /* Regular value */
491 
492         /* Copy the data cell */
493         NewDataCell = CmpCopyCell(SourceHive,
494                                   Value->Data,
495                                   DestinationHive,
496                                   StorageType);
497         if (NewDataCell == HCELL_NIL)
498         {
499             /* Not enough storage space */
500             HvFreeCell(DestinationHive, NewValueCell);
501             NewValueCell = HCELL_NIL;
502             goto Quit;
503         }
504 
505         NewValue = (PCM_KEY_VALUE)HvGetCell(DestinationHive, NewValueCell);
506         ASSERT(NewValue);
507         NewValue->DataLength = DataSize;
508         NewValue->Data = NewDataCell;
509         HvReleaseCell(DestinationHive, NewValueCell);
510     }
511 
512 Quit:
513     HvReleaseCell(SourceHive, SourceValueCell);
514 
515     /* Return the copied value body cell index */
516     return NewValueCell;
517 }
518 
519 NTSTATUS
520 NTAPI
521 CmpCopyKeyValueList(IN PHHIVE SourceHive,
522                     IN PCHILD_LIST SrcValueList,
523                     IN PHHIVE DestinationHive,
524                     IN OUT PCHILD_LIST DestValueList,
525                     IN HSTORAGE_TYPE StorageType)
526 {
527     NTSTATUS Status = STATUS_SUCCESS;
528     PCELL_DATA SrcListData = NULL, DestListData = NULL;
529     HCELL_INDEX NewValue;
530     ULONG Index;
531 
532     PAGED_CODE();
533 
534     /* Reset the destination value list */
535     DestValueList->Count = 0;
536     DestValueList->List = HCELL_NIL;
537 
538     /* Check if the list is empty */
539     if (!SrcValueList->Count)
540         return STATUS_SUCCESS;
541 
542     /* Get the source value list */
543     SrcListData = HvGetCell(SourceHive, SrcValueList->List);
544     ASSERT(SrcListData);
545 
546     /* Copy the actual values */
547     for (Index = 0; Index < SrcValueList->Count; Index++)
548     {
549         NewValue = CmpCopyValue(SourceHive,
550                                 SrcListData->u.KeyList[Index],
551                                 DestinationHive,
552                                 StorageType);
553         if (NewValue == HCELL_NIL)
554         {
555             /* Not enough storage space, stop there and cleanup afterwards */
556             Status = STATUS_INSUFFICIENT_RESOURCES;
557             break;
558         }
559 
560         /* Add this value cell to the child list */
561         Status = CmpAddValueToList(DestinationHive,
562                                    NewValue,
563                                    Index,
564                                    StorageType,
565                                    DestValueList);
566         if (!NT_SUCCESS(Status))
567         {
568             /* Not enough storage space, stop there */
569 
570             /* Cleanup the newly-created value here, the other ones will be cleaned up afterwards */
571             if (!CmpFreeValue(DestinationHive, NewValue))
572                 HvFreeCell(DestinationHive, NewValue);
573             break;
574         }
575     }
576 
577     /* Revert-cleanup if failure */
578     if (!NT_SUCCESS(Status) && (DestValueList->List != HCELL_NIL))
579     {
580         /* Do not use CmpRemoveValueFromList but directly delete the data */
581 
582         /* Get the destination value list */
583         DestListData = HvGetCell(DestinationHive, DestValueList->List);
584         ASSERT(DestListData);
585 
586         /* Delete each copied value */
587         while (Index--)
588         {
589             NewValue = DestListData->u.KeyList[Index];
590             if (!CmpFreeValue(DestinationHive, NewValue))
591                 HvFreeCell(DestinationHive, NewValue);
592         }
593 
594         /* Release and free the list */
595         HvReleaseCell(DestinationHive, DestValueList->List);
596         HvFreeCell(DestinationHive, DestValueList->List);
597 
598         DestValueList->Count = 0;
599         DestValueList->List = HCELL_NIL;
600     }
601 
602     /* Release the cells */
603     HvReleaseCell(SourceHive, SrcValueList->List);
604 
605     return Status;
606 }
607