If(FATType == FAT12)
FATOffset = N + (N/2); //注意等式并没有乘以浮点数1.5,除以2的值四舍五入;
ThisFATSecNum = BPB_RsvdSecCnt + (FATOffset / BPB_BytesPerSec);
ThisFATEntOffset = REM(FATOffset / BPB_BytesPerSec);
现在我们必须考虑扇区边界的情况。
If(ThisFATEntOffset == (BPB_BytesPerSec - 1))
{
}
现在我们可以像FAT16一样使用WORD数据类型来对FAT12的数据进行读取,但仍需要注意,如果簇号为偶数,我们取16-Bit中的低12-Bit,如果是奇数则取高12-Bit,如果簇号是奇数,则取高12-Bit(这是因为对于FAT12来说,簇号占据12Bit,也就是说,要存储两个簇号,需要3个字节,回忆一下FAT16里面的簇号排列,
现在一下子取16位,即两个字节,FAT12表里的簇号也是从0开始的,第0簇和第1簇一共三个字节存放FAT12的起始标志,从第2簇(第四个字节)开始存放FAT表项,现在假设要读取第2簇的内容,ThisFATEntOffset的值为3,也就是说从第三个字节开始读取第2簇的内容,记住,簇号为12bit,第3个字节存放这12bit的低8位,第4个字节的低4位存放这12bit的高4位。见下图:
FAT12ClusEntryVal = *((WORD *) &SecBuff[ThisFATEntOffset]);
If(N & 0x0001)
FAT12ClusEntryVal = FAT12ClusEntryVal >> 4;
Else
FAT12ClusEntryVal = FAT12ClusEntryVal & 0x0FFF;
设置簇的值按照下面的代码进行:
If(N & 0x0001) {
FAT12ClusEntryVal = FAT12ClusEntryVal << 4;
*((WORD *) &SecBuff[ThisFATEntOffset]) =
(*((WORD *) &SecBuff[ThisFATEntOffset])) & 0x000F;
}
Else {
FAT12ClusEntryVal = FAT12ClusEntryVal & 0x0FFF;
*((WORD *) &SecBuff[ThisFATEntOffset]) =
(*((WORD *) &SecBuff[ThisFATEntOffset])) & 0xF000;
}
*((WORD *) &SecBuff[ThisFATEntOffset]) =
(*((WORD *) &SecBuff[ThisFATEntOffset])) | FAT12ClusEntryVal;
NOTE:这里的>>为右移操作符,并往高4位填充0;<<为左移操作符,同时低4位用0填充。数据区中的文件是按照以下方式与FAT表相对应的:数据区中文件存放的第一个簇号被记录在目录项中,文件就是根据这个簇号与FAT表相关联,数据区中文件的位置由前面讨论的FirstSectorofCluster来计算。
对于一个大小为0的文件——一个没有数据的文件——在目录项中分配的第一个簇的簇号为0,此簇(参见前面讨论的ThisFATSecNum和ThisFATEntryOffset)的内容要么是一个EOC标记(End Of Cluster chain簇链表结束标记)要么就是该文件的下一个簇的簇号。EOC的值和FAT类型有关(假设FATContent是需要检测看是否包含EOC标记的簇的内容)。
isEOF = FALSE;
if (FATType == FAT12) {
if(FATContent >= 0x0FF8)
isEOF = TRUE;
}
Else if (FATType == FAT16) {
if (FATContent >= 0xFFF8)
isEOF = TRUE;
}
Else if (FATType == FAT32) {
if (FATContent >= 0x0FFFFFF8)
isEOF = TRUE;
}
NOTE:包含EOC标记的簇属于当前文件并且是当前文件的最后一个簇。Microsoft的操作系统设置EOC标记时FAT12使用0x0FFF,FAT16使用0xFFFF,FAT32使用0x0FFFFFFF,但有一些运行于Microsoft系统的工具并不使用这个值。
还有一个特殊的标记就是“坏簇(Bad Cluster)标记”,任何包含“坏簇”标记的簇都不应该被列入到剩余簇的范畴内,这个“坏簇”标记对于FAT12是0x0FF7,FAT16是0xFFF7,FAT32是0xFFFFFF7。另外,这些坏簇看起来也像是丢失的簇——它们似乎已经被分配出去,因为它们的值并不为0,但同时它们又不属于任何文件。磁盘修复程序一定要认出这些被标记为坏簇标记的“丢失簇”,并且不去修改它们的内容。
NOTE:对于FAT12和FAT16而言,坏簇标记不可能是某个已经分配簇的簇号,但是对于FAT32而言,0x0FFFFFF7就有可能是某个簇的簇号,因此FAT32文件系统必须避免把0x0FFFFFF7这个数字分配出去作为某个簇的簇号。
FAT表中剩余簇的列表就是卷中所有内容为0的簇的列表。这些数据必须尽早取得并记录下来以表示剩余簇是已经被使用的。这个列表并没有存储在卷的任何一个地方,它必须在系统挂上(mount)该卷时,由FAT扫描程序获得内容为0的簇的列表。FAT32的BPB_FSInfo扇区可能会包含剩余簇的数量,请参阅FAT32关于BPB_FSInfo扇区的讨论部分。
在FAT卷起始部分的两个保留扇区到底是做什么用的呢?第一个保留簇FAT[0],它的低位8-bit为BPB_Media,剩余的位用1填充,比如BPB_Media的内容为0xF8,那么FAT12的内容为0x0FF8,FAT16为0xFFF8,FAT32为0x0FFFFFF8,第二个保留簇FAT[1]在格式化的时候被填充EOC标记。FAT12卷此域不用,其值始终为EOC标记。FAT16和FAT32此域的高2bit可以被用于标记磁盘是否为“脏”(下面描述),剩余的位均用“1”填充。请注意FAT16和FAT32这两位的位置是不一样的,因为是高位2-bit。
对于FAT16:
ClnShutBitMask = 0x8000;
HrdErrBitMask = 0x4000;
对于FAT32:
ClnShutBitMask = 0x08000000;
HrdErrBitMask = 0x04000000;
Bit ClnShutBitMask : 如果此位为1,那么卷是“干净”的。如果为0,那么此卷是“脏”的。这意味着系统在上次卸载(unmount)此卷时,没有正常地断开连接,此时建议使用ChkDsk/ScanDisk等工具来检测磁盘是否有错误。
Bit HrdErrBitMask : 如果此位为1,表示没有发生磁盘读/写错误。如果为0,表示系统在上次挂载该卷时,有发生过磁盘读/写错误,此时建议运行ChkDsk/Scandisk等工具来扫描磁盘表面看看是否有出现新的坏簇。
关于FAT表这里还有两个重要的问题:
1. FAT表的结束扇区不一定就是FAT表的最后一个扇区,FAT表的结尾扇区位于簇号为CountofClusters + 1(参阅前面关于CountofClusters的计算式)的簇中。这个扇区未必在FAT表的最后面。FAT程序不应该尝试着去访问CountofClusters + 1以后的簇,FAT格式化程序应该把这个簇号后面所有的簇用0填充;
2. BPB_FATSz16(对于FAT32为BPB_FATSz32)的值会比它实际需要的大,也就是说,在FAT表中可能有部分扇区没有被使用。因此,FAT表的结束扇区都是由CountofClusters + 1来计算得到,而不是使用BPB_FATSz16/32来计算。FAT程序不应该尝试去访问这些“额外”的扇区。FAT格式化程序应该把这些扇区用0来填充。