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