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