ArcOpen的大体流程我们看过了。大致分为这几步
函数会尝试找到文件所在分区的设备句柄,如果还没有对应的句柄。那么使用DEVICE.FuncTable中的Open函数打开设备,并为这个设备分配句柄。
打开设备后条用XxxMount识别分区格式,识别成功返回另外的FuncTable,存储到设备的FileData.FileFuncTable域。
为文件分配一个句柄,在对应的FileData.DeviceId为上面创建设备句柄,FileData.FuncTable为设备的FileData.FileFuncTable。
最后调用文件的FileData.FuncTable.Open函数打开文件。
挂载分区时做了什么
之前我们忽略了XxxMount函数。现在来读读比较简单的FatMount (freeldr\freeldr\fs\fat.c)。
const DEVVTBL***** FatMount**(ULONG DeviceId)**
{
.......****.
// 生成一个FAT_VOLUME_INFO结构
Volume = MmHeapAlloc**(sizeof(FAT_VOLUME_INFO));**
if (****!Volume)
return NULL;
RtlZeroMemory(Volume, sizeof(FAT_VOLUME_INFO));
// 读第一个扇区
Position**.HighPart = 0;**
Position**.LowPart = 0;**
ret = ArcSeek**(*DeviceId, &Position, SeekAbsolute);*
if (ret !****= ESUCCESS)
{
MmHeapFree**(Volume)****;**
return NULL;
}
ret = ArcRead**(DeviceId, Buffer, sizeof(Buffer)**, &Count)****;
if (ret !****= ESUCCESS |****| Count !****= sizeof(Buffer)****)
{
MmHeapFree**(Volume)****;**
return NULL;
}
// 判断是否有fat分区标志
if (!RtlEqualMemory(BootSector->FileSystemType, “FAT12 “, 8) &****&
!RtlEqualMemory(BootSector-****>FileSystemType, “FAT16 “, 8) &****&
!RtlEqualMemory(BootSector32-****>FileSystemType, “FAT32 “, 8) &****&
!RtlEqualMemory(BootSectorX->FileSystemType, “FATX”, 4))
{
MmHeapFree**(Volume)****;**
return NULL;
}
// 获得分区大小等信息
ret = ArcGetFileInformation**(*DeviceId, &FileInformation);*
if (ret !****= ESUCCESS)
{
MmHeapFree**(Volume)****;**
return NULL;
}
SectorCount**.HighPart = FileInformation.EndingAddress.HighPart;**
SectorCount**.LowPart = FileInformation.EndingAddress.LowPart;**
SectorCount**.QuadPart /****= SECTOR_SIZE;**
Volume**-****>DeviceId = DeviceId;**
// 打开分区
if (!FatOpenVolume(Volume, BootSector, SectorCount.QuadPart))
{
MmHeapFree**(Volume)****;**
return NULL;
}
// 存储FAT_VOLUME_INFO结构
FatVolumes**[DeviceId]** = Volume**;**
// 返回fat文件读写的FuncTable
return &FatFuncTable;
}
**
**
函数中的DeviceId是设备的句柄。
生成FAT_VOLUME_INFO结构。这个结构里面存储了FAT分区的基本信息。包括扇区大小,每个簇的扇区数等等。
typedef struct _FAT_VOLUME_INFO
{
ULONG BytesPerSector**;** /* Number of bytes per sector */
ULONG SectorsPerCluster**;** /* Number of sectors per cluster */
ULONG FatSectorStart**;** /* Starting sector of 1st FAT table */
ULONG ActiveFatSectorStart**;** /* Starting sector of active FAT table */
ULONG NumberOfFats**;** /* Number of FAT tables */
ULONG SectorsPerFat**;** /* Sectors per FAT table */
ULONG RootDirSectorStart**;** /* Starting sector of the root directory (non-fat32) */
ULONG RootDirSectors**;** /* Number of sectors of the root directory (non-fat32) */
ULONG RootDirStartCluster**;** /* Starting cluster number of the root directory (fat32 only) */
ULONG DataSectorStart**;** /* Starting sector of the data area */
ULONG FatType**;** /* FAT12, FAT16, FAT32, FATX16 or FATX32 */
ULONG DeviceId**;**
} FAT_VOLUME_INFO**;**
**
**
读取第一个山区,判断是否有fat标志。如果没有直接返回,挂载失败。之后使用ArcGetFileInformation获得分区大小。ArcGetFileInformation里面调用了FileData.FuncTable.GetFileInformation。因为当前DeviceId是设备句柄,所以他实际调用的是DiskGetFileInformation(freeldr\freeldr\arch\i386\hardware.c)。这个函数很简单,通过FileInformation返回分区开始和结束的地址,这里就不列出了。
这里的代码用FileInformation**.**EndingAddress / SECTOR_SIZE计算出了该分区的扇区数SectorCount。这里应该BUG。因为EndingAddress是分区结束地址,真的扇区数应该是 (分区开始地址 - EndingAddress ) / SECTOR_SIZE。好在SectorCount只是判断fat分区的一个依据,而且一般C盘计算出的SectorCount误差不会很大,影响不大。
最后执行FatOpenVolume真正执行分区的挂载、初始化。初始化结束后将生成的Volume放到fat.c维护的全局数组FatVolumes里,之后对fat分区进行操作(读写)时,通过设备的DeviceId就可以找到对应的FAT_VOLUME_INFO结构。
最后函数返回FatFuncTable函数数组
const DEVVTBL FatFuncTable =
{
FatClose,
FatGetFileInformation,
FatOpen,
FatRead,
FatSeek,
L”fastfat”,
}****;
用户可以通过这些函数就读写改fat分区啦。
那么FatOpenVolume都干了什么呢。
这个函数简单来说就是根据分区内容填写了Volume结构,已经算是一个分区的具体实现细节了,和整体架构无关,不多说了。这个函数在freeldr\freeldr\fs\fat.c中。
打开文件时做了什么
上一篇文章中还有一个地方没说,就是打开设备并创建完文件的句柄后,ArcOpen调用了文件对应的FileData.FuncTable.Open。对于fat分区而言这个函数是FatOpen(freeldr\freeldr\fs\fat.c). 这个函数也是和分区结构有关的了,有一点比较重要就是函数最后调用了FsSetDeviceSpecific把一个和文件相关的内部结构与文件句柄相关联。以后使用FatRead对文件句柄进行读操作时直接就可以获得这个结构啦。
LONG FatOpen**(CHAR*** Path, OPENMODE OpenMode, ULONG***** FileId**)**
{
.....****.
// 根据文件的FileId获得文件所在的设备句柄FileData.DeviceId, 从而获得FatMount时生成的Volume结构。
DeviceId = FsGetDeviceId**(FileId);*
FatVolume = FatVolumes**[DeviceId]****;**
// 从DeviceId设备中读取并查询fat表,判断path表示的文件是否存在
RtlZeroMemory(&TempFileInfo, sizeof(TempFileInfo)****);
ret = FatLookupFile**(*FatVolume, Path, DeviceId, &TempFileInfo);*
if (ret !****= ESUCCESS)
return ENOENT**;**
// 判断是否是目录
IsDirectory = (TempFileInfo.Attributes & ATTR_DIRECTORY) !****= 0**;**
if (IsDirectory &****& OpenMode !****= OpenDirectory)
return EISDIR**;**
else if (**!*IsDirectory &&* OpenMode !=** OpenReadOnly**)
return ENOTDIR**;**
// 生成FAT_FILE_INFO结构,里面存放了文件的信息(开始的扇区等)
FileHandle = MmHeapAlloc**(sizeof(FAT_FILE_INFO));**
if (****!FileHandle)
return ENOMEM**;**
RtlCopyMemory**(*FileHandle, &TempFileInfo, sizeof(FAT_FILE_INFO)****);*
FileHandle**-****>Volume = FatVolume;**
// 把这个结构和文件对应的FileData.Specific关联。之后进行FatRead等操作时可以直接获得这个结构了
FsSetDeviceSpecific**(FileId, FileHandle);*
return ESUCCESS**;**
}