Your Ad Here
首页 | 编程语言 | 网站建设 | 游戏天堂 | 冲浪宝典 | 网络安全 | 操作系统 | 软件时空 | 硬件指南 | 病毒相关 | IT 认证
软讯网络 > 软件时空 > 软件相关 > SD卡中的文件系统
【标  题】:SD卡中的文件系统
【关键字】:SD
【来  源】:http://blog.csdn.net/fredzeng/archive/2006/09/16/1231592.aspx

SD卡中的文件系统

Your Ad Here

 

最近在解一个SD卡不能烧写WinCE的镜像的问题,问题是出现在UT上的,UT提供了一种功能,可以利用保存了镜像文件的SDMMC卡来升级image.一般来讲,UT下的一些utils都是比较轻量级的,比如就说这个升级image的功能,仅仅使用了一个很简单的FAT文件系统和一个精简的SDMMC的驱动,而在image,SDMMC驱动就分成了SDMMC总线驱动,SDMMC 客户端驱动和 SDMMC控制器驱动,FAT文件系统也是由微软提供的FATFS.dll来实现的。
遇到的故障现象是有些SD卡上的镜像文件不能被烧写程序搜寻到,报不能找到WINCEIMG.CKS错误。寻找问题的开始是从比较问题卡和正常卡烧写image过程中的异同,为了让两张测试卡处于相同的环境,我用WinHEX工具以磁盘镜像的方式将两张盘做了镜像拷贝,按照我的意思是想让它们完全一下,但事实证明我忽略了很重要的一点导致走了很大的弯路。用过WinHEX的朋友都知道,它的磁盘管理工具具有很强的数据分析功能,能分别以逻辑扇区和物理扇区两种方式来分析磁盘数据,我在做镜像操作的过程中,错误的使用了逻辑扇区镜像,这导致这两张卡的物理扇区布局是完全不同的。接下来我开始用debug的信息来调试这两张卡,从源码上分析烧写程序首先是调用FAT文件系统的初始化函数,它做的第一个工作就是找到分区的引导扇区DBRDBRMBR不同,MBR每个磁盘最多只有一个,也可以没有,而DBR是每个分区一个,磁盘有多少个分区就有多少个DBR扇区。找到DBR扇区就可以得到相应分区的物理参数信息(BPB),这些信息包括总扇区数,每扇区的字节数,FAT表数目和大小,根目录的位置等等,这些信息对于FAT文件系统来讲是基本参数,直接决定后面的文件操作的正常与否。所有的数据读取操作都是以扇区为单位进行的,对应的工作程序就是SDMMC的驱动代码了。
所以我第一个工作就开始来比较DBR扇区了,结果出乎我意料,不一样!!!正常的卡从第一个物理扇区读出了正确的512字节的DBR,而错误的卡从第二个物理扇区读出了错误的DBR。我首先开始怀疑是SDMMC的驱动代码的问题,然后开始全力排查驱动,直到我追到最底层的往SDMMC的控制寄存器发读取扇区的command了,我还是没有发现驱动存在任何问题。垂头丧气去吃了晚饭准备开始加班了。
第二次打开WinHEX,把问题卡插到读卡器中,这次不知道怎么学乖了,从磁盘管理器的“物理媒介”栏把SD卡打开,忽然发现里面的数据很陌生了,和正常的卡做比较,发现物理扇区并没有一一对上,问题卡物理扇区上的数据和在烧写程序中读到的值相同。这说明UTSDMMC的驱动并没有问题,问题出在FAT文件系统对DBR的解析上。
重新打开UT下的FAT源代码,考察其对DBR的辨识算法,其相关代码如下:
for (i = 0; i < 1024; i++) {
s = diskFuncP->read(ctxP->diskP, i, buf, 1);
              if ((((buf[0] == 0xEB) && (buf[2] == 0x90)) || (buf[0] == 0xE9))
                     && ((buf[510] == 0x55) && (buf[511] == 0xaa))
                     && ((strncmp("FAT", &buf[54], 3)== 0)||(strncmp("FAT32", &buf[82], 5)== 0))) {
//判定扇区iDBR扇区,开始计算FAT分区的参数
}
代码很明朗,就是从0开始搜索物理扇区,一个一个扇区的读,直到发现有一个扇区对应比特符合DBR的特征为止,然后它会把这个扇区的编号作为一个base_sector_number开始进行FAT表和根目录的定位。我马上意识到,FAT不可能这么简单,估计是有伪DBR扇区在作怪,用WinHEX查,在问题卡上果然还存在一个扇区和DBR格式相像,进一步证明它才是真正的DBR扇区,问题的出现就是因为它的物理扇区编号位于伪DBR扇区的编号之后,我们的程序找错了DBR扇区。
问题已经找到了,接下来该考虑怎么解决了。怎样分别真假DBR呢,这个让我头疼了好久,因为解决这个问题需要非常detail的了解FAT,而且还要考虑兼容FAT16/32两种格式。你或许会说,查MBR不就搞定了么,DBR是可以通过查询MBR来得知其所占据的扇区号的。但问题是很多SD卡往往都没有MBR,不信你拿读卡器在PCFormat一张卡,让WinHEX去读物理0扇区,肯定没有MBR。(很多U盘工具可以将SD卡格式化成多个分区,这个时候会有MBR生成,Windows会把有MBRU盘或SD卡作为硬盘看待,图标会显示到硬盘栏而不是可移动存储设备栏)
看完了FAT白皮书,还是没有发现有什么DBR有什么防伪特征,到微软的WinCEsourcecode中找FATFSsource code看,也没有总结出好的经验。
没办法,用最笨的也是最根本的方法,验证FAT表和根目录的有效性。因为错误的DBR扇区往往仅仅剩下一个躯壳,与之相对应的FAT表和根目录往往被后来的格式化所破坏。
开始学习DBR每个byte代表的意思,写代码求出FAT表的偏移和根目录的便宜,然后检查FAT表的前2-4个字节,如果是FAT16,那么应该前两个字节一定是是0xF8 0xFF,如果是FAT32,前四个自己一定是0xF8 0xFF 0xFF 0x0F;如果FAT表有备份,那么还要检查第二个FAT表的内容是否合法;然后找到根目录的第11个字节,这个字节是根目录属性,用这个值和0x08与,等于零的话说明是错误的根目录路径。经过这些判定,伪DBR扇区基本上会识别出来,用这种方法,那些不能用的SDMMC卡终于可以工作了。理论上讲,还会有不能正确辨识的情况存在,但是能排除绝大部分卡就起到效果了。希望知道更完美解决方案的哥们提供好的建议,谢谢。下面是源代码,蓝色字部分是本文的解决方案加入的代码。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
for (i = 0; i < 1024; i++) {
s = ctxP->diskFuncP->read(ctxP->diskP, i, buf, 1);
if ((((buf[0] == 0xEB) && (buf[2] == 0x90)) || (buf[0] == 0xE9))
&& ((buf[510] == 0x55) && (buf[511] == 0xaa))
&& ((strncmp("FAT", &buf[54], 3)== 0)||(strncmp("FAT32", &buf[82], 5)== 0)))
{
int nFatsector,nRootsector,nFATnum;
p = (FAT_BiosParameterBlock_T *) buf;
RootDirSectors= ((p->maxRoot *32)+(p->bytesPerSector-1))/p->bytesPerSector ;       nFatsector=ctxP->reservedBlocks + i + p->reservedSectors;
if (p->FATSz16 != 0)
FatSz = p->FATSz16 ;               
else
FatSz = p->FATSz32 ;
nFATnum=p->numberOfFAT;
nRootsector=nFatsector+nFATnum*FatSz;
DM_SerialPrintf("looks like DBR sector = %d nFATnum=%d",i,nFATnum);
if(nFATnum<1)
       continue;
s = ctxP->diskFuncP->read(ctxP->diskP, nFatsector, buf, 1);
DM_SerialPrintf("DBR sector = %d",i);
DM_SerialPrintf("FAT1=%d buf[0]==0x%x buf[1]=0x%x buf[2]=0x%x buf[3]=0x%x ,nFatsector=0x%x",nFatsector,buf[0],buf[1],buf[2],buf[3],nFatsector);
if(p->FATSz16!=0)
{
       if( buf[0]!=0xF8 || buf[1]!=0xFF ) //is not valide FAT16
       continue;
}
else
{
       if( buf[0]!=0xF8 || buf[1]!=0xFF || buf[2]!=0xFF || buf[3]!=0x0F ) //is not valide FAT32
       continue;
}
if(nFATnum>1) //verify the second FAT table
{
s = ctxP->diskFuncP->read(ctxP->diskP, nFatsector+FatSz, buf, 1);
       DM_SerialPrintf("FAT2=%d buf[0]==0x%x buf[1]=0x%x buf[2]=0x%x buf[3]=0x%x ,nFatsector=0x%x",nFatsector+FatSz,buf[0],buf[1],buf[2],buf[3],nFatsector);
if(p->FATSz16!=0)
{
if( buf[0]!=0xF8 || buf[1]!=0xFF ) //is not valide FAT16
continue;
}
else
{
if( buf[0]!=0xF8 || buf[1]!=0xFF || buf[2]!=0xFF || buf[3]!=0x0F ) //is not valide FAT32
       continue;
}
}     
s = ctxP->diskFuncP->read(ctxP->diskP, nRootsector, buf, 1);
DM_SerialPrintf("ROOTDIR=%d buf[0]==0x%x buf[11]=0x%x numberOfFAT=%d",nRootsector,buf[0],buf[11],nFATnum);
if((buf[0]!=0xE5)&&(buf[11]&0x08)==0) //not a valid root directory
continue;
ctxP->baseBlock = ctxP->reservedBlocks + i;
       found = 1;
              }
       }
嵌入式系统相关的几组数字:【上一篇】
服务设计原理:服务模式和反模式--MSDN:【下一篇】
【相关文章】
  • 一直不知CSDN默认有博客服务
  • 更换新的Windows Platform SDK
  • Visual Studio 2005 SDK 3.0 发布
  • Windows SharePoint Services 2.0 Beta2 Technical Refresh SDK 下载
  • FreeBSD编程
  • FREEBSD系统FAQ
  • 在freebsd下如何打开PDF文件
  • 快速重装MSDN帮助库
  • vc、sdk,用3行代码播放Flash
  • 《MSDN杂志》十月期文章推荐
  • 【随机文章】
  • VC学习:虚拟按键的总结及示例
  • 不能ASP图像组件来生成图像的ASP计数器程序(二)
  • 一些不必要使用多线程的场合
  • Oracle常用傻瓜问题1000问
  • 简单易懂,Session的工作方式
  • MS Internet Explorer (VML) Exploit (SP2)
  • 微软地球
  • 8088 汇编速查手册(收藏)
  • JAVA commons-fileupload文件上传组件使用说明
  • Servlet基础例程-HelloServlet(Linux版本)
  • 【相关评论】
    没有相关评论
    【发表评论】
    姓名:
    邮件:
    随机码*
    评论*
          
    |  首 页  |  版权声明  |  联系我们   |  网站地图  |
    CopyRight © 2004-2007 bbb软讯网络 All Rigths Reserved.