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

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

lib/cmlib/hivecell.c
  1. HCELL_INDEX CMAPI HvAllocateCell(PHHIVE RegistryHive, SIZE_T Size, HSTORAGE_TYPE Storage, HCELL_INDEX Vicinity)
  2. {
  3.    PHCELL FreeCell;
  4.    HCELL_INDEX FreeCellOffset;
  5.    PHCELL NewCell;
  6.    PHBIN Bin;
  7.    /* 加上HCELL的大小并且按16位向上对齐 */
  8.    Size = ROUND_UP(Size + sizeof(HCELL), 16);
  9.    /* 从FreeDisplay中找到Size大小的空闲CELL, FreeCellOffset是CELL的索引 */
  10.    FreeCellOffset = HvpFindFree(RegistryHive, Size, Storage);
  11.    /* 如果没有符合的CELL, 需要在末尾增加一个BIN, 从新的BIN中分配需要的CELL, FreeCellOffset是CELL索引 */
  12.    if (FreeCellOffset == HCELL_NIL)
  13.    {
  14.       Bin = HvpAddBin(RegistryHive, Size, Storage);
  15.       if (Bin == NULL)
  16.          return HCELL_NIL;
  17.       FreeCellOffset = Bin->FileOffset + sizeof(HBIN);
  18.       FreeCellOffset |= Storage << HCELL_TYPE_SHIFT;
  19.    }
  20.    /* 根据索引获得HCELL结构 */
  21.    FreeCell = HvpGetCellHeader(RegistryHive, FreeCellOffset);
  22.    /* 分配的CELL有可能大于用户请求的长度, 如果大于Size + 16那么将CELL分解成两个。剩余的空间重新加入FreeDisplay中 */
  23.    if ((ULONG)FreeCell->Size > Size + 16)
  24.    {
  25.       NewCell = (PHCELL)((ULONG_PTR)FreeCell + Size);
  26.       NewCell->Size = FreeCell->Size - Size;
  27.       FreeCell->Size = Size;
  28.       HvpAddFree(RegistryHive, NewCell, FreeCellOffset + Size);
  29.       if (Storage == Stable)
  30.          HvMarkCellDirty(RegistryHive, FreeCellOffset + Size, FALSE);
  31.    }
  32.    /* 如果CELL类型是非易失的, 需要将对应的DirtyVector置位, 这样新增内容最终会被刷新到硬盘 */
  33.    if (Storage == Stable)
  34.       HvMarkCellDirty(RegistryHive, FreeCellOffset, FALSE);
  35.    /* 把这个CELL标记成已使用(大小为负) */
  36.    FreeCell->Size = -FreeCell->Size;
  37.    RtlZeroMemory(FreeCell + 1, Size - sizeof(HCELL));
  38.    return FreeCellOffset;
  39. }
这个函数从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
  1. static HCELL_INDEX CMAPI HvpFindFree(PHHIVE RegistryHive, ULONG Size, HSTORAGE_TYPE Storage)
  2. {
  3.    PHCELL_INDEX FreeCellData;
  4.    HCELL_INDEX FreeCellOffset;
  5.    PHCELL_INDEX pFreeCellOffset;
  6.    ULONG Index;
  7.    /* 从Index开始递增搜索比Size大的CELL */
  8.    for (Index = HvpComputeFreeListIndex(Size); Index < 24; Index++)
  9.    {
  10.       pFreeCellOffset = &RegistryHive->Storage[Storage].FreeDisplay[Index];
  11.       while (*pFreeCellOffset != HCELL_NIL)
  12.       {
  13.          /*  根据CELL的索引获得HCELL结构 */
  14.          FreeCellData = (PHCELL_INDEX)HvGetCell(RegistryHive, *pFreeCellOffset);
  15.          /* 找到了比Size大的CELL, 从空闲链里摘除, 并返回给用户 */
  16.          if ((ULONG)HvpGetCellFullSize(RegistryHive, FreeCellData) >= Size)
  17.          {
  18.             FreeCellOffset = *pFreeCellOffset;
  19.             *pFreeCellOffset = *FreeCellData;
  20.             return FreeCellOffset;
  21.          }
  22.          pFreeCellOffset = FreeCellData;
  23.       }
  24.    }
  25.    return HCELL_NIL;
  26. }
HvpFindFree会从小到大搜索FreeDisplay链表, 如果找到了大于等于Size的CELL,便把CELL从链表里面摘除,并把CELL的索引返回给用户。如果没有合适的空闲CELL将会返回HCELL_NIL。很直观。
这里有个有个HvGetCell函数,它的作用是根据CELL索引找到CELL指针(这个指针指向紧随着HCELL结构的内存地址)。我们来看看这个函数是如何实现的。
HvAllocateCell -> HvpFindFree -> HvpFindFree
lib/cmlib/hivecell.c
  1. PVOID CMAPI HvGetCell(PHHIVE RegistryHive, HCELL_INDEX CellIndex)
  2. {  /* HvpGetCellHeader获得HCELL结构, 紧跟着HCELL就是CELL内容, 返回给用户 */
  3.    return (PVOID)(HvpGetCellHeader(RegistryHive, CellIndex) + 1);
  4. }
调用HvpGetCellHeader根据CELL索引获得HCELL结构, 紧跟着HCELL就是CELL内容, 返回给用户。我们继续看HvpGetCellHeader。
HvAllocateCell -> HvpFindFree -> HvpFindFree -> HvpGetCellHeader
lib/cmlib/hivecell.c
  1. static __inline PHCELL CMAPI HvpGetCellHeader(PHHIVE RegistryHive, HCELL_INDEX CellIndex)
  2. {
  3.    PVOID Block;
  4.    if (!RegistryHive->Flat)
  5.    {
  6.       ULONG CellType;
  7.       ULONG CellBlock;
  8.       ULONG CellOffset;
  9.       CellType = (CellIndex & HCELL_TYPE_MASK) >> HCELL_TYPE_SHIFT;      // 最高位是CellType(Stable或Volatile)
  10.       CellBlock = (CellIndex & HCELL_BLOCK_MASK) >> HCELL_BLOCK_SHIFT;   // Block是4kb, 提取出Block的序号
  11.       CellOffset = (CellIndex & HCELL_OFFSET_MASK) >> HCELL_OFFSET_SHIFT;// CELL在Block内的偏移
  12.       // 查询对应的BlockList表得到CELL地址
  13.       Block = (PVOID)RegistryHive->Storage[CellType].BlockList[CellBlock].BlockAddress;
  14.       return (PVOID)((ULONG_PTR)Block + CellOffset);
  15.    }
  16.    else
  17.    {
  18.       /* 如果HIVE是以Flat模式打开的, 将没有意义BlockList。同事BaseBlock指向的内容就是读出的HIVE文件本身。
  19.           这种模式只能读不能写。*/
  20.       return (PVOID)((ULONG_PTR)RegistryHive->BaseBlock + HV_BLOCK_SIZE +
  21.                      CellIndex);
  22.    }
  23. }
正常情况下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文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值