- DBGKD_WAIT_STATE_CHANGE64 is used in KD protocol 5, not number 6 that we use. Proto...
[reactos.git] / reactos / lib / cmlib / hivecell.c
index 1e84baf..994ca98 100644 (file)
@@ -9,14 +9,17 @@
 #define NDEBUG
 #include <debug.h>
 
-static PHCELL __inline CMAPI
+static __inline PHCELL CMAPI
 HvpGetCellHeader(
    PHHIVE RegistryHive,
    HCELL_INDEX CellIndex)
 {
    PVOID Block;
 
-   ASSERT(CellIndex != HCELL_NULL);
+   CMLTRACE(CMLIB_HCELL_DEBUG, "%s - Hive %p, CellIndex %08lx\n",
+       __FUNCTION__, RegistryHive, CellIndex);
+
+   ASSERT(CellIndex != HCELL_NIL);
    if (!RegistryHive->Flat)
    {
       ULONG CellType;
@@ -27,18 +30,42 @@ HvpGetCellHeader(
       CellBlock = (CellIndex & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT;
       CellOffset = (CellIndex & HCELL_OFFSET_MASK) >> HCELL_OFFSET_SHIFT;
       ASSERT(CellBlock < RegistryHive->Storage[CellType].Length);
-      Block = (PVOID)RegistryHive->Storage[CellType].BlockList[CellBlock].Block;
+      Block = (PVOID)RegistryHive->Storage[CellType].BlockList[CellBlock].BlockAddress;
       ASSERT(Block != NULL);
       return (PVOID)((ULONG_PTR)Block + CellOffset);
    }
    else
    {
-      ASSERT((CellIndex & HCELL_TYPE_MASK) == HvStable);
-      return (PVOID)((ULONG_PTR)RegistryHive->HiveHeader + HV_BLOCK_SIZE +
+      ASSERT((CellIndex & HCELL_TYPE_MASK) == Stable);
+      return (PVOID)((ULONG_PTR)RegistryHive->BaseBlock + HV_BLOCK_SIZE +
                      CellIndex);
    }
 }
 
+BOOLEAN CMAPI
+HvIsCellAllocated(IN PHHIVE RegistryHive,
+                  IN HCELL_INDEX CellIndex)
+{
+   ULONG Type, Block;
+
+   /* If it's a flat hive, the cell is always allocated */
+   if (RegistryHive->Flat)
+      return TRUE;
+
+   /* Otherwise, get the type and make sure it's valid */
+   Type = HvGetCellType(CellIndex);
+   Block = HvGetCellBlock(CellIndex);
+   if (Block >= RegistryHive->Storage[Type].Length)
+      return FALSE;
+
+   /* Try to get the cell block */
+   if (RegistryHive->Storage[Type].BlockList[Block].BlockAddress)
+      return TRUE;
+
+   /* No valid block, fail */
+   return FALSE;
+}
+
 PVOID CMAPI
 HvGetCell(
    PHHIVE RegistryHive,
@@ -47,54 +74,77 @@ HvGetCell(
    return (PVOID)(HvpGetCellHeader(RegistryHive, CellIndex) + 1);
 }
 
-static LONG __inline CMAPI
+static __inline LONG CMAPI
 HvpGetCellFullSize(
    PHHIVE RegistryHive,
    PVOID Cell)
 {
+   UNREFERENCED_PARAMETER(RegistryHive);
    return ((PHCELL)Cell - 1)->Size;
 }
 
 LONG CMAPI
-HvGetCellSize(
-   PHHIVE RegistryHive,
-   PVOID Cell)
+HvGetCellSize(IN PHHIVE Hive,
+              IN PVOID Address)
 {
    PHCELL CellHeader;
+   LONG Size;
 
-   CellHeader = (PHCELL)Cell - 1;
-   if (CellHeader->Size < 0)
-      return CellHeader->Size + sizeof(HCELL);
-   else
-      return CellHeader->Size - sizeof(HCELL);
+   UNREFERENCED_PARAMETER(Hive);
+
+   CellHeader = (PHCELL)Address - 1;
+   Size = CellHeader->Size * -1;
+   Size -= sizeof(HCELL);
+   return Size;
 }
 
-VOID CMAPI
+BOOLEAN CMAPI
 HvMarkCellDirty(
    PHHIVE RegistryHive,
-   HCELL_INDEX CellIndex)
+   HCELL_INDEX CellIndex,
+   BOOLEAN HoldingLock)
 {
-   LONG CellSize;
    ULONG CellBlock;
    ULONG CellLastBlock;
 
    ASSERT(RegistryHive->ReadOnly == FALSE);
 
-   if ((CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT != HvStable)
-      return;
+   CMLTRACE(CMLIB_HCELL_DEBUG, "%s - Hive %p, CellIndex %08lx, HoldingLock %b\n",
+       __FUNCTION__, RegistryHive, CellIndex, HoldingLock);
+
+   if ((CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT != Stable)
+      return FALSE;
 
    CellBlock = (CellIndex & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT;
    CellLastBlock = ((CellIndex + HV_BLOCK_SIZE - 1) & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT;
 
-   CellSize = HvpGetCellFullSize(RegistryHive, HvGetCell(RegistryHive, CellIndex));
-   if (CellSize < 0)
-      CellSize = -CellSize;
-
    RtlSetBits(&RegistryHive->DirtyVector,
               CellBlock, CellLastBlock - CellBlock);
+   return TRUE;
 }
 
-static ULONG __inline CMAPI
+BOOLEAN CMAPI
+HvIsCellDirty(IN PHHIVE Hive,
+              IN HCELL_INDEX Cell)
+{
+   BOOLEAN IsDirty = FALSE;
+
+   /* Sanity checks */
+   ASSERT(Hive->ReadOnly == FALSE);
+
+   /* Volatile cells are always "dirty" */
+   if (HvGetCellType(Cell) == Volatile)
+      return TRUE;
+
+   /* Check if the dirty bit is set */
+   if (RtlCheckBit(&Hive->DirtyVector, Cell / HV_BLOCK_SIZE))
+       IsDirty = TRUE;
+
+   /* Return result as boolean*/
+   return IsDirty;
+}
+
+static __inline ULONG CMAPI
 HvpComputeFreeListIndex(
    ULONG Size)
 {
@@ -136,7 +186,7 @@ HvpAddFree(
    HCELL_INDEX FreeIndex)
 {
    PHCELL_INDEX FreeBlockData;
-   HV_STORAGE_TYPE Storage;
+   HSTORAGE_TYPE Storage;
    ULONG Index;
 
    ASSERT(RegistryHive != NULL);
@@ -162,8 +212,8 @@ HvpRemoveFree(
 {
    PHCELL_INDEX FreeCellData;
    PHCELL_INDEX pFreeCellOffset;
-   HV_STORAGE_TYPE Storage;
-   ULONG Index;
+   HSTORAGE_TYPE Storage;
+   ULONG Index, FreeListIndex;
 
    ASSERT(RegistryHive->ReadOnly == FALSE);
 
@@ -171,7 +221,7 @@ HvpRemoveFree(
    Index = HvpComputeFreeListIndex((ULONG)CellBlock->Size);
 
    pFreeCellOffset = &RegistryHive->Storage[Storage].FreeDisplay[Index];
-   while (*pFreeCellOffset != HCELL_NULL)
+   while (*pFreeCellOffset != HCELL_NIL)
    {
       FreeCellData = (PHCELL_INDEX)HvGetCell(RegistryHive, *pFreeCellOffset);
       if (*pFreeCellOffset == CellIndex)
@@ -182,6 +232,24 @@ HvpRemoveFree(
       pFreeCellOffset = FreeCellData;
    }
 
+   /* Something bad happened, print a useful trace info and bugcheck */
+   CMLTRACE(CMLIB_HCELL_DEBUG, "-- beginning of HvpRemoveFree trace --\n");
+   CMLTRACE(CMLIB_HCELL_DEBUG, "block we are about to free: %08x\n", CellIndex);
+   CMLTRACE(CMLIB_HCELL_DEBUG, "chosen free list index: %d\n", Index);
+   for (FreeListIndex = 0; FreeListIndex < 24; FreeListIndex++)
+   {
+      CMLTRACE(CMLIB_HCELL_DEBUG, "free list [%d]: ", FreeListIndex);
+      pFreeCellOffset = &RegistryHive->Storage[Storage].FreeDisplay[FreeListIndex];
+      while (*pFreeCellOffset != HCELL_NIL)
+      {
+         CMLTRACE(CMLIB_HCELL_DEBUG, "%08x ", *pFreeCellOffset);
+         FreeCellData = (PHCELL_INDEX)HvGetCell(RegistryHive, *pFreeCellOffset);
+         pFreeCellOffset = FreeCellData;
+      }
+      CMLTRACE(CMLIB_HCELL_DEBUG, "\n");
+   }
+   CMLTRACE(CMLIB_HCELL_DEBUG, "-- end of HvpRemoveFree trace --\n");
+
    ASSERT(FALSE);
 }
 
@@ -189,7 +257,7 @@ static HCELL_INDEX CMAPI
 HvpFindFree(
    PHHIVE RegistryHive,
    ULONG Size,
-   HV_STORAGE_TYPE Storage)
+   HSTORAGE_TYPE Storage)
 {
    PHCELL_INDEX FreeCellData;
    HCELL_INDEX FreeCellOffset;
@@ -199,7 +267,7 @@ HvpFindFree(
    for (Index = HvpComputeFreeListIndex(Size); Index < 24; Index++)
    {
       pFreeCellOffset = &RegistryHive->Storage[Storage].FreeDisplay[Index];
-      while (*pFreeCellOffset != HCELL_NULL)
+      while (*pFreeCellOffset != HCELL_NIL)
       {
          FreeCellData = (PHCELL_INDEX)HvGetCell(RegistryHive, *pFreeCellOffset);
          if ((ULONG)HvpGetCellFullSize(RegistryHive, FreeCellData) >= Size)
@@ -212,7 +280,7 @@ HvpFindFree(
       }
    }
 
-   return HCELL_NULL;
+   return HCELL_NIL;
 }
 
 NTSTATUS CMAPI
@@ -230,15 +298,15 @@ HvpCreateHiveFreeCellList(
    /* Initialize the free cell list */
    for (Index = 0; Index < 24; Index++)
    {
-      Hive->Storage[HvStable].FreeDisplay[Index] = HCELL_NULL;
-      Hive->Storage[HvVolatile].FreeDisplay[Index] = HCELL_NULL;
+      Hive->Storage[Stable].FreeDisplay[Index] = HCELL_NIL;
+      Hive->Storage[Volatile].FreeDisplay[Index] = HCELL_NIL;
    }
 
    BlockOffset = 0;
    BlockIndex = 0;
-   while (BlockIndex < Hive->Storage[HvStable].Length)
+   while (BlockIndex < Hive->Storage[Stable].Length)
    {
-      Bin = (PHBIN)Hive->Storage[HvStable].BlockList[BlockIndex].Bin;
+      Bin = (PHBIN)Hive->Storage[Stable].BlockList[BlockIndex].BinAddress;
 
       /* Search free blocks and add to list */
       FreeOffset = sizeof(HBIN);
@@ -269,8 +337,9 @@ HvpCreateHiveFreeCellList(
 HCELL_INDEX CMAPI
 HvAllocateCell(
    PHHIVE RegistryHive,
-   ULONG Size,
-   HV_STORAGE_TYPE Storage)
+   SIZE_T Size,
+   HSTORAGE_TYPE Storage,
+   HCELL_INDEX Vicinity)
 {
    PHCELL FreeCell;
    HCELL_INDEX FreeCellOffset;
@@ -279,6 +348,9 @@ HvAllocateCell(
 
    ASSERT(RegistryHive->ReadOnly == FALSE);
 
+   CMLTRACE(CMLIB_HCELL_DEBUG, "%s - Hive %p, Size %x, %s, Vicinity %08lx\n",
+       __FUNCTION__, RegistryHive, Size, (Storage == 0) ? "Stable" : "Volatile", Vicinity);
+
    /* Round to 16 bytes multiple. */
    Size = ROUND_UP(Size + sizeof(HCELL), 16);
 
@@ -286,11 +358,11 @@ HvAllocateCell(
    FreeCellOffset = HvpFindFree(RegistryHive, Size, Storage);
 
    /* If no free cell was found we need to extend the hive file. */
-   if (FreeCellOffset == HCELL_NULL)
+   if (FreeCellOffset == HCELL_NIL)
    {
       Bin = HvpAddBin(RegistryHive, Size, Storage);
       if (Bin == NULL)
-         return HCELL_NULL;
+         return HCELL_NIL;
       FreeCellOffset = Bin->FileOffset + sizeof(HBIN);
       FreeCellOffset |= Storage << HCELL_TYPE_SHIFT;
    }
@@ -298,22 +370,30 @@ HvAllocateCell(
    FreeCell = HvpGetCellHeader(RegistryHive, FreeCellOffset);
 
    /* Split the block in two parts */
-   /* FIXME: There is some minimal cell size that we must respect. */
-   if ((ULONG)FreeCell->Size > Size + sizeof(HCELL_INDEX))
+
+   /* The free block that is created has to be at least
+      sizeof(HCELL) + sizeof(HCELL_INDEX) big, so that free
+      cell list code can work. Moreover we round cell sizes
+      to 16 bytes, so creating a smaller block would result in
+      a cell that would never be allocated. */
+   if ((ULONG)FreeCell->Size > Size + 16)
    {
       NewCell = (PHCELL)((ULONG_PTR)FreeCell + Size);
       NewCell->Size = FreeCell->Size - Size;
       FreeCell->Size = Size;
       HvpAddFree(RegistryHive, NewCell, FreeCellOffset + Size);
-      if (Storage == HvStable)
-         HvMarkCellDirty(RegistryHive, FreeCellOffset + Size);
+      if (Storage == Stable)
+         HvMarkCellDirty(RegistryHive, FreeCellOffset + Size, FALSE);
    }
 
-   if (Storage == HvStable)
-      HvMarkCellDirty(RegistryHive, FreeCellOffset);
+   if (Storage == Stable)
+      HvMarkCellDirty(RegistryHive, FreeCellOffset, FALSE);
    FreeCell->Size = -FreeCell->Size;
    RtlZeroMemory(FreeCell + 1, Size - sizeof(HCELL));
 
+   CMLTRACE(CMLIB_HCELL_DEBUG, "%s - CellIndex %08lx\n",
+       __FUNCTION__, FreeCellOffset);
+
    return FreeCellOffset;
 }
 
@@ -327,16 +407,19 @@ HvReallocateCell(
    PVOID NewCell;
    LONG OldCellSize;
    HCELL_INDEX NewCellIndex;
-   HV_STORAGE_TYPE Storage;
+   HSTORAGE_TYPE Storage;
 
-   ASSERT(CellIndex != HCELL_NULL);
+   ASSERT(CellIndex != HCELL_NIL);
+
+   CMLTRACE(CMLIB_HCELL_DEBUG, "%s - Hive %p, CellIndex %08lx, Size %x\n",
+       __FUNCTION__, RegistryHive, CellIndex, Size);
 
    Storage = (CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT;
 
    OldCell = HvGetCell(RegistryHive, CellIndex);
    OldCellSize = HvGetCellSize(RegistryHive, OldCell);
-   ASSERT(OldCellSize < 0);
-  
+   ASSERT(OldCellSize > 0);
+
    /*
     * If new data size is larger than the current, destroy current
     * data block and allocate a new one.
@@ -344,15 +427,15 @@ HvReallocateCell(
     * FIXME: Merge with adjacent free cell if possible.
     * FIXME: Implement shrinking.
     */
-   if (Size > (ULONG)-OldCellSize)
+   if (Size > (ULONG)OldCellSize)
    {
-      NewCellIndex = HvAllocateCell(RegistryHive, Size, Storage);
-      if (NewCellIndex == HCELL_NULL)
-         return HCELL_NULL;
+      NewCellIndex = HvAllocateCell(RegistryHive, Size, Storage, HCELL_NIL);
+      if (NewCellIndex == HCELL_NIL)
+         return HCELL_NIL;
 
       NewCell = HvGetCell(RegistryHive, NewCellIndex);
-      RtlCopyMemory(NewCell, OldCell, (SIZE_T)-OldCellSize);
-      
+      RtlCopyMemory(NewCell, OldCell, (SIZE_T)OldCellSize);
+
       HvFreeCell(RegistryHive, CellIndex);
 
       return NewCellIndex;
@@ -373,18 +456,21 @@ HvFreeCell(
    ULONG CellBlock;
 
    ASSERT(RegistryHive->ReadOnly == FALSE);
-   
+
+   CMLTRACE(CMLIB_HCELL_DEBUG, "%s - Hive %p, CellIndex %08lx\n",
+       __FUNCTION__, RegistryHive, CellIndex);
+
    Free = HvpGetCellHeader(RegistryHive, CellIndex);
 
-   ASSERT(Free->Size < 0);   
-   
+   ASSERT(Free->Size < 0);
+
    Free->Size = -Free->Size;
 
    CellType = (CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT;
    CellBlock = (CellIndex & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT;
 
    /* FIXME: Merge free blocks */
-   Bin = (PHBIN)RegistryHive->Storage[CellType].BlockList[CellBlock].Bin;
+   Bin = (PHBIN)RegistryHive->Storage[CellType].BlockList[CellBlock].BinAddress;
 
    if ((CellIndex & ~HCELL_TYPE_MASK) + Free->Size <
        Bin->FileOffset + Bin->Size)
@@ -406,11 +492,23 @@ HvFreeCell(
       {
          if ((ULONG_PTR)Neighbor + Neighbor->Size == (ULONG_PTR)Free)
          {
-            Neighbor->Size += Free->Size;
-            if (CellType == HvStable)
-               HvMarkCellDirty(RegistryHive,
-                               (HCELL_INDEX)((ULONG_PTR)Neighbor - (ULONG_PTR)Bin +
-                               Bin->FileOffset));
+            HCELL_INDEX NeighborCellIndex =
+               (HCELL_INDEX)((ULONG_PTR)Neighbor - (ULONG_PTR)Bin +
+               Bin->FileOffset) | (CellIndex & HCELL_TYPE_MASK);
+
+            if (HvpComputeFreeListIndex(Neighbor->Size) !=
+                HvpComputeFreeListIndex(Neighbor->Size + Free->Size))
+            {
+               HvpRemoveFree(RegistryHive, Neighbor, NeighborCellIndex);
+               Neighbor->Size += Free->Size;
+               HvpAddFree(RegistryHive, Neighbor, NeighborCellIndex);
+            }
+            else
+               Neighbor->Size += Free->Size;
+
+            if (CellType == Stable)
+               HvMarkCellDirty(RegistryHive, NeighborCellIndex, FALSE);
+
             return;
          }
          Neighbor = (PHCELL)((ULONG_PTR)Neighbor + Neighbor->Size);
@@ -424,6 +522,6 @@ HvFreeCell(
    /* Add block to the list of free blocks */
    HvpAddFree(RegistryHive, Free, CellIndex);
 
-   if (CellType == HvStable)
-      HvMarkCellDirty(RegistryHive, CellIndex);
+   if (CellType == Stable)
+      HvMarkCellDirty(RegistryHive, CellIndex, FALSE);
 }