您的位置:首页 > Web前端 > React

ReactOS-Freeldr注册表HIVE文件格式2

2011-06-14 22:02 357 查看
上一节读了HIVE文件读入内存时的初始化操作。现在来看看实际对内存中的HIVE文件的操作。
首先是从空闲CELL中分配一个指定大小的CELL。HvAllocateCell就是做这件事情的。
这个函数有四个参数:
1. RegistryHive HHIVE结构指针
2. Size 需要分配的CELL大小(不包括HCELL结构的大小)
3. Storage 分配的CELL是Stable还是Volatile。
4. Vicinity 这个参数在Freeldr中没有使用

lib/cmlib/hivecell.c

HCELL_INDEX CMAPI HvAllocateCell(PHHIVE RegistryHive, SIZE_T Size, HSTORAGE_TYPE Storage, HCELL_INDEX Vicinity)

{

PHCELL FreeCell;

HCELL_INDEX FreeCellOffset;

PHCELL NewCell;

PHBIN Bin;

/* 加上HCELL的大小并且按16位向上对齐 */

Size = ROUND_UP(Size + sizeof(HCELL), 16);

/* 从FreeDisplay中找到Size大小的空闲CELL, FreeCellOffset是CELL的索引 */

FreeCellOffset = HvpFindFree(RegistryHive, Size, Storage);

/* 如果没有符合的CELL, 需要在末尾增加一个BIN, 从新的BIN中分配需要的CELL, FreeCellOffset是CELL索引 */

if (FreeCellOffset == HCELL_NIL)

{

Bin = HvpAddBin(RegistryHive, Size, Storage);

if (Bin == NULL)

return HCELL_NIL;

FreeCellOffset = Bin->FileOffset + sizeof(HBIN);

FreeCellOffset |= Storage << HCELL_TYPE_SHIFT;

}

/* 根据索引获得HCELL结构 */

FreeCell = HvpGetCellHeader(RegistryHive, FreeCellOffset);

/* 分配的CELL有可能大于用户请求的长度, 如果大于Size + 16那么将CELL分解成两个。剩余的空间重新加入FreeDisplay中 */

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 == Stable)

HvMarkCellDirty(RegistryHive, FreeCellOffset + Size, FALSE);

}

/* 如果CELL类型是非易失的, 需要将对应的DirtyVector置位, 这样新增内容最终会被刷新到硬盘 */

if (Storage == Stable)

HvMarkCellDirty(RegistryHive, FreeCellOffset, FALSE);

/* 把这个CELL标记成已使用(大小为负) */

FreeCell->Size = -FreeCell->Size;

RtlZeroMemory(FreeCell + 1, Size - sizeof(HCELL));

return FreeCellOffset;

}

这个函数从FreeDisplay中搜索大小符合的CELL,如果不存在需要生成一个BIN。最后把CELL的索引返回给调用者。

因为调用者传入的Size不包含HCELL的大小,8行先调整大小并且向上按8字节对其。
10行调用HvpFindFree从FreeDisplay中搜索符合Size大小的CELL,如果找到FreeCellOffset为CELL的索引。
如果没找则在末尾增加一个BIN,从这个BIN中分配符合大小的CELL,FreeCellOffset为CELL的索引(12-19)。
因为返回的CELL可能比请求的大,所以21行调用HvpGetCellHeader根据索引获得HCELL指针,如果返回的CELL比请求的大小大16字节以上的话则把CELL分配成两个,剩余的空间重新加入空闲列表FreeDisplay(23-31)。
最后如果Storage为Stable时,说明这些信息最终要写入硬盘的HIVE文件,所以要把CELL所在Block的DirtyVetor置位,这样注册表刷新文件时会把这个改变回写到硬盘的HIVE文件中。(33-34)
因为我们分配的CELL项是空闲的,所以要把大小变为负数,代表此CELL已经被使用。最后把CELL内容清0,把CELL索引返回给调用者。

从FreeDisplay中寻找空闲块是HvpFindFree的工作。
这个函数接受大小Size和存储类型Storage,返回找到的CELL的索引,如果没找到返回HCELL_NIL。
HvAllocateCell -> HvpFindFree
lib/cmlib/hivecell.c

static HCELL_INDEX CMAPI HvpFindFree(PHHIVE RegistryHive, ULONG Size, HSTORAGE_TYPE Storage)

