在FAT32文件系统中,文件名分为两种——长文件名、短文件名。这两种文件名都是存储在目录入口中的(DIR_ENTRY简写DE)中的,不过具体的DE组织结构和数据结构有所不同。
1、 短文件名
短文件名存储在短文件名入口(SHORT_DIR_ENTRY简写SDE)中,而且一个短文件名只需要一个SDE即可。
一个SDE大小为32字节,他的具体定义如下表:
名字 |
偏移 (字节) |
大小 (字节数) |
描述 |
mName |
0 |
11 |
短文件名(文件名和扩展名) |
mAttr |
11 |
1 |
文件属性: ATTR_READ_ONLY 0x01 ATTR_HIDDEN 0x02 ATTR_SYSTEM 0x04 ATTR_VOLUME_ID 0x08 ATTR_DIRECTORY 0x10 ATTR_ARCHIVE 0x20 ATTR_LONG_NAME ATTR_READ_ONLY| ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID 高两位属性保留,且应该设置为0,文件创建后不应该修改这两个位 |
mNameCase |
12 |
1 |
这个是段文件名的大小标志: FNAME_LOWER_CASE 0x08——01000 FEXT_NAME_LOWER_CASE 0x10——10000 也就是说如果文件名是小写的话01000,而如果扩展名是小写的话则设置10000,而如果文件名和扩展名都是小写的则11000(0x18)。(这个字节的含义只是我不完全统计的结果) |
mCrtTimeTenth |
13 |
1 |
{文件创建时间的毫秒部分,这是一个十分之一秒的数量,其有效值是0-199,我基本没有使用} |
mCrtTime |
14 |
2 |
文件创建的时间 |
mCrtDate |
16 |
2 |
文件创建的日期 |
mLstAccDate |
18 |
2 |
文件最后的访问日期 |
mFstClusHI |
20 |
2 |
这个文件起始簇号的高两个字节 |
mWrtTime |
22 |
2 |
文件最后写的时间,创建也可以说成是写 |
mWrtDate |
24 |
2 |
文件最后写的日期,创建也可以说成是写 |
mFstClusLO |
26 |
2 |
这个文件的起始簇号的低两个字节 |
mFileSize |
28 |
4 |
文件大小(字节数) |
在SDE的前11个字节包含了8个字节的文件名和3个字节扩展名(在磁盘上不显式在文件名和扩展名中间保存一个分割符”.”),也就是说短文件需要符合8.3规则。8.3规则具体如下
a、 文件名不得长于8个字节
b、 扩展名不得长于3个字节
c、 文件名、扩展名必须是ASCLL中0x80前的字符(也就是说不得有中文等)
d、 文件名中的字符必须同时大写或者小写
e、 扩展名中的字符必须同时大小或者小写
f、 可以没有扩展名但是必须有文件名(这一点仿佛有问题,在如果系统盘是FAT32,则会看到一个名为“.rnd”)。
g、 文件名前、扩展名后不得有空格。
h、 除了在第一个字符可以为0x05以外,其他字符不可以是小于0x20的字符
i、 文件名扩展中不得包含如下字符0x22(”), 0x2A(*), 0x2B(+), 0x2C(,), 0x2E(.), 0x2F(/), 0x3A(:), 0x3B(;), 0x3C(<), 0x3D(=), 0x3E,(>) 0x3F(?), 0x5B([), 0x5C(/), 0x5D(]), and 0x7C(}).
mName还有如下情况
A、如果mName [0] == 0xE5,表示当前入口是已经删除了的。
B、 如果mName [0] == 0x00,表示当前入口没有使用过,在这以后也没有分配目录入口了,如果是遍历的话,到这里就可以结束了。
C、 如果mName [0] == 0x05,则实际文件名在这里的字符应该是0xE5。0xE5是 KANJI 引导字符。
D、前面说了mName实际分为两各部分8-字节文件名,和3-字节扩展名。如果文件名或扩展名字符数不够的话则以空格(0x20)填充。
E、 在mName中的英文字符都是以大写的形式存储。
以下是一个例子
“foo.bar” -> “FOO BAR”
“FOO.BAR” -> “FOO BAR”
“Foo.Bar” -> “FOO BAR”
“foo” -> “FOO “
“foo.” -> “FOO “
“PICKLE.A” -> “PICKLE A “
“prettybg.big” -> “PRETTYBGBIG”
“.big” -> illegal, DIR_Name[0] cannot be 0x20(其实应该是可以的)
在这里好有一个需要注意的就是时间和日期格式。
时间(mCrtTime、mWrtTime)具体由两个字节(16位)表示,其精度为2秒(0是最低有效位,15是最高有效位)
0-4位:其有效值是0-29,表示两秒数量,表示实际的秒为0-58
5-10位:有效值是0-59,表示分钟
11-15位:有效值是0-23,表示小时
日期(mCrtDate、mLstAccDate、mWrtDate)具体由两个字节表示(16位),这是一个相对于 01/01/1980的一个日期(0是最低有效位,15是最高有效位)
0-4位:有效值是1-31,表示日
5-8位:有效值是1-12,表示月
9-15位:有效值0-127,相对于1980的年号(也就是说最大的年号是2107年)
2、 长文件名
长文件名存储在长文件名入口(LONG_DIR_ENTRY简写LDE)中,通常一个长文件名的完整数据需要一个SDE和一系列的LDE组成,LDE逆序存放。
一个LDE大小为32字节,他的具体定义如下表:
名字 |
偏移 (字节) |
大小 (字节数) |
描述 |
mOrder |
0 |
1 |
这表示当前DE在LDE序列中的序号 如果这个值是和0x40相与的结果,则表示这是LDE序列中最后一个LDE了 |
mName1 |
1 |
10 |
LDE中的前1-5个字符,Unicode。 |
mAttr |
11 |
1 |
文件属性,只能是ATTR_LONG_NAME(0x0F) |
mNameCase |
12 |
1 |
在这里一直为0 |
mChksum |
13 |
1 |
校验和。这个值由于此对应的SDE的前11字节计算而得,具体计算等下再讲。 |
mName2 |
14 |
12 |
LDE中的前6-11个字符,Unicode。 |
mFstClusLO |
26 |
2 |
0 |
mName3 |
28 |
4 |
LDE中的前12-13个字符,Unicode。 |
在LDE中存储文件名的话,不会保存文件的其他信息,所以这里重要的数据就是三部分的名字,长文件名是以NUL结束的,其后是以0xFFFF补充的。
如文件名为“The quick brown.fox”的磁盘结构
长文件名的长度最大是255个字符,不包括NULL。长文件路径最大是260,包括NUL。有效字符集可以是短文件名中任何字符组合,也可以多次出现”.”。空格在长文件名中是可以的,但是在短文件名通常是不可以的,下面的几个字符在长文件名中时可以的
+ , ; = [ ]
嵌入的空格是可以的,但是在开始和末尾的空格是不允许。
文件名开始和嵌入的点”.”也是可以的,但是末尾的点是不允许的。
长文件名存储在LDE中,而且不再以多字节字符形式,而是Unicode。
校验和的计算
unsigned char ChkSum (unsigned char *pFcbName)
{
short FcbNameLen;
unsigned char Sum;
Sum = 0;
for (FcbNameLen=11; FcbNameLen!=0; FcbNameLen--) {
// NOTE: The operation is an unsigned char rotate right
Sum = ((Sum & 1) ? 0x80 : 0) + (Sum >> 1) + *pFcbName++;
}
return (Sum);
}
关于FAT32目录入口(DE)的第0C(mNameCase)字节分析了一下。发现如下规律
纯短文件名则有如下规律:
文件名小写 00001000
扩展名小写 00010000
两者可以相与。也就是说如果文件名为小写的话,则0C字节的第3位为1。如果扩展名为小写的话,则0C字节第4位为1.
综上:
文件名小写,扩展名小写,如aaa.aaa 则00011000=18h
文件名大写,扩展名大写,如AAA.AAA 则00000000=00h
文件名大写,扩展名小写,如AAA.aaa 则00010000=10h
文件名小写,扩展名大写,如aaa.AAA 则00001000=08h
长文件名不管是LDE还是与之对应的SDE中的0C字节都是0
本人菜鸟