FAT32组成
- 希望朋友们能指正文章错误,一起进步,谢谢。
#文件系统/fat32
FAT32系统大致可以分为4部分:
进一步划分,如下:
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卡只有一个分区。
- 结束标记
引导扇区一般都是以0X55AA结束(包括MBR、DBR、EBR、fsinfo等)。
DBR区:
#文件系统/fat32/dbr
- DBR:
#dbr
分区引导扇区(DOS Boot Record),占512字节(包括0x55AA的结束标记)。
高级格式化命令写到该扇区的内容,是由硬盘的MBR装载的程序段
注意,DBR结构与分区格式有关,NTFS与FAT32的DBR格式不同,FAT32详细信息如下:
EB 58 90代表 汇编跳转指令(JMP 58),注意计算跳转目标地址时,以该指令的下一字节为基准,该指令本身占用两个字节,所以实际执行的下一条指令应该位于5AH。紧接着跳转指令的是一条空指令NOP(90H)
Dirty bit: 就是0x41偏移,reserved3的值。
一个区分磁盘(识别身份)的方法:使用卷序列号区分(因为是随机生成)。
DBR区(包含保留区)大小:保留扇区数 x 扇区大小512 = FAT1的起始偏移
保留区:在第六和第七扇区有DBR和fsinfo备份(有的sd卡只备份DBR),其他的扇区貌似没有用到。(有的sd卡12扇区会有这样的信息,但是目前还不知道是干什么用的,有知道的朋友望指点一下)
- fsinfo 信息扇区:#fsinfo
FAT区
#文件系统/fat32/fat表
磁盘最小的数据存储单元为扇区,一般都是512字节大小;
数据簇(块),一般由8–64个扇区组成; 假设本文的簇大小为4K
用户文件,都是以簇为单位来进行存储,一个1k的文件,也占用的是4k的大小;10k的文件占用3个簇,而且这3个簇并不一定连续。 FAT表就是记录文件使用的簇情况的!
FAT表,每一个表项对应一个簇,长度为4个字节(对应32bit的簇地址),0号和1号表项系统保留。
实际数据从2号表项开始,一般根目录(目录也是文件),占用2号簇(表项值0x0FFFFFFF)
表项值,记录文件的下一个簇号,如果是最后一个簇,就标记为0x0FFFFFFF,表示文件结束。
如果未被使用,就是0。
如果簇中有坏扇区,就标记为0xFFFFFFF7,不会被使用。
详解如下:
下边是空SD卡只copy了一个readme.txt(8.9k)情况下的FAT表:
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
FAT32文件创建时间和修改时间(只能精确到2s级):
FAT32根据文件目录项判断文件名大小写的方法(文件目录项0CH字节的值。仅限8.3命名规则):
\1. 此值为18H时,文件名和扩展名都小写。
\2. 此值为10H时,文件名大写而扩展名小写。
\3. 此值为08H时,文件名小写而扩展名大写。
\4. 此值为00H时,文件名和扩展名都大写。
如果文件名大小写混合该怎么办?
lcase置0,使用长文件目录项表示。
如下图:
长文件目录项(LDE):#lde
一个长文件名的完整数据需要一个SDE和一系列的LDE组成,LDE逆序存放。
在LDE中存储文件名的话,不会保存文件的其他信息,所以这里重要的数据就是三部分的名字,长文件名是以NULL结束的,其后是以0xFFFF补充的。
如文件名为“The quick brown.fox”的磁盘结构
总结:
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 文件夹有什么作用?
7、windows上是如何获取SD卡容量的?嵌入式设备又该如何读取?
windows PC上是直接扫描FAT表的空余项来确定SD卡使用容量的,PC性能强劲,可以快速准确读出容量。
FAT32在windows上有一个bug:大量删除或者写入文件,大概率不更新fsinfo容量信息,所以嵌入式设备需要有一个容量矫正的机制,平时通过读取fsinfo来获取容量,但是挂载后要起线程统计SD卡中所有文件大小,根据和fsinfo比较来判定是否需要花费时间矫正容量。
8、数据恢复
文件删除原理:
- 文件目录项的首字节被改成E5,文件起始簇号的高位被清零
- FAT 对应的簇链被清零
- 数据区没有发生改变
文件簇链号连续,并且起始簇号高位为0的情况下数据是比较好恢复的;
但是如果簇链不连续 或者 起始簇号高位不为0的情况下,如何恢复?
这个没有特别好的办法,自认为比较靠谱的思路:
高位清0的问题:可参考 前后目录项,如果创建时间相等,可以“近似”认为簇高位和前后一致
簇号不连续: 这个没有特别好的办法,只能了解文件格式,遍历数据区,匹配文件标识,找到类似的数据然后拼接,比较辛苦。
9、那些只读的tf卡,和正常的卡哪里有区别???? 待解答
拓展:虚拟机服务器等不方便插卡的环境,可以用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