{

PHCELL_INDEX FreeCellData;

HCELL_INDEX FreeCellOffset;

PHCELL_INDEX pFreeCellOffset;

ULONG Index;

/* 从Index开始递增搜索比Size大的CELL */

for (Index = HvpComputeFreeListIndex(Size); Index < 24; Index++)

{

pFreeCellOffset = &RegistryHive->Storage[Storage].FreeDisplay[Index];

while (*pFreeCellOffset != HCELL_NIL)

{

/* 根据CELL的索引获得HCELL结构 */

FreeCellData = (PHCELL_INDEX)HvGetCell(RegistryHive, *pFreeCellOffset);

/* 找到了比Size大的CELL, 从空闲链里摘除, 并返回给用户 */

if ((ULONG)HvpGetCellFullSize(RegistryHive, FreeCellData) >= Size)

{

FreeCellOffset = *pFreeCellOffset;

*pFreeCellOffset = *FreeCellData;

return FreeCellOffset;

}

pFreeCellOffset = FreeCellData;

}

}

return HCELL_NIL;

}

HvpFindFree会从小到大搜索FreeDisplay链表, 如果找到了大于等于Size的CELL,便把CELL从链表里面摘除,并把CELL的索引返回给用户。如果没有合适的空闲CELL将会返回HCELL_NIL。很直观。

这里有个有个HvGetCell函数,它的作用是根据CELL索引找到CELL指针(这个指针指向紧随着HCELL结构的内存地址)。我们来看看这个函数是如何实现的。
HvAllocateCell -> HvpFindFree -> HvpFindFree
lib/cmlib/hivecell.c

PVOID CMAPI HvGetCell(PHHIVE RegistryHive, HCELL_INDEX CellIndex)

{ /* HvpGetCellHeader获得HCELL结构, 紧跟着HCELL就是CELL内容, 返回给用户 */

return (PVOID)(HvpGetCellHeader(RegistryHive, CellIndex) + 1);

}

调用HvpGetCellHeader根据CELL索引获得HCELL结构, 紧跟着HCELL就是CELL内容, 返回给用户。我们继续看HvpGetCellHeader。
HvAllocateCell -> HvpFindFree -> HvpFindFree -> HvpGetCellHeader
lib/cmlib/hivecell.c

static __inline PHCELL CMAPI HvpGetCellHeader(PHHIVE RegistryHive, HCELL_INDEX CellIndex)

{

PVOID Block;

if (!RegistryHive->Flat)

{

ULONG CellType;

ULONG CellBlock;

ULONG CellOffset;

CellType = (CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT; // 最高位是CellType(Stable或Volatile)

CellBlock = (CellIndex & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT; // Block是4kb, 提取出Block的序号

CellOffset = (CellIndex & HCELL_OFFSET_MASK) >> HCELL_OFFSET_SHIFT;// CELL在Block内的偏移

// 查询对应的BlockList表得到CELL地址

Block = (PVOID)RegistryHive->Storage[CellType].BlockList[CellBlock].BlockAddress;

return (PVOID)((ULONG_PTR)Block + CellOffset);

}

else

{

/* 如果HIVE是以Flat模式打开的, 将没有意义BlockList。同事BaseBlock指向的内容就是读出的HIVE文件本身。

这种模式只能读不能写。*/

return (PVOID)((ULONG_PTR)RegistryHive->BaseBlock + HV_BLOCK_SIZE +

CellIndex);

}

}

正常情况下Flat为FALSE, 所以HvpGetCellHeader将把CellIndex分解为存储类型CellType、CELL所在块编号CellBlock、CELL在块内偏移CellOffset(6-11行)。

之后查询对应的BlockList表得到CELL的实际地址,返回给用户(13-14)。这个过程类似X86的页表,不过它只有一层 :)

这里有个特殊情况,就是HIVE可以以Flat模式初始化。这个模式下HIVE只读,并且没有初始化Storage数组,同事HHIVE.BaseBlock指针直接指向HIVE文件内容,20行处理了这种情况。

到这里我们了解了HvpFindFree、HvGetCell、HvpGetCellHeader三个函数的作用。
我们回到HvAllocateCell的第10行,这里调用HvpFindFree从FreeDisplay中找到一个合适的空闲CELL,希望你还记得 :)
下面的代码你应该大部分经可以看明白了。还剩最后一点,如果HvpFindFree失败(没有合适的空闲区块),那么函数将会调用HvpAddBin在HIVE末尾新生成一个BIN,这个BIN将有足够的空间容纳请求的CELL。
这个函数逻辑很简单但代码比较多,大致是将请求的Size根据4kb向上对其,生成一个BIN,并且更新BlockList和DirtyVector,新生成的BIN占用的Block在尾部,具体代码这里就不列出了。有兴趣可以读读lib/cmlib/hivebin.c文件。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: