初始化FAT卷

       读到这里,细心的读者一定会发现一个有趣的问题,前面说过FAT的类型(FAT12,FAT16或是FAT32)是根据总的簇数来判别——并且数据区中最大可取得的扇区数由FAT表的大小来决定——那么当一个磁盘还没有被格式化时,我们无法得到这BPB数据,这时是如何检测并计算出正确的值来放到BPB_SecPerClus和BPB_FATSz16或是BPB_FATSz32中呢?Microsoft的操作系统使用一些固定的值和表格配合一个巧妙的算法来完成这些工作。
       Microsoft只在软盘上使用FAT12文件系统,因为软盘的种类很少,并且都有固定的参数,格式化时使用的是一张简单的表:
     “如果这是一张这种格式的软盘,那么它的BPB看起来就是这个样子(If it is a floppy of this type, then the BPB look like this.)”
       对于FAT12格式的计算相对简单,所有写到BPB中的值都可以在一张纸上用手计算出来的(当然得小心——Cluster的值始终小于4085),如果存储介质的容量大于4M,那就别再麻烦FAT12了,只需要把BPB_SecPerClus的值改小一点,这个卷就成FAT16了。
       本节以下部分描述如何驱动每扇区512字节的FAT卷。如果扇区大小不是这样子的话,你将不能使用这个表格和算法。不同磁盘的扇区大小不一。这里根据磁盘容量的大小来选择一个“合适的值”来简单区分FAT类型。如果磁盘容量小于该值就是FAT16;如果大于或等于该值,就是FAT32。Windows操作系统选择该值为512MB,任何小于512MB的卷都是FAT16卷,任何大于或等于512MB的卷都是FAT32卷。
       这里请特别注意,别过早下结论。
       有很多FAT16卷的容量都大于512MB,因为有很多不同的方法可以强制把磁盘格式化成FAT16格式而不是象默认的那样格式化成FAT32格式,并且不同的FAT程序遵循不同的规定来格式化磁盘。这里我们讨论的只是MS-DOS和Windows默认情况下是如何处理未格式化的磁盘。这里有两个表格,一个用于FAT16,一个用于FAT32。这个表格其中的一项是根据每扇区512字节的磁盘空间大小计算而来(该数值将写入到BPB_TotSec16或是BPB_TotSec32中),还有一个值用来设置BPB_SecPerClus。

struct DSKSZTOSECPERCLUS {
          DWORD   DiskSize;
          BYTE        SecPerClusVal;
};




DSKSZTOSECPERCLUS DskTableFAT16 [ ] = {
{ 8400, 0 },            / *磁盘容量最大为4.1MB,SecPerClusVal的值为0表示这是一个错误 */
{ 32680, 2 },          / *磁盘容量最大为16MB,1K Cluster */
{ 262144, 4 },        / *磁盘容量最大为128MB,2K Cluster */
{ 524288, 8 },       / *磁盘容量最大为256MB,4K Cluster */
{ 1048576, 16 },   / *磁盘容量最大为512MB,8K Cluster */
/ *除非强制使用FAT16,否则以下数据不使用 */ 
{ 2097152, 32 },    / *磁盘容量最大为1GB,16K Cluster */
{ 4194304, 64 },    / *磁盘容量最大为2GB,32K Cluster */
{ 0xFFFFFFFF, 0 }   / *磁盘容量超过2GB,SecPerClusVal的值为0意味着这是一个错误 */
};


DSKSZTOSECPERCLUS DskTableFAT32 [ ] = {
{ 66600, 0 },            / *磁盘容量最大为32.5MB,SecPerClusVal的值为0表示这是一个错误 */
{ 532480, 1 },         / *磁盘容量最大为260MB,5K Cluster */
{ 16777216, 8 },      / *磁盘容量最大为8GB,4K Cluster */
{ 33554432, 16 },    / *磁盘容量最大为16GB,8K Cluster */
{ 67108864, 32 },    / *磁盘容量最大为32GB,16K Cluster */
{ 0xFFFFFFFF, 64 },  / *磁盘容量超过了32GB,32K Cluster */
};


这样,只要给出磁盘的大小和FAT的类型就可以确定BPB_SecPerClus的值,现在我们唯一所缺少的就是FATSz16和FATSz32的大小。这里我们假设BPB_RootEntCnt,BPB_RsvdSecCnt和BPB_NumFATs的值已经按照上面的约定正确地设置。同时我们还假设DiskSize就是我们要写到BPB_TotSec32或是BPB_TotSec16的值。

RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytesPerSec - 1)) / BPB_BytesPerSec;

TmpVal1 = DskSize – (BPB_RsvdSecCnt + RootDirSectors);

TmpVal2 = (256 * BPB_SecPerClus) + BPB_NumFATs;

If (FATType == FAT32)
   TmpVal2 = TmpVal2 / 2;
   FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
If (FATType == FAT32)  {
    BPB_FATSz16 = 0;
    BPB_FATSz32 = FATSz;
}
else {
    BPB_FATSz16 = LOWORD(FATSz);
   
}


      不要花费太多功夫去琢磨上面的代码是如何工作的,这个算法的原理十分复杂,放在这里的目的只是说明Microsoft的操作系统是如何实现磁盘初始化的。但是,上面的代码并不完美,在一些偶然的情况下,它会使FAT16的FATSz比实际需要的要多出2个扇区,或使FAT32的FATSz多8个扇区,不过它永远不会使FATSz的值比实际需要的小。因为FATSz的值比实际的大并不会影响文件系统的正常工作,唯一的缺点就是造成很少一部分磁盘空间的浪费。由于以上的代码是如此的简单可靠,这在很大程度上弥补了它的不足。