https://git.reactos.org/?p=reactos.git;a=commitdiff;h=54c552392f62f7369c6d7…
commit 54c552392f62f7369c6d7e8395a671fe593afda1
Author: George Bișoc <george.bisoc(a)reactos.org>
AuthorDate: Wed Oct 26 19:21:49 2022 +0200
Commit: George Bișoc <george.bisoc(a)reactos.org>
CommitDate: Sun Nov 19 20:44:27 2023 +0100
[SDK][CMLIB] Implement self-heal registry helpers
This implements cmheal.c file which provides the basic registry self-heal
infrastructure needed by the public CmCheckRegistry function. The infrastructure provides
a range of various self-heal helpers for the hive, such as subkey, class, values and node
healing functions.
---
sdk/lib/cmlib/CMakeLists.txt | 1 +
sdk/lib/cmlib/cmheal.c | 912 +++++++++++++++++++++++++++++++++++++++++++
sdk/lib/cmlib/cmlib.h | 73 ++++
3 files changed, 986 insertions(+)
diff --git a/sdk/lib/cmlib/CMakeLists.txt b/sdk/lib/cmlib/CMakeLists.txt
index 2ed0bc304b1..91ccbe6c3e5 100644
--- a/sdk/lib/cmlib/CMakeLists.txt
+++ b/sdk/lib/cmlib/CMakeLists.txt
@@ -5,6 +5,7 @@ add_definitions(
list(APPEND SOURCE
cminit.c
+ cmheal.c
cmindex.c
cmkeydel.c
cmname.c
diff --git a/sdk/lib/cmlib/cmheal.c b/sdk/lib/cmlib/cmheal.c
new file mode 100644
index 00000000000..9489d621bed
--- /dev/null
+++ b/sdk/lib/cmlib/cmheal.c
@@ -0,0 +1,912 @@
+/*
+ * PROJECT: ReactOS Kernel
+ * LICENSE: GPL-2.0-or-later (
https://spdx.org/licenses/GPL-2.0-or-later)
+ * PURPOSE: Configuration Manager Library - Registry Self-Heal Routines
+ * COPYRIGHT: Copyright 2022 George Bișoc <george.bisoc(a)reactos.org>
+ */
+
+#include "cmlib.h"
+#define NDEBUG
+#include <debug.h>
+
+/* GLOBALS ********************************************************************/
+
+#if !defined(CMLIB_HOST) && !defined(_BLDR_)
+extern BOOLEAN CmpSelfHeal;
+extern ULONG CmpBootType;
+#endif
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+/**
+ * @brief
+ * Removes a cell from a fast key index.
+ *
+ * @param[in,out] FastIndex
+ * The fast key index where a cell has to be removed.
+ *
+ * @param[in] Index
+ * The index which points to the location of the
+ * cell that is to be removed.
+ *
+ * @param[in] UpdateCount
+ * If set to TRUE, the function will update the fast
+ * index count accordingly by one value less. If set
+ * to FALSE, the count won't be updated. See Remarks
+ * for further information.
+ *
+ * @remarks
+ * In case where the fast index count is not updated is
+ * when the key index is not a root but a leaf. In such
+ * scenario such leaf is the actual key index itself
+ * so updating the fast index count is not necessary (aka
+ * UpdateCount is set to FALSE).
+ */
+static
+VOID
+CmpRemoveFastIndexKeyCell(
+ _Inout_ PCM_KEY_FAST_INDEX FastIndex,
+ _In_ ULONG Index,
+ _In_ BOOLEAN UpdateCount)
+{
+ ULONG MoveCount;
+ ASSERT(Index < FastIndex->Count);
+
+ /* Calculate the number of trailing cells */
+ MoveCount = FastIndex->Count - Index - 1;
+ if (MoveCount != 0)
+ {
+ /* Remove the cell now by moving one location ahead */
+ RtlMoveMemory(&FastIndex->List[Index],
+ &FastIndex->List[Index + 1],
+ MoveCount * sizeof(CM_INDEX));
+ }
+
+ /* Update the fast index count if asked */
+ if (UpdateCount)
+ FastIndex->Count--;
+}
+
+/**
+ * @brief
+ * Removes a cell from a normal key index.
+ *
+ * @param[in,out] KeyIndex
+ * The key index where a cell has to be removed.
+ *
+ * @param[in] Index
+ * The index which points to the location of the
+ * cell that is to be removed.
+ */
+static
+VOID
+CmpRemoveIndexKeyCell(
+ _Inout_ PCM_KEY_INDEX KeyIndex,
+ _In_ ULONG Index)
+{
+ ULONG MoveCount;
+ ASSERT(Index < KeyIndex->Count);
+
+ /* Calculate the number of trailing cells */
+ MoveCount = KeyIndex->Count - Index - 1;
+ if (MoveCount != 0)
+ {
+ /* Remove the cell now by moving one location ahead */
+ RtlMoveMemory(&KeyIndex->List[Index],
+ &KeyIndex->List[Index + 1],
+ MoveCount * sizeof(HCELL_INDEX));
+ }
+
+ /* Update the key index count */
+ KeyIndex->Count--;
+}
+
+/**
+ * @brief
+ * Removes a cell from a key value list node.
+ *
+ * @param[in,out] ValueListNode
+ * The value list node which is used by the
+ * function to update the value list count.
+ *
+ * @param[in,out] ValueListData
+ * The value list data of which a cell has to be removed.
+ *
+ * @param[in] Index
+ * The index which points to the location of the
+ * cell that is to be removed.
+ */
+static
+VOID
+CmpRemoveValueFromValueList(
+ _Inout_ PCM_KEY_NODE ValueListNode,
+ _Inout_ PCELL_DATA ValueListData,
+ _In_ ULONG Index)
+{
+ ULONG MoveCount;
+ ASSERT(Index < ValueListNode->ValueList.Count);
+
+ /* Calculate the number of trailing values */
+ MoveCount = ValueListNode->ValueList.Count - Index - 1;
+ if (MoveCount != 0)
+ {
+ /* Remove the value now by moving one location ahead */
+ RtlMoveMemory(&ValueListData->u.KeyList[Index],
+ &ValueListData->u.KeyList[Index + 1],
+ MoveCount * sizeof(HCELL_INDEX));
+ }
+
+ /* Update the value list count */
+ ValueListNode->ValueList.Count--;
+}
+
+/**
+ * @brief
+ * Removes the offending subkey from a root index.
+ *
+ * @param[in] Hive
+ * A pointer to a hive descriptor containing faulty data.
+ *
+ * @param[in] RootIndex
+ * The root index where a leaf is obtained from. Such
+ * leaf is used to check deep down the leaf for the offending
+ * subkey.
+ *
+ * @param[in] TargetKey
+ * The offending target subkey to be removed.
+ *
+ * @return
+ * Returns TRUE if the function successfully removed the target
+ * key, FALSE otherwise.
+ */
+static
+BOOLEAN
+CmpRemoveSubkeyInRoot(
+ _In_ PHHIVE Hive,
+ _In_ PCM_KEY_INDEX RootIndex,
+ _In_ HCELL_INDEX TargetKey)
+{
+ PCM_KEY_INDEX Leaf;
+ PCM_KEY_FAST_INDEX FastIndex;
+ HCELL_INDEX LeafCell;
+ ULONG RootCountIndex;
+ ULONG LeafCountIndex;
+
+ PAGED_CODE();
+
+ ASSERT(RootIndex);
+
+ /* Loop the root index */
+ for (RootCountIndex = 0; RootCountIndex < RootIndex->Count; RootCountIndex++)
+ {
+ /*
+ * Release the leaf cell from previous iteration
+ * of the loop. Make sure what we're releasing is
+ * valid to begin with.
+ */
+ if (RootCountIndex)
+ {
+ ASSERT(Leaf);
+ ASSERT(LeafCell == RootIndex->List[RootCountIndex - 1]);
+ HvReleaseCell(Hive, LeafCell);
+ }
+
+ /* Get the leaf cell and the leaf for this index */
+ LeafCell = RootIndex->List[RootCountIndex];
+ Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
+ if (!Leaf)
+ {
+ DPRINT1("Couldn't get the leaf from cell\n");
+ return FALSE;
+ }
+
+ /* Start looping the leaf */
+ for (LeafCountIndex = 0; LeafCountIndex < Leaf->Count; LeafCountIndex++)
+ {
+ /* Is the leaf a fast leaf or a hash one? */
+ if ((Leaf->Signature == CM_KEY_FAST_LEAF) ||
+ (Leaf->Signature == CM_KEY_HASH_LEAF))
+ {
+ /* It is one of the two, get the fast index */
+ FastIndex = (PCM_KEY_FAST_INDEX)Leaf;
+
+ /*
+ * Is the subkey cell from the fast
+ * index the one we one we're actually
+ * searching?
+ */
+ if (FastIndex->List[LeafCountIndex].Cell == TargetKey)
+ {
+ HvReleaseCell(Hive, LeafCell);
+ HvMarkCellDirty(Hive, LeafCell, FALSE);
+ CmpRemoveFastIndexKeyCell(FastIndex, LeafCountIndex, TRUE);
+ DPRINT1("The offending key cell has BEEN FOUND in fast index
(fast index 0x%p, index %lu)\n",
+ FastIndex, LeafCountIndex);
+ return TRUE;
+ }
+ }
+ else
+ {
+ /*
+ * The leaf is neither of the two. Check if
+ * the target offending cell is inside the leaf
+ * itself.
+ */
+ if (Leaf->List[LeafCountIndex] == TargetKey)
+ {
+ HvReleaseCell(Hive, LeafCell);
+ HvMarkCellDirty(Hive, LeafCell, FALSE);
+ CmpRemoveIndexKeyCell(Leaf, LeafCountIndex);
+ DPRINT1("The offending key cell has BEEN FOUND in leaf (leaf
0x%p, index %lu)\n",
+ Leaf, LeafCountIndex);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ /*
+ * We have searched everywhere but we couldn't
+ * hunt the offending target key cell.
+ */
+ DPRINT1("No target key has been found to remove\n");
+ return FALSE;
+}
+
+/**
+ * @brief
+ * Removes the offending subkey from a leaf index.
+ *
+ * @param[in] Hive
+ * A pointer to a hive descriptor containing faulty data.
+ *
+ * @param[in] KeyNode
+ * A pointer to a key node of the parent. This node is
+ * used by the function to mark the whole subkeys list
+ * of the parent dirty.
+ *
+ * @param[in] Leaf
+ * A pointer to a leaf key index of which the offending
+ * subkey is to be removed from.
+ *
+ * @param[in] TargetKey
+ * The offending target subkey to remove.
+ *
+ * @return
+ * Returns TRUE if the function successfully removed the target
+ * key, FALSE otherwise.
+ */
+static
+BOOLEAN
+CmpRemoveSubKeyInLeaf(
+ _In_ PHHIVE Hive,
+ _In_ PCM_KEY_NODE KeyNode,
+ _In_ PCM_KEY_INDEX Leaf,
+ _In_ HCELL_INDEX TargetKey)
+{
+ PCM_KEY_FAST_INDEX FastIndex;
+ ULONG LeafIndex;
+
+ /* Loop the leaf index */
+ for (LeafIndex = 0; LeafIndex < Leaf->Count; LeafIndex++)
+ {
+ /*
+ * Check if the main leaf is a fast
+ * leaf or a hash one.
+ */
+ if ((Leaf->Signature == CM_KEY_FAST_LEAF) ||
+ (Leaf->Signature == CM_KEY_HASH_LEAF))
+ {
+ /* It is one of the two, get the fast index */
+ FastIndex = (PCM_KEY_FAST_INDEX)Leaf;
+
+ /*
+ * Is the subkey cell from the fast
+ * index the one we're actually
+ * searching?
+ */
+ if (FastIndex->List[LeafIndex].Cell == TargetKey)
+ {
+ HvMarkCellDirty(Hive, KeyNode->SubKeyLists[Stable], FALSE);
+ CmpRemoveFastIndexKeyCell(FastIndex, LeafIndex, FALSE);
+
+ /*
+ * Since this fast index actually came from the
+ * actual leaf index itself, just update its count
+ * rather than that of the fast index.
+ */
+ Leaf->Count--;
+ DPRINT1("The offending key cell has BEEN FOUND in fast index (fast
index 0x%p, leaf index %lu)\n",
+ FastIndex, LeafIndex);
+ return TRUE;
+ }
+ }
+ else
+ {
+ /*
+ * The leaf is neither of the two. The offending
+ * cell must come directly from the normal leaf
+ * at this point.
+ */
+ if (Leaf->List[LeafIndex] == TargetKey)
+ {
+ HvMarkCellDirty(Hive, KeyNode->SubKeyLists[Stable], FALSE);
+ CmpRemoveIndexKeyCell(Leaf, LeafIndex);
+ DPRINT1("The offending key cell has BEEN FOUND in leaf (leaf 0x%p,
index %lu)\n",
+ Leaf, LeafIndex);
+ return TRUE;
+ }
+ }
+ }
+
+ /*
+ * We have searched everywhere but we couldn't
+ * hunt the offending target key cell.
+ */
+ DPRINT1("No target key has been found to remove\n");
+ return FALSE;
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+/**
+ * @brief
+ * Checks if self healing is permitted by the kernel and/or
+ * bootloader. Self healing is also triggered if such a
+ * request was prompted by the user to fix a broken hive.
+ * Such a request tipically comes from a registry repair
+ * tool such as the ReactOS Check Registry Utility.
+ *
+ * @param[in] FixHive
+ * If set to TRUE, self heal is triggered and the target
+ * hive will be fixed. Otherwise the hive will not be fixed.
+ *
+ * @return
+ * Returns TRUE if self healing is possible, FALSE otherwise.
+ */
+BOOLEAN
+CMAPI
+CmIsSelfHealEnabled(
+ _In_ BOOLEAN FixHive)
+{
+ PAGED_CODE();
+
+ if (FixHive)
+ return TRUE;
+
+#if !defined(CMLIB_HOST) && !defined(_BLDR_)
+ if (CmpSelfHeal || (CmpBootType & HBOOT_TYPE_SELF_HEAL))
+ return TRUE;
+#endif
+
+ return FALSE;
+}
+
+/**
+ * @brief
+ * Repairs the parent key from damage by removing the
+ * offending subkey cell.
+ *
+ * @param[in,out] Hive
+ * A pointer to a hive descriptor containing faulty data.
+ *
+ * @param[in] TargetKey
+ * The offending target cell to remove from the parent.
+ *
+ * @param[in] ParentKey
+ * The damaged parent key cell to heal.
+ *
+ * @param[in] FixHive
+ * If set to TRUE, self heal is triggered and the target
+ * hive will be fixed. Otherwise the hive will not be fixed.
+ *
+ * @return
+ * Returns TRUE if the function successfully healed the parent
+ * key, FALSE otherwise.
+ */
+BOOLEAN
+CMAPI
+CmpRepairParentKey(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX TargetKey,
+ _In_ HCELL_INDEX ParentKey,
+ _In_ BOOLEAN FixHive)
+{
+ PCM_KEY_INDEX KeyIndex;
+ PCM_KEY_NODE KeyNode;
+ BOOLEAN ParentRepaired;
+
+ PAGED_CODE();
+
+ /* The target key must NEVER be NIL! */
+ ASSERT(TargetKey != HCELL_NIL);
+
+ /* Assume the parent hasn't been repaired yet */
+ ParentRepaired = FALSE;
+
+ /* Is self healing possible? */
+ if (!CmIsSelfHealEnabled(FixHive))
+ {
+ DPRINT1("Self healing not possible\n");
+ return ParentRepaired;
+ }
+
+ /* Obtain a node from the parent */
+ KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, ParentKey);
+ if (!KeyNode)
+ {
+ DPRINT1("Couldn't get the parent key node\n");
+ return ParentRepaired;
+ }
+
+ /* Obtain the index as well since we got the parent node */
+ KeyIndex = (PCM_KEY_INDEX)HvGetCell(Hive, KeyNode->SubKeyLists[Stable]);
+ if (!KeyIndex)
+ {
+ DPRINT1("Couldn't get the key index from parent node\n");
+ HvReleaseCell(Hive, ParentKey);
+ return ParentRepaired;
+ }
+
+ /* Check if this is a root */
+ if (KeyIndex->Signature == CM_KEY_INDEX_ROOT)
+ {
+ /* It is, call the specific helper to discard the damaged key down the root */
+ ParentRepaired = CmpRemoveSubkeyInRoot(Hive,
+ KeyIndex,
+ TargetKey);
+ }
+ else if ((KeyIndex->Signature == CM_KEY_INDEX_LEAF) ||
+ (KeyIndex->Signature == CM_KEY_FAST_LEAF) ||
+ (KeyIndex->Signature == CM_KEY_HASH_LEAF))
+ {
+ /* Otherwise call the leaf helper */
+ ParentRepaired = CmpRemoveSubKeyInLeaf(Hive,
+ KeyNode,
+ KeyIndex,
+ TargetKey);
+ }
+ else
+ {
+ /*
+ * Generally CmCheckRegistry detects if a key index
+ * in the subkeys list is totally broken (we understand
+ * that if its signature is not root or leaf) and it will
+ * purge the whole subkeys list in such cases. With that
+ * being said, we should never reach this code path. But
+ * if for whatever reason we reach here then something
+ * is seriously wrong.
+ */
+ DPRINT1("The key index signature is invalid (KeyIndex->Signature ==
%lu)", KeyIndex->Signature);
+ ASSERT(FALSE);
+ }
+
+ /*
+ * If we successfully removed the offending key
+ * cell mark down the parent as dirty and punt down
+ * the subkey count as well. Mark the hive as in
+ * self heal mode as well.
+ */
+ if (ParentRepaired)
+ {
+ HvMarkCellDirty(Hive, ParentKey, FALSE);
+ KeyNode->SubKeyCounts[Stable]--;
+ Hive->BaseBlock->BootType |= HBOOT_TYPE_SELF_HEAL;
+ DPRINT1("The subkey has been removed, the parent is now repaired\n");
+ }
+
+ HvReleaseCell(Hive, KeyNode->SubKeyLists[Stable]);
+ HvReleaseCell(Hive, ParentKey);
+ return ParentRepaired;
+}
+
+/**
+ * @brief
+ * Repairs the parent of the node from damage due
+ * to parent cell and parent node incosistency.
+ *
+ * @param[in,out] Hive
+ * A pointer to a hive descriptor containing faulty data.
+ *
+ * @param[in] CurrentCell
+ * The current cell to be marked as dirty.
+ *
+ * @param[in] ParentCell
+ * The sane parent cell which is used by the
+ * function for new parent node assignment.
+ *
+ * @param[in,out] CellData
+ * The cell data of the current cell of which
+ * its parent node is to be repaired.
+ *
+ * @param[in] FixHive
+ * If set to TRUE, self heal is triggered and the target
+ * hive will be fixed. Otherwise the hive will not be fixed.
+ *
+ * @return
+ * Returns TRUE if the function successfully healed the
+ * parent node, FALSE otherwise.
+ */
+BOOLEAN
+CMAPI
+CmpRepairParentNode(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX CurrentCell,
+ _In_ HCELL_INDEX ParentCell,
+ _Inout_ PCELL_DATA CellData,
+ _In_ BOOLEAN FixHive)
+{
+ PAGED_CODE();
+
+ /* Is self healing possible? */
+ if (!CmIsSelfHealEnabled(FixHive))
+ {
+ DPRINT1("Self healing not possible\n");
+ return FALSE;
+ }
+
+ /*
+ * Mark the cell where we got the actual
+ * cell data as dirty and fix the node.
+ */
+ HvMarkCellDirty(Hive, CurrentCell, FALSE);
+ CellData->u.KeyNode.Parent = ParentCell;
+
+ /* Mark the hive as in self healing mode since we repaired it */
+ Hive->BaseBlock->BootType |= HBOOT_TYPE_SELF_HEAL;
+ return TRUE;
+}
+
+/**
+ * @brief
+ * Repairs the key node signature from damage
+ * due to signature corruption.
+ *
+ * @param[in,out] Hive
+ * A pointer to a hive descriptor containing faulty data.
+ *
+ * @param[in] CurrentCell
+ * The current cell to be marked as dirty.
+ *
+ * @param[in,out] CellData
+ * The cell data of the current cell of which
+ * its signature is to be repaired.
+ *
+ * @param[in] FixHive
+ * If set to TRUE, self heal is triggered and the target
+ * hive will be fixed. Otherwise the hive will not be fixed.
+ *
+ * @return
+ * Returns TRUE if the function successfully healed the
+ * key node signature, FALSE otherwise.
+ */
+BOOLEAN
+CMAPI
+CmpRepairKeyNodeSignature(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX CurrentCell,
+ _Inout_ PCELL_DATA CellData,
+ _In_ BOOLEAN FixHive)
+{
+ PAGED_CODE();
+
+ /* Is self healing possible? */
+ if (!CmIsSelfHealEnabled(FixHive))
+ {
+ DPRINT1("Self healing not possible\n");
+ return FALSE;
+ }
+
+ /*
+ * Mark the cell where we got the actual
+ * cell data as dirty and fix the key signature.
+ */
+ HvMarkCellDirty(Hive, CurrentCell, FALSE);
+ CellData->u.KeyNode.Signature = CM_KEY_NODE_SIGNATURE;
+
+ /* Mark the hive as in self healing mode since we repaired it */
+ Hive->BaseBlock->BootType |= HBOOT_TYPE_SELF_HEAL;
+ return TRUE;
+}
+
+/**
+ * @brief
+ * Repairs the class from damage due to class
+ * corruption within the node key.
+ *
+ * @param[in,out] Hive
+ * A pointer to a hive descriptor containing faulty data.
+ *
+ * @param[in] CurrentCell
+ * The current cell to be marked as dirty.
+ *
+ * @param[in,out] CellData
+ * The cell data of the current cell of which
+ * its class is to be repaired.
+ *
+ * @param[in] FixHive
+ * If set to TRUE, self heal is triggered and the target
+ * hive will be fixed. Otherwise the hive will not be fixed.
+ *
+ * @return
+ * Returns TRUE if the function successfully healed the
+ * class of node key, FALSE otherwise.
+ */
+BOOLEAN
+CMAPI
+CmpRepairClassOfNodeKey(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX CurrentCell,
+ _Inout_ PCELL_DATA CellData,
+ _In_ BOOLEAN FixHive)
+{
+ PAGED_CODE();
+
+ /* Is self healing possible? */
+ if (!CmIsSelfHealEnabled(FixHive))
+ {
+ DPRINT1("Self healing not possible\n");
+ return FALSE;
+ }
+
+ /*
+ * Mark the cell where we got the actual
+ * cell data as dirty and fix the class field
+ * of key node.
+ */
+ HvMarkCellDirty(Hive, CurrentCell, FALSE);
+ CellData->u.KeyNode.Class = HCELL_NIL;
+ CellData->u.KeyNode.ClassLength = 0;
+
+ /* Mark the hive as in self healing mode since we repaired it */
+ Hive->BaseBlock->BootType |= HBOOT_TYPE_SELF_HEAL;
+ return TRUE;
+}
+
+/**
+ * @brief
+ * Repairs the value list count of key due to
+ * corruption. The process involves by removing
+ * one damaged value less from the list.
+ *
+ * @param[in,out] Hive
+ * A pointer to a hive descriptor containing faulty data.
+ *
+ * @param[in] CurrentCell
+ * The current cell to be marked as dirty.
+ *
+ * @param[in] ListCountIndex
+ * The value count index which points to the actual
+ * value in the list to be removed.
+ *
+ * @param[in,out] ValueListData
+ * The value list cell data containing the actual list
+ * of which the damaged is to be removed from.
+ *
+ * @param[in] FixHive
+ * If set to TRUE, self heal is triggered and the target
+ * hive will be fixed. Otherwise the hive will not be fixed.
+ *
+ * @return
+ * Returns TRUE if the function successfully healed the
+ * value list count, FALSE otherwise.
+ */
+BOOLEAN
+CMAPI
+CmpRepairValueListCount(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX CurrentCell,
+ _In_ ULONG ListCountIndex,
+ _Inout_ PCELL_DATA ValueListData,
+ _In_ BOOLEAN FixHive)
+{
+ PCM_KEY_NODE ValueListNode;
+
+ PAGED_CODE();
+
+ /* Is self healing possible? */
+ if (!CmIsSelfHealEnabled(FixHive))
+ {
+ DPRINT1("Self healing not possible\n");
+ return FALSE;
+ }
+
+ /*
+ * Obtain a node from the cell that we mark it as dirty.
+ * The node is that of the current cell of which its
+ * value list is being validated.
+ */
+ ValueListNode = (PCM_KEY_NODE)HvGetCell(Hive, CurrentCell);
+ if (!ValueListNode)
+ {
+ DPRINT1("Could not get a node from the current cell\n");
+ return FALSE;
+ }
+
+ /*
+ * Mark the current cell and value list as dirty
+ * as we will be making changes onto them.
+ */
+ HvMarkCellDirty(Hive, CurrentCell, FALSE);
+ HvMarkCellDirty(Hive, ValueListNode->ValueList.List, FALSE);
+
+ /*
+ * Now remove the value from the list and mark the
+ * hive as in self healing mode.
+ */
+ CmpRemoveValueFromValueList(ValueListNode, ValueListData, ListCountIndex);
+ Hive->BaseBlock->BootType |= HBOOT_TYPE_SELF_HEAL;
+ HvReleaseCell(Hive, CurrentCell);
+ return TRUE;
+}
+
+/**
+ * @brief
+ * Repairs the value list due to corruption. The
+ * process involes by purging the whole damaged
+ * list.
+ *
+ * @param[in,out] Hive
+ * A pointer to a hive descriptor containing faulty data.
+ *
+ * @param[in] CurrentCell
+ * The current cell to be marked as dirty.
+ *
+ * @param[in] FixHive
+ * If set to TRUE, self heal is triggered and the target
+ * hive will be fixed. Otherwise the hive will not be fixed.
+ *
+ * @return
+ * Returns TRUE if the function successfully healed the
+ * value list, FALSE otherwise.
+ */
+BOOLEAN
+CMAPI
+CmpRepairValueList(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX CurrentCell,
+ _In_ BOOLEAN FixHive)
+{
+ PCM_KEY_NODE ValueListNode;
+
+ PAGED_CODE();
+
+ /* Is self healing possible? */
+ if (!CmIsSelfHealEnabled(FixHive))
+ {
+ DPRINT1("Self healing not possible\n");
+ return FALSE;
+ }
+
+ /* Obtain a node */
+ ValueListNode = (PCM_KEY_NODE)HvGetCell(Hive, CurrentCell);
+ if (!ValueListNode)
+ {
+ DPRINT1("Could not get a node from the current cell\n");
+ return FALSE;
+ }
+
+ /* Purge out the whole list */
+ HvMarkCellDirty(Hive, CurrentCell, FALSE);
+ ValueListNode->ValueList.List = HCELL_NIL;
+ ValueListNode->ValueList.Count = 0;
+
+ Hive->BaseBlock->BootType |= HBOOT_TYPE_SELF_HEAL;
+ HvReleaseCell(Hive, CurrentCell);
+ return TRUE;
+}
+
+/**
+ * @brief
+ * Repairs the subkey list count due to corruption.
+ * The process involves by fixing the count itself
+ * with a sane count.
+ *
+ * @param[in,out] Hive
+ * A pointer to a hive descriptor containing faulty data.
+ *
+ * @param[in] CurrentCell
+ * The current cell to be marked as dirty.
+ *
+ * @param[in] Count
+ * The healthy count which is used by the function
+ * to fix the subkeys list count.
+ *
+ * @param[in,out] CellData
+ * The cell data of the current cell of which its
+ * subkeys list is to be fixed.
+ *
+ * @param[in] FixHive
+ * If set to TRUE, self heal is triggered and the target
+ * hive will be fixed. Otherwise the hive will not be fixed.
+ *
+ * @return
+ * Returns TRUE if the function successfully healed the
+ * subkeys list count, FALSE otherwise.
+ */
+BOOLEAN
+CMAPI
+CmpRepairSubKeyCounts(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX CurrentCell,
+ _In_ ULONG Count,
+ _Inout_ PCELL_DATA CellData,
+ _In_ BOOLEAN FixHive)
+{
+ PAGED_CODE();
+
+ /* Is self healing possible? */
+ if (!CmIsSelfHealEnabled(FixHive))
+ {
+ DPRINT1("Self healing not possible\n");
+ return FALSE;
+ }
+
+ /*
+ * Mark the cell where we got the actual
+ * cell data as dirty and fix the subkey
+ * counts.
+ */
+ HvMarkCellDirty(Hive, CurrentCell, FALSE);
+ CellData->u.KeyNode.SubKeyCounts[Stable] = Count;
+
+ /* Mark the hive as in self healing mode since we repaired it */
+ Hive->BaseBlock->BootType |= HBOOT_TYPE_SELF_HEAL;
+ return TRUE;
+}
+
+/**
+ * @brief
+ * Repairs the subkey list due to corruption. The process
+ * involves by purging the whole damaged subkeys list.
+ *
+ * @param[in,out] Hive
+ * A pointer to a hive descriptor containing faulty data.
+ *
+ * @param[in] CurrentCell
+ * The current cell to be marked as dirty.
+ *
+ * @param[in,out] CellData
+ * The cell data of the current cell of which its
+ * subkeys list is to be fixed.
+ *
+ * @param[in] FixHive
+ * If set to TRUE, self heal is triggered and the target
+ * hive will be fixed. Otherwise the hive will not be fixed.
+ *
+ * @return
+ * Returns TRUE if the function successfully healed the
+ * subkeys list, FALSE otherwise.
+ */
+BOOLEAN
+CMAPI
+CmpRepairSubKeyList(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX CurrentCell,
+ _Inout_ PCELL_DATA CellData,
+ _In_ BOOLEAN FixHive)
+{
+ PAGED_CODE();
+
+ /* Is self healing possible? */
+ if (!CmIsSelfHealEnabled(FixHive))
+ {
+ DPRINT1("Self healing not possible\n");
+ return FALSE;
+ }
+
+ /*
+ * Mark the cell where we got the actual
+ * cell data as dirty and fix the subkey
+ * list.
+ */
+ HvMarkCellDirty(Hive, CurrentCell, FALSE);
+ CellData->u.KeyNode.SubKeyLists[Stable] = HCELL_NIL;
+ CellData->u.KeyNode.SubKeyCounts[Stable] = 0;
+
+ /* Mark the hive as in self healing mode since we repaired it */
+ Hive->BaseBlock->BootType |= HBOOT_TYPE_SELF_HEAL;
+ return TRUE;
+}
+
+/* EOF */
diff --git a/sdk/lib/cmlib/cmlib.h b/sdk/lib/cmlib/cmlib.h
index 4f02daf32e5..65f66f581b0 100644
--- a/sdk/lib/cmlib/cmlib.h
+++ b/sdk/lib/cmlib/cmlib.h
@@ -482,6 +482,79 @@ ULONG CMAPI
HvpHiveHeaderChecksum(
PHBASE_BLOCK HiveHeader);
+//
+// Registry Self-Heal Routines
+//
+BOOLEAN
+CMAPI
+CmIsSelfHealEnabled(
+ _In_ BOOLEAN FixHive);
+
+BOOLEAN
+CMAPI
+CmpRepairParentKey(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX TargetKey,
+ _In_ HCELL_INDEX ParentKey,
+ _In_ BOOLEAN FixHive);
+
+BOOLEAN
+CMAPI
+CmpRepairParentNode(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX DirtyCell,
+ _In_ HCELL_INDEX ParentCell,
+ _Inout_ PCELL_DATA CellData,
+ _In_ BOOLEAN FixHive);
+
+BOOLEAN
+CMAPI
+CmpRepairKeyNodeSignature(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX DirtyCell,
+ _Inout_ PCELL_DATA CellData,
+ _In_ BOOLEAN FixHive);
+
+BOOLEAN
+CMAPI
+CmpRepairClassOfNodeKey(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX DirtyCell,
+ _Inout_ PCELL_DATA CellData,
+ _In_ BOOLEAN FixHive);
+
+BOOLEAN
+CMAPI
+CmpRepairValueList(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX CurrentCell,
+ _In_ BOOLEAN FixHive);
+
+BOOLEAN
+CMAPI
+CmpRepairValueListCount(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX CurrentCell,
+ _In_ ULONG ListCountIndex,
+ _Inout_ PCELL_DATA ValueListData,
+ _In_ BOOLEAN FixHive);
+
+BOOLEAN
+CMAPI
+CmpRepairSubKeyCounts(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX CurrentCell,
+ _In_ ULONG Count,
+ _Inout_ PCELL_DATA CellData,
+ _In_ BOOLEAN FixHive);
+
+BOOLEAN
+CMAPI
+CmpRepairSubKeyList(
+ _Inout_ PHHIVE Hive,
+ _In_ HCELL_INDEX CurrentCell,
+ _Inout_ PCELL_DATA CellData,
+ _In_ BOOLEAN FixHive);
/* Old-style Public "Cmlib" functions */