FAT32组成

  • 希望朋友们能指正文章错误,一起进步,谢谢。

#文件系统/fat32

FAT32系统大致可以分为4部分:

cubemx fatfs 长文件名 fat32文件名长度_cubemx fatfs 长文件名

进一步划分,如下:

cubemx fatfs 长文件名 fat32文件名长度_cubemx fatfs 长文件名_02

SD卡,windows下用winhex工具查看;linux下读取/dev/sdb1(节点名字视情况),发现SD卡都是以DBR开始的,MBR信息在哪里?

以linux为例,MBR信息是在/dev/sdb节点下,直接从这个设备文件节点读512字节的数据,就是MBR信息。 /dev/sdb1只是磁盘分区节点


MBR区

#文件系统/fat32/mbr

  • MBR: 主引导记录(Main Boot Record),占446字节。
    负责磁盘操作系统(DOS)对磁盘进行读写时分区合法性的判别、分区引导信息的定位,它由磁盘操作系统(DOS)在对硬盘进行初始化时产生。
  • DPT: 磁盘分区表(Disk Partition Table),每一项占16字节,一般SD卡只有一个分区。

cubemx fatfs 长文件名 fat32文件名长度_cubemx fatfs 长文件名_03

  • 结束标记
    引导扇区一般都是以0X55AA结束(包括MBR、DBR、EBR、fsinfo等)。

DBR区:

#文件系统/fat32/dbr

  • DBR: #dbr分区引导扇区(DOS Boot Record),占512字节(包括0x55AA的结束标记)。
    高级格式化命令写到该扇区的内容,是由硬盘的MBR装载的程序段
    注意,DBR结构与分区格式有关,NTFS与FAT32的DBR格式不同,FAT32详细信息如下:

cubemx fatfs 长文件名 fat32文件名长度_文件目录_04

EB 58 90代表 汇编跳转指令(JMP 58),注意计算跳转目标地址时,以该指令的下一字节为基准,该指令本身占用两个字节,所以实际执行的下一条指令应该位于5AH。紧接着跳转指令的是一条空指令NOP(90H)

Dirty bit: 就是0x41偏移,reserved3的值。

一个区分磁盘(识别身份)的方法:使用卷序列号区分(因为是随机生成)。

DBR区(包含保留区)大小:保留扇区数 x 扇区大小512 = FAT1的起始偏移

保留区:在第六和第七扇区有DBR和fsinfo备份(有的sd卡只备份DBR),其他的扇区貌似没有用到。(有的sd卡12扇区会有这样的信息,但是目前还不知道是干什么用的,有知道的朋友望指点一下)

cubemx fatfs 长文件名 fat32文件名长度_数据_05

  • fsinfo 信息扇区:#fsinfo

cubemx fatfs 长文件名 fat32文件名长度_文件目录_06


FAT区

#文件系统/fat32/fat表
磁盘最小的数据存储单元为扇区,一般都是512字节大小;

数据簇(块),一般由8–64个扇区组成; 假设本文的簇大小为4K

用户文件,都是以簇为单位来进行存储,一个1k的文件,也占用的是4k的大小;10k的文件占用3个簇,而且这3个簇并不一定连续。 FAT表就是记录文件使用的簇情况的!

FAT表,每一个表项对应一个簇,长度为4个字节(对应32bit的簇地址),0号和1号表项系统保留。

实际数据从2号表项开始,一般根目录(目录也是文件),占用2号簇(表项值0x0FFFFFFF)

表项值,记录文件的下一个簇号,如果是最后一个簇,就标记为0x0FFFFFFF,表示文件结束。

如果未被使用,就是0。

如果簇中有坏扇区,就标记为0xFFFFFFF7,不会被使用。

详解如下:

cubemx fatfs 长文件名 fat32文件名长度_文件目录_07

下边是空SD卡只copy了一个readme.txt(8.9k)情况下的FAT表:

cubemx fatfs 长文件名 fat32文件名长度_文件目录_08

FAT表占据扇区数

= 簇总数 * 4 Bytes/ 扇区占据字节数

定位FAT绝对位置的方法如下:

1、首先从MBR的分区表中得知分区的起始扇区,例如[0x1c6],偏移到此扇区。

2、然后从DBR中得知DBR的保留扇区数,FAT表的个数,FAT表的大小。

