对于软件分析,特别是复杂的大型系统软件来说我个人的步骤一般是:

1、 了解设计者的设计思想,用行话说就是软件架构、层次、模块组成。

2、 根据软件结构和模块整理出主要的数据结构或者类。

3、 结合数据结构进一步熟悉软件架构、层次和模块构成。对于复杂系统,这个过程花的时间可能会很长。直到你能画出整个系统的架构图。在你画出架构图之间一般不要去深入分析源码细节,这很容易迷失在代码中。

4、 根据你画的架构图熟悉各个模块,这个步骤可以有选择性的去熟悉你感兴趣的模块。如果模块还很复杂很大的话你需要重复前面三个步骤,直到你可以很轻松的去了解模块里面的细节,也就是读源码。

下面我们就按照以上四个步骤来分析FAT文件系统

网上关于FAT文件系统结构组成的文章很多,由4个部分组成:引导扇区、FAT表、根目录区、数据区

现在先粗略讲解下前面3个部分。

引导扇区:

扇区大小这里设为512byte,磁盘的第一个扇区作为引导扇区,引导扇区的内容主要包括操作系统引导信息、磁盘基本信息、文件系统相关信息(BPB数据结构)、分区表信息(磁盘分区信息)。由于这里我们文件系统只涉及数据存储而且磁盘也不局限于硬盘,所以引导信息和磁盘基本信息不分析,反正网上相关信息很多。暂时也不涉及多分区,分区信息也不分析。我们现在主要关注文件系统信息。这个信息对应的数据结构是ff.h文件中定义的struct FATFS。

FAT表:

FAT(file access table文件访问表),记录着文件和目录所在的簇(clust)或者块(block),如果一个文件涉及多个簇则通过簇链将上下簇联系起来。我们以FAT16为例来说明FAT表项内容。这里我们定义总簇数为4096,每簇扇区数为16,每个扇区大小为512字节。每一个表项长度为2个字节,对应磁盘的一个簇。由于0号和1号簇用做引导区和FAT记录区,所以0号和1号表项不能用,2号表项对应根目录起始簇号,即根目录的起始簇为第二簇。表项标志内容:簇链结束标志:0xFFFF,保留标志:0XFFF0,坏簇标志:0XFFF7。空闲簇:0x0000。若有一个文件占用3个簇,文件起始簇号是3,FAT表如下所示:

由上表可知0簇和1簇用作引导和FAT表,2、3簇用作根目录区,这里我们定义根目录项数为512,每个目录占32个字节。文件起始簇号为4,文件占用4、5、6共3簇。这里文件占用的3个簇连续,也可能不连续,表项6为0xffff表示文件在这一簇结束。表项7、8的记录为0x0000,表示空闲。表项总数为磁盘总簇数+2。

 

根目录区:

根目录是所有文件和目录的入口,现在我们只要了解根目录起始簇为2,占用2个簇就行了,具体信息到目录和文件管理的时候再分析。

下面介绍以上模块对应的数据结构:

文件系统结构:

typedef struct {

BYTE fs_type;      /* FAT sub-type (0:Not mounted) */

BYTE drv;          /* Physical drive number */

BYTE csize;             /* Sectors per cluster (1,2,4...128) */

BYTE n_fats;            /* Number of FAT copies (1,2) */

BYTE wflag;             /* win[] dirty flag (1:must be written back) */

BYTE fsi_flag;     /* fsinfo dirty flag (1:must be written back) */

WORD id;                /* File system mount ID */

WORD n_rootdir;         /* Number of root directory entries (FAT12/16) */

#if _MAX_SS != 512

WORD ssize;             /* Bytes per sector (512, 1024, 2048 or 4096) */

#endif

#if _FS_REENTRANT

_SYNC_t  sobj;              /* Identifier of sync object */

#endif

#if !_FS_READONLY

DWORD    last_clust;        /* Last allocated cluster */

DWORD    free_clust;        /* Number of free clusters */

DWORD    fsi_sector;        /* fsinfo sector (FAT32) */

#endif

#if _FS_RPATH

DWORD    cdir;              /* Current directory start cluster (0:root) */

#endif

DWORD    n_fatent;     /* Number of FAT entries (= number of clusters + 2) */

DWORD    fsize;             /* Sectors per FAT */

DWORD    fatbase;      /* FAT start sector */

DWORD    dirbase;      /* Root directory start sector (FAT32:Cluster#) */

DWORD    database;     /* Data start sector */

DWORD    winsect;      /* Current sector appearing in the win[] */

BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and Data on tiny cfg) */

} FATFS;

