1 /* 2 * PROJECT: Registry manipulation library 3 * LICENSE: GPL - See COPYING in the top level directory 4 * COPYRIGHT: Copyright 2005 Filip Navara <navaraf@reactos.org> 5 * Copyright 2001 - 2005 Eric Kohl 6 */ 7 8 #include "cmlib.h" 9 #define NDEBUG 10 #include <debug.h> 11 12 /* DECLARATIONS *************************************************************/ 13 14 #if !defined(CMLIB_HOST) && !defined(_BLDR_) 15 VOID 16 NTAPI 17 CmpLazyFlush(VOID); 18 #endif 19 20 /* FUNCTIONS *****************************************************************/ 21 22 static __inline PHCELL CMAPI 23 HvpGetCellHeader( 24 PHHIVE RegistryHive, 25 HCELL_INDEX CellIndex) 26 { 27 PVOID Block; 28 29 CMLTRACE(CMLIB_HCELL_DEBUG, "%s - Hive %p, CellIndex %08lx\n", 30 __FUNCTION__, RegistryHive, CellIndex); 31 32 ASSERT(CellIndex != HCELL_NIL); 33 if (!RegistryHive->Flat) 34 { 35 ULONG CellType = HvGetCellType(CellIndex); 36 ULONG CellBlock = HvGetCellBlock(CellIndex); 37 ULONG CellOffset = (CellIndex & HCELL_OFFSET_MASK) >> HCELL_OFFSET_SHIFT; 38 39 ASSERT(CellBlock < RegistryHive->Storage[CellType].Length); 40 Block = (PVOID)RegistryHive->Storage[CellType].BlockList[CellBlock].BlockAddress; 41 ASSERT(Block != NULL); 42 return (PHCELL)((ULONG_PTR)Block + CellOffset); 43 } 44 else 45 { 46 ASSERT(HvGetCellType(CellIndex) == Stable); 47 return (PHCELL)((ULONG_PTR)RegistryHive->BaseBlock + HBLOCK_SIZE + 48 CellIndex); 49 } 50 } 51 52 BOOLEAN CMAPI 53 HvIsCellAllocated(IN PHHIVE RegistryHive, 54 IN HCELL_INDEX CellIndex) 55 { 56 ULONG Type, Block; 57 58 /* If it's a flat hive, the cell is always allocated */ 59 if (RegistryHive->Flat) 60 return TRUE; 61 62 /* Otherwise, get the type and make sure it's valid */ 63 Type = HvGetCellType(CellIndex); 64 Block = HvGetCellBlock(CellIndex); 65 if (Block >= RegistryHive->Storage[Type].Length) 66 return FALSE; 67 68 /* Try to get the cell block */ 69 if (RegistryHive->Storage[Type].BlockList[Block].BlockAddress) 70 return TRUE; 71 72 /* No valid block, fail */ 73 return FALSE; 74 } 75 76 PCELL_DATA CMAPI 77 HvpGetCellData( 78 _In_ PHHIVE Hive, 79 _In_ HCELL_INDEX CellIndex) 80 { 81 return (PCELL_DATA)(HvpGetCellHeader(Hive, CellIndex) + 1); 82 } 83 84 static __inline LONG CMAPI 85 HvpGetCellFullSize( 86 PHHIVE RegistryHive, 87 PVOID Cell) 88 { 89 UNREFERENCED_PARAMETER(RegistryHive); 90 return ((PHCELL)Cell - 1)->Size; 91 } 92 93 LONG CMAPI 94 HvGetCellSize(IN PHHIVE Hive, 95 IN PVOID Address) 96 { 97 PHCELL CellHeader; 98 LONG Size; 99 100 UNREFERENCED_PARAMETER(Hive); 101 102 CellHeader = (PHCELL)Address - 1; 103 Size = CellHeader->Size * -1; 104 Size -= sizeof(HCELL); 105 return Size; 106 } 107 108 BOOLEAN CMAPI 109 HvMarkCellDirty( 110 PHHIVE RegistryHive, 111 HCELL_INDEX CellIndex, 112 BOOLEAN HoldingLock) 113 { 114 ULONG CellBlock; 115 ULONG CellLastBlock; 116 117 ASSERT(RegistryHive->ReadOnly == FALSE); 118 119 CMLTRACE(CMLIB_HCELL_DEBUG, "%s - Hive %p, CellIndex %08lx, HoldingLock %u\n", 120 __FUNCTION__, RegistryHive, CellIndex, HoldingLock); 121 122 if (HvGetCellType(CellIndex) != Stable) 123 return TRUE; 124 125 CellBlock = HvGetCellBlock(CellIndex); 126 CellLastBlock = HvGetCellBlock(CellIndex + HBLOCK_SIZE - 1); 127 128 RtlSetBits(&RegistryHive->DirtyVector, 129 CellBlock, CellLastBlock - CellBlock); 130 RegistryHive->DirtyCount++; 131 132 /* 133 * FIXME: Querying a lazy flush operation is needed to 134 * ensure that the dirty data is being flushed to disk 135 * accordingly. However, this operation has to be done 136 * in a helper like HvMarkDirty that marks specific parts 137 * of the hive as dirty. Since we do not have such kind 138 * of helper we have to perform an eventual lazy flush 139 * when marking cells as dirty here for the moment being, 140 * so that not only we flush dirty data but also write 141 * logs. 142 */ 143 #if !defined(CMLIB_HOST) && !defined(_BLDR_) 144 if (!(RegistryHive->HiveFlags & HIVE_NOLAZYFLUSH)) 145 { 146 CmpLazyFlush(); 147 } 148 #endif 149 return TRUE; 150 } 151 152 BOOLEAN CMAPI 153 HvIsCellDirty(IN PHHIVE Hive, 154 IN HCELL_INDEX Cell) 155 { 156 BOOLEAN IsDirty = FALSE; 157 158 /* Sanity checks */ 159 ASSERT(Hive->ReadOnly == FALSE); 160 161 /* Volatile cells are always "dirty" */ 162 if (HvGetCellType(Cell) == Volatile) 163 return TRUE; 164 165 /* Check if the dirty bit is set */ 166 if (RtlCheckBit(&Hive->DirtyVector, Cell / HBLOCK_SIZE)) 167 IsDirty = TRUE; 168 169 /* Return result as boolean*/ 170 return IsDirty; 171 } 172 173 static __inline ULONG CMAPI 174 HvpComputeFreeListIndex( 175 ULONG Size) 176 { 177 ULONG Index; 178 static CCHAR FindFirstSet[128] = { 179 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 180 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 181 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 182 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 183 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 184 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 185 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 186 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6}; 187 188 ASSERT(Size >= (1 << 3)); 189 Index = (Size >> 3) - 1; 190 if (Index >= 16) 191 { 192 if (Index > 127) 193 Index = 23; 194 else 195 Index = FindFirstSet[Index] + 16; 196 } 197 198 return Index; 199 } 200 201 static NTSTATUS CMAPI 202 HvpAddFree( 203 PHHIVE RegistryHive, 204 PHCELL FreeBlock, 205 HCELL_INDEX FreeIndex) 206 { 207 PHCELL_INDEX FreeBlockData; 208 HSTORAGE_TYPE Storage; 209 ULONG Index; 210 211 ASSERT(RegistryHive != NULL); 212 ASSERT(FreeBlock != NULL); 213 214 Storage = HvGetCellType(FreeIndex); 215 Index = HvpComputeFreeListIndex((ULONG)FreeBlock->Size); 216 217 FreeBlockData = (PHCELL_INDEX)(FreeBlock + 1); 218 *FreeBlockData = RegistryHive->Storage[Storage].FreeDisplay[Index]; 219 RegistryHive->Storage[Storage].FreeDisplay[Index] = FreeIndex; 220 221 /* FIXME: Eventually get rid of free bins. */ 222 223 return STATUS_SUCCESS; 224 } 225 226 static VOID CMAPI 227 HvpRemoveFree( 228 PHHIVE RegistryHive, 229 PHCELL CellBlock, 230 HCELL_INDEX CellIndex) 231 { 232 PHCELL_INDEX FreeCellData; 233 PHCELL_INDEX pFreeCellOffset; 234 HSTORAGE_TYPE Storage; 235 ULONG Index, FreeListIndex; 236 237 ASSERT(RegistryHive->ReadOnly == FALSE); 238 239 Storage = HvGetCellType(CellIndex); 240 Index = HvpComputeFreeListIndex((ULONG)CellBlock->Size); 241 242 pFreeCellOffset = &RegistryHive->Storage[Storage].FreeDisplay[Index]; 243 while (*pFreeCellOffset != HCELL_NIL) 244 { 245 FreeCellData = (PHCELL_INDEX)HvGetCell(RegistryHive, *pFreeCellOffset); 246 if (*pFreeCellOffset == CellIndex) 247 { 248 *pFreeCellOffset = *FreeCellData; 249 return; 250 } 251 pFreeCellOffset = FreeCellData; 252 } 253 254 /* Something bad happened, print a useful trace info and bugcheck */ 255 CMLTRACE(CMLIB_HCELL_DEBUG, "-- beginning of HvpRemoveFree trace --\n"); 256 CMLTRACE(CMLIB_HCELL_DEBUG, "block we are about to free: %08x\n", CellIndex); 257 CMLTRACE(CMLIB_HCELL_DEBUG, "chosen free list index: %u\n", Index); 258 for (FreeListIndex = 0; FreeListIndex < 24; FreeListIndex++) 259 { 260 CMLTRACE(CMLIB_HCELL_DEBUG, "free list [%u]: ", FreeListIndex); 261 pFreeCellOffset = &RegistryHive->Storage[Storage].FreeDisplay[FreeListIndex]; 262 while (*pFreeCellOffset != HCELL_NIL) 263 { 264 CMLTRACE(CMLIB_HCELL_DEBUG, "%08x ", *pFreeCellOffset); 265 FreeCellData = (PHCELL_INDEX)HvGetCell(RegistryHive, *pFreeCellOffset); 266 pFreeCellOffset = FreeCellData; 267 } 268 CMLTRACE(CMLIB_HCELL_DEBUG, "\n"); 269 } 270 CMLTRACE(CMLIB_HCELL_DEBUG, "-- end of HvpRemoveFree trace --\n"); 271 272 ASSERT(FALSE); 273 } 274 275 static HCELL_INDEX CMAPI 276 HvpFindFree( 277 PHHIVE RegistryHive, 278 ULONG Size, 279 HSTORAGE_TYPE Storage) 280 { 281 PHCELL_INDEX FreeCellData; 282 HCELL_INDEX FreeCellOffset; 283 PHCELL_INDEX pFreeCellOffset; 284 ULONG Index; 285 286 for (Index = HvpComputeFreeListIndex(Size); Index < 24; Index++) 287 { 288 pFreeCellOffset = &RegistryHive->Storage[Storage].FreeDisplay[Index]; 289 while (*pFreeCellOffset != HCELL_NIL) 290 { 291 FreeCellData = (PHCELL_INDEX)HvGetCell(RegistryHive, *pFreeCellOffset); 292 if ((ULONG)HvpGetCellFullSize(RegistryHive, FreeCellData) >= Size) 293 { 294 FreeCellOffset = *pFreeCellOffset; 295 *pFreeCellOffset = *FreeCellData; 296 return FreeCellOffset; 297 } 298 pFreeCellOffset = FreeCellData; 299 } 300 } 301 302 return HCELL_NIL; 303 } 304 305 NTSTATUS CMAPI 306 HvpCreateHiveFreeCellList( 307 PHHIVE Hive) 308 { 309 HCELL_INDEX BlockOffset; 310 PHCELL FreeBlock; 311 ULONG BlockIndex; 312 ULONG FreeOffset; 313 PHBIN Bin; 314 NTSTATUS Status; 315 ULONG Index; 316 317 /* Initialize the free cell list */ 318 for (Index = 0; Index < 24; Index++) 319 { 320 Hive->Storage[Stable].FreeDisplay[Index] = HCELL_NIL; 321 Hive->Storage[Volatile].FreeDisplay[Index] = HCELL_NIL; 322 } 323 324 BlockOffset = 0; 325 BlockIndex = 0; 326 while (BlockIndex < Hive->Storage[Stable].Length) 327 { 328 Bin = (PHBIN)Hive->Storage[Stable].BlockList[BlockIndex].BinAddress; 329 330 /* Search free blocks and add to list */ 331 FreeOffset = sizeof(HBIN); 332 while (FreeOffset < Bin->Size) 333 { 334 FreeBlock = (PHCELL)((ULONG_PTR)Bin + FreeOffset); 335 if (FreeBlock->Size > 0) 336 { 337 Status = HvpAddFree(Hive, FreeBlock, Bin->FileOffset + FreeOffset); 338 if (!NT_SUCCESS(Status)) 339 return Status; 340 341 FreeOffset += FreeBlock->Size; 342 } 343 else 344 { 345 FreeOffset -= FreeBlock->Size; 346 } 347 } 348 349 BlockIndex += Bin->Size / HBLOCK_SIZE; 350 BlockOffset += Bin->Size; 351 } 352 353 return STATUS_SUCCESS; 354 } 355 356 HCELL_INDEX CMAPI 357 HvAllocateCell( 358 PHHIVE RegistryHive, 359 ULONG Size, 360 HSTORAGE_TYPE Storage, 361 HCELL_INDEX Vicinity) 362 { 363 PHCELL FreeCell; 364 HCELL_INDEX FreeCellOffset; 365 PHCELL NewCell; 366 PHBIN Bin; 367 368 ASSERT(RegistryHive->ReadOnly == FALSE); 369 370 CMLTRACE(CMLIB_HCELL_DEBUG, "%s - Hive %p, Size %x, %s, Vicinity %08lx\n", 371 __FUNCTION__, RegistryHive, Size, (Storage == 0) ? "Stable" : "Volatile", Vicinity); 372 373 /* Round to 16 bytes multiple. */ 374 Size = ROUND_UP(Size + sizeof(HCELL), 16); 375 376 /* First search in free blocks. */ 377 FreeCellOffset = HvpFindFree(RegistryHive, Size, Storage); 378 379 /* If no free cell was found we need to extend the hive file. */ 380 if (FreeCellOffset == HCELL_NIL) 381 { 382 Bin = HvpAddBin(RegistryHive, Size, Storage); 383 if (Bin == NULL) 384 return HCELL_NIL; 385 FreeCellOffset = Bin->FileOffset + sizeof(HBIN); 386 FreeCellOffset |= Storage << HCELL_TYPE_SHIFT; 387 } 388 389 FreeCell = HvpGetCellHeader(RegistryHive, FreeCellOffset); 390 391 /* Split the block in two parts */ 392 393 /* The free block that is created has to be at least 394 sizeof(HCELL) + sizeof(HCELL_INDEX) big, so that free 395 cell list code can work. Moreover we round cell sizes 396 to 16 bytes, so creating a smaller block would result in 397 a cell that would never be allocated. */ 398 if ((ULONG)FreeCell->Size > Size + 16) 399 { 400 NewCell = (PHCELL)((ULONG_PTR)FreeCell + Size); 401 NewCell->Size = FreeCell->Size - Size; 402 FreeCell->Size = Size; 403 HvpAddFree(RegistryHive, NewCell, FreeCellOffset + Size); 404 if (Storage == Stable) 405 HvMarkCellDirty(RegistryHive, FreeCellOffset + Size, FALSE); 406 } 407 408 if (Storage == Stable) 409 HvMarkCellDirty(RegistryHive, FreeCellOffset, FALSE); 410 411 FreeCell->Size = -FreeCell->Size; 412 RtlZeroMemory(FreeCell + 1, Size - sizeof(HCELL)); 413 414 CMLTRACE(CMLIB_HCELL_DEBUG, "%s - CellIndex %08lx\n", 415 __FUNCTION__, FreeCellOffset); 416 417 return FreeCellOffset; 418 } 419 420 HCELL_INDEX CMAPI 421 HvReallocateCell( 422 PHHIVE RegistryHive, 423 HCELL_INDEX CellIndex, 424 ULONG Size) 425 { 426 PVOID OldCell; 427 PVOID NewCell; 428 LONG OldCellSize; 429 HCELL_INDEX NewCellIndex; 430 HSTORAGE_TYPE Storage; 431 432 ASSERT(CellIndex != HCELL_NIL); 433 434 CMLTRACE(CMLIB_HCELL_DEBUG, "%s - Hive %p, CellIndex %08lx, Size %x\n", 435 __FUNCTION__, RegistryHive, CellIndex, Size); 436 437 Storage = HvGetCellType(CellIndex); 438 439 OldCell = HvGetCell(RegistryHive, CellIndex); 440 OldCellSize = HvGetCellSize(RegistryHive, OldCell); 441 ASSERT(OldCellSize > 0); 442 443 /* 444 * If new data size is larger than the current, destroy current 445 * data block and allocate a new one. 446 * 447 * FIXME: Merge with adjacent free cell if possible. 448 * FIXME: Implement shrinking. 449 */ 450 if (Size > (ULONG)OldCellSize) 451 { 452 NewCellIndex = HvAllocateCell(RegistryHive, Size, Storage, HCELL_NIL); 453 if (NewCellIndex == HCELL_NIL) 454 return HCELL_NIL; 455 456 NewCell = HvGetCell(RegistryHive, NewCellIndex); 457 RtlCopyMemory(NewCell, OldCell, (SIZE_T)OldCellSize); 458 459 HvFreeCell(RegistryHive, CellIndex); 460 461 return NewCellIndex; 462 } 463 464 return CellIndex; 465 } 466 467 VOID CMAPI 468 HvFreeCell( 469 PHHIVE RegistryHive, 470 HCELL_INDEX CellIndex) 471 { 472 PHCELL Free; 473 PHCELL Neighbor; 474 PHBIN Bin; 475 ULONG CellType; 476 ULONG CellBlock; 477 478 ASSERT(RegistryHive->ReadOnly == FALSE); 479 480 CMLTRACE(CMLIB_HCELL_DEBUG, "%s - Hive %p, CellIndex %08lx\n", 481 __FUNCTION__, RegistryHive, CellIndex); 482 483 Free = HvpGetCellHeader(RegistryHive, CellIndex); 484 485 ASSERT(Free->Size < 0); 486 487 Free->Size = -Free->Size; 488 489 CellType = HvGetCellType(CellIndex); 490 CellBlock = HvGetCellBlock(CellIndex); 491 492 /* FIXME: Merge free blocks */ 493 Bin = (PHBIN)RegistryHive->Storage[CellType].BlockList[CellBlock].BinAddress; 494 495 if ((CellIndex & ~HCELL_TYPE_MASK) + Free->Size < 496 Bin->FileOffset + Bin->Size) 497 { 498 Neighbor = (PHCELL)((ULONG_PTR)Free + Free->Size); 499 if (Neighbor->Size > 0) 500 { 501 HvpRemoveFree(RegistryHive, Neighbor, 502 ((HCELL_INDEX)((ULONG_PTR)Neighbor - (ULONG_PTR)Bin + 503 Bin->FileOffset)) | (CellIndex & HCELL_TYPE_MASK)); 504 Free->Size += Neighbor->Size; 505 } 506 } 507 508 Neighbor = (PHCELL)(Bin + 1); 509 while (Neighbor < Free) 510 { 511 if (Neighbor->Size > 0) 512 { 513 if ((ULONG_PTR)Neighbor + Neighbor->Size == (ULONG_PTR)Free) 514 { 515 HCELL_INDEX NeighborCellIndex = 516 ((HCELL_INDEX)((ULONG_PTR)Neighbor - (ULONG_PTR)Bin + 517 Bin->FileOffset)) | (CellIndex & HCELL_TYPE_MASK); 518 519 if (HvpComputeFreeListIndex(Neighbor->Size) != 520 HvpComputeFreeListIndex(Neighbor->Size + Free->Size)) 521 { 522 HvpRemoveFree(RegistryHive, Neighbor, NeighborCellIndex); 523 Neighbor->Size += Free->Size; 524 HvpAddFree(RegistryHive, Neighbor, NeighborCellIndex); 525 } 526 else 527 Neighbor->Size += Free->Size; 528 529 if (CellType == Stable) 530 HvMarkCellDirty(RegistryHive, NeighborCellIndex, FALSE); 531 532 return; 533 } 534 Neighbor = (PHCELL)((ULONG_PTR)Neighbor + Neighbor->Size); 535 } 536 else 537 { 538 Neighbor = (PHCELL)((ULONG_PTR)Neighbor - Neighbor->Size); 539 } 540 } 541 542 /* Add block to the list of free blocks */ 543 HvpAddFree(RegistryHive, Free, CellIndex); 544 545 if (CellType == Stable) 546 HvMarkCellDirty(RegistryHive, CellIndex, FALSE); 547 } 548 549 550 #define CELL_REF_INCREMENT 10 551 552 BOOLEAN 553 CMAPI 554 HvTrackCellRef( 555 IN OUT PHV_TRACK_CELL_REF CellRef, 556 IN PHHIVE Hive, 557 IN HCELL_INDEX Cell) 558 { 559 PHV_HIVE_CELL_PAIR NewCellArray; 560 561 PAGED_CODE(); 562 563 /* Sanity checks */ 564 ASSERT(CellRef); 565 ASSERT(Hive); 566 ASSERT(Cell != HCELL_NIL); 567 568 /* NOTE: The hive cell is already referenced! */ 569 570 /* Less than 4? Use the static array */ 571 if (CellRef->StaticCount < STATIC_CELL_PAIR_COUNT) 572 { 573 /* Add the reference */ 574 CellRef->StaticArray[CellRef->StaticCount].Hive = Hive; 575 CellRef->StaticArray[CellRef->StaticCount].Cell = Cell; 576 CellRef->StaticCount++; 577 return TRUE; 578 } 579 580 DPRINT("HvTrackCellRef: Static array full, use dynamic array.\n"); 581 582 /* Sanity checks */ 583 if (CellRef->Max == 0) 584 { 585 /* The dynamic array must not have been allocated already */ 586 ASSERT(CellRef->CellArray == NULL); 587 ASSERT(CellRef->Count == 0); 588 } 589 else 590 { 591 /* The dynamic array must be allocated */ 592 ASSERT(CellRef->CellArray); 593 } 594 ASSERT(CellRef->Count <= CellRef->Max); 595 596 if (CellRef->Count == CellRef->Max) 597 { 598 /* Allocate a new reference table */ 599 NewCellArray = CmpAllocate((CellRef->Max + CELL_REF_INCREMENT) * sizeof(HV_HIVE_CELL_PAIR), 600 TRUE, 601 TAG_CM); 602 if (!NewCellArray) 603 { 604 DPRINT1("HvTrackCellRef: Cannot reallocate the reference table.\n"); 605 /* We failed, dereference the hive cell */ 606 HvReleaseCell(Hive, Cell); 607 return FALSE; 608 } 609 610 /* Free the old reference table and use the new one */ 611 if (CellRef->CellArray) 612 { 613 /* Copy the handles from the old table to the new one */ 614 RtlCopyMemory(NewCellArray, 615 CellRef->CellArray, 616 CellRef->Max * sizeof(HV_HIVE_CELL_PAIR)); 617 CmpFree(CellRef->CellArray, 0); // TAG_CM 618 } 619 CellRef->CellArray = NewCellArray; 620 CellRef->Max += CELL_REF_INCREMENT; 621 } 622 623 // ASSERT(CellRef->Count < CellRef->Max); 624 625 /* Add the reference */ 626 CellRef->CellArray[CellRef->Count].Hive = Hive; 627 CellRef->CellArray[CellRef->Count].Cell = Cell; 628 CellRef->Count++; 629 return TRUE; 630 } 631 632 VOID 633 CMAPI 634 HvReleaseFreeCellRefArray( 635 IN OUT PHV_TRACK_CELL_REF CellRef) 636 { 637 ULONG i; 638 639 PAGED_CODE(); 640 641 ASSERT(CellRef); 642 643 /* Any references in the static array? */ 644 if (CellRef->StaticCount > 0) 645 { 646 /* Sanity check */ 647 ASSERT(CellRef->StaticCount <= STATIC_CELL_PAIR_COUNT); 648 649 /* Loop over them and release them */ 650 for (i = 0; i < CellRef->StaticCount; i++) 651 { 652 HvReleaseCell(CellRef->StaticArray[i].Hive, 653 CellRef->StaticArray[i].Cell); 654 } 655 656 /* We can reuse the static array */ 657 CellRef->StaticCount = 0; 658 } 659 660 /* Any references in the dynamic array? */ 661 if (CellRef->Count > 0) 662 { 663 /* Sanity checks */ 664 ASSERT(CellRef->Count <= CellRef->Max); 665 ASSERT(CellRef->CellArray); 666 667 /* Loop over them and release them */ 668 for (i = 0; i < CellRef->Count; i++) 669 { 670 HvReleaseCell(CellRef->CellArray[i].Hive, 671 CellRef->CellArray[i].Cell); 672 } 673 674 /* We can reuse the dynamic array */ 675 CmpFree(CellRef->CellArray, 0); // TAG_CM 676 CellRef->CellArray = NULL; 677 CellRef->Count = CellRef->Max = 0; 678 } 679 } 680