ePass产品中的FAT8文件系统

发布时间:2010-2-12 10:50
分类名称:Private



本文件详细描述了在ePass系列产品的PKI设计/实现中, token内部文件系统的设计与实现。

在本文中,“文件”特指我们自己实现的FAT8文件系统中的文件,“物理文件”特指由token中COS管理的文件。

设计原因

之所以不直接使用物理文件,是出于以下几个原因:

在确定了使用大的物理文件来保存数据的策略后,进一步考虑物理文件中数据的管理方式。数据的管理应该满足下列需求:

  1. 方便管理,可以任意创建/删除文件。
  2. 高效的利用存储空间。
  3. 支持动态长度的文件。

有鉴于此,我们决定采用FAT的文件系统来管理物理文件内部的数据,即,将一个物理文件按照FAT文件系统的概念划分成簇,具有自己的FAT表,而文件相 对于物理文件来说,只是一个或者多个小数据块。仅仅使用各种token COS都会提供的读写二进制文件的功能,就可以满足我们动态增加/删除文件的需求。

构造

决定使用FAT文件系统之后,需要确定文件系统的实现方式。1.44Mb软盘采用的是FAT12的文件系统,早期的DOS采用FAT16的文件系 统,Windows98开始使用FAT32文件系统。由于token中的存储区都比较小,一般为8k~32k,所以我们采用FAT8文件系统。

在ePass使用的FAT8文件系统中,我们对常见意义上的文件系统作了大规模的削减,因为我们没有必要支持目录结构,而且没有必要支持以字符串作为文件 名等特性。所以在ePass的FAT8文件系统中,只有文件,没有目录,而且文件以一个唯一的序号作为标识。

FAT8文件系统最多支持创建28=256个文件项,每个文件项的标识仅需要一个字节。事实上,文件系统内部使用了几个字节用于管 理,所以实际能够创建的文件数目256-3=253个,下文将详细描述。

我们可以把一个物理文件看作是一个虚拟的磁盘,其上有文件分配表(File Allocation Table, FAT),数据簇等等。

下面是文件系统的构造:

ePass产品中的FAT8文件系统 - Dsliu - Dspace

图1 FAT8 文件系统内部结构

如图,一个文件系统由三大部分组成:文件头、FAT、数据簇。

文件头

文件头(16字节)标识了以下内容:

typedef struct
{
UCHAR pucFlag[3]; // 文件系统标记,ePass1000/2000中目前使用'NFS'
UCHAR ucSizeOfCluster; // 每簇字节数
DWORD dwVersion; // 文件系统版本号
UCHAR ucTotalCluster; // 虚拟磁盘的总簇数
UCHAR ucFreeCluster; // 虚拟磁盘的可用簇数
UCHAR ucNumberOfFDT; // FDT 表中已创建文件数
UCHAR pucReserve[5]; // 保留未用
}FS_HEADER;

数据簇

我们将数据区划分为多个相同大小的块,每个块称之为一个“簇”。每个簇从索引上与FAT中相应位置的字节相关联。

ePass产品中的FAT8文件系统 - Dsliu - Dspace

图2 FAT8文件系统的簇

FAT

紧接着文件头之后是256字节的FAT,每一个字节对应一个簇的状态,即第0个字节标识第0个簇的状态,第1个字节标识第1个簇的状态,以此类推。FAT 中每一个字节的含义如下:

ePass产品中的FAT8文件系统 - Dsliu - Dspace

图3 已经创建了文件的FAT8文件系统的FAT

如图3所示就是已经创建了几个文件的FAT内容,下面以这个FAT为例,说明一下FAT的构造。

