经典FAT文件系统格式详解

FAT/FAT32曾经是windows下主流文件格式,虽然FAT已经这么多年了,也见识到一些缺点,但熟悉FAT,还是对文件系统认识有很大帮助。

 

一般来说,需要具备一些前期知识:

1. 文件存在flash或硬盘里,还是一个个字节进行存储的,存储介子本身不负责具体内容。如果要对硬盘的数据进行识别,必须需要一定格式,还需要一个驱动程序进行识别。

2. 文件格式最主要作用:格式化管理,快速查找文件

 

还有几个知识点:

扇区:一般为512,最小分割单位,但不是最小管理单位。

簇:管理最小单位,一般是由多个扇区组成,有248163264几种情况。如果=16,即8K=16*512,也就是说一个文件最小也得占8K空间

DBR及保留扇区FAT16不存在)、文件分配表区(FAT1FAT2)、数据区(DATA区)。

反过来说,一个文件至少占一族,或多簇。最后一簇往往是未存满的,这样肯定会带来空间的浪费。(但也没太好办法,毕竟是要快速查找文件的)

当然,这个可以根据实际情况来设定,毕竟应用场景不一样。

 

FAT16的根目录区只有32个扇区,计算一下,每个扇区512字节,共32个扇区,而每个文件名至少要占用32个字节(后面会介绍),很显然,根目录最多只能放512个文件了。

      FAT16 这里存在致命缺陷的:

例如:我们的文件是存在根目录的,那么最多也只能存放512个文件(假设文件名又很长的话,这个数量就更小了)

 FAT32对这方面进行了优化,可以支持更多的文件数量,但格式变得复杂了,查找时也是先从前面扇区往后跳(会根据目录最后指向下一个扇区地址),所以时间上也浪费很多,搜索起来非常慢

 

FAT大体格式:

《经典FAT文件系统格式详解》

这里:FAT表有两个,一般FAT2为镜像。FAT表非常重要,一般如果文件名乱码,或一下子消失了,往往是FAT表坏掉了。

MBR:  Main Boot Record 主引导记录区

DBR:Dos Boot Record 操作系统引导记录区

 

 

以下,是我对FAT16格式注解:

FAT格式:(FAT16)

《经典FAT文件系统格式详解》

《经典FAT文件系统格式详解》

 

FAT32格式:

《经典FAT文件系统格式详解》

《经典FAT文件系统格式详解》

《经典FAT文件系统格式详解》

FAT表格式:

《经典FAT文件系统格式详解》

FAT表之后就是目录区:

 

目录区是由一个个目录项构成,类似于FAT表。其中每一个目录项占用32个字节,可以是代表长文件名目录项、文件目录项、子目录项等。对于短文件名格式的目录项,其参数的含义如1所示(不会画这种表,从别处引用了一个)[1]

《经典FAT文件系统格式详解》

通过这些信息,也基本知道文件信息,比如创建信息,修改信息等

也可以看到有两个限制:

1. 文件名的长度为8字节,那长一点文件名怎么办?

2.扩展名为3字节,这个虽然是限制,但实际还可以接受。

其实,这个历史原因导致的,原先Dos系统下就根本没考虑中文这种场景(现在windows系统都是基于DOS发展起来)

 

那长一点文件名怎么办?

先看下FAT32对长文件名的处理:

表15   FAT32长文件目录项32个字节的表示定义

字节偏移
(16进制)

字节数

定义

0x0

1

属性字节位意义

7

保留未用

6

1表示长文件最后一个目录项

5

保留未用

4

顺序号数值

3

2

1

0

0x1~0xA

10

长文件名unicode码①

0xB

1

长文件名目录项标志,取值0FH

0xC

1

系统保留

0xD

1

校验值(根据短文件名计算得出)

0xE~0x19

12

长文件名unicode码②

0x1A~0x1B

2

文件起始簇号(目前常置0)

0x1C~0x1F

4

长文件名unicode码③

 

  • 0x00~0x00:1 个字节,长文件名目录项的序列号,一个文件的第一个目录项序列号为 1,然后依次递增。如果是该文件的最后一个长文件名目录项,则将该目录项的序号与 0x40 进行“或(OR)运算”的结果写入该位置。如果该长文件名目录项对应的文件或子目录被删除,则将该字节设置成删除标志0xE5。
  • 0x01~0x0A:10 个字节,长文件名的第 1~5 个字符。长文件名使用 Unicode 码,每个字符需要两个字节的空间。如果文件名结束但还有未使用的字节,则会在文件名后先填充两个字节的“00”,然后开始使用 0xFF 填充。
  • 0x0B~0x0B:1 个字节,长目录项的属性标志,一定是 0x0F。
  • 0x0C~0x0C:保留。
  • 0x0D~0x0D:1 个字节,校验和。如果一个文件的长文件名需要几个长文件名目录项进行存储,则这些长文件名目录项具有相同的校验和。
  • 0x0E~0x19:12 个字节,文件名的第 6~11 个字符,未使用的字节用 0xFF 填充。
  • 0x1A~0x1B:2 个字节,保留。
  • 0x1C~0x1F:4 个字节,文件名的第 12~13 个字符,未使用的字节用 0xFF 填充。

总共这个也是32字节(跟短文件名一致),那么如何区别?关键看0B位置,如果是=0F,那么就是长文件名。

长文件名的实现有赖于目录项偏移为0xB的属性字节,当此字节的属性为:只读、隐藏、系统、卷标,即其值为0FH时,DOS和WIN32会认为其不合法而忽略其存在。这正是长文件名存在的依据。