3、因此FAT1=分区起始扇区+DBR保留扇区

FAT2=分区起始扇区+DBR保留扇区+FAT1

数据区:

** 所有的簇从2开始进行编号(也就是说根目录是第一个簇) **

有一个疑问:0、1簇存的是什么东西? 猜测可能是引导扇区 + FAT表

— 目录区:

(1)目录所在的扇区,以32 Bytes划分为一个单位,每个单位称为一个目录项。

(2)如果文件名>8 byte 或者后缀名>3, 则为长文件目录,否则为短文件目录

(3)目录项被删除,只是第一个字节被设为0xE5,实际数据没有被删除(数据恢复原理)

短文件目录项(SDE):#sde

cubemx fatfs 长文件名 fat32文件名长度_文件名_09

FAT32文件创建时间和修改时间(只能精确到2s级):

cubemx fatfs 长文件名 fat32文件名长度_cubemx fatfs 长文件名_10

FAT32根据文件目录项判断文件名大小写的方法(文件目录项0CH字节的值。仅限8.3命名规则):

\1. 此值为18H时,文件名和扩展名都小写。

\2. 此值为10H时,文件名大写而扩展名小写。

\3. 此值为08H时,文件名小写而扩展名大写。

\4. 此值为00H时,文件名和扩展名都大写。

如果文件名大小写混合该怎么办?

lcase置0,使用长文件目录项表示。

如下图:

cubemx fatfs 长文件名 fat32文件名长度_cubemx fatfs 长文件名_11

长文件目录项(LDE):#lde

一个长文件名的完整数据需要一个SDE和一系列的LDE组成,LDE逆序存放。

cubemx fatfs 长文件名 fat32文件名长度_数据_12

在LDE中存储文件名的话,不会保存文件的其他信息,所以这里重要的数据就是三部分的名字,长文件名是以NULL结束的,其后是以0xFFFF补充的。

如文件名为“The quick brown.fox”的磁盘结构

cubemx fatfs 长文件名 fat32文件名长度_文件目录_13

cubemx fatfs 长文件名 fat32文件名长度_cubemx fatfs 长文件名_14

总结:

1、根据磁盘0号扇区MBR的分区表得知分区的起始位置,既DBR;

2、根据DBR中BPB记录的信息,得知DBR保留扇区数,FAT的大小,FAT的个数;

3、根据上述信息可以算出数据的起始位置,数据区=分区起始扇区+DBR保留扇区+(FAT表 * 2);

4、计算根目录所在的绝对位置,根目录=数据区的起始扇区+(簇大小 * 2);

5、根据根目录中的目录项信息得知,根目录下的文件以及子目录等所对应的簇;

6、根据文件的簇号就可以找到文件内容的绝对扇区;

7、如果一个文件占用多个簇,则需要根据FAT表项得知下一个数据簇的簇号。

7、如果根目录下的目录项是子目录的话,则根据子目录中的文件目录项得知文件内容的簇号;

8、如果子目录中还有子目录的话,则根据这种方法一直找下去即可。

疑问:

1、目录文件数量超级多, 一个cluster放不下如何处理?

目录和普通文件一样,可以占有多个空闲数据块,形成簇链。

2、stat函数是否读取的是fsinfo?

是。

3、FAT32最大容量?

DBR中total sector字段是32位,也就是说最多只能有2^32个扇区,这样算最多不超过2T。

4、FAT32文件名长度?(长目录项0x40算,最大长度不能超过800+)

Linux文件名长度限制为255个Byte

Windows文件名长度限制为260个Byte,目录名必须小于248个Byte

5、SD卡中的 System Volume Information 文件夹有什么作用?

cubemx fatfs 长文件名 fat32文件名长度_文件名_15

7、windows上是如何获取SD卡容量的?嵌入式设备又该如何读取?

windows PC上是直接扫描FAT表的空余项来确定SD卡使用容量的,PC性能强劲,可以快速准确读出容量。

FAT32在windows上有一个bug:大量删除或者写入文件,大概率不更新fsinfo容量信息,所以嵌入式设备需要有一个容量矫正的机制,平时通过读取fsinfo来获取容量,但是挂载后要起线程统计SD卡中所有文件大小,根据和fsinfo比较来判定是否需要花费时间矫正容量。

8、数据恢复