磁盘格式化的时候将相关信息写入到第一簇的第一个扇区,挂载文件系统时将相关信息读到上面的文件系统结构变量中。

目录结构:

typedef struct {

FATFS*   fs;                /* Pointer to the owner file system object */

WORD id;                /* Owner file system mount ID */

WORD index;             /* Current read/write index number */

DWORD    sclust;            /* Table start cluster (0:Root dir) */

DWORD    clust;             /* Current cluster */

DWORD    sect;              /* Current sector */

BYTE*    dir;          /* Pointer to the current SFN entry in the win[] */

BYTE*    fn;                /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */

#if _USE_LFN

WCHAR*   lfn;          /* Pointer to the LFN working buffer */

WORD lfn_idx;      /* Last matched LFN index number (0xFFFF:No LFN) */

#endif

} DIR;

这个结构其实并不是目录存储结构,只是作为内存中的管理结构。目录项存储结构是32个字节长度。为了方便调试我申请了32M内存空间,分成4096簇,每簇16个扇区,每扇区512字节。用vc创建了一个工程,用内存模拟磁盘进行操作。

#define SECTOR_SIZE   512           //512byte

#define SECTORS_PER_CLUST 16   //每个簇有8个扇区

#define SUM_CLUSTS4096   // 磁盘总簇数

#define SUM_SECTORS (SUM_CLUSTS*SECTORS_PER_CLUST) //总扇区数

移植FAT文件系统主要做的工作时修改diskio.c文件中的磁盘读写函数,将其改成读写内存。

申请一块内存,模拟磁盘空间

char virtualDisk[SUM_SECTORS*SECTOR_SIZE];

//初始化磁盘,暂时不用

DSTATUS disk_initialize (

BYTE drv               /* Physical drive nmuber (0..) */

)

{

return 0;

}

//获得磁盘状态,不用

DSTATUS disk_status (

BYTE drv      /* Physical drive nmuber (0..) */

)

{

return 0;

}

//读扇区

//drv:磁盘编号0~9

//*buff:数据接收缓冲首地址

//sector:扇区地址

//count:需要读取的扇区数

void drv_read(BYTE *buf,   DWORD sector)

{

memcpy(buf,&virtualDisk[sector*SECTOR_SIZE],SECTOR_SIZE);

}

DRESULT disk_read (

BYTE drv,     /* Physical drive nmuber (0..) */

BYTE *buff,        /* Data buffer to store read data */

DWORD sector, /* Sector address (LBA) */

BYTE count         /* Number of sectors to read (1..255) */

)

{

U8 res=0;

if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误

switch(drv)

{

case SD_CARD://SD卡

case EX_FLASH://外部flash

for(;count>0;count--)

{

drv_read(buff,sector);

sector++;

buff+=SECTOR_SIZE;

}

res=0;

break;

default:

res=1;

}

//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值

if(res==0x00)return RES_OK;

else return RES_ERROR;

}

//写扇区

//drv:磁盘编号0~9

//*buff:发送数据首地址

//sector:扇区地址

//count:需要写入的扇区数

#if _READONLY == 0

void drv_write(const BYTE *buf, DWORD sector)

{

memcpy(&virtualDisk[sector*SECTOR_SIZE], buf, SECTOR_SIZE);

}

