FAT32文件系统学习(1) —— BPB的理解

194 篇文章 9 订阅

origin: https://www.cnblogs.com/fantacity/p/3895771.html
 

FAT 32 文件系统学习

 

1、本文的目标

       本文将通过实际读取一个FAT32格式的U盘来简单了解和学习FAT32文件系统的格式。虽然目前windwos操作系统的主流文件系统格式是NTFS,但是FAT32由于其兼容性原因,还是有一定的学习价值。为了能做出一个窗体程序提供直观的感觉,本文的代码采用c#编写,对应的c++代码也会附上。

2、本文目录

1、本文的目标

2、什么是FAT32

2.1 FAT32的构成

3、引导区

3.1 读取引导扇区

3.2 BPB参数

3.3 程序实现

2、什么是FAT32

      FAT32是Windwos系统硬盘格式分区的一种。这种格式采用32位的文件分配表,使其对磁盘的管理能力大大增强,突破了FAT16对配一个分区的容量只有2GB的限制。虽然目前已被更优异的NTFS分区格式所取代[1]。其实说白了就是FAT表的每一项长度都是32位,所以叫做FAT32。至于每一项存放的内容是什么,下面的内容会慢慢进行分析。

  • FAT32的构成

       FAT32 文件系统将逻辑盘的空间划分为三部分,依次是引导区 (BOOT区)、文件分配表区(FAT区)、数据区(DATA区)[2]。引导区和文件分配表区又合称为系统区。本文将简单学习引导区的内容,文件分配表区和数据区将在下一篇文章中学习。

 3、引导区

  • 读取引导扇区

       引导区从第一扇区开始,保存了每个扇区的字节数,一个簇的扇区数,FAT表的起始位置,FAT表的个数以及FAT表的扇区数等信息。之后还留有若干保留扇区。首先我们先看一下如何从一个U盘当中读取引导区(这里只读取第一个扇区,接下来的读取方法相同)。为了直接读取磁盘的逻辑扇区,我们需要用到windows api当中的几个函数,分别是CreateFile(这里用来创建磁盘的句柄),ReadFile(这里用于读取磁盘扇区)。首先来看一下这两个函数的定义:

复制代码

1 HANDLE WINAPI CreateFile(
2   _In_      LPCTSTR lpFileName,                          // 要打开的文件的名或设备名。
3   _In_      DWORD dwDesiredAccess,                       // 指定类型的访问对象。
4   _In_      DWORD dwShareMode,                           // 文件共享模式
5   _In_opt_  LPSECURITY_ATTRIBUTES lpSecurityAttributes,  // 定义了文件的安全特性
6   _In_      DWORD dwCreationDisposition, 
7   _In_      DWORD dwFlagsAndAttributes, 
8   _In_opt_  HANDLE hTemplateFile                         // 返回句柄
9 );

复制代码

       这个函数在msdn上可以查到,这里需要说明一下的是,dwShareMode需要指定为FILE_SHARE_WRITE才能读取扇区的数据(这里是为什么我也不太清楚为什么,希望高人指教)。dwCreationDisposition需要制定为OPEN_EXISTING ,表示文件必须已经存在,由设备提出要求。函数如执行成功,则返回文件句柄。否则返回的句柄 = INVALID_HANDLE_VALUE表示出错,会设置GetLastError。具体失败原因可以查询ErrorCode。

       第二个函数是ReadFile,其定义如下:

复制代码

1 BOOL ReadFile(
2     HANDLE hFile,               //文件的句柄
3     LPVOIDl pBuffer,            //用于保存读入数据的一个缓冲区
4     DWORD nNumberOfBytesToRead, //要读入的字节数
5     LPDWORD lpNumberOfBytesRead,//指向实际读取字节数的指针
6     LPOVERLAPPED lpOverlapped
7     //如文件打开时指定了FILE_FLAG_OVERLAPPED,那么必须,用这个参  数引用一个特殊的结构。
8     //该结构定义了一次异步读取操作。否则,应将这个参数设为NULL
9 );