文件删除原理:

  1. 文件目录项的首字节被改成E5,文件起始簇号的高位被清零
  2. FAT 对应的簇链被清零
  3. 数据区没有发生改变

文件簇链号连续,并且起始簇号高位为0的情况下数据是比较好恢复的;

但是如果簇链不连续 或者 起始簇号高位不为0的情况下,如何恢复?

这个没有特别好的办法,自认为比较靠谱的思路:

高位清0的问题:可参考 前后目录项,如果创建时间相等,可以“近似”认为簇高位和前后一致

簇号不连续: 这个没有特别好的办法,只能了解文件格式,遍历数据区,匹配文件标识,找到类似的数据然后拼接,比较辛苦。

9、那些只读的tf卡,和正常的卡哪里有区别???? 待解答

cubemx fatfs 长文件名 fat32文件名长度_数据_16

拓展:虚拟机服务器等不方便插卡的环境,可以用dd命令来创建一个虚拟的fat32文件来调试,方法如下:

/* 首先用dd命令创建一块4G大小的模拟文件 */
lucky@DESKTOP-FOM81C2:~$ sudo dd if=/dev/zero of=sdcard.img bs=512 count=8388608 //块大小512 总共count个块

/* 格式化img文件为FAT32, 如果没有mkfs.vfat可以install dosfstools */
lucky@DESKTOP-FOM81C2:~$ sudo mkfs.vfat -F 32 sdcard.img //如果不指定-F 32,有的程序会格式化为FAT16

lucky@DESKTOP-FOM81C2:~$ hexdump -C -s 0 -n 512 sdcard.img
00000000  eb 58 90 6d 6b 66 73 2e  66 61 74 00 02 08 20 00  |.X.mkfs.fat... .|
00000010  02 00 00 00 00 f8 00 00  20 00 40 00 00 00 00 00  |........ .@.....|
00000020  cc cc 0c 00 38 03 00 00  00 00 00 00 02 00 00 00  |....8...........|
00000030  01 00 06 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  80 00 29 eb 5c 6c b7 4e  4f 20 4e 41 4d 45 20 20  |..).\l.NO NAME  |
00000050  20 20 46 41 54 33 32 20  20 20 0e 1f be 77 7c ac  |  FAT32   ...w|.|
00000060  22 c0 74 0b 56 b4 0e bb  07 00 cd 10 5e eb f0 32  |".t.V.......^..2|
00000070  e4 cd 16 cd 19 eb fe 54  68 69 73 20 69 73 20 6e  |.......This is n|
00000080  6f 74 20 61 20 62 6f 6f  74 61 62 6c 65 20 64 69  |ot a bootable di|
00000090  73 6b 2e 20 20 50 6c 65  61 73 65 20 69 6e 73 65  |sk.  Please inse|
000000a0  72 74 20 61 20 62 6f 6f  74 61 62 6c 65 20 66 6c  |rt a bootable fl|
000000b0  6f 70 70 79 20 61 6e 64  0d 0a 70 72 65 73 73 20  |oppy and..press |
000000c0  61 6e 79 20 6b 65 79 20  74 6f 20 74 72 79 20 61  |any key to try a|
000000d0  67 61 69 6e 20 2e 2e 2e  20 0d 0a 00 00 00 00 00  |gain ... .......|
000000e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200
  
/* 查询用户的uid和gid */
lucky@DESKTOP-FOM81C2:~$ id lucky
uid=1000(lucky) gid=1000(lucky) groups=1000(lucky)......

/* 挂载这个文件到指定位置, 挂载时需要指定uid和gid,否则读写权限不够 -o为指定挂载选项 */
lucky@DESKTOP-FOM81C2:~$ sudo mount /home/lucky/sdcard.img /mnt/mmc01 -o rw,uid=xxxx,gid=xxxx

lucky@DESKTOP-FOM81C2:~$ mount
/home/lucky/sdcard.img on /mnt/mmc01 type vfat (rw,relatime,uid=1000,gid=1000,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro)

/* 确定分区节点 */
lucky@DESKTOP-FOM81C2:~$ df -h //信息如下/dev/loop16 和 /home/lucky/sdcard.img 都可以作为分区节点使用,hexdump查看内容是一样的
文件系统        容量   已用    可用 已用% 挂载点
/dev/loop16    4.0G    0   4.0G   0%  /mnt/mmc01