DRESULT disk_write (

BYTE drv,          /* Physical drive nmuber (0..) */

const BYTE *buff,          /* Data to be written */

DWORD sector,      /* Sector address (LBA) */

BYTE count             /* Number of sectors to write (1..255) */

)

{

U8 res=0;

if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误

switch(drv)

{

case SD_CARD://SD卡

case EX_FLASH://外部flash

for(;count>0;count--)

{

drv_write(buff,sector);

sector++;

buff+=SECTOR_SIZE;

}

res=0;

break;

default:

res=1;

}

//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值

if(res == 0x00)return RES_OK;

else return RES_ERROR;

}

#endif /* _READONLY */

//其他表参数的获得

//drv:磁盘编号0~9

//ctrl:控制代码

//*buff:发送/接收缓冲区指针

DRESULT disk_ioctl (

BYTE drv,     /* Physical drive nmuber (0..) */

BYTE ctrl,         /* Control code */

void *buff         /* Buffer to send/receive control data */

)

{

DRESULT res;

switch(ctrl)

{

case CTRL_SYNC:

res = RES_OK;

break;

case GET_SECTOR_SIZE:

*(WORD*)buff = SECTOR_SIZE;

res = RES_OK;

break;

case GET_BLOCK_SIZE:

*(WORD*)buff = SECTORS_PER_CLUST;

res = RES_OK;

break;

case GET_SECTOR_COUNT:

*(DWORD*)buff = SUM_SECTORS;

res = RES_OK;

break;

default:

res = RES_PARERR;

break;

}

return res;

}

//获得时间,不用

DWORD get_fattime (void)

{

return 0;

}

主函数

void main()

{

FATFS fs,u8 ret,u8 drvnum=1,format=1;

u16  bytes_per_clust  = 8192; //每簇字节数 8k

//挂载文件系统.实际就是为struct FATFS结构指针,申请一块内存,存储相关信息

// drvnum:磁盘盘符号,每个盘符号对应一个磁盘,实际就是对应FATFS数组的索引

// format 0或者1 :格式化所使用的模式,模式涉及到很多硬件信息,我们在内存中模拟,所以为

//了简单起见,使用模式一

ret = f_mount(drvnum, &fs);

ret =f_mkfs (drvnum, format, bytes_per_clust);

return;

}

用vc调试可以看到第一扇区数如下

地址0x00460940为我们申请的内存数组virtualDisk的首地址,这里引导扇区之后就只FAT表,由该地址偏移512字节可以得到FAT表第一个表项的内容,地址:0x00460940+0x200=0x00460b40

由上图可知FAT表第一项为fff0(保留簇),第二项为0xffff(结束簇)。

