FAT32 FSInfo扇区结构和备份启动扇区
FAT3的FAT表可能非常大,而不像FAT16的大小被限制在128K以内,或是FAT12的FAT表大小被限制在6K范围以内一样,因此,在FAT32的卷中存放着“最新”的剩余簇的数量,在API函数想知道剩余空间时(比如在DIR命令的最后显示剩余空间)不必马上去计算该数值,FSInfo的扇区号存放在BPB_FSInfo中,对于Microsoft的操作系统此值为1,下表是FSInfo的结构:
名称 | Offset(byte) | 大小(Byte) | 描述 |
FSI_LeadSig | 0 | 4 | 值为0x41615252,这个标记用来表示该扇区为FSInfo扇区。 |
FSI_Reserved1 | 4 | 480 | 保留为以后扩展使用,FAT32格式化程序应该把此域全部设置为0,当前版本的FAT程序不可以访问该域。 |
FSI_StrucSig | 484 | 4 | 值为0x61417272,更具体地表明该扇区已经被使用。 |
FSI_Free_Count | 488 | 4 | 保存最新的剩余簇的数量,如果为0xFFFFFFFF表示剩余簇未知,需要重新计算,除此之外的其他值都可以用,而且不要求十分精确,但必须保证其值≤磁盘所有的簇数。 |
FSI_Nxt_Free | 492 | 4 | 该域为FAT驱动程序提供一条有利的线索,它告诉驱动程序应该从哪里开始寻找剩余簇,因为FAT32的FAT表可能非常庞大,如果已经分配的簇很多的话要从头开始查找剩余簇,这将耗费大量时间。通常这个值被设定为驱动程序最后分配出去的簇号,如果值为0xFFFFFFFF,那么驱动程序必须从簇2开始查找,除此之外其他的值都可以使用,当然,前提是这个值必须是合法的。 |
FSI_Reserved2 | 496 | 12 | 保留为以后扩展使用,FAT32格式化程序应该把此域全部设置为0,当前版本的FAT程序不可以访问该域。 |
FSI_TrailSig | 508 | 4 | 值为0xAA550000,此结束标记用来表示这是一个FSInfo扇区,注意此域的高两位偏移量为510和511,这和启动扇区在相同偏移处的标记是一样的。 |
FAT32区别于FAT12/FAT16的另外一个地方就是BPB_BkBootSec,FAT12/FAT16有可能由于丢失启动扇区的内容而使整个卷都无法访问,这是一个“单点错误(Single Point of Failure)”,为了避免这种严重的后果,FAT32引进了BPB_BkBootSec,FAT32在扇区号为6的地方完整地拷贝了一份启动扇区的备份,包括BPB的内容。
当启动扇区的内容被损坏后,磁盘修复程序只需要把启动备份扇区中的数据拷贝回启动扇区即可,即使在启动扇区被损坏的情况下,磁盘驱动程序仍然可以在更换硬盘之前正常访问该卷。
在第二种情况下——扇区0被损坏——这就是为什么BPB_BkBootSec的值为什么必须为6的原因,当扇区0不可读时,很多不同的操作系统都是硬性检查FAT32卷在扇区6的启动备份。保存在扇区6中的是一个完整的启动记录。Microsoft的FAT32的“启动扇区”实际上是由3个长度为512字节的扇区组成,在BPB_BkBootSec扇区开始的启动备份中完整的包含了这3个扇区,FSInfo也在其中,虽然在备份中BPB_FSInfo的内容和0扇区中BPB_FSInfo所指向的是同一个FSInfo结构。
NOTE:这三个扇区和启动扇区一样也在偏移量为510和511的地方包含标记0xAA55(参看前面的叙述)。
FAT目录结构(FAT Directory Structure)
这里我们先忽略长目录项的情况,只讨论短目录项。
FAT目录其实就是一个由32Bytes的线性表构成的“文件”。根目录(root directory)是一个特殊的目录,它存在于每一个FAT卷中,对于FAT12/16,根目录的扇区号是相对于该FAT卷第一个扇区(0扇区)的偏移量。
FirstDirRootSecNum = BPB_RsvdSecCnt + (BPB_NumFATs * BPB_FATsz16);
FAT32的根目录由簇链组成,其扇区数是不确定的,这点和普通文件相同,根目录的第一个扇区号存储在BPB_RootClus中,根目录不同于其他的目录,没有日期和时间戳,也没有目录名(“/”并不是其目录名),同时根目录里没有“.”和“..”这两个目录项,根目录另一个特殊的地方在于,根目录中有一个设置了ATTR_VOLUME_ID位(见下表)的文件,这个文件在整个FAT卷中是唯一的。
名称 | Offset (Byte) | 大小(Byte) | 描述 |
DIR_Name | 0 | 11 | 短文件名 |
DIR_Attr | 11 | 1 | 文件属性: ATTR_READ_ONLY 0x01 ATTR_HIDDEN 0x02 ATTR_SYSTEM 0x04 ATTR_VOLUME_ID 0x08 ATTR_DIRECTORY 0x10 ATTR_ARCHIVE 0x20 ATTR_LONG_NAME ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID 前两个属性位为保留位,在文件创建时应该把这两位设为0,在以后的使用中不能读写和更改。 |
DIR_NTRes | 12 | 1 | 保留给Windows NT使用,在文件创建时设置该位为0,在以后的使用中不能读写和更改。 |
DIR_CrtTimeTeenth | 13 | 1 | 文件创建时间的毫秒级时间戳,由于DIR_CrtTime的精度为2秒,所以此域的有效值在0-199之间。 |
DIR_CrtTime | 14 | 2 | 文件创建时间。 |
DIR_CrtData | 16 | 2 | 文件创建日期。 |
DIR_LastAccDate | 18 | 2 | 最后访问日期,请注意并没有最后访问时间域,而只有日期,这日期是指文件被读写的日期,如果是写,该日期还应该被写到DIR_WrDate中。 |
DIR_FstClusHI | 20 | 2 | 该目录项簇号的高位字(FAT12/16此位为0) |
DIR_WrtTime | 22 | 2 | 最后写的时间,文件创建被认作写 |
DIR_WrtDate | 24 | 2 | 最后写的日期,文件创建被认作写 |
DIR_FstClusL0 | 26 | 2 | 该目录项簇号的低位字 |
DIR_FileSize | 28 | 2 | 文件大小,由32-bit双字组成 |
DIR_Name[0]
此处特别注释目录项的第一个字节(DIR_Name[0])。
●如果DIR_Name[0] == 0xE5,则此目录为空(目录项不包含文件和目录)
●如果DIR_Name[0] == 0x00,则此目录为空(同0xE5),并且此后的不再分配有目录项(此后所有的DIR_Name[0]均为0)。
不同于0xE5,如果DIR_Name[0]的值为0,那么FAT程序将不会再去检测其后续的磁盘空间,因为这些空间都是空闲的。
●如果DIR_Name[0] == 0x05,则文件名在该位的实际值为0xE5,但0xE5是日文中合法的字符,当需要用0xE5来作为DIR_Name[0]时使用0x05来代替,避免程序误认为该目录项为空。
DIR_Name域实际由两部分组成:8个字符的主文件名和3个字符的扩展名。两部分如果字符数不够的话用空格(0x20)填充(Trailing Space Padded)。
DIR_Name[0]不允许为0x20,主文件名和扩展文件名的间隔“.”并不真实存在于DIR_Name中,小写字母不允许出现在DIR_Name中(这些字符因为不同的国家和地区而异)。
●以下字符不允许出现在DIR_Name中的任何位置:
0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D还有0x7C。
以下是一些例子显示用户的输入文件名如何与DIR_Name对应:
“foo.bar” → “FOO BAR”
“FOO.BAR” → “FOO BAR”
“Foo.Bar" → “FOO BAR”
“foo” → “FOO”
“PICKLE.A” → “PICKLE A”
“prettybg.big” → “PRETTYBGBIG”
“.big” → 非法,DIR_Name[0]不能为0x20。
在FAT的目录中,所有的文件名都是唯一的,上面例子中的三个文件名看起来似乎各有千秋,事实上它们都是相同的文件名,在同一个目录中,只能有一个DIR_Name被设置为“FOO BAR”。
文件属性(DIR_Attr):
ATTR_READ_ONLY:对这个文件的写操作将会失败。
ATTR_HIDDEN:正常模式显示该目录列表时不显示该文件。
ATTR_SYSTEM:是系统文件。
ATTR_VOLUME_ID:在一个FAT卷中,只能有一个“文件”设置此位,并且该文件必须在根目录中,该文件的文件名实际上就是该卷的卷标,并且该文件的DIR_FstClusHI和DIR_FstClusL0必须为0(卷标文件不分配空间)。
ATTR_DIRECTORY:目录
ATTR_ARCHIEVE:此属性用于支持一些备份程序,当文件创建,改名或写入时,FAT文件系统会设置此位,备份程序可以利用此位来判断卷中的哪些程序从上次备份到现在有更改过。
另外,ATTR_LONG_NAME位实际上表明该“文件”实际上为另外一个有长文件名的文件的一部分,在下一个章节我们将详细讨论长文件名的情形。
创建一个目录时,该“文件”的ATTR_DIRECTORY位被置位的同时DIR_FileSize被设置为0,ATTR_DIRECTORY被置位的文件不使用DIR_FileSize并且该域通常为0(目录所占空间为其起始簇到EOC结束的簇链所占的空间),每个目录项分配一个簇(除非是FAT12/FAT16的根目录),将DIR_FstClusL0和DIR_FstClusHI的值设置为该簇的簇号,然后在FAT表中为该簇设置一个EOC标志,并把该簇的每一个字节设置为0,如果这是根目录,那么你的工作就完成了(根目录没有“.”和“..”)。否则,你必须在该目录空间(就是刚刚分配的那个簇)的头两个32-byte 创建两个特殊的目录项。
第一个目录项的DIR_Name设置为:
“. ”
“.. ”
我们称这两个目录为“点”和“点点”,这两个目录的DIR_FileSize均设置为0,同时两个目录的时间和日期和日期标志也设置为以包含它们本身的目录相同,将“.”目录项的DIR_ClusL0和DIR_ClusHI设置为与它自身所在的目录(包含这两个“.”和“..”目录项的目录)相同。
最后把“..”目录项的DIR_ClusL0和CLUS_ClusHI设置为与刚刚创建目录项所在目录的第一个簇号(如果刚创建的目录在根目录则这些值为0,FAT32也是如此)。
“.”和“..”的特征可以概括如下:
“.”目录指向该目录本身;
“..”目录指向该目录的上级目录。
还记得DOS操作系统吗?要返回上级目录需要输入CD .. 这里的“..”就是上级目录的意思,这个指令的意思就是进入当前目录的上级目录。