[CMLIB] Adjust parameter types and annotations. Add diagnostic assertions.
[reactos.git] / sdk / lib / cmlib / cmvalue.c
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 PUNICODE_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 CmpFree(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 }