FAT文件系统解析(一) 引导扇区、FAT表及根目录区分析相关推荐

  1. FAT 文件系统 - MBR

    MBR 之前为了让MCU能够读取SD卡的数据,研究了很久FAT文件系统,查了很多资料,走了很多弯路,比如MBR和DBR的区别,就花了很久才弄清楚. MBR(主引导记录)严格说并不属于FAT文件系统的部 ...

  2. 手工数据恢复你也行:FAT文件系统DBR损坏后的恢复

    对于FAT16文件系统,因为没有DBR备份扇区,所以当DBR损坏时,就需要根据分区中的数据存储情况重建其DBR,手工恢复如此,软件也如此,只不过软件是虚拟出一个文件系统而已. 对于FAT32文件系统, ...

  3. fat文件系统基础知识

    文件分配表(英语:File Allocation Table,首字母缩略字:FAT),是一种由微软发明并拥有部分专利的文件系统,供MS-DOS使用,也是所有非NT核心的Windows系统使用的文件系统 ...

  4. 《NTFS文件系统扇区存储探秘》——第1章 FAT文件系统的数据结构 1.1 主引导记录...

    本节书摘来自异步社区<NTFS文件系统扇区存储探秘>一书中的第1章,第1.1节,作者:宋群生 , 宋亚琼著,更多章节内容可以访问云栖社区"异步社区"公众号查看 第1章 ...

  5. 【技术三千问】之《FAT文件系统问题解析》,干货汇总!

    技术三千问: [技术三千问]之<玩转ART-Pi>,看这篇就够了!干货汇总 [技术三千问]之<AT组件问题汇总与解析>,干货汇总! [技术三千问]之<UART串口问题解析 ...

  6. 杰理之AC104如何解析FAT文件系统中的dir文件【篇】

    AC104 FAT文件系统无法解析由我司packres.exe打包工具生成的dir文件,本文将介绍如何实现将dir文件放在FAT文件系统的SD卡中,通过小机解析dir文件数据并进行解码播放,具体包括如 ...

  7. syslinux引导扇区不支持ntfs文件系统_实战 FAT12 文件系统

    1. 引言 经过一系列的文章,我们终于完成了从实地址模式跳转到保护模式,并且实现了分段.分页以及保护模式下的中断与异常机制. 保护模式究竟"保护"了什么 可是我们除了最初的时候,在 ...

  8. syslinux引导扇区不支持ntfs文件系统_磁盘与文件系统

    1.磁盘的组成 从一个磁盘的俯视图来看,其结构如下: 扇区:最小的物理存储单位,目前主要有512bytes与4K格式 磁道:由一个个同心圆环组成,从0开始由外向内编号 磁柱:不同盘面上相同磁道编号则组 ...

  9. 建立FAT文件系统学习笔记

    FAT文件系统的概况图 1.MBR 进行百度得到,计算机在按下power键以后,开始执行主板bios程序.进行完一系列检测和配置以后.开始按bios中设定的系统引导顺序引导系统.假定现在是硬盘.Bio ...

  10. FAT文件系统原理(转载)

    转自: http://blog.chinaunix.net/uid-24611346-id-3246892.html 一.硬盘的物理结构:     硬盘存储数据是根据电.磁转换原理实现的.硬盘由一个或 ...

最新文章

  1. Python 可视化库
  2. java 微信高级群发_微信高级群发接口demo
  3. WebUploader 设置单个文件上传
  4. vue-cli详细教程
  5. 5000字权威指南分享!企业如何正确制定 IT 战略及其路线图
  6. verilog之状态机详细解释(二)
  7. 构建基本脚本(转)*****好文章*****
  8. JNI系列(2):jstring操作
  9. 如何在开发时部署和运行前后端分离的JavaWeb项目
  10. LINUX矩阵键盘简单介绍,矩阵键盘程序流程图详细介绍
  11. 用Python写一个语音播放软件
  12. Java动态代理的实现和源码分析
  13. 阿里云服务器linux 启动网卡失败,提示does not seem to be present,delaying initialization
  14. python 计算标准体重程序
  15. 如何用易语言做锁机软件
  16. HttpServletRequest获取路径的几个方法
  17. 中国防卫科技学院计算机,2014高考专业介绍:科技防卫
  18. 安卓手机屏幕失灵后通讯录导出
  19. 【附源码】Java计算机毕业设计基于微信小程序停车系统(程序+LW+部署)
  20. oj2451: 股市风云

热门文章

  1. redis java jar_Redis在java开发中使用
  2. linux无线网卡模拟ap,TP-Link无线网卡怎么设置虚拟AP
  3. 计算机无法添加本地策略组,win7本地组策略编辑器不能编辑怎么解决
  4. 使用Bandicam录屏
  5. yuki翻译器钩子_YUKI GALGAME翻译器
  6. 64位windows10操作系统,如何使用32位的IE浏览器
  7. SQL 数据库清理数据库日志
  8. (三)cuda8和cuda10的切换
  9. 酒店管理系统(功能结构图、E-R图、用例图)
  10. Android基于Ymodem协议升级嵌入式MCU主控