由图3中的FAT可以看出,文件系统中至少已经创建了3个文件(之所以说“至少”,是因为创建的文件可能并没有分配空间,详细信息请查阅本文后续的FDT 内容)。

  1. 第一个文件占用了2个簇(图中蓝色部分),分别是第0×00和第0×08个簇。FAT中第0×00个字节的值为0×08,表示该文件的下一个簇是第 0×08个簇。FAT中第0×08个字节的值为0xFF,表示已经到了这个文件的末尾了,没有占用更多的簇了。
  2. 第二个文件占用了5个簇,从第0×01个簇开始,沿着其每个字节的指向,直到第0×06个簇结束。
  3. 第三个文件占用了6个簇,分析方法同上。

从上面的例子中我们可以知道每个文件占用的簇在文件系统中的分布,但是一个文件不可能一定恰好占用一个或者多个完整的簇。有可能一个文件占用了多个完整的 簇,但是其在占用的最后一个簇中仅使用了3个字节,为了解决这个问题,我们必须引入FDT的概念。

FDT

除了FAT之外,我们还需要记录每一个创建的文件的信息,例如文件的ID,起始簇的序号,文件的大小及附加属性等,每个文件需要5个字节来保存信息,如下 所示:

typedef struct
{
UCHAR ucID; // 文件ID
UCHAR ucStartCluster; // 开始簇
USHORT usSize; // 文件大小
UCHAR ucAttr; // 文件属性
}FS_FDT;

所有已经创建的文件的信息被保存到一个文件中,这个文件被称为FDT(File Descript Table)。特别地,FDT文件的ID固定为0×00,FDT文件中的第一个文件描述项就是其自身。FDT文件随着FAT8文件系统中创建的文件的数目 增加而增大,FDT文件本身除文件ID固定为0×00之外,其余同普通文件一致。

对于一个文件,如果其“开始簇”(即ucStartCluster)为0xFE,则表示这个文件尚未分配空间,与之对应的,其“文件大小”(即 usSize)必然为0。

由上述所描述的信息,我们就可以创建/删除/读写文件了,并且文件的大小是可以动态指定的,也即是说,如果需要,随时可以更改文件的大小(如果是增大文 件,则需要有足够的剩余簇)。

同时,我们知道,因为0×00/0xFE/0xFF具有特殊用途,所以,真正可以使用的簇无法达到256个。实际上,一般情况下也不会真正划分256个 簇,详细情况请查阅“访问/格式化/簇的大小”一节。

访问

格式化

要使用磁盘来存储数据,必须先格式化,我们的FAT8文件系统也不例外。为了提供文件访问功能,我们必须先将存储空间按照我们制定的格式填充。

首先是16字节的文件头。这里主要要注意的是关于簇的信息。

簇的大小:簇的大小是经过计算得来的。因为对于给定的存储空间,我们知道文件头和FAT需要占用16+256=272字节,剩下的数据空间 才是真正划分为簇的。为了访问方便,我们将每个簇划分为32的整数倍大小。这样做会导致一般情况下,并不会真正划分256个簇。
簇的数目: 真正用于保存数据的空间大小除以256之后,得到粗略的簇的大小,然后将这个粗略值向上取到32的整数倍,得到真正划分时簇的大小,然后再将真正用于保存 数据的空间大小除以真正的簇大小,才得到实际划分的簇的数目。

对于FAT,只需要全部填为0×00即可。
在格式化的时候,我们需要创建一个文件,即FDT。创建FDT按照创建普通文件的方式操作即可。

创建文件

首先确认指定的ID尚未被使用。然后确认FDT文件有足够的空间可以保存新的文件的描述信息(5个字节),如果FDT文件不够大,会自动分配一个簇。然后 保存该文件的描述信息,其中文件的起始簇设为0xFE,表示尚未为该文件分配空间。直到调用者设置文件大小,才为该文件分配空间。 分配空间的过程为:查看FAT,找到第一个尚未分配的簇,即FAT中为0×00的那个字节所对应的簇。根据需要找到足够的尚未分配的簇,将FAT中这些簇 对应的字节,按照链接的方式修改其内容,然后修改该文件的描述信息,将起始簇改为

删除文件

读写文件

(未完,待续…)