xref: /reactos/sdk/lib/cmlib/hivecell.c (revision 3c797b31)
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