复制代码

       该函数用于读取文件,这里指的是读取U盘扇区。其中lpOverlapped设为NULL即可。需要特别注意的是,由于磁盘是以扇区为单位进行读写的,所以这里读取的字节数必须是512的倍数!其他参数注释写的很明白,这里就不再解释了。配合SetFilePointer函数可以读取指定起始位置上指定字节的数据。

       首先先来看一下用c++是如何读取引导扇区的数据的。

复制代码

 1     // 笔者的U盘盘符为G
 2     WCHAR szDiscFile[] = _T("\\\\.\\G:"); 
 3     // 打开设备句柄
 4     HANDLE hDisc = CreateFile(szDiscFile, GENERIC_READ,  FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
 5     if (hDisc == INVALID_HANDLE_VALUE)
 6     {
 7         // 打开设备失败
 8         return 0;
 9     }
10     // 从第一个扇区起始位置开始读取
11     SetFilePointer(hDisc, 0, 0, FILE_BEGIN);
12     // 需要读取字节数
13     DWORD dwNumber2Read = 512;
14     // 实际读取的字节数
15     DWORD dwRealNumber;
16     // 分配缓冲区
17     char* buffer = new char[512];
18     bool bRet = ReadFile(hDisc, buffer, dwNumber2Read, &dwRealNumber, NULL);
19 
20     // 扫尾工作,释放缓冲区,关闭句柄
21     delete[] buffer;
22     CloseHandle(hDisc);

复制代码

        如果我们在21行处下各断点的话可以看到buffer在内存中的数据,这里为了方便,笔者直接把这512个字节的数据保存到了文件中,用二进制文件读取软件打开来看了下,部分数据如下图所示:

  • BPB参数

       好了,接下来重点来了。首先,最开始的3各字节的数据分别是跳转指令与空指令,因为在汇编当中0xEB是跳转指令,0x58是跳转的地址,而0x90则是空指令。至于为什么要在这里放上一句跳转指令呢,这个还得从启动区开始讲起,为了节约篇幅,我就简单介绍一下:一般第一个扇区叫做启动区,cpu把扇区当中的数据当作指令来执行,当读取到EB 58 这个指令时,遍跳转到0x58这个地址并继续读取指令来执行,而0x58地址之后的内容通常都是载入操作系统的指令。如果希望知道详细内容的读者不妨去看一下《30天自制操作系统》这本书,第一天结尾部分有很详细的说明。总之这边的话FAT32规定这个3各字节的内容必须是EB 58 90,只要记住就行了(笑)。(如1L所说,EB 58 90 对应汇编代码即为JUMP 0x58; NOP;)。

       而从0x03~0x0A这8个字节的数据表示OEM,这里即为“MSDOS5.0”。

       我们把从0x000B开始的79个字节的数据叫做BPB(BIOS Paramter Block),关于BPB的详细说明请参见下表[5]:

BPB参数信息
偏移量字节数含义
0x00B2每扇区字数0x0200
0x00D1每簇扇区数0x08
0x00E2保留扇区数0x03F8
0x0101FAT个数0x02
0x0112根目录项数,FAT32以突破该限制,无效 0x0000 
0x0132扇区总数,小于32M使用 0x0000 
0x0151存储介质描述负 0x0F8 
0x0162每FAT表占用扇区数 ,小于32M使用0x0000 
0x0182逻辑每磁道扇区数 0x003F 
0x01A2逻辑磁头数 0x00FF
0x01C4系统隐含扇区数 0x00000080
0x0204扇区总数,大于32M使用 0x00784F80
0x0244每FAT表扇区数,大于32M使用 0x00001E04 
0x0282标记 0x0000
0x02A2版本 (通常为零)0x0000
0x02C4根目录起始簇 0x00000002 
0x0302Boot占用扇区数 0x0001 
0x0322备份引导扇区位置 0x0006
0x03414保留14个字节的0x00 
0x0421扩展引导标记 0x29 
0x0434序列号 0x6A9C4125 
0x04710卷标转成字符即“NO NAME”
0x0528文件系统 转成字符即“FAT32” 

 

        我来解释一下其中的几个参数。首先是保留扇区数,它可以理解为是FAT表的起始位置。我们可以先来看一下下面这张图,有助于更好的理解保留扇区数的意思。

       可以看到引导扇区后面紧跟这若干保留扇区,至于保留扇区的作用会在下一篇中分析,这里先跳过。而保留扇区的后面紧跟着的是FAT1和FAT2。所以FAT1表的起始地址是(引导扇区+保留扇区)*扇区字节数?不对。这里有个需要注意的地方是,保留扇区数这个参数其实已经包含了引导扇区了,所以在计算FAT1表位置的时候直接通过保留扇区数这个参数来计算偏移就行了。这一点需要特别注意。(这里笔者看了几篇文献的说法不一,有说需要加上保留扇区的,有说不用加的,可能是版本不一样的关系。但是笔者亲自实践之后发现是不需要加上的。)

       至于FAT表留到下一篇再讲,这里说明一下为什么FAT会有两张。文件分配表区共保存了两个相同的文件分配表,因为文件所占用的存储空间(簇链)及空闲空间的管理都是通过FAT实现的,FAT如此重要,保存两个以便第一个损坏时,还有第二个可用[4]。这样FAT表个数这个参数也解释了。

  • 程序实现

        为了用窗体程序直观的显示各个参数,接下来把上面的程序改写为c#。众所周知,c#调用系统函数大多都需要靠间接调用c的动态库来实现,这里的CreateFile和ReadFile也不例外。下面我们先编写一个FileReader类来提供这些系统api调用。部分代码如下,完整的FileReader点我下载

复制代码

class FileReader
{
  [System.Runtime.InteropServices.DllImport("kernel32", SetLastError = true, ThrowOnUnmappableChar = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
    static extern unsafe System.IntPtr CreateFile
    (
        string FileName,          // file name
        uint DesiredAccess,       // access mode
        uint ShareMode,           // share mode
        uint SecurityAttributes,  // Security Attributes
        uint CreationDisposition, // how to create
        uint FlagsAndAttributes,  // file attributes
        int hTemplateFile         // handle to template file
    );

  public bool Open(string FileName)
    {
        // open the existing file for reading       
        handle = CreateFile
        (
            FileName,
            GENERIC_READ,
            FILE_SHARE_WRITE,
            0,
            OPEN_EXISTING,
            0,
            0
        );

        if (handle != System.IntPtr.Zero)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

复制代码

        由于本文的重点不是学习c#窗体编程,所以略过这部分,直接通过调用FileReader提供的系统API并编写窗体程序,效果如下图所示(本程序参考[3]),需要完整工程的读者可以点我下载,请我VS2012及以上打开。

       好了,今天这篇文章就先写到这边。由于笔者才疏学浅,对于FAT32也是刚开始学习,如果有错误的地方欢迎批评指正。同时这也是我第一次发随笔,如果排版等方面有不妥的地方也欢迎提出。接下来会继续学习接下去的FAT表区和数据区并继续更新接下来的学习心得。

 

参考文献:

1、http://baike.baidu.com/view/45233.htm?fr=aladdin

2、FAT32文件格式 http://blog.csdn.net/shrekmu/article/details/5950414

3、读写U盘(FAT32)引导扇区 http://blog.csdn.net/zhanglei8893/article/details/5912903

4、F​A​T​3​2​文​件​系​统​格​式​详​解 http://wenku.baidu.com/link?url=zrGv8nld-bc-7KT_TKbo2vWplaiIHhmJ9_ydRZBZdZ4zy8odQFwS6komz2gz1AHX36T_EN1CKZ_16d19upW9pDauno6zEmpw10wlTSTwcoi

5、基​于​U​盘​F​A​T​3​2​文​件​系​统​的​分​析 http://wenku.baidu.com/link?url=cIKgrwV66y4CoyuOEB1-OhjRY9tnXtIAoZuYEwDCjxbyRomSIiJgBAXGxq6LudfwuopUpYhiVd8TjxrBFoVyPs0NX3OqbnoWjyn4ZAx60Wi

分类: c#,c++,操作系统

 

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值