diff --git a/reactos/lib/cmlib/cmdata.h b/reactos/lib/cmlib/cmdata.h new file mode 100644 index 00000000000..a04a0f44944 --- /dev/null +++ b/reactos/lib/cmlib/cmdata.h @@ -0,0 +1,134 @@ +/* + * PROJECT: registry manipulation library + * LICENSE: GPL - See COPYING in the top level directory + * COPYRIGHT: Copyright 2005 Filip Navara + * Copyright 2001 - 2005 Eric Kohl + */ + +#ifndef CMLIB_CMDATA_H +#define CMLIB_CMDATA_H + +#define REG_INIT_BLOCK_LIST_SIZE 32 +#define REG_INIT_HASH_TABLE_SIZE 3 +#define REG_EXTEND_HASH_TABLE_SIZE 4 +#define REG_VALUE_LIST_CELL_MULTIPLE 4 + +#define REG_KEY_CELL_ID 0x6b6e +#define REG_HASH_TABLE_CELL_ID 0x666c +#define REG_VALUE_CELL_ID 0x6b76 +#define REG_SECURITY_CELL_ID 0x6b73 + +#include + +typedef struct _KEY_CELL +{ + /* Key cell identifier "kn" (0x6b6e) */ + USHORT Id; + + /* Flags */ + USHORT Flags; + + /* Time of last flush */ + LARGE_INTEGER LastWriteTime; + + /* ? */ + ULONG UnUsed1; + + /* Block offset of parent key cell */ + HCELL_INDEX ParentKeyOffset; + + /* Count of sub keys for the key in this key cell (stable & volatile) */ + ULONG NumberOfSubKeys[HvMaxStorageType]; + + /* Block offset of has table for FIXME: subkeys/values? (stable & volatile) */ + HCELL_INDEX HashTableOffset[HvMaxStorageType]; + + /* Count of values contained in this key cell */ + ULONG NumberOfValues; + + /* Block offset of VALUE_LIST_CELL */ + HCELL_INDEX ValueListOffset; + + /* Block offset of security cell */ + HCELL_INDEX SecurityKeyOffset; + + /* Block offset of registry key class */ + HCELL_INDEX ClassNameOffset; + + /* ? */ + ULONG Unused4[5]; + + /* Size in bytes of key name */ + USHORT NameSize; + + /* Size of class name in bytes */ + USHORT ClassSize; + + /* Name of key (not zero terminated) */ + UCHAR Name[0]; +} KEY_CELL, *PKEY_CELL; + +/* KEY_CELL.Flags constants */ +#define REG_KEY_VOLATILE_CELL 0x01 +#define REG_KEY_ROOT_CELL 0x0C +#define REG_KEY_LINK_CELL 0x10 +#define REG_KEY_NAME_PACKED 0x20 + +/* + * Hash record + * + * HashValue: + * packed name: four letters of value's name + * otherwise: Zero! + */ +typedef struct _HASH_RECORD +{ + HCELL_INDEX KeyOffset; + ULONG HashValue; +} HASH_RECORD, *PHASH_RECORD; + +typedef struct _HASH_TABLE_CELL +{ + USHORT Id; + USHORT HashTableSize; + HASH_RECORD Table[0]; +} HASH_TABLE_CELL, *PHASH_TABLE_CELL; + +typedef struct _VALUE_LIST_CELL +{ + HCELL_INDEX ValueOffset[0]; +} VALUE_LIST_CELL, *PVALUE_LIST_CELL; + +typedef struct _VALUE_CELL +{ + USHORT Id; // "kv" + USHORT NameSize; // length of Name + ULONG DataSize; // length of datas in the cell pointed by DataOffset + HCELL_INDEX DataOffset;// datas are here if high bit of DataSize is set + ULONG DataType; + USHORT Flags; + USHORT Unused1; + UCHAR Name[0]; /* warning : not zero terminated */ +} VALUE_CELL, *PVALUE_CELL; + +/* VALUE_CELL.Flags constants */ +#define REG_VALUE_NAME_PACKED 0x0001 + +/* VALUE_CELL.DataSize mask constants */ +#define REG_DATA_SIZE_MASK 0x7FFFFFFF +#define REG_DATA_IN_OFFSET 0x80000000 + +typedef struct _SECURITY_CELL +{ + USHORT Id; // "sk" + USHORT Reserved; + HCELL_INDEX PrevSecurityCell; + HCELL_INDEX NextSecurityCell; + ULONG RefCount; + ULONG SdSize; + UCHAR Data[0]; +} SECURITY_CELL, *PSECURITY_CELL; + +#include + +#endif /* CMLIB_CMDATA_H */ diff --git a/reactos/lib/cmlib/cminit.c b/reactos/lib/cmlib/cminit.c new file mode 100644 index 00000000000..084de452f39 --- /dev/null +++ b/reactos/lib/cmlib/cminit.c @@ -0,0 +1,82 @@ +/* + * PROJECT: registry manipulation library + * LICENSE: GPL - See COPYING in the top level directory + * COPYRIGHT: Copyright 2005 Filip Navara + * Copyright 2001 - 2005 Eric Kohl + */ + +#include "cmlib.h" + +BOOLEAN CMAPI +CmCreateRootNode( + PREGISTRY_HIVE Hive, + PCWSTR Name) +{ + PKEY_CELL KeyCell; + HCELL_INDEX RootCellIndex; + ULONG NameSize; + + NameSize = wcslen(Name) * sizeof(WCHAR); + RootCellIndex = HvAllocateCell(Hive, sizeof(KEY_CELL) + NameSize, HvStable); + if (RootCellIndex == HCELL_NULL) + return FALSE; + + Hive->HiveHeader->RootCell = RootCellIndex; + Hive->HiveHeader->Checksum = HvpHiveHeaderChecksum(Hive->HiveHeader); + + KeyCell = (PKEY_CELL)HvGetCell(Hive, RootCellIndex); + KeyCell->Id = REG_KEY_CELL_ID; + KeyCell->Flags = REG_KEY_ROOT_CELL; + KeyCell->LastWriteTime.QuadPart = 0; + KeyCell->ParentKeyOffset = HCELL_NULL; + KeyCell->NumberOfSubKeys[0] = 0; + KeyCell->NumberOfSubKeys[1] = 0; + KeyCell->HashTableOffset[0] = HCELL_NULL; + KeyCell->HashTableOffset[1] = HCELL_NULL; + KeyCell->NumberOfValues = 0; + KeyCell->ValueListOffset = HCELL_NULL; + KeyCell->SecurityKeyOffset = HCELL_NULL; + KeyCell->ClassNameOffset = HCELL_NULL; + KeyCell->NameSize = NameSize; + KeyCell->ClassSize = 0; + memcpy(KeyCell->Name, Name, NameSize); + + return TRUE; +} + +static VOID CMAPI +CmpPrepareKey( + PREGISTRY_HIVE RegistryHive, + PKEY_CELL KeyCell) +{ + PKEY_CELL SubKeyCell; + PHASH_TABLE_CELL HashCell; + ULONG i; + + ASSERT(KeyCell->Id == REG_KEY_CELL_ID); + + KeyCell->HashTableOffset[HvVolatile] = HCELL_NULL; + KeyCell->NumberOfSubKeys[HvVolatile] = 0; + + /* Enumerate and add subkeys */ + if (KeyCell->NumberOfSubKeys[HvStable] > 0) + { + HashCell = HvGetCell(RegistryHive, KeyCell->HashTableOffset[HvStable]); + + for (i = 0; i < KeyCell->NumberOfSubKeys[HvStable]; i++) + { + SubKeyCell = HvGetCell(RegistryHive, HashCell->Table[i].KeyOffset); + CmpPrepareKey(RegistryHive, SubKeyCell); + } + } +} + +VOID CMAPI +CmPrepareHive( + PREGISTRY_HIVE RegistryHive) +{ + PKEY_CELL RootCell; + + RootCell = HvGetCell(RegistryHive, RegistryHive->HiveHeader->RootCell); + CmpPrepareKey(RegistryHive, RootCell); +} diff --git a/reactos/lib/cmlib/cmlib.h b/reactos/lib/cmlib/cmlib.h new file mode 100644 index 00000000000..1554d38c2c6 --- /dev/null +++ b/reactos/lib/cmlib/cmlib.h @@ -0,0 +1,175 @@ +/* + * PROJECT: registry manipulation library + * LICENSE: GPL - See COPYING in the top level directory + * COPYRIGHT: Copyright 2005 Filip Navara + * Copyright 2001 - 2005 Eric Kohl + */ + +#ifndef CMLIB_H +#define CMLIB_H + +#include +#include "hivedata.h" +#include "cmdata.h" + +#ifndef ROUND_UP +#define ROUND_UP(a,b) ((((a)+(b)-1)/(b))*(b)) +#define ROUND_DOWN(a,b) (((a)/(b))*(b)) +#endif + +#define CMAPI + +typedef struct _BLOCK_LIST_ENTRY +{ + PHBIN Bin; + PVOID Block; +} BLOCK_LIST_ENTRY, *PBLOCK_LIST_ENTRY; + +struct _REGISTRY_HIVE; + +typedef PVOID (CMAPI *PHV_ALLOCATE)( + ULONG Size, + BOOLEAN Paged); + +typedef VOID (CMAPI *PHV_FREE)( + PVOID Ptr); + +typedef BOOLEAN (CMAPI *PHV_FILE_READ)( + struct _REGISTRY_HIVE *RegistryHive, + ULONG FileType, + ULONG FileOffset, + PVOID Buffer, + ULONG BufferLength); + +typedef BOOLEAN (CMAPI *PHV_FILE_WRITE)( + struct _REGISTRY_HIVE *RegistryHive, + ULONG FileType, + ULONG FileOffset, + PVOID Buffer, + ULONG BufferLength); + +typedef BOOLEAN (CMAPI *PHV_FILE_SET_SIZE)( + struct _REGISTRY_HIVE *RegistryHive, + ULONG FileType, + ULONG FileSize); + +typedef BOOLEAN (CMAPI *PHV_FILE_FLUSH)( + struct _REGISTRY_HIVE *RegistryHive, + ULONG FileType); + +typedef struct _REGISTRY_HIVE +{ + PHIVE_HEADER HiveHeader; + BOOLEAN ReadOnly; + BOOLEAN Flat; + RTL_BITMAP DirtyBitmap; + struct + { + ULONG BlockListSize; + PBLOCK_LIST_ENTRY BlockList; + HCELL_INDEX FreeListOffset[24]; + } Storage[HvMaxStorageType]; + + PHV_ALLOCATE Allocate; + PHV_FREE Free; + PHV_FILE_READ FileRead; + PHV_FILE_WRITE FileWrite; + PHV_FILE_SET_SIZE FileSetSize; + PHV_FILE_FLUSH FileFlush; + PVOID Opaque; +} REGISTRY_HIVE, *PREGISTRY_HIVE; + +/* + * Public functions. + */ + +#define HV_OPERATION_CREATE_HIVE 1 +#define HV_OPERATION_MEMORY 2 +#define HV_OPERATION_MEMORY_INPLACE 3 + +NTSTATUS CMAPI +HvInitialize( + PREGISTRY_HIVE *RegistryHive, + ULONG Operation, + ULONG_PTR ChunkBase, + SIZE_T ChunkSize, + PHV_ALLOCATE Allocate, + PHV_FREE Free, + PHV_FILE_READ FileRead, + PHV_FILE_WRITE FileWrite, + PHV_FILE_SET_SIZE FileSetSize, + PHV_FILE_FLUSH FileFlush, + PVOID Opaque); + +VOID CMAPI +HvFree( + PREGISTRY_HIVE RegistryHive); + +PVOID CMAPI +HvGetCell( + PREGISTRY_HIVE RegistryHive, + HCELL_INDEX CellOffset); + +LONG CMAPI +HvGetCellSize( + PREGISTRY_HIVE RegistryHive, + PVOID Cell); + +HCELL_INDEX CMAPI +HvAllocateCell( + PREGISTRY_HIVE RegistryHive, + ULONG Size, + HV_STORAGE_TYPE Storage); + +HCELL_INDEX CMAPI +HvReallocateCell( + PREGISTRY_HIVE RegistryHive, + HCELL_INDEX CellOffset, + ULONG Size); + +VOID CMAPI +HvFreeCell( + PREGISTRY_HIVE RegistryHive, + HCELL_INDEX CellOffset); + +VOID CMAPI +HvMarkCellDirty( + PREGISTRY_HIVE RegistryHive, + HCELL_INDEX CellOffset); + +BOOLEAN CMAPI +HvSyncHive( + PREGISTRY_HIVE RegistryHive); + +BOOLEAN CMAPI +HvWriteHive( + PREGISTRY_HIVE RegistryHive); + +BOOLEAN CMAPI +CmCreateRootNode( + PREGISTRY_HIVE Hive, + PCWSTR Name); + +VOID CMAPI +CmPrepareHive( + PREGISTRY_HIVE RegistryHive); + +/* + * Private functions. + */ + +PHBIN CMAPI +HvpAddBin( + PREGISTRY_HIVE RegistryHive, + ULONG Size, + HV_STORAGE_TYPE Storage); + +NTSTATUS CMAPI +HvpCreateHiveFreeCellList( + PREGISTRY_HIVE Hive); + +ULONG CMAPI +HvpHiveHeaderChecksum( + PHIVE_HEADER HiveHeader); + +#endif /* CMLIB_H */ diff --git a/reactos/lib/cmlib/cmlib.mak b/reactos/lib/cmlib/cmlib.mak new file mode 100644 index 00000000000..afcba83d48f --- /dev/null +++ b/reactos/lib/cmlib/cmlib.mak @@ -0,0 +1,55 @@ +CMLIB_BASE = $(LIB_BASE_)cmlib +CMLIB_BASE_ = $(CMLIB_BASE)$(SEP) +CMLIB_INT = $(INTERMEDIATE_)$(CMLIB_BASE)_host +CMLIB_INT_ = $(INTERMEDIATE_)$(CMLIB_BASE)_host$(SEP) +CMLIB_OUT = $(OUTPUT_)$(CMLIB_BASE)_host +CMLIB_OUT_ = $(OUTPUT_)$(CMLIB_BASE)_host$(SEP) + +$(CMLIB_INT): | $(LIB_INT) + $(ECHO_MKDIR) + ${mkdir} $@ + +ifneq ($(INTERMEDIATE),$(OUTPUT)) +$(CMLIB_OUT): | $(OUTPUT_)$(LIB_BASE) + $(ECHO_MKDIR) + ${mkdir} $@ +endif + +CMLIB_HOST_TARGET = \ + $(CMLIB_OUT)$(SEP)cmlib.a + +CMLIB_HOST_SOURCES = $(addprefix $(CMLIB_BASE_), \ + hivebin.c \ + hivecell.c \ + hiveinit.c \ + ) + +CMLIB_HOST_OBJECTS = \ + $(subst $(CMLIB_BASE), $(CMLIB_INT), $(CMLIB_HOST_SOURCES:.c=.o)) + +CMLIB_HOST_CFLAGS = -O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ + -DCMLIB_HOST -D_M_IX86 -I$(CMLIB_BASE) -Iinclude/reactos -DDBG + +$(CMLIB_HOST_TARGET): $(CMLIB_HOST_OBJECTS) | $(CMLIB_OUT) + $(ECHO_AR) + $(host_ar) -r $@ $(CMLIB_HOST_OBJECTS) + +$(CMLIB_INT_)hivebin.o: $(CMLIB_BASE_)hivebin.c | $(CMLIB_INT) + $(ECHO_CC) + ${host_gcc} $(CMLIB_HOST_CFLAGS) -c $< -o $@ + +$(CMLIB_INT_)hivecell.o: $(CMLIB_BASE_)hivecell.c | $(CMLIB_INT) + $(ECHO_CC) + ${host_gcc} $(CMLIB_HOST_CFLAGS) -c $< -o $@ + +$(CMLIB_INT_)hiveinit.o: $(CMLIB_BASE_)hiveinit.c | $(CMLIB_INT) + $(ECHO_CC) + ${host_gcc} $(CMLIB_HOST_CFLAGS) -c $< -o $@ + +.PHONY: cmlib_host +cmlib_host: $(CMLIB_HOST_TARGET) + +.PHONY: cmlib_host_clean +cmlib_host_clean: + -@$(rm) $(CMLIB_HOST_TARGET) $(CMLIB_HOST_OBJECTS) 2>$(NUL) +clean: cmlib_host_clean diff --git a/reactos/lib/cmlib/cmlib.rbuild b/reactos/lib/cmlib/cmlib.rbuild new file mode 100644 index 00000000000..deb06d140be --- /dev/null +++ b/reactos/lib/cmlib/cmlib.rbuild @@ -0,0 +1,14 @@ + + . + + + + + cmlib.h + cminit.c + hivebin.c + hivecell.c + hiveinit.c + hivesum.c + hivewrt.c + diff --git a/reactos/lib/cmlib/hivebin.c b/reactos/lib/cmlib/hivebin.c new file mode 100644 index 00000000000..c9c6206df18 --- /dev/null +++ b/reactos/lib/cmlib/hivebin.c @@ -0,0 +1,98 @@ +/* + * PROJECT: registry manipulation library + * LICENSE: GPL - See COPYING in the top level directory + * COPYRIGHT: Copyright 2005 Filip Navara + * Copyright 2005 Hartmut Birr + * Copyright 2001 - 2005 Eric Kohl + */ + +#include "cmlib.h" + +PHBIN CMAPI +HvpAddBin( + PREGISTRY_HIVE RegistryHive, + ULONG Size, + HV_STORAGE_TYPE Storage) +{ + PBLOCK_LIST_ENTRY BlockList; + PHBIN Bin; + ULONG BinSize; + ULONG i; + ULONG BitmapSize; + ULONG BlockCount; + ULONG OldBlockListSize; + PCELL_HEADER Block; + + BinSize = ROUND_UP(Size + sizeof(HBIN), HV_BLOCK_SIZE); + BlockCount = BinSize / HV_BLOCK_SIZE; + + Bin = RegistryHive->Allocate(BinSize, TRUE); + if (Bin == NULL) + return NULL; + RtlZeroMemory(Bin, sizeof(HBIN)); + + Bin->Signature = HV_BIN_SIGNATURE; + Bin->BinOffset = RegistryHive->Storage[Storage].BlockListSize * + HV_BLOCK_SIZE; + Bin->BinSize = BinSize; + + /* Allocate new block list */ + OldBlockListSize = RegistryHive->Storage[Storage].BlockListSize; + BlockList = RegistryHive->Allocate(sizeof(BLOCK_LIST_ENTRY) * + (OldBlockListSize + BlockCount), TRUE); + if (BlockList == NULL) + { + RegistryHive->Free(Bin); + return NULL; + } + + if (OldBlockListSize > 0) + { + RtlCopyMemory(BlockList, RegistryHive->Storage[Storage].BlockList, + OldBlockListSize * sizeof(BLOCK_LIST_ENTRY)); + RegistryHive->Free(RegistryHive->Storage[Storage].BlockList); + } + + RegistryHive->Storage[Storage].BlockList = BlockList; + RegistryHive->Storage[Storage].BlockListSize += BlockCount; + + for (i = 0; i < BlockCount; i++) + { + RegistryHive->Storage[Storage].BlockList[OldBlockListSize + i].Block = + (PVOID)((ULONG_PTR)Bin + (i * HV_BLOCK_SIZE)); + RegistryHive->Storage[Storage].BlockList[OldBlockListSize + i].Bin = Bin; + } + + /* Initialize a free block in this heap. */ + Block = (PCELL_HEADER)(Bin + 1); + Block->CellSize = BinSize - sizeof(HBIN); + + if (Storage == HvStable) + { + /* Calculate bitmap size in bytes (always a multiple of 32 bits). */ + BitmapSize = ROUND_UP(RegistryHive->Storage[HvStable].BlockListSize, + sizeof(ULONG) * 8) / 8; + + /* Grow bitmap if necessary. */ + if (BitmapSize > RegistryHive->DirtyBitmap.SizeOfBitMap / 8) + { + PULONG BitmapBuffer; + + BitmapBuffer = RegistryHive->Allocate(BitmapSize, TRUE); + RtlZeroMemory(BitmapBuffer, BitmapSize); + RtlCopyMemory(BitmapBuffer, + RegistryHive->DirtyBitmap.Buffer, + RegistryHive->DirtyBitmap.SizeOfBitMap / 8); + RegistryHive->Free(RegistryHive->DirtyBitmap.Buffer); + RtlInitializeBitMap(&RegistryHive->DirtyBitmap, BitmapBuffer, + BitmapSize * 8); + } + + /* Mark new bin dirty. */ + RtlSetBits(&RegistryHive->DirtyBitmap, + Bin->BinOffset / HV_BLOCK_SIZE, + BlockCount); + } + + return Bin; +} diff --git a/reactos/lib/cmlib/hivecell.c b/reactos/lib/cmlib/hivecell.c new file mode 100644 index 00000000000..35dc705588d --- /dev/null +++ b/reactos/lib/cmlib/hivecell.c @@ -0,0 +1,429 @@ +/* + * PROJECT: registry manipulation library + * LICENSE: GPL - See COPYING in the top level directory + * COPYRIGHT: Copyright 2005 Filip Navara + * Copyright 2001 - 2005 Eric Kohl + */ + +#include "cmlib.h" +#define NDEBUG +#include + +static PCELL_HEADER __inline CMAPI +HvpGetCellHeader( + PREGISTRY_HIVE RegistryHive, + HCELL_INDEX CellIndex) +{ + PVOID Block; + + ASSERT(CellIndex != HCELL_NULL); + if (!RegistryHive->Flat) + { + ULONG CellType; + ULONG CellBlock; + ULONG CellOffset; + + CellType = (CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT; + CellBlock = (CellIndex & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT; + CellOffset = (CellIndex & HCELL_OFFSET_MASK) >> HCELL_OFFSET_SHIFT; + ASSERT(CellBlock < RegistryHive->Storage[CellType].BlockListSize); + Block = RegistryHive->Storage[CellType].BlockList[CellBlock].Block; + 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 + + CellIndex); + } +} + +PVOID CMAPI +HvGetCell( + PREGISTRY_HIVE RegistryHive, + HCELL_INDEX CellIndex) +{ + return (PVOID)(HvpGetCellHeader(RegistryHive, CellIndex) + 1); +} + +static LONG __inline CMAPI +HvpGetCellFullSize( + PREGISTRY_HIVE RegistryHive, + PVOID Cell) +{ + return ((PCELL_HEADER)Cell - 1)->CellSize; +} + +LONG CMAPI +HvGetCellSize( + PREGISTRY_HIVE RegistryHive, + PVOID Cell) +{ + PCELL_HEADER CellHeader; + + CellHeader = (PCELL_HEADER)Cell - 1; + if (CellHeader->CellSize < 0) + return CellHeader->CellSize + sizeof(CELL_HEADER); + else + return CellHeader->CellSize - sizeof(CELL_HEADER); +} + +VOID CMAPI +HvMarkCellDirty( + PREGISTRY_HIVE RegistryHive, + HCELL_INDEX CellIndex) +{ + LONG CellSize; + ULONG CellBlock; + ULONG CellLastBlock; + + ASSERT(RegistryHive->ReadOnly == FALSE); + + if ((CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT != HvStable) + return; + + 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->DirtyBitmap, + CellBlock, CellLastBlock - CellBlock); +} + +static ULONG __inline CMAPI +HvpComputeFreeListIndex( + ULONG Size) +{ + ULONG Index; + static CCHAR FindFirstSet[256] = { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; + + Index = (Size >> 3) - 1; + if (Index >= 16) + { + if (Index > 255) + Index = 23; + else + Index = FindFirstSet[Index] + 7; + } + + return Index; +} + +static NTSTATUS CMAPI +HvpAddFree( + PREGISTRY_HIVE RegistryHive, + PCELL_HEADER FreeBlock, + HCELL_INDEX FreeIndex) +{ + PHCELL_INDEX FreeBlockData; + HV_STORAGE_TYPE Storage; + ULONG Index; + + ASSERT(RegistryHive != NULL); + ASSERT(FreeBlock != NULL); + + Storage = (FreeIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT; + Index = HvpComputeFreeListIndex(FreeBlock->CellSize); + + FreeBlockData = (PHCELL_INDEX)(FreeBlock + 1); + *FreeBlockData = RegistryHive->Storage[Storage].FreeListOffset[Index]; + RegistryHive->Storage[Storage].FreeListOffset[Index] = FreeIndex; + + /* FIXME: Eventually get rid of free bins. */ + + return STATUS_SUCCESS; +} + +static VOID CMAPI +HvpRemoveFree( + PREGISTRY_HIVE RegistryHive, + PCELL_HEADER CellBlock, + HCELL_INDEX CellIndex) +{ + PHCELL_INDEX FreeCellData; + PHCELL_INDEX pFreeCellOffset; + HV_STORAGE_TYPE Storage; + ULONG Index; + + ASSERT(RegistryHive->ReadOnly == FALSE); + + Storage = (CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT; + Index = HvpComputeFreeListIndex(CellBlock->CellSize); + + pFreeCellOffset = &RegistryHive->Storage[Storage].FreeListOffset[Index]; + while (*pFreeCellOffset != HCELL_NULL) + { + FreeCellData = (PHCELL_INDEX)HvGetCell(RegistryHive, *pFreeCellOffset); + if (*pFreeCellOffset == CellIndex) + { + *pFreeCellOffset = *FreeCellData; + return; + } + pFreeCellOffset = FreeCellData; + } + + ASSERT(FALSE); +} + +static HCELL_INDEX CMAPI +HvpFindFree( + PREGISTRY_HIVE RegistryHive, + ULONG Size, + HV_STORAGE_TYPE Storage) +{ + PHCELL_INDEX FreeCellData; + HCELL_INDEX FreeCellOffset; + PHCELL_INDEX pFreeCellOffset; + ULONG Index; + + for (Index = HvpComputeFreeListIndex(Size); Index < 24; Index++) + { + pFreeCellOffset = &RegistryHive->Storage[Storage].FreeListOffset[Index]; + while (*pFreeCellOffset != HCELL_NULL) + { + FreeCellData = (PHCELL_INDEX)HvGetCell(RegistryHive, *pFreeCellOffset); + if (HvpGetCellFullSize(RegistryHive, FreeCellData) >= Size) + { + FreeCellOffset = *pFreeCellOffset; + *pFreeCellOffset = *FreeCellData; + return FreeCellOffset; + } + pFreeCellOffset = FreeCellData; + } + } + + return HCELL_NULL; +} + +NTSTATUS CMAPI +HvpCreateHiveFreeCellList( + PREGISTRY_HIVE Hive) +{ + HCELL_INDEX BlockOffset; + PCELL_HEADER FreeBlock; + ULONG BlockIndex; + ULONG FreeOffset; + PHBIN Bin; + NTSTATUS Status; + ULONG Index; + + /* Initialize the free cell list */ + for (Index = 0; Index < 24; Index++) + { + Hive->Storage[HvStable].FreeListOffset[Index] = HCELL_NULL; + Hive->Storage[HvVolatile].FreeListOffset[Index] = HCELL_NULL; + } + + BlockOffset = 0; + BlockIndex = 0; + while (BlockIndex < Hive->Storage[HvStable].BlockListSize) + { + Bin = Hive->Storage[HvStable].BlockList[BlockIndex].Bin; + + /* Search free blocks and add to list */ + FreeOffset = sizeof(HBIN); + while (FreeOffset < Bin->BinSize) + { + FreeBlock = (PCELL_HEADER)((ULONG_PTR)Bin + FreeOffset); + if (FreeBlock->CellSize > 0) + { + Status = HvpAddFree(Hive, FreeBlock, Bin->BinOffset + FreeOffset); + if (!NT_SUCCESS(Status)) + return Status; + + FreeOffset += FreeBlock->CellSize; + } + else + { + FreeOffset -= FreeBlock->CellSize; + } + } + + BlockIndex += Bin->BinSize / HV_BLOCK_SIZE; + BlockOffset += Bin->BinSize; + } + + return STATUS_SUCCESS; +} + +HCELL_INDEX CMAPI +HvAllocateCell( + PREGISTRY_HIVE RegistryHive, + ULONG Size, + HV_STORAGE_TYPE Storage) +{ + PCELL_HEADER FreeCell; + HCELL_INDEX FreeCellOffset; + PCELL_HEADER NewCell; + PHBIN Bin; + + ASSERT(RegistryHive->ReadOnly == FALSE); + + /* Round to 16 bytes multiple. */ + Size = ROUND_UP(Size + sizeof(CELL_HEADER), 16); + + /* First search in free blocks. */ + FreeCellOffset = HvpFindFree(RegistryHive, Size, Storage); + + /* If no free cell was found we need to extend the hive file. */ + if (FreeCellOffset == HCELL_NULL) + { + Bin = HvpAddBin(RegistryHive, Size, Storage); + if (Bin == NULL) + return HCELL_NULL; + FreeCellOffset = Bin->BinOffset + sizeof(HBIN); + FreeCellOffset |= Storage << HCELL_TYPE_SHIFT; + } + + FreeCell = HvpGetCellHeader(RegistryHive, FreeCellOffset); + + /* Split the block in two parts */ + /* FIXME: There is some minimal cell size that we must respect. */ + if (FreeCell->CellSize > Size + sizeof(HCELL_INDEX)) + { + NewCell = (PCELL_HEADER)((ULONG_PTR)FreeCell + Size); + NewCell->CellSize = FreeCell->CellSize - Size; + FreeCell->CellSize = Size; + HvpAddFree(RegistryHive, NewCell, FreeCellOffset + Size); + if (Storage == HvStable) + HvMarkCellDirty(RegistryHive, FreeCellOffset + Size); + } + + if (Storage == HvStable) + HvMarkCellDirty(RegistryHive, FreeCellOffset); + FreeCell->CellSize = -FreeCell->CellSize; + RtlZeroMemory(FreeCell + 1, Size - sizeof(CELL_HEADER)); + + return FreeCellOffset; +} + +HCELL_INDEX CMAPI +HvReallocateCell( + PREGISTRY_HIVE RegistryHive, + HCELL_INDEX CellIndex, + ULONG Size) +{ + PVOID OldCell; + PVOID NewCell; + LONG OldCellSize; + HCELL_INDEX NewCellIndex; + HV_STORAGE_TYPE Storage; + + ASSERT(CellIndex != HCELL_NULL); + + Storage = (CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT; + + OldCell = HvGetCell(RegistryHive, CellIndex); + OldCellSize = HvGetCellSize(RegistryHive, OldCell); + ASSERT(OldCellSize < 0); + + /* + * If new data size is larger than the current, destroy current + * data block and allocate a new one. + * + * FIXME: Merge with adjacent free cell if possible. + * FIXME: Implement shrinking. + */ + if (Size > -OldCellSize) + { + NewCellIndex = HvAllocateCell(RegistryHive, Size, Storage); + if (NewCellIndex == HCELL_NULL) + return HCELL_NULL; + + NewCell = HvGetCell(RegistryHive, NewCellIndex); + RtlCopyMemory(NewCell, OldCell, -OldCellSize); + + HvFreeCell(RegistryHive, CellIndex); + + return NewCellIndex; + } + + return CellIndex; +} + +VOID CMAPI +HvFreeCell( + PREGISTRY_HIVE RegistryHive, + HCELL_INDEX CellIndex) +{ + PCELL_HEADER Free; + PCELL_HEADER Neighbor; + PHBIN Bin; + ULONG CellType; + ULONG CellBlock; + + ASSERT(RegistryHive->ReadOnly == FALSE); + + Free = HvpGetCellHeader(RegistryHive, CellIndex); + + ASSERT(Free->CellSize < 0); + + Free->CellSize = -Free->CellSize; + + CellType = (CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT; + CellBlock = (CellIndex & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT; + + /* FIXME: Merge free blocks */ + Bin = RegistryHive->Storage[CellType].BlockList[CellBlock].Bin; + + if ((CellIndex & ~HCELL_TYPE_MASK) + Free->CellSize < + Bin->BinOffset + Bin->BinSize) + { + Neighbor = (PCELL_HEADER)((ULONG_PTR)Free + Free->CellSize); + if (Neighbor->CellSize > 0) + { + HvpRemoveFree(RegistryHive, Neighbor, + ((HCELL_INDEX)Neighbor - (HCELL_INDEX)Bin + + Bin->BinOffset) | (CellIndex & HCELL_TYPE_MASK)); + Free->CellSize += Neighbor->CellSize; + } + } + + Neighbor = (PCELL_HEADER)(Bin + 1); + while (Neighbor < Free) + { + if (Neighbor->CellSize > 0) + { + if ((ULONG_PTR)Neighbor + Neighbor->CellSize == (ULONG_PTR)Free) + { + Neighbor->CellSize += Free->CellSize; + if (CellType == HvStable) + HvMarkCellDirty(RegistryHive, + (HCELL_INDEX)Neighbor - (HCELL_INDEX)Bin + + Bin->BinOffset); + return; + } + Neighbor = (PCELL_HEADER)((ULONG_PTR)Neighbor + Neighbor->CellSize); + } + else + { + Neighbor = (PCELL_HEADER)((ULONG_PTR)Neighbor - Neighbor->CellSize); + } + } + + /* Add block to the list of free blocks */ + HvpAddFree(RegistryHive, Free, CellIndex); + + if (CellType == HvStable) + HvMarkCellDirty(RegistryHive, CellIndex); +} diff --git a/reactos/lib/cmlib/hivedata.h b/reactos/lib/cmlib/hivedata.h new file mode 100644 index 00000000000..f73ad34c129 --- /dev/null +++ b/reactos/lib/cmlib/hivedata.h @@ -0,0 +1,138 @@ +/* + * PROJECT: registry manipulation library + * LICENSE: GPL - See COPYING in the top level directory + * COPYRIGHT: Copyright 2005 Filip Navara + * Copyright 2001 - 2005 Eric Kohl + */ + +#ifndef CMLIB_HIVEDATA_H +#define CMLIB_HIVEDATA_H + +#define HV_BLOCK_SIZE 4096 +#define HV_LOG_HEADER_SIZE FIELD_OFFSET(HIVE_HEADER, Reserved2) +#define HV_SIGNATURE 0x66676572 +#define HV_BIN_SIGNATURE 0x6e696268 + +#define HV_MAJOR_VER 1 +#define HV_MINOR_VER 3 +#define HV_FORMAT_MEMORY 1 + +#define HV_TYPE_PRIMARY 0 +#define HV_TYPE_ALTERNATE 1 +#define HV_TYPE_LOG 2 +#define HV_TYPE_EXTERNAL 3 +#define HV_TYPE_MAX 4 + +/** + * @name HCELL_INDEX + * + * A handle to cell index. The highest bit specifies the cell storage and + * the other bits specify index into the hive file. The value HCELL_NULL + * (-1) is reserved for marking invalid cells. + */ + +typedef ULONG HCELL_INDEX, *PHCELL_INDEX; + +#define HCELL_NULL ((HCELL_INDEX)-1) +#define HCELL_TYPE_MASK 0x80000000 +#define HCELL_BLOCK_MASK 0x7ffff000 +#define HCELL_OFFSET_MASK 0x00000fff +#define HCELL_TYPE_SHIFT 31 +#define HCELL_BLOCK_SHIFT 12 +#define HCELL_OFFSET_SHIFT 0 + +#include + +/** + * @name HIVE_HEADER + * + * On-disk header for registry hive file. + */ + +typedef struct _HIVE_HEADER +{ + /* Hive identifier "regf" (0x66676572) */ + ULONG Signature; + + /* Update counter */ + ULONG Sequence1; + + /* Update counter */ + ULONG Sequence2; + + /* When this hive file was last modified */ + LARGE_INTEGER TimeStamp; + + /* Registry format major version (1) */ + ULONG Major; + + /* Registry format minor version (3) + Version 3 added fast indexes, version 5 has large value optimizations */ + ULONG Minor; + + /* Registry file type (0 - Primary, 1 - Log) */ + ULONG Type; + + /* Registry format (1 is the only defined value so far) */ + ULONG Format; + + /* Offset into file from the byte after the end of the base block. + If the hive is volatile, this is the actual pointer to the KEY_CELL */ + HCELL_INDEX RootCell; + + /* Size of each hive block ? */ + ULONG Length; + + /* (1?) */ + ULONG Cluster; + + /* Name of hive file */ + WCHAR FileName[32]; + + ULONG Reserved1[99]; + + /* Checksum of first 0x200 bytes */ + ULONG Checksum; + + ULONG Reserved2[0x380]; +} HIVE_HEADER, *PHIVE_HEADER; + +typedef struct _BIN_HEADER +{ + /* Bin identifier "hbin" (0x6E696268) */ + ULONG Signature; + + /* Block offset of this bin */ + HCELL_INDEX BinOffset; + + /* Size in bytes, multiple of the block size (4KB) */ + ULONG BinSize; + + ULONG Reserved[2]; + + /* When this bin was last modified */ + LARGE_INTEGER DateModified; + + /* ? (In-memory only) */ + ULONG MemAlloc; +} HBIN, *PHBIN; + +typedef struct _CELL_HEADER +{ + /* <0 if used, >0 if free */ + LONG CellSize; +} CELL_HEADER, *PCELL_HEADER; + +#include + +#define IsFreeCell(Cell)(Cell->CellSize >= 0) +#define IsUsedCell(Cell)(Cell->CellSize < 0) + +typedef enum _HV_STORAGE_TYPE +{ + HvStable = 0, + HvVolatile, + HvMaxStorageType +} HV_STORAGE_TYPE; + +#endif /* CMLIB_HIVEDATA_H */ diff --git a/reactos/lib/cmlib/hiveinit.c b/reactos/lib/cmlib/hiveinit.c new file mode 100644 index 00000000000..df9a8247e72 --- /dev/null +++ b/reactos/lib/cmlib/hiveinit.c @@ -0,0 +1,377 @@ +/* + * PROJECT: registry manipulation library + * LICENSE: GPL - See COPYING in the top level directory + * COPYRIGHT: Copyright 2005 Filip Navara + * Copyright 2001 - 2005 Eric Kohl + */ + +#include "cmlib.h" +#define NDEBUG +#include + +/** + * @name HvpVerifyHiveHeader + * + * Internal function to verify that a hive header has valid format. + */ + +BOOLEAN CMAPI +HvpVerifyHiveHeader( + PHIVE_HEADER HiveHeader) +{ + if (HiveHeader->Signature != HV_SIGNATURE || + HiveHeader->Major != HV_MAJOR_VER || + HiveHeader->Minor > HV_MINOR_VER || + HiveHeader->Type != HV_TYPE_PRIMARY || + HiveHeader->Format != HV_FORMAT_MEMORY || + HiveHeader->Cluster != 1 || + HiveHeader->Sequence1 != HiveHeader->Sequence2 || + HvpHiveHeaderChecksum(HiveHeader) != HiveHeader->Checksum) + { + return FALSE; + } + + return TRUE; +} + +/** + * @name HvpFreeHiveBins + * + * Internal function to free all bin storage associated with hive + * descriptor. + */ + +VOID CMAPI +HvpFreeHiveBins( + PREGISTRY_HIVE Hive) +{ + ULONG i; + PHBIN Bin; + ULONG Storage; + + for (Storage = HvStable; Storage < HvMaxStorageType; Storage++) + { + Bin = NULL; + for (i = 0; i < Hive->Storage[Storage].BlockListSize; i++) + { + if (Hive->Storage[Storage].BlockList[i].Bin == NULL) + continue; + if (Hive->Storage[Storage].BlockList[i].Bin != Bin) + { + Bin = Hive->Storage[Storage].BlockList[i].Bin; + Hive->Free(Hive->Storage[Storage].BlockList[i].Bin); + } + Hive->Storage[Storage].BlockList[i].Bin = NULL; + Hive->Storage[Storage].BlockList[i].Block = NULL; + } + + if (Hive->Storage[Storage].BlockListSize) + Hive->Free(Hive->Storage[Storage].BlockList); + } +} + +/** + * @name HvpCreateHive + * + * Internal helper function to initalize hive descriptor structure for + * newly created hive. + * + * @see HvInitialize + */ + +NTSTATUS CMAPI +HvpCreateHive( + PREGISTRY_HIVE RegistryHive) +{ + PHIVE_HEADER HiveHeader; + ULONG Index; + + HiveHeader = RegistryHive->Allocate(sizeof(HIVE_HEADER), FALSE); + if (HiveHeader == NULL) + return STATUS_NO_MEMORY; + RtlZeroMemory(HiveHeader, sizeof(HIVE_HEADER)); + HiveHeader->Signature = HV_SIGNATURE; + HiveHeader->Major = HV_MAJOR_VER; + HiveHeader->Minor = HV_MINOR_VER; + HiveHeader->Type = HV_TYPE_PRIMARY; + HiveHeader->Format = HV_FORMAT_MEMORY; + HiveHeader->Cluster = 1; + HiveHeader->RootCell = HCELL_NULL; + HiveHeader->Length = HV_BLOCK_SIZE; + HiveHeader->Sequence1 = 1; + HiveHeader->Sequence2 = 1; + /* FIXME: Fill in the file name */ + HiveHeader->Checksum = HvpHiveHeaderChecksum(HiveHeader); + + RegistryHive->HiveHeader = HiveHeader; + for (Index = 0; Index < 24; Index++) + { + RegistryHive->Storage[HvStable].FreeListOffset[Index] = HCELL_NULL; + RegistryHive->Storage[HvVolatile].FreeListOffset[Index] = HCELL_NULL; + } + RtlInitializeBitMap(&RegistryHive->DirtyBitmap, NULL, 0); + + return STATUS_SUCCESS; +} + +/** + * @name HvpInitializeMemoryHive + * + * Internal helper function to initalize hive descriptor structure for + * a hive stored in memory. The data of the hive are copied and it is + * prepared for read/write access. + * + * @see HvInitialize + */ + +NTSTATUS CMAPI +HvpInitializeMemoryHive( + PREGISTRY_HIVE Hive, + ULONG_PTR ChunkBase, + SIZE_T ChunkSize) +{ + SIZE_T BlockIndex; + PHBIN Bin, NewBin; + ULONG i; + ULONG BitmapSize; + PULONG BitmapBuffer; + + if (ChunkSize < sizeof(HIVE_HEADER) || + !HvpVerifyHiveHeader((PHIVE_HEADER)ChunkBase)) + { + return STATUS_REGISTRY_CORRUPT; + } + + Hive->HiveHeader = Hive->Allocate(sizeof(HIVE_HEADER), FALSE); + if (Hive->HiveHeader == NULL) + { + return STATUS_NO_MEMORY; + } + RtlCopyMemory(Hive->HiveHeader, (PVOID)ChunkBase, sizeof(HIVE_HEADER)); + + /* + * Build a block list from the in-memory chunk and copy the data as + * we go. + */ + + Hive->Storage[HvStable].BlockListSize = (ChunkSize / HV_BLOCK_SIZE) - 1; + Hive->Storage[HvStable].BlockList = + Hive->Allocate(Hive->Storage[HvStable].BlockListSize * + sizeof(BLOCK_LIST_ENTRY), FALSE); + if (Hive->Storage[HvStable].BlockList == NULL) + { + DPRINT1("Allocating block list failed\n"); + Hive->Free(Hive->HiveHeader); + return STATUS_NO_MEMORY; + } + + for (BlockIndex = 0; BlockIndex < Hive->Storage[HvStable].BlockListSize; ) + { + Bin = (PHBIN)((ULONG_PTR)ChunkBase + (BlockIndex + 1) * HV_BLOCK_SIZE); + if (Bin->Signature != HV_BIN_SIGNATURE || + (Bin->BinSize % HV_BLOCK_SIZE) != 0) + { + Hive->Free(Hive->HiveHeader); + Hive->Free(Hive->Storage[HvStable].BlockList); + return STATUS_REGISTRY_CORRUPT; + } + + NewBin = Hive->Allocate(Bin->BinSize, TRUE); + if (NewBin == NULL) + { + Hive->Free(Hive->HiveHeader); + Hive->Free(Hive->Storage[HvStable].BlockList); + return STATUS_NO_MEMORY; + } + + Hive->Storage[HvStable].BlockList[BlockIndex].Bin = NewBin; + Hive->Storage[HvStable].BlockList[BlockIndex].Block = NewBin; + + RtlCopyMemory(NewBin, Bin, Bin->BinSize); + + if (Bin->BinSize > HV_BLOCK_SIZE) + { + for (i = 1; i < Bin->BinSize / HV_BLOCK_SIZE; i++) + { + Hive->Storage[HvStable].BlockList[BlockIndex + i].Bin = NewBin; + Hive->Storage[HvStable].BlockList[BlockIndex + i].Block = + (PVOID)((ULONG_PTR)NewBin + (i * HV_BLOCK_SIZE)); + } + } + + BlockIndex += Bin->BinSize / HV_BLOCK_SIZE; + } + + if (HvpCreateHiveFreeCellList(Hive)) + { + HvpFreeHiveBins(Hive); + Hive->Free(Hive->HiveHeader); + return STATUS_NO_MEMORY; + } + + BitmapSize = ROUND_UP(Hive->Storage[HvStable].BlockListSize, + sizeof(ULONG) * 8) / 8; + BitmapBuffer = (PULONG)Hive->Allocate(BitmapSize, TRUE); + if (BitmapBuffer == NULL) + { + HvpFreeHiveBins(Hive); + Hive->Free(Hive->HiveHeader); + return STATUS_NO_MEMORY; + } + + RtlInitializeBitMap(&Hive->DirtyBitmap, BitmapBuffer, BitmapSize * 8); + RtlClearAllBits(&Hive->DirtyBitmap); + + return STATUS_SUCCESS; +} + +/** + * @name HvpInitializeMemoryInplaceHive + * + * Internal helper function to initalize hive descriptor structure for + * a hive stored in memory. The in-memory data of the hive are directly + * used and it is read-only accessible. + * + * @see HvInitialize + */ + +NTSTATUS CMAPI +HvpInitializeMemoryInplaceHive( + PREGISTRY_HIVE Hive, + ULONG_PTR ChunkBase, + SIZE_T ChunkSize) +{ + if (ChunkSize < sizeof(HIVE_HEADER) || + !HvpVerifyHiveHeader((PHIVE_HEADER)ChunkBase)) + { + return STATUS_REGISTRY_CORRUPT; + } + + Hive->HiveHeader = (PHIVE_HEADER)ChunkBase; + Hive->ReadOnly = TRUE; + Hive->Flat = TRUE; + + return STATUS_SUCCESS; +} + +/** + * @name HvInitialize + * + * Allocate a new hive descriptor structure and intialize it. + * + * @param RegistryHive + * Output variable to store pointer to the hive descriptor. + * @param Operation + * - HV_OPERATION_CREATE_HIVE + * Create a new hive for read/write access. + * - HV_OPERATION_MEMORY + * Load and copy in-memory hive for read/write access. The + * pointer to data passed to this routine can be freed after + * the function is executed. + * - HV_OPERATION_MEMORY_INPLACE + * Load an in-memory hive for read-only access. The pointer + * to data passed to this routine MUSTN'T be freed until + * HvFree is called. + * @param ChunkBase + * Pointer to hive data. + * @param ChunkSize + * Size of passed hive data. + * + * @return + * STATUS_NO_MEMORY - A memory allocation failed. + * STATUS_REGISTRY_CORRUPT - Registry corruption was detected. + * STATUS_SUCCESS + * + * @see HvFree + */ + +NTSTATUS CMAPI +HvInitialize( + PREGISTRY_HIVE *RegistryHive, + ULONG Operation, + ULONG_PTR ChunkBase, + SIZE_T ChunkSize, + PHV_ALLOCATE Allocate, + PHV_FREE Free, + PHV_FILE_READ FileRead, + PHV_FILE_WRITE FileWrite, + PHV_FILE_SET_SIZE FileSetSize, + PHV_FILE_FLUSH FileFlush, + PVOID Opaque) +{ + NTSTATUS Status; + PREGISTRY_HIVE Hive; + + /* + * Create a new hive structure that will hold all the maintenance data. + */ + + Hive = Allocate(sizeof(REGISTRY_HIVE), TRUE); + if (Hive == NULL) + return STATUS_NO_MEMORY; + RtlZeroMemory(Hive, sizeof(REGISTRY_HIVE)); + + Hive->Allocate = Allocate; + Hive->Free = Free; + Hive->FileRead = FileRead; + Hive->FileWrite = FileWrite; + Hive->FileSetSize = FileSetSize; + Hive->FileFlush = FileFlush; + Hive->Opaque = Opaque; + + switch (Operation) + { + case HV_OPERATION_CREATE_HIVE: + Status = HvpCreateHive(Hive); + break; + + case HV_OPERATION_MEMORY: + Status = HvpInitializeMemoryHive(Hive, ChunkBase, ChunkSize); + break; + + case HV_OPERATION_MEMORY_INPLACE: + Status = HvpInitializeMemoryInplaceHive(Hive, ChunkBase, ChunkSize); + break; + + default: + /* FIXME: A better return status value is needed */ + Status = STATUS_NOT_IMPLEMENTED; + ASSERT(FALSE); + } + + if (!NT_SUCCESS(Status)) + { + Hive->Free(Hive); + return Status; + } + + *RegistryHive = Hive; + + return Status; +} + +/** + * @name HvFree + * + * Free all stroage and handles associated with hive descriptor. + */ + +VOID CMAPI +HvFree( + PREGISTRY_HIVE RegistryHive) +{ + if (!RegistryHive->ReadOnly) + { + /* Release hive bitmap */ + if (RegistryHive->DirtyBitmap.Buffer) + { + RegistryHive->Free(RegistryHive->DirtyBitmap.Buffer); + } + + HvpFreeHiveBins(RegistryHive); + } + + RegistryHive->Free(RegistryHive); +} + +/* EOF */ diff --git a/reactos/lib/cmlib/hivesum.c b/reactos/lib/cmlib/hivesum.c new file mode 100644 index 00000000000..58e0bcf9c0a --- /dev/null +++ b/reactos/lib/cmlib/hivesum.c @@ -0,0 +1,32 @@ +/* + * PROJECT: registry manipulation library + * LICENSE: GPL - See COPYING in the top level directory + * COPYRIGHT: Copyright 2005 Filip Navara + * Copyright 2001 - 2005 Eric Kohl + */ + +#include "cmlib.h" + +/** + * @name HvpHiveHeaderChecksum + * + * Compute checksum of hive header and return it. + */ + +ULONG CMAPI +HvpHiveHeaderChecksum( + PHIVE_HEADER HiveHeader) +{ + PULONG Buffer = (PULONG)HiveHeader; + ULONG Sum = 0; + ULONG i; + + for (i = 0; i < 127; i++) + Sum ^= Buffer[i]; + if (Sum == (ULONG)-1) + Sum = (ULONG)-2; + if (Sum == 0) + Sum = 1; + + return Sum; +} diff --git a/reactos/lib/cmlib/hivewrt.c b/reactos/lib/cmlib/hivewrt.c new file mode 100644 index 00000000000..8d3d232ee26 --- /dev/null +++ b/reactos/lib/cmlib/hivewrt.c @@ -0,0 +1,277 @@ +/* + * PROJECT: registry manipulation library + * LICENSE: GPL - See COPYING in the top level directory + * COPYRIGHT: Copyright 2005 Filip Navara + * Copyright 2001 - 2005 Eric Kohl + */ + +#include "cmlib.h" +#define NDEBUG +#include + +static BOOLEAN CMAPI +HvpWriteLog( + PREGISTRY_HIVE RegistryHive) +{ + ULONG FileOffset; + ULONG BufferSize; + ULONG BitmapSize; + PUCHAR Buffer; + PUCHAR Ptr; + ULONG BlockIndex; + ULONG LastIndex; + PVOID BlockPtr; + BOOLEAN Success; + + ASSERT(RegistryHive->ReadOnly == FALSE); + + DPRINT("HvpWriteLog called\n"); + + if (RegistryHive->HiveHeader->Sequence1 != + RegistryHive->HiveHeader->Sequence2) + { + return FALSE; + } + + BitmapSize = RegistryHive->DirtyBitmap.SizeOfBitMap; + BufferSize = HV_LOG_HEADER_SIZE + sizeof(ULONG) + BitmapSize; + BufferSize = ROUND_UP(BufferSize, HV_BLOCK_SIZE); + + DPRINT("Bitmap size %lu buffer size: %lu\n", BitmapSize, BufferSize); + + Buffer = RegistryHive->Allocate(BufferSize, TRUE); + if (Buffer == NULL) + { + return FALSE; + } + + /* Update first update counter and checksum */ + RegistryHive->HiveHeader->Type = HV_TYPE_LOG; + RegistryHive->HiveHeader->Sequence1++; + RegistryHive->HiveHeader->Checksum = + HvpHiveHeaderChecksum(RegistryHive->HiveHeader); + + /* Copy hive header */ + RtlCopyMemory(Buffer, RegistryHive->HiveHeader, HV_LOG_HEADER_SIZE); + Ptr = Buffer + HV_LOG_HEADER_SIZE; + RtlCopyMemory(Ptr, "DIRT", 4); + Ptr += 4; + RtlCopyMemory(Ptr, RegistryHive->DirtyBitmap.Buffer, BitmapSize); + + /* Write hive block and block bitmap */ + Success = RegistryHive->FileWrite(RegistryHive, HV_TYPE_LOG, + 0, Buffer, BufferSize); + if (!Success) + { + return FALSE; + } + + RegistryHive->Free(Buffer); + + /* Write dirty blocks */ + FileOffset = BufferSize; + BlockIndex = 0; + while (BlockIndex < RegistryHive->Storage[HvStable].BlockListSize) + { + LastIndex = BlockIndex; + BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitmap, 1, BlockIndex); + if (BlockIndex == ~0 || BlockIndex < LastIndex) + { + break; + } + + BlockPtr = RegistryHive->Storage[HvStable].BlockList[BlockIndex].Block; + + /* Write hive block */ + Success = RegistryHive->FileWrite(RegistryHive, HV_TYPE_LOG, + FileOffset, BlockPtr, + HV_BLOCK_SIZE); + if (!Success) + { + return FALSE; + } + + BlockIndex++; + FileOffset += HV_BLOCK_SIZE; + } + + Success = RegistryHive->FileSetSize(RegistryHive, HV_TYPE_LOG, FileOffset); + if (!Success) + { + DPRINT("FileSetSize failed\n"); + return FALSE; + } + + /* Flush the log file */ + Success = RegistryHive->FileFlush(RegistryHive, HV_TYPE_LOG); + if (!Success) + { + DPRINT("FileFlush failed\n"); + } + + /* Update first and second update counter and checksum. */ + RegistryHive->HiveHeader->Sequence2++; + RegistryHive->HiveHeader->Checksum = + HvpHiveHeaderChecksum(RegistryHive->HiveHeader); + + /* Write hive header again with updated sequence counter. */ + Success = RegistryHive->FileWrite(RegistryHive, HV_TYPE_LOG, + 0, RegistryHive->HiveHeader, + HV_LOG_HEADER_SIZE); + if (!Success) + { + return FALSE; + } + + /* Flush the log file */ + Success = RegistryHive->FileFlush(RegistryHive, HV_TYPE_LOG); + if (!Success) + { + DPRINT("FileFlush failed\n"); + } + + return TRUE; +} + +static BOOLEAN CMAPI +HvpWriteHive( + PREGISTRY_HIVE RegistryHive, + BOOLEAN OnlyDirty) +{ + ULONG FileOffset; + ULONG BlockIndex; + ULONG LastIndex; + PVOID BlockPtr; + BOOLEAN Success; + + ASSERT(RegistryHive->ReadOnly == FALSE); + + DPRINT("HvpWriteHive called\n"); + + if (RegistryHive->HiveHeader->Sequence1 != + RegistryHive->HiveHeader->Sequence2) + { + return FALSE; + } + + /* Update first update counter and checksum */ + RegistryHive->HiveHeader->Type = HV_TYPE_PRIMARY; + RegistryHive->HiveHeader->Sequence1++; + RegistryHive->HiveHeader->Checksum = + HvpHiveHeaderChecksum(RegistryHive->HiveHeader); + + /* Write hive block */ + Success = RegistryHive->FileWrite(RegistryHive, HV_TYPE_PRIMARY, + 0, RegistryHive->HiveHeader, + sizeof(HIVE_HEADER)); + if (!Success) + { + return FALSE; + } + + BlockIndex = 0; + while (BlockIndex < RegistryHive->Storage[HvStable].BlockListSize) + { + if (OnlyDirty) + { + LastIndex = BlockIndex; + BlockIndex = RtlFindSetBits(&RegistryHive->DirtyBitmap, 1, BlockIndex); + if (BlockIndex == ~0 || BlockIndex < LastIndex) + { + break; + } + } + + BlockPtr = RegistryHive->Storage[HvStable].BlockList[BlockIndex].Block; + FileOffset = (ULONGLONG)(BlockIndex + 1) * (ULONGLONG)HV_BLOCK_SIZE; + + /* Write hive block */ + Success = RegistryHive->FileWrite(RegistryHive, HV_TYPE_PRIMARY, + FileOffset, BlockPtr, + HV_BLOCK_SIZE); + if (!Success) + { + return FALSE; + } + + BlockIndex++; + } + + Success = RegistryHive->FileFlush(RegistryHive, HV_TYPE_PRIMARY); + if (!Success) + { + DPRINT("FileFlush failed\n"); + } + + /* Update second update counter and checksum */ + RegistryHive->HiveHeader->Sequence2++; + RegistryHive->HiveHeader->Checksum = + HvpHiveHeaderChecksum(RegistryHive->HiveHeader); + + /* Write hive block */ + Success = RegistryHive->FileWrite(RegistryHive, HV_TYPE_PRIMARY, + 0, RegistryHive->HiveHeader, + sizeof(HIVE_HEADER)); + if (!Success) + { + return FALSE; + } + + Success = RegistryHive->FileFlush(RegistryHive, HV_TYPE_PRIMARY); + if (!Success) + { + DPRINT("FileFlush failed\n"); + } + + return TRUE; +} + +BOOLEAN CMAPI +HvSyncHive( + PREGISTRY_HIVE RegistryHive) +{ + ASSERT(RegistryHive->ReadOnly == FALSE); + + if (RtlFindSetBits(&RegistryHive->DirtyBitmap, 1, 0) == ~0) + { + return TRUE; + } + + /* Update hive header modification time */ + KeQuerySystemTime(&RegistryHive->HiveHeader->TimeStamp); + + /* Update log file */ + if (!HvpWriteLog(RegistryHive)) + { + return FALSE; + } + + /* Update hive file */ + if (!HvpWriteHive(RegistryHive, TRUE)) + { + return FALSE; + } + + /* Clear dirty bitmap. */ + RtlClearAllBits(&RegistryHive->DirtyBitmap); + + return TRUE; +} + +BOOLEAN CMAPI +HvWriteHive( + PREGISTRY_HIVE RegistryHive) +{ + ASSERT(RegistryHive->ReadOnly == FALSE); + + /* Update hive header modification time */ + KeQuerySystemTime(&RegistryHive->HiveHeader->TimeStamp); + + /* Update hive file */ + if (!HvpWriteHive(RegistryHive, FALSE)) + { + return FALSE; + } + + return TRUE; +}