[MKHIVE] Create reparse points, and use them instead of pseudo-connections between cells

svn path=/trunk/; revision=64552
This commit is contained in:
Hervé Poussineau 2014-10-05 21:30:57 +00:00
parent 6435bdf92c
commit dc6d1450bc
5 changed files with 135 additions and 88 deletions

View file

@ -330,16 +330,19 @@ CmiCreateSubKey(
NTSTATUS NTSTATUS
CmiAddSubKey( CmiAddSubKey(
IN PCMHIVE RegistryHive, IN PCMHIVE RegistryHive,
IN PCM_KEY_NODE ParentKeyCell,
IN HCELL_INDEX ParentKeyCellOffset, IN HCELL_INDEX ParentKeyCellOffset,
IN PCUNICODE_STRING SubKeyName, IN PCUNICODE_STRING SubKeyName,
IN ULONG CreateOptions, IN ULONG CreateOptions,
OUT PCM_KEY_NODE *pSubKeyCell, OUT PCM_KEY_NODE *pSubKeyCell,
OUT HCELL_INDEX *pBlockOffset) OUT HCELL_INDEX *pBlockOffset)
{ {
PCM_KEY_NODE ParentKeyCell;
HCELL_INDEX NKBOffset; HCELL_INDEX NKBOffset;
NTSTATUS Status; NTSTATUS Status;
ParentKeyCell = (PCM_KEY_NODE)HvGetCell(&RegistryHive->Hive, ParentKeyCellOffset);
if (!ParentKeyCell)
return STATUS_UNSUCCESSFUL;
VERIFY_KEY_CELL(ParentKeyCell); VERIFY_KEY_CELL(ParentKeyCell);
/* Create the new key */ /* Create the new key */
@ -396,18 +399,22 @@ CmiCompareHashI(
NTSTATUS NTSTATUS
CmiScanForSubKey( CmiScanForSubKey(
IN PCMHIVE RegistryHive, IN PCMHIVE RegistryHive,
IN PCM_KEY_NODE KeyCell, IN HCELL_INDEX ParentKeyCellOffset,
IN PCUNICODE_STRING SubKeyName, IN PCUNICODE_STRING SubKeyName,
IN ULONG Attributes, IN ULONG Attributes,
OUT PCM_KEY_NODE *pSubKeyCell, OUT PCM_KEY_NODE *pSubKeyCell,
OUT HCELL_INDEX *pBlockOffset) OUT HCELL_INDEX *pBlockOffset)
{ {
PCM_KEY_NODE KeyCell;
PCM_KEY_FAST_INDEX HashBlock; PCM_KEY_FAST_INDEX HashBlock;
PCM_KEY_NODE CurSubKeyCell; PCM_KEY_NODE CurSubKeyCell;
BOOLEAN CaseInsensitive; BOOLEAN CaseInsensitive;
ULONG Storage; ULONG Storage;
ULONG i; ULONG i;
KeyCell = (PCM_KEY_NODE)HvGetCell(&RegistryHive->Hive, ParentKeyCellOffset);
if (!KeyCell)
return STATUS_UNSUCCESSFUL;
VERIFY_KEY_CELL(KeyCell); VERIFY_KEY_CELL(KeyCell);
ASSERT(RegistryHive); ASSERT(RegistryHive);
@ -528,13 +535,13 @@ CmiAllocateValueCell(
NTSTATUS NTSTATUS
CmiAddValueKey( CmiAddValueKey(
IN PCMHIVE RegistryHive, IN PCMHIVE RegistryHive,
IN PCM_KEY_NODE KeyCell,
IN HCELL_INDEX KeyCellOffset, IN HCELL_INDEX KeyCellOffset,
IN PCUNICODE_STRING ValueName, IN PCUNICODE_STRING ValueName,
OUT PCM_KEY_VALUE *pValueCell, OUT PCM_KEY_VALUE *pValueCell,
OUT HCELL_INDEX *pValueCellOffset) OUT HCELL_INDEX *pValueCellOffset)
{ {
PVALUE_LIST_CELL ValueListCell; PVALUE_LIST_CELL ValueListCell;
PCM_KEY_NODE KeyCell;
PCM_KEY_VALUE NewValueCell; PCM_KEY_VALUE NewValueCell;
HCELL_INDEX ValueListCellOffset; HCELL_INDEX ValueListCellOffset;
HCELL_INDEX NewValueCellOffset; HCELL_INDEX NewValueCellOffset;
@ -542,6 +549,9 @@ CmiAddValueKey(
HSTORAGE_TYPE Storage; HSTORAGE_TYPE Storage;
NTSTATUS Status; NTSTATUS Status;
KeyCell = HvGetCell(&RegistryHive->Hive, KeyCellOffset);
if (!KeyCell)
return STATUS_UNSUCCESSFUL;
Storage = (KeyCell->Flags & KEY_IS_VOLATILE) ? Volatile : Stable; Storage = (KeyCell->Flags & KEY_IS_VOLATILE) ? Volatile : Stable;
if (KeyCell->ValueList.List == HCELL_NIL) if (KeyCell->ValueList.List == HCELL_NIL)
{ {
@ -614,15 +624,19 @@ CmiAddValueKey(
NTSTATUS NTSTATUS
CmiScanForValueKey( CmiScanForValueKey(
IN PCMHIVE RegistryHive, IN PCMHIVE RegistryHive,
IN PCM_KEY_NODE KeyCell, IN HCELL_INDEX KeyCellOffset,
IN PCUNICODE_STRING ValueName, IN PCUNICODE_STRING ValueName,
OUT PCM_KEY_VALUE *pValueCell, OUT PCM_KEY_VALUE *pValueCell,
OUT HCELL_INDEX *pValueCellOffset) OUT HCELL_INDEX *pValueCellOffset)
{ {
PCM_KEY_NODE KeyCell;
PVALUE_LIST_CELL ValueListCell; PVALUE_LIST_CELL ValueListCell;
PCM_KEY_VALUE CurValueCell; PCM_KEY_VALUE CurValueCell;
ULONG i; ULONG i;
KeyCell = (PCM_KEY_NODE)HvGetCell(&RegistryHive->Hive, KeyCellOffset);
if (!KeyCell)
return STATUS_UNSUCCESSFUL;
*pValueCell = NULL; *pValueCell = NULL;
*pValueCellOffset = HCELL_NIL; *pValueCellOffset = HCELL_NIL;

View file

@ -33,7 +33,6 @@ CmiInitializeTempHive(
NTSTATUS NTSTATUS
CmiAddSubKey( CmiAddSubKey(
IN PCMHIVE RegistryHive, IN PCMHIVE RegistryHive,
IN PCM_KEY_NODE ParentKeyCell,
IN HCELL_INDEX ParentKeyCellOffset, IN HCELL_INDEX ParentKeyCellOffset,
IN PCUNICODE_STRING SubKeyName, IN PCUNICODE_STRING SubKeyName,
IN ULONG CreateOptions, IN ULONG CreateOptions,
@ -43,7 +42,7 @@ CmiAddSubKey(
NTSTATUS NTSTATUS
CmiScanForSubKey( CmiScanForSubKey(
IN PCMHIVE RegistryHive, IN PCMHIVE RegistryHive,
IN PCM_KEY_NODE KeyCell, IN HCELL_INDEX ParentKeyCellOffset,
IN PCUNICODE_STRING SubKeyName, IN PCUNICODE_STRING SubKeyName,
IN ULONG Attributes, IN ULONG Attributes,
OUT PCM_KEY_NODE *pSubKeyCell, OUT PCM_KEY_NODE *pSubKeyCell,
@ -52,7 +51,6 @@ CmiScanForSubKey(
NTSTATUS NTSTATUS
CmiAddValueKey( CmiAddValueKey(
IN PCMHIVE RegistryHive, IN PCMHIVE RegistryHive,
IN PCM_KEY_NODE KeyCell,
IN HCELL_INDEX KeyCellOffset, IN HCELL_INDEX KeyCellOffset,
IN PCUNICODE_STRING ValueName, IN PCUNICODE_STRING ValueName,
OUT PCM_KEY_VALUE *pValueCell, OUT PCM_KEY_VALUE *pValueCell,
@ -61,7 +59,7 @@ CmiAddValueKey(
NTSTATUS NTSTATUS
CmiScanForValueKey( CmiScanForValueKey(
IN PCMHIVE RegistryHive, IN PCMHIVE RegistryHive,
IN PCM_KEY_NODE KeyCell, IN HCELL_INDEX KeyCellOffset,
IN PCUNICODE_STRING ValueName, IN PCUNICODE_STRING ValueName,
OUT PCM_KEY_VALUE *pValueCell, OUT PCM_KEY_VALUE *pValueCell,
OUT HCELL_INDEX *pValueCellOffset); OUT HCELL_INDEX *pValueCellOffset);

View file

@ -52,6 +52,9 @@ unsigned char BitScanReverse(ULONG * const Index, unsigned long Mask);
#define BitScanReverse64 _BitScanReverse64 #define BitScanReverse64 _BitScanReverse64
#endif #endif
typedef DWORD REGSAM;
typedef LPVOID LPSECURITY_ATTRIBUTES;
NTSTATUS NTAPI NTSTATUS NTAPI
RtlAnsiStringToUnicodeString( RtlAnsiStringToUnicodeString(
IN OUT PUNICODE_STRING UniDest, IN OUT PUNICODE_STRING UniDest,

View file

@ -51,8 +51,7 @@ CMHIVE SystemHive; /* \Registry\Machine\SYSTEM */
static PMEMKEY static PMEMKEY
CreateInMemoryStructure( CreateInMemoryStructure(
IN PCMHIVE RegistryHive, IN PCMHIVE RegistryHive,
IN HCELL_INDEX KeyCellOffset, IN HCELL_INDEX KeyCellOffset)
IN PCUNICODE_STRING KeyName)
{ {
PMEMKEY Key; PMEMKEY Key;
@ -60,54 +59,50 @@ CreateInMemoryStructure(
if (!Key) if (!Key)
return NULL; return NULL;
InitializeListHead (&Key->SubKeyList);
InitializeListHead (&Key->KeyList);
Key->RegistryHive = RegistryHive; Key->RegistryHive = RegistryHive;
Key->KeyCellOffset = Key->KeyCellOffsetInParentHive = KeyCellOffset; Key->KeyCellOffset = KeyCellOffset;
Key->KeyCell = (PCM_KEY_NODE)HvGetCell (&RegistryHive->Hive, Key->KeyCellOffset);
if (!Key->KeyCell)
{
free(Key);
return NULL;
}
Key->KeyCell->SubKeyLists[Stable] = HCELL_NIL;
Key->KeyCell->SubKeyLists[Volatile] = HCELL_NIL;
return Key; return Key;
} }
LIST_ENTRY CmiReparsePointsHead;
static LONG static LONG
RegpOpenOrCreateKey( RegpOpenOrCreateKey(
IN HKEY hParentKey, IN HKEY hParentKey,
IN PCWSTR KeyName, IN PCWSTR KeyName,
IN BOOL AllowCreation, IN BOOL AllowCreation,
IN BOOL Volatile,
OUT PHKEY Key) OUT PHKEY Key)
{ {
PWSTR LocalKeyName; PWSTR LocalKeyName;
PWSTR End; PWSTR End;
UNICODE_STRING KeyString; UNICODE_STRING KeyString;
NTSTATUS Status; NTSTATUS Status;
PMEMKEY ParentKey; PREPARSE_POINT CurrentReparsePoint;
PMEMKEY CurrentKey; PMEMKEY CurrentKey;
PCMHIVE ParentRegistryHive;
HCELL_INDEX ParentCellOffset;
PLIST_ENTRY Ptr; PLIST_ENTRY Ptr;
PCM_KEY_NODE SubKeyCell; PCM_KEY_NODE SubKeyCell;
HCELL_INDEX BlockOffset; HCELL_INDEX BlockOffset;
BOOLEAN ParentIsSystem = FALSE;
DPRINT("RegpCreateOpenKey('%S')\n", KeyName); DPRINT("RegpCreateOpenKey('%S')\n", KeyName);
if (*KeyName == L'\\') if (*KeyName == L'\\')
{ {
KeyName++; KeyName++;
ParentKey = RootKey; ParentRegistryHive = RootKey->RegistryHive;
ParentCellOffset = RootKey->KeyCellOffset;
} }
else if (hParentKey == NULL) else if (hParentKey == NULL)
{ {
ParentKey = RootKey; ParentRegistryHive = RootKey->RegistryHive;
ParentCellOffset = RootKey->KeyCellOffset;
} }
else else
{ {
ParentKey = HKEY_TO_MEMKEY(RootKey); ParentRegistryHive = HKEY_TO_MEMKEY(RootKey)->RegistryHive;
ParentCellOffset = HKEY_TO_MEMKEY(RootKey)->KeyCellOffset;
} }
LocalKeyName = (PWSTR)KeyName; LocalKeyName = (PWSTR)KeyName;
@ -130,77 +125,55 @@ RegpOpenOrCreateKey(
} }
} }
/* Redirect from 'CurrentControlSet' to 'ControlSet001' */
if (!strncmpiW(LocalKeyName, L"CurrentControlSet", 17) && ParentIsSystem)
{
RtlInitUnicodeString(&KeyString, L"ControlSet001");
ParentIsSystem = FALSE;
}
else
{
ParentIsSystem = (strncmpiW(LocalKeyName, L"SYSTEM", 6) == 0);
}
Status = CmiScanForSubKey( Status = CmiScanForSubKey(
ParentKey->RegistryHive, ParentRegistryHive,
ParentKey->KeyCell, ParentCellOffset,
&KeyString, &KeyString,
OBJ_CASE_INSENSITIVE, OBJ_CASE_INSENSITIVE,
&SubKeyCell, &SubKeyCell,
&BlockOffset); &BlockOffset);
if (NT_SUCCESS(Status)) if (NT_SUCCESS(Status))
{ {
/* Check subkey in memory structure */ /* Search for a possible reparse point */
Ptr = ParentKey->SubKeyList.Flink; Ptr = CmiReparsePointsHead.Flink;
while (Ptr != &ParentKey->SubKeyList) while (Ptr != &CmiReparsePointsHead)
{ {
CurrentKey = CONTAINING_RECORD(Ptr, MEMKEY, KeyList); CurrentReparsePoint = CONTAINING_RECORD(Ptr, REPARSE_POINT, ListEntry);
if (CurrentKey->KeyCellOffsetInParentHive == BlockOffset) if (CurrentReparsePoint->SourceHive == ParentRegistryHive &&
CurrentReparsePoint->SourceKeyCellOffset == BlockOffset)
{ {
goto nextsubkey; ParentRegistryHive = CurrentReparsePoint->DestinationHive;
BlockOffset = CurrentReparsePoint->DestinationKeyCellOffset;
break;
} }
Ptr = Ptr->Flink; Ptr = Ptr->Flink;
} }
/* If we go there, this means that key exists, but we don't know it */
ASSERT(FALSE);
} }
else if (Status == STATUS_OBJECT_NAME_NOT_FOUND && AllowCreation)
if (AllowCreation && Status == STATUS_OBJECT_NAME_NOT_FOUND)
{ {
Status = CmiAddSubKey( Status = CmiAddSubKey(
ParentKey->RegistryHive, ParentRegistryHive,
ParentKey->KeyCell, ParentCellOffset,
ParentKey->KeyCellOffset,
&KeyString, &KeyString,
0, Volatile ? REG_OPTION_VOLATILE : 0,
&SubKeyCell, &SubKeyCell,
&BlockOffset); &BlockOffset);
if (NT_SUCCESS(Status))
{
/* Now, SubKeyCell/BlockOffset are valid */
CurrentKey = CreateInMemoryStructure(
ParentKey->RegistryHive,
BlockOffset,
&KeyString);
if (!CurrentKey)
return ERROR_OUTOFMEMORY;
/* Add CurrentKey in ParentKey */
InsertTailList(&ParentKey->SubKeyList, &CurrentKey->KeyList);
}
} }
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
return ERROR_UNSUCCESSFUL; return ERROR_UNSUCCESSFUL;
nextsubkey: nextsubkey:
ParentKey = CurrentKey; ParentCellOffset = BlockOffset;
if (End) if (End)
LocalKeyName = End + 1; LocalKeyName = End + 1;
else else
break; break;
} }
*Key = MEMKEY_TO_HKEY(ParentKey); CurrentKey = CreateInMemoryStructure(ParentRegistryHive, ParentCellOffset);
if (!CurrentKey)
return ERROR_OUTOFMEMORY;
*Key = MEMKEY_TO_HKEY(CurrentKey);
return ERROR_SUCCESS; return ERROR_SUCCESS;
} }
@ -211,7 +184,7 @@ RegCreateKeyW(
IN LPCWSTR lpSubKey, IN LPCWSTR lpSubKey,
OUT PHKEY phkResult) OUT PHKEY phkResult)
{ {
return RegpOpenOrCreateKey(hKey, lpSubKey, TRUE, phkResult); return RegpOpenOrCreateKey(hKey, lpSubKey, TRUE, FALSE, phkResult);
} }
static PWSTR static PWSTR
@ -270,7 +243,26 @@ RegOpenKeyW(
IN LPCWSTR lpSubKey, IN LPCWSTR lpSubKey,
OUT PHKEY phkResult) OUT PHKEY phkResult)
{ {
return RegpOpenOrCreateKey(hKey, lpSubKey, FALSE, phkResult); return RegpOpenOrCreateKey(hKey, lpSubKey, FALSE, FALSE, phkResult);
}
LONG WINAPI
RegCreateKeyExW(
IN HKEY hKey,
IN LPCWSTR lpSubKey,
IN DWORD Reserved,
IN LPWSTR lpClass OPTIONAL,
IN DWORD dwOptions,
IN REGSAM samDesired,
IN LPSECURITY_ATTRIBUTES lpSecurityAttributes OPTIONAL,
OUT PHKEY phkResult,
OUT LPDWORD lpdwDisposition OPTIONAL)
{
return RegpOpenOrCreateKey(hKey,
lpSubKey,
TRUE,
(dwOptions & REG_OPTION_VOLATILE) != 0,
phkResult);
} }
LONG WINAPI LONG WINAPI
@ -308,7 +300,7 @@ RegpOpenOrCreateValue(
Status = CmiScanForValueKey( Status = CmiScanForValueKey(
ParentKey->RegistryHive, ParentKey->RegistryHive,
ParentKey->KeyCell, ParentKey->KeyCellOffset,
&ValueString, &ValueString,
ValueCell, ValueCell,
ValueCellOffset); ValueCellOffset);
@ -316,7 +308,6 @@ RegpOpenOrCreateValue(
{ {
Status = CmiAddValueKey( Status = CmiAddValueKey(
ParentKey->RegistryHive, ParentKey->RegistryHive,
ParentKey->KeyCell,
ParentKey->KeyCellOffset, ParentKey->KeyCellOffset,
&ValueString, &ValueString,
ValueCell, ValueCell,
@ -428,9 +419,6 @@ RegSetValueExW(
HvMarkCellDirty(&Key->RegistryHive->Hive, ValueCellOffset, FALSE); HvMarkCellDirty(&Key->RegistryHive->Hive, ValueCellOffset, FALSE);
} }
if (cbData > Key->KeyCell->MaxValueDataLen)
Key->KeyCell->MaxValueDataLen = cbData;
HvMarkCellDirty(&Key->RegistryHive->Hive, Key->KeyCellOffset, FALSE); HvMarkCellDirty(&Key->RegistryHive->Hive, Key->KeyCellOffset, FALSE);
DPRINT("Return status 0x%08x\n", Status); DPRINT("Return status 0x%08x\n", Status);
@ -482,27 +470,46 @@ ConnectRegistry(
IN LPCWSTR Path) IN LPCWSTR Path)
{ {
NTSTATUS Status; NTSTATUS Status;
PREPARSE_POINT ReparsePoint;
PMEMKEY NewKey; PMEMKEY NewKey;
LONG rc; LONG rc;
ReparsePoint = (PREPARSE_POINT)malloc(sizeof(REPARSE_POINT));
if (!ReparsePoint)
return FALSE;
Status = CmiInitializeTempHive(HiveToConnect); Status = CmiInitializeTempHive(HiveToConnect);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
{ {
DPRINT1("CmiInitializeTempHive() failed with status 0x%08x\n", Status); DPRINT1("CmiInitializeTempHive() failed with status 0x%08x\n", Status);
free(ReparsePoint);
return FALSE; return FALSE;
} }
/* Create key */ /* Create key */
rc = RegCreateKeyW( rc = RegCreateKeyExW(
RootKey, RootKey,
Path, Path,
(PHKEY)&NewKey); 0,
NULL,
REG_OPTION_VOLATILE,
0,
NULL,
(PHKEY)&NewKey,
NULL);
if (rc != ERROR_SUCCESS) if (rc != ERROR_SUCCESS)
{
free(ReparsePoint);
return FALSE; return FALSE;
}
ReparsePoint->SourceHive = NewKey->RegistryHive;
ReparsePoint->SourceKeyCellOffset = NewKey->KeyCellOffset;
NewKey->RegistryHive = HiveToConnect; NewKey->RegistryHive = HiveToConnect;
NewKey->KeyCellOffset = HiveToConnect->Hive.BaseBlock->RootCell; NewKey->KeyCellOffset = HiveToConnect->Hive.BaseBlock->RootCell;
NewKey->KeyCell = (PCM_KEY_NODE)HvGetCell (&HiveToConnect->Hive, NewKey->KeyCellOffset); ReparsePoint->DestinationHive = NewKey->RegistryHive;
ReparsePoint->DestinationKeyCellOffset = NewKey->KeyCellOffset;
InsertTailList(&CmiReparsePointsHead, &ReparsePoint->ListEntry);
return TRUE; return TRUE;
} }
@ -513,9 +520,11 @@ RegInitializeRegistry(VOID)
{ {
UNICODE_STRING RootKeyName = RTL_CONSTANT_STRING(L"\\"); UNICODE_STRING RootKeyName = RTL_CONSTANT_STRING(L"\\");
NTSTATUS Status; NTSTATUS Status;
HKEY ControlSetKey; PMEMKEY ControlSetKey, CurrentControlSetKey;
PREPARSE_POINT ReparsePoint;
InitializeListHead(&CmiHiveListHead); InitializeListHead(&CmiHiveListHead);
InitializeListHead(&CmiReparsePointsHead);
Status = CmiInitializeTempHive(&RootHive); Status = CmiInitializeTempHive(&RootHive);
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
@ -526,8 +535,7 @@ RegInitializeRegistry(VOID)
RootKey = CreateInMemoryStructure( RootKey = CreateInMemoryStructure(
&RootHive, &RootHive,
RootHive.Hive.BaseBlock->RootCell, RootHive.Hive.BaseBlock->RootCell);
&RootKeyName);
/* Create DEFAULT key */ /* Create DEFAULT key */
ConnectRegistry( ConnectRegistry(
@ -563,7 +571,27 @@ RegInitializeRegistry(VOID)
RegCreateKeyW( RegCreateKeyW(
NULL, NULL,
L"Registry\\Machine\\SYSTEM\\ControlSet001", L"Registry\\Machine\\SYSTEM\\ControlSet001",
&ControlSetKey); (HKEY*)&ControlSetKey);
/* Create 'CurrentControlSet' key */
RegCreateKeyExW(
NULL,
L"Registry\\Machine\\SYSTEM\\CurrentControlSet",
0,
NULL,
REG_OPTION_VOLATILE,
0,
NULL,
(HKEY*)&CurrentControlSetKey,
NULL);
/* Connect 'CurrentControlSet' to 'ControlSet001' */
ReparsePoint = (PREPARSE_POINT)malloc(sizeof(REPARSE_POINT));
ReparsePoint->SourceHive = CurrentControlSetKey->RegistryHive;
ReparsePoint->SourceKeyCellOffset = CurrentControlSetKey->KeyCellOffset;
ReparsePoint->DestinationHive = ControlSetKey->RegistryHive;
ReparsePoint->DestinationKeyCellOffset = ControlSetKey->KeyCellOffset;
InsertTailList(&CmiReparsePointsHead, &ReparsePoint->ListEntry);
} }
VOID VOID

View file

@ -6,15 +6,19 @@
#pragma once #pragma once
typedef struct _REPARSE_POINT
{
LIST_ENTRY ListEntry;
PCMHIVE SourceHive;
HCELL_INDEX SourceKeyCellOffset;
PCMHIVE DestinationHive;
HCELL_INDEX DestinationKeyCellOffset;
} REPARSE_POINT, *PREPARSE_POINT;
typedef struct _MEMKEY typedef struct _MEMKEY
{ {
LIST_ENTRY KeyList;
LIST_ENTRY SubKeyList;
/* Information on hard disk structure */ /* Information on hard disk structure */
HCELL_INDEX KeyCellOffsetInParentHive;
HCELL_INDEX KeyCellOffset; HCELL_INDEX KeyCellOffset;
PCM_KEY_NODE KeyCell;
PCMHIVE RegistryHive; PCMHIVE RegistryHive;
} MEMKEY, *PMEMKEY; } MEMKEY, *PMEMKEY;