文件啊文件(2)FAT32格式~
来源:互联网 发布:程序员标准形象 编辑:程序博客网 时间:2024/06/05 18:48
最近倍儿迷糊。。
关于磁盘结构:
在逻辑磁盘的层面上,数据存取单位是扇区,一般每个扇区512个字节(用ReadFile与逻辑磁盘设备进行通信时,粒度是512字节),但文件系统在存储数据时是以簇为单位的,一般8个扇区为一个簇(只是一般,这些基本信息是在第一个扇区存放的)
关于FAT:
FAT——File Allocation Table 就是一个以簇号为下标的大数组,存储信息是下一个簇的簇号(0表示为空簇,0x0ffffff7以上表示文件结束,否则表示为下一个簇的簇号),这让每读完一个簇就可以查表接到下一个簇,一连串接下去。很古老很简单,所以有很多缺点。
其中目录是特殊的文件,其数据就是一个个32位的目录描述项,存储的是文件的各种信息。
WINHEX看磁盘结构很方便:
从引导扇区能够获得很多信息。从中可以看到保留扇区为38,这是一个特殊的簇(0簇),39扇区开始就是FAT(1簇),FAT之后就是根目录所在的簇(第二簇)了,并且从此开始按照每簇8扇区线性排列。
目录描述项:
目录是特殊的文件,存储着文件信息,目录描述项中的文件名是短文件名,也就是8.3文件名,8字符文件名,3字符格式名:
typedef struct _FATDirEntry
{
union
{
struct { unsigned char Filename[8], Ext[3]; };
unsigned char ShortName[11];
};
略去...
}FATDirEntry,*PFATDirEntry;
这么设计是历史原因,实际上文件名都很长,于是需要一种方式来解决超出8.3的文件名,就是在目录项的数组里面加入另外一种专门存储长文件名的结构。FAT32的做法是把它放在该目录项的前一项里面,不够就再前一项,结构如下:
typedef struct _slot
{
unsigned char id; // sequence number for slot
WCHAR name0_4[5]; // first 5 characters in name
unsigned char attr; // attribute byte
unsigned char reserved; // always 0
unsigned char alias_checksum; // checksum for 8.3 alias
WCHAR name5_10[6]; // 6 more characters in name
unsigned char start[2]; // starting cluster number
WCHAR name11_12[2]; // last 2 characters in name
}slot,*pslot;
32位结构实际上能存储的文件名也就12个字符,id表示是第几段扩展,从1开始.
下面是解析的代码,练练手,一些结构来源于ROS 0.3.11 打印出根目录下的文件信息
效果如下,图里的字符串处理上有bug,代码里的已经修正过了。
FAT由于比较古老,在设计之初只是为了实现一些基本功能,有很多的局限性,在FAT上其实还有改进,就是FATX(传说用于XBOX360),而微软自己实现了ntfs...ntfs在设计上又有很多概念性的突破,是很成功的设计。。有待我研究。。
关于磁盘结构:
在逻辑磁盘的层面上,数据存取单位是扇区,一般每个扇区512个字节(用ReadFile与逻辑磁盘设备进行通信时,粒度是512字节),但文件系统在存储数据时是以簇为单位的,一般8个扇区为一个簇(只是一般,这些基本信息是在第一个扇区存放的)
关于FAT:
FAT——File Allocation Table 就是一个以簇号为下标的大数组,存储信息是下一个簇的簇号(0表示为空簇,0x0ffffff7以上表示文件结束,否则表示为下一个簇的簇号),这让每读完一个簇就可以查表接到下一个簇,一连串接下去。很古老很简单,所以有很多缺点。
其中目录是特殊的文件,其数据就是一个个32位的目录描述项,存储的是文件的各种信息。
WINHEX看磁盘结构很方便:
从引导扇区能够获得很多信息。从中可以看到保留扇区为38,这是一个特殊的簇(0簇),39扇区开始就是FAT(1簇),FAT之后就是根目录所在的簇(第二簇)了,并且从此开始按照每簇8扇区线性排列。
目录描述项:
目录是特殊的文件,存储着文件信息,目录描述项中的文件名是短文件名,也就是8.3文件名,8字符文件名,3字符格式名:
typedef struct _FATDirEntry
{
union
{
struct { unsigned char Filename[8], Ext[3]; };
unsigned char ShortName[11];
};
略去...
}FATDirEntry,*PFATDirEntry;
这么设计是历史原因,实际上文件名都很长,于是需要一种方式来解决超出8.3的文件名,就是在目录项的数组里面加入另外一种专门存储长文件名的结构。FAT32的做法是把它放在该目录项的前一项里面,不够就再前一项,结构如下:
typedef struct _slot
{
unsigned char id; // sequence number for slot
WCHAR name0_4[5]; // first 5 characters in name
unsigned char attr; // attribute byte
unsigned char reserved; // always 0
unsigned char alias_checksum; // checksum for 8.3 alias
WCHAR name5_10[6]; // 6 more characters in name
unsigned char start[2]; // starting cluster number
WCHAR name11_12[2]; // last 2 characters in name
}slot,*pslot;
32位结构实际上能存储的文件名也就12个字符,id表示是第几段扩展,从1开始.
下面是解析的代码,练练手,一些结构来源于ROS 0.3.11 打印出根目录下的文件信息
#define FAT_ENTRY_DELETED(DirEntry) ((DirEntry).Filename[0] == 0xe5) //如果目录项开头是0xe5,表明是删除了的文件,数据恢复之类的用得到#define FAT_ENTRY_END(DirEntry) ((DirEntry).Filename[0] == 0)#define FAT_ENTRY_LONG(DirEntry) (((DirEntry).Attrib & 0x3f) == 0x0f) //长文件名项的标志#define FAT_ENTRY_VOLUME(DirEntry) (((DirEntry).Attrib & 0x1f) == 0x08)#pragma pack(1)typedef struct _BOOT_SECTOR{ BYTE JMPCode[3]; // 0 unsigned char OEMName[8]; // 3 WORD BytesPerSector; // 11 unsigned char SectorsPerCluster; // 13 unsigned short ReservedSectors; // 14 unsigned char FATCount; // 16 unsigned short RootEntries, Sectors; // 17 unsigned char Media; // 21 unsigned short FATSectors, SectorsPerTrack, Heads; // 22 unsigned long HiddenSectors, SectorsHuge; // 28 unsigned long SectorsperFAT; // 36 unsigned short ExtFlag; // 40 unsigned short FSVersion; // 42 unsigned long Rootdir1stcluster; // 44 unsigned short FSInfoSector; // 48 unsigned short BootBackup; // 50 unsigned char Res3[12]; // 52 unsigned char Drive; // 64 unsigned char Res4; // 65 unsigned char ExtBootSignature; // 66 unsigned long VolumeID; // 67 unsigned char VolumeLabel[11], SysType[8]; // 71 unsigned char Res2[420]; // 90 unsigned short Signature1; // 510}BOOT_SECTOR,*PBOOT_SECTOR;//自定义结构typedef struct _FAT_INFO{ ULONG fat_start; //fat 起始扇区,在保留扇区之后 ULONG fat_count; //表数 一般是2,两个FAT紧挨着的 ULONG SectorsPerFAT; //一个FAT占的扇区数 ULONG BytesPerSector; //扇区字节数 一般是512 ULONG FATDiscriPerSection; //每个扇区所能存放的描述项 ULONG SectorsPerCluster; //簇的扇区数 一般是8 ULONG RootDirStart; //根目录的起始,紧跟在FAT之后 HANDLE hDisk;}FAT_INFO,*PFAT_INFO;typedef struct _FATDirEntry{ union { struct { unsigned char Filename[8], Ext[3]; }; unsigned char ShortName[11]; }; unsigned char Attrib; unsigned char lCase; unsigned char CreationTimeMs; unsigned short CreationTime,CreationDate,AccessDate; union { unsigned short FirstClusterHigh; // FAT32 unsigned short ExtendedAttributes; // FAT12/FAT16 }; unsigned short UpdateTime; //time create/update unsigned short UpdateDate; //date create/update unsigned short FirstCluster; unsigned long FileSize;}FATDirEntry,*PFATDirEntry;typedef struct _slot{ unsigned char id; // sequence number for slot WCHAR name0_4[5]; // first 5 characters in name unsigned char attr; // attribute byte unsigned char reserved; // always 0 unsigned char alias_checksum; // checksum for 8.3 alias WCHAR name5_10[6]; // 6 more characters in name unsigned char start[2]; // starting cluster number WCHAR name11_12[2]; // last 2 characters in name}slot,*pslot;// 自定义结构,记录读取的FAT扇区缓存,因为FAT描述项大多数情况下是连续的,所以没必要每次插寻FAT都读扇区typedef struct _CURRENT_CLUSTER_CONTEXT{ PVOID pBuffer; ULONG offset;}CCC,*PCCC;VOID PrintFileNameByDir(ULONG DirStartCluster);ULONG FAT32ClusterToSector(ULONG Cluster);ULONG FAT32GetFATSectorByCluster(PFAT_INFO pfi,ULONG Cluster,PULONG ArryNum);ULONG FAT32GetNextCluster(ULONG CurrentCluster);CCC g_Ccontext = {0};FAT_INFO fi = {0};int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]){ _tsetlocale(LC_ALL,_T("chs") ); fi.hDisk = NULL;//初始化,得到k盘句柄 fi.hDisk = CreateFile(L"////.//K:", / GENERIC_READ , / FILE_SHARE_READ | FILE_SHARE_WRITE, / NULL, / OPEN_EXISTING, / FILE_ATTRIBUTE_NORMAL, / NULL ); if ( INVALID_HANDLE_VALUE == fi.hDisk ) { printf( "Open Disk Error! error code is %d/n", GetLastError() ); system("pause"); return 0; } PBYTE pByteRead = new BYTE[512]; DWORD dwRead;//读取引导扇区 ReadFile( fi.hDisk, (LPVOID)pByteRead, 512, &dwRead,NULL ); PBOOT_SECTOR pbs = (PBOOT_SECTOR)pByteRead;/* printf("OEM:%s/n",pbs->OEMName);*/ //存储一些结构信息 fi.BytesPerSector = pbs->BytesPerSector; //一般是512 fi.SectorsPerCluster = pbs->SectorsPerCluster; //一般是8 fi.SectorsPerFAT = pbs->SectorsperFAT; fi.fat_count = pbs->FATCount; //一般是两个紧挨着,第二个应该是备份的,跟第一个一模一样 fi.fat_start = pbs->ReservedSectors*fi.BytesPerSector; //保留扇区之后便是 FAT fi.RootDirStart = fi.fat_start + fi.fat_count*fi.SectorsPerFAT*fi.BytesPerSector; //FAT之后便是根目录 fi.FATDiscriPerSection = fi.BytesPerSector / sizeof(ULONG); delete pByteRead;//得到一些磁盘信息,初始化完毕// printf("fat addr:0x%x/n",fi.fat_start);// printf("root addr:0x%x/n",fi.RootDirStart); //根目录开始的地方是第二簇,从此开始线性排列 PrintFileNameByDir(2); if (g_Ccontext.pBuffer!=0) { free(g_Ccontext.pBuffer); } system("pause"); return 0;}//************************************// Method: PrintFileNameByDir// FullName: PrintFileNameByDir// Access: public// Returns: VOID// Qualifier:// Parameter: ULONG DirStartCluster//************************************VOID PrintFileNameByDir(ULONG DirStartCluster){ DWORD dwRead; CString s,sTmp; BOOL bRootDir = FALSE; if (DirStartCluster == 2) { bRootDir =TRUE; } ULONG uStartSector = FAT32ClusterToSector(DirStartCluster); PFATDirEntry DirEnrtyArry = new FATDirEntry[16*8]; do { // 读取一个簇 SetFilePointer(fi.hDisk,uStartSector*0x200,0,FILE_BEGIN); if (!ReadFile(fi.hDisk,DirEnrtyArry,512*8,&dwRead,0)) { break; } for (int i=0;i<512*8/32;i++) { if (!FAT_ENTRY_DELETED(DirEnrtyArry[i]) && !FAT_ENTRY_END(DirEnrtyArry[i]) && !FAT_ENTRY_LONG(DirEnrtyArry[i])) { //得到了一个未删除的目录项,向上查看是否有长文件名项 int j =i-1; while(j>0 && FAT_ENTRY_LONG(DirEnrtyArry[j]) && !FAT_ENTRY_DELETED(DirEnrtyArry[j])) { pslot sloter = (pslot)&DirEnrtyArry[j]; sTmp.Format(L"%.5s",sloter->name0_4); s+=sTmp; sTmp.Format(L"%.6s",sloter->name5_10); s+=sTmp; sTmp.Format(L"%.2s",sloter->name11_12); s+=sTmp; printf_s("%.13ws",s); s.Format(L""); j--; } if (j==i-1) { printf_s("8.3: %-8.8s.%-3.3s/n",DirEnrtyArry[i].Filename,DirEnrtyArry[i].Ext); } else printf("/n");//这样递归显然是容易出问题的,这里只是调试用// if (DirEnrtyArry[i].FileSize == 0// && (i>1 || bRootDir&&i>0))// {// //不去递归 "." ".." 这两个文件夹// PrintFileNameByDir(MAKELONG(DirEnrtyArry[i].FirstCluster,DirEnrtyArry[i].FirstClusterHigh));// } } } //得到目录的下一个簇 DirStartCluster = FAT32GetNextCluster(DirStartCluster); uStartSector = FAT32ClusterToSector(DirStartCluster); } while (uStartSector); delete DirEnrtyArry;}//从簇得到扇区号,从第二簇开始线性排列//************************************// Method: FAT32ClusterToSector// FullName: FAT32ClusterToSector// Access: public// Returns: ULONG 扇区号// Qualifier:// Parameter: PFAT_INFO pfi// Parameter: ULONG Cluster//************************************ULONG FAT32ClusterToSector(ULONG Cluster){ if (Cluster<2) { return 0; } return fi.RootDirStart/512 + fi.SectorsPerCluster*(Cluster-2);}//************************************// Method: FAT32GetNextCluster// FullName: FAT32GetNextCluster// Access: public// Returns: ULONG 返回下一个簇,如果没有,返回0// Qualifier:// Parameter: ULONG CurrentCluster 当前簇//************************************ULONG FAT32GetNextCluster(ULONG CurrentCluster){ DWORD dwRead; PULONG ClusterArry ; // 获得该fat起始扇区 ULONG offset = fi.fat_start + fi.BytesPerSector*(CurrentCluster/fi.FATDiscriPerSection); //如果是读过的扇区,则不用重新读,否则就要读取 if (g_Ccontext.offset == offset) { ClusterArry = (PULONG)g_Ccontext.pBuffer; } else { if (g_Ccontext.pBuffer!=0) { delete g_Ccontext.pBuffer; } ClusterArry = (PULONG)malloc(512); g_Ccontext.pBuffer = (PVOID)ClusterArry; g_Ccontext.offset = offset; SetFilePointer(fi.hDisk,offset,0,FILE_BEGIN); ReadFile( fi.hDisk,ClusterArry, 512, &dwRead,NULL ); } //获得数组下标 ULONG Index = CurrentCluster%fi.FATDiscriPerSection; ULONG uNum = ClusterArry[Index]; if (uNum == 0 || uNum >=0x0ffffff8 ) { return 0; } return uNum;}
效果如下,图里的字符串处理上有bug,代码里的已经修正过了。
FAT由于比较古老,在设计之初只是为了实现一些基本功能,有很多的局限性,在FAT上其实还有改进,就是FATX(传说用于XBOX360),而微软自己实现了ntfs...ntfs在设计上又有很多概念性的突破,是很成功的设计。。有待我研究。。
- 文件啊文件(2)FAT32格式~
- FAT32文件
- 解决FAT32格式下,不能复制大文件的问题
- SD卡FAT32文件结构(1)
- FAT32文件操作系统
- 解决FAT32格式的U盘无法拷贝4G以上文件的方法
- SD卡FAT32文件结构(2)---创建与写入BMP位图
- CentOS6.5 U盘安装(解决了FAT32格式造成的不能复制iso文件至U盘根目录问题)
- FAT32学习笔记(三)文件的删除,目录的删除,FAT32表的用途,FsInfo的作用
- FAT32学习笔记(三)文件的删除,目录的删除,FAT32表的用途,FsInfo的作用
- FAT32和NTFS文件的比较
- 文件恢复学习笔记 获取文件格式 fat32
- FAT32文件系统中最大文件限制大小
- 不格式化U盘拷贝大于4G的文件,把U盘FAT32转换成NTFS格式
- U盘,拷贝4G以上的大文件时提示磁盘已满解决办法,将U盘格式FAT32格式转为NTFS格式
- lucene索引文件的格式(2)
- FAT32、NTFS文件直接操作小工具(上图MARK记录)
- FAT32学习笔记(二)长文件名的处理,文件内容的查找,目录的处理
- WORD调用EXCEL数据
- flash登录界面正常显示与当前系统编码页不同的语种版本
- QT 发射信号,接收槽,双线程演示(QtSDK演示程序)
- 【纯笔记】文件啊文件(1)
- 详细解析Linux scp命令的应用
- 文件啊文件(2)FAT32格式~
- 过华为论-观华为二十年盛衰史有感
- Linux配置教程之五:apache安装配置
- 一个学习silverlight游戏的网站
- 黑马程序员--数组与集合
- Extjs+struts2 实现文件上传
- C#Zip方式压缩文件和解压缩文件代码
- 文件(3)NTFS文件格式解析完了
- sybase Sybase Central v6.0启动 弹出插件无法加载