长文件名中的字符采用unicode形式编码(一个巨大的进步哦),每个字符占据2字节的空间。

从上表中可以获得文件名长度:0x01~0x0A:10字节;  +   0x0E~0x19 12字节; + 0x1C~0x1F 4字节;

总共26字节,即13个字符!(被拆七零八落,有点可怜)

 

那么怎么表达一个长文件名呢?从以上两个表来看,都无法做到兼容

解决办法:短文件名(32)+长文件名(32)….

规则:

   (1)、取长文件名的前6个字符(第1个字符改成0x01)加上”~1″形成短文件名,扩展名不变。(放在第1组32字节)
    (2)、如果已存在这个文件名,则符号”~”后的数字递增,直到5。(如果是看二进制是可以看到这些奇怪的名称,有时候在文件fat表破坏掉之后也会出来~1这样的奇怪文件)
    (3)、如果文件名中”~”后面的数字达到5,则短文件名只使用长文件名的前两个字母。通过数学操纵长文件名的剩余字母生成短文件名的后四个字母,然后加后缀”~1″直到最后(如果有必要,或是其他数字以避免重复的文件名)。
    (4)、如果存在老OS或程序无法读取的字符,换以”_”

    (5)、从第2组32字节开始,以13字节为一组,分别表示的文件名。如果存在多组,则以倒叙的办法表示,前面的是文件名末尾部分。

 

有点懵,举个例子说明下更清晰:

假设一个文件,文件名为 ZHUANCHU-12345678-20190910H095830.bin

文件名实际长度=33,那么/13,需要三组长文件名来表示。

3组H095830         不足部分用FF替代
2组5678-20190910
1组 ZHUANCHU-1234

这个格式参考上面的 《长文件名格式》

 

那么FAT格式文件名大概是这样的:

《经典FAT文件系统格式详解》

其中0F表示长文件名的标志。

96表示短文件名校验值(三组都一样)

红色框起来的是文件名的分组。根据长文件名格式定义,被拆分成不同地方。

 

好了。我们再回到文章前面的,计算下,如果我们的文件名大概都是这样的:“ZHUANCHU-12345678-20190910H095830.bin”,只是数字不一样

那么FAT根目录下最多可以存放多少个这样的文件?(假设全部文件容量要大大小于存储空间)

一个文件名占 3长+1短,共4个坑。总共512个坑,那最多只能放128这样的文件!

这也是,为什么我们存储空间是够的,但存储是失败的原因~~

 

so,根目录的文件名可不是随意的,建议小于8字节最佳!(这样只要一组短文件名就可以了)~~

 

搞定了文件名的含义,我们再来看看文件内容存放(实际就是关注起始地址)

 

文件起始地址

文件起始地址(文件内容) = (保留扇区数 + FAT表扇区数 * FAT表个数(2) + (文件起始簇号-2)*每簇扇区数)*每扇区字节数   

 

文件起始簇号:(在短文件名里)高在前

《经典FAT文件系统格式详解》

实际计算:感觉文件起始簇号不用-2OK

例如下列文件  实验1:

《经典FAT文件系统格式详解》

数据区偏移地址:ADDR=(8+256*2+(0x09C3 – 2)*16)* 512= 20,721,664=0x013C3000

根目录有 32*512=0x4000 (固定32个扇区表示目录,内容区)

实际地址=0x013C3000+0x4000=0x013C7000

《经典FAT文件系统格式详解》

 

实验2:

假设 rec/文件夹有一个文件为:888888.txt 里面内容为 “jjw”

来分析下,子文件/rec下一个888888.txt文件(实际跟放在哪里没有关系)

《经典FAT文件系统格式详解》

数据区偏移地址:ADDR=(8+256*2+(0x09C4 – 2)*16)* 512= 20,729,856= 0x013C5000

实际地址=0x013C5000+0x4000=0x013C9000

《经典FAT文件系统格式详解》

 

再来说下,时间信息格式:

日期时间,可以表示创建信息,修改信息,访问信息

格式都一样,这里以创建信息举例:

0x0E~0x0F 

文件创建时间 

0x785C = (0111100001011100)(2进制)

即为 15:02:57(注释1)

0x10~0x11

文件创建日期

0x4508 = (0100010100001000)(2进制)

即为 2014/8/8(注释2)

        注释101111 000010 11100

       1)这里高5位代表小时,由于2^5 = 32,足够表示24小时,这边01111(2进制) = 15(10进制)

       2)6位代表分钟,同理2^6 = 64,足够表示60分钟,这边000010(2进制) = 2

       3)5位表示秒的1/2 计算结果需要加上毫秒位上的进位,这边11100(2进制) = 28(10进制),所以秒数 = 28*2 = 56,再加上毫秒上的进位1所以结果为57

 

       注释20100010 1000 01000

       1)这里高7位代表从1980年开始的年数,笔者计算了下可以到2108年,总之还有90多年可以使用,这边0100010(2进制) = 34,所以年份 = 1980+34 = 2014

       2)4位代表月份2^4=16,可以表示12个月份,这边 1000(2进制) = 8(10进制)

       3)5位代表日期2^5 = 32,可以表示28~31天,这边 01000(2进制) = 8(10进制)

 

这些信息,都蛮奇怪了。不过经常接触嵌入式,也就不奇怪了。

 

    原文作者:小刚中国
    原文地址: https://blog.csdn.net/winafa/article/details/115319185
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