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_INDEXCMAPIHvAllocateCell(PHHIVERegistryHive,SIZE_TSize,HSTORAGE_TYpestorage,HCELL_INDEXVicinity)
  2. {
  3. PHCELLFreeCell;
  4. HCELL_INDEXFreeCellOffset;
  5. PHCELLNewCell;
  6. PHBINBin;
  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,Storage);
  15. if(Bin==NULL)
  16. returnHCELL_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. returnFreeCellOffset;
  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. staticHCELL_INDEXCMAPIHvpFindFree(PHHIVERegistryHive,ULONGSize,HSTORAGE_TYpestorage)
  2. {
  3. PHCELL_INDEXFreeCellData;
  4. HCELL_INDEXFreeCellOffset;
  5. PHCELL_INDEXpFreeCellOffset;
  6. ULONGIndex;
  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. returnFreeCellOffset;
  21. }
  22. pFreeCellOffset=FreeCellData;
  23. }
  24. }
  25. returnHCELL_NIL;
  26. }
HvpFindFree会从小到大搜索Freedisplay链表,如果找到了大于等于Size的CELL,便把CELL从链表里面摘除,并把CELL的索引返回给用户。如果没有合适的空闲CELL将会返回HCELL_NIL。很直观。
这里有个有个HvGetCell函数,它的作用是根据CELL索引找到CELL指针(这个指针指向紧随着HCELL结构的内存地址)。我们来看看这个函数是如何实现的。
HvAllocateCell ->HvpFindFree ->HvpFindFree
lib/cmlib/hivecell.c
  1. PVOIDCMAPIHvGetCell(PHHIVERegistryHive,HCELL_INDEXCellIndex)
  2. {/*HvpGetCellHeader获得HCELL结构,紧跟着HCELL就是CELL内容,返回给用户*/
  3. return(PVOID)(HvpGetCellHeader(RegistryHive,CellIndex)+1);
  4. }
调用HvpGetCellHeader根据CELL索引获得HCELL结构,返回给用户。我们继续看HvpGetCellHeader。
HvAllocateCell ->HvpFindFree ->HvpFindFree -> HvpGetCellHeader
lib/cmlib/hivecell.c
  1. static__inlinePHCELLCMAPIHvpGetCellHeader(PHHIVERegistryHive,HCELL_INDEXCellIndex)
  2. {
  3. PVOIDBlock;
  4. if(!RegistryHive->Flat)
  5. {
  6. ULONGCellType;
  7. ULONGCellBlock;
  8. ULONGCellOffset;
  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文件

相关文章

react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接...
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc ...