搜索
bottom↓
回复: 260

[开源][交流]如何读写FAT32文件系统(9-24-2008 Updated)

  [复制链接]

出0入296汤圆

发表于 2008-9-24 16:43:03 | 显示全部楼层 |阅读模式
本帖最后由 Gorgon_Meducer 于 2012-8-1 21:23 编辑


>>写在前面的话

有待添加内容


[专题索引]

    >> 磁盘引导扇区与磁盘分区表          ——如何从硬盘(U盘)中找到我们需要的分区    1楼
    >> BIOS Parameter Block 与 FAT类型   ——如何区分FAT12、FAT16和FAT32              2楼
    >> 磁盘分区逻辑扇区的读取            ——如何以逻辑扇区编号来访问实际的物理扇区   3楼
    >> FAT32目录的访问                   ——如何从指定的目录中找到想要的文件         4楼
    >> 文件的存放结构                    ——如何读写指定的文件                       5楼


[参考文献]

    A、硬盘物理结构和FAT文件系统解析(一)
    B、硬盘物理结构和FAT文件系统解析(二)


[相关下载]

    A、FAT32文件系统白皮书(E文原版)
    B、硬盘物理结构和FAT文件系统解析ourdev_486205.pdf(文件大小:970K)


[更新日至]

    [2008-9-24]  更新第一章、第二章、第三章、第四章、第五章的核心代码和实例。具体讲解部
                 分有待补充。

出0入296汤圆

 楼主| 发表于 2008-9-24 16:46:21 | 显示全部楼层
</font>&lt;有待添加内容>

出0入296汤圆

 楼主| 发表于 2008-9-24 16:45:59 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2012-8-1 21:52 编辑


[文件的存放结构]    ——如何读写指定的文件





<font color=blue>[原理解析]






[数据结构]





[关键代码]


>>如何从文件的簇链中获取当前簇的后续簇

  1. /***********************************************************
  2. *   函数说明:  获取当前簇的下一个簇                       *
  3. *   输入:      BPB缓冲,当前簇号                          *
  4. *   输出:      下一个簇号                                 *
  5. *   调用函数:  无                                         *
  6. ***********************************************************/
  7. static UINT32 Get_Next_Cluster(FAT_BPB *pFATBPB,UINT32 dwCluster)
  8. {
  9.     UINT32 dwSectorNumber;
  10.     UINT16 wByteOffset;
  11.     BYTE   chSectorBuffer[512];
  12.    
  13.     dwSectorNumber = pFATBPB->BPB_ResvdSecCnt +
  14.                 ((dwCluster * 4) / (pFATBPB->BPB_BytePerSec));
  15.     wByteOffset = (dwCluster * 4) % (pFATBPB->BPB_BytePerSec);

  16.     /* 读取指定的扇区 */
  17.     if (!GET_SECTOR(dwSectorNumber,chSectorBuffer))
  18.     {
  19.         /* 读取错误 */
  20.         return FAT_ITEM_BAD_CLUSTER;
  21.     }
  22.    
  23.     /* 获取下一个簇的簇号 */
  24.     return TYPE_CONVERSION
  25.             (
  26.                 &chSectorBuffer[wByteOffset],
  27.                 UINT32
  28.             );
  29. }
复制代码
>>如何根据获取的目录项信息顺序的读取到指定的文件内容

  1. /***********************************************************
  2. *   函数说明:  从指定的文件里面顺序读取一个扇区的数据     *
  3. *   输入:      BPB指针,文件入口地址,读写指针,          *
  4. *               存放数据的缓冲区                           *
  5. *   输出:      读取数据区的实际大小                       *
  6. *   调用函数:  Get_Next_Cluster()                         *
  7. * -------------------------------------------------------- *
  8. *   [使用说明]                                             *
  9. *           当开始一个新文件的读取时,需要通过pReadPoint将 *
  10. *       一个0扇区编号传递给函数;其余时候,传递任何数值都  *
  11. *       将被无视,函数通过指针pReadPoint告知外部当前的读写 *
  12. *       位置。                                             *
  13. ***********************************************************/
  14. BOOL Read_File_Sectors
  15.         (
  16.             FAT_BPB *pFATBPB,
  17.             FAT32_DIR_ENTRY *pFileEntry,
  18.             UINT32 *pReadPoint,
  19.             BYTE *pchBuffer,
  20.             UINT16 *pwSize
  21.         )
  22. {
  23.     static UINT32 s_dwReadPoint = 0;
  24.     static UINT32 s_dwRemainFileSize = 0;
  25.     static UINT32 s_dwReadCluster = 0;
  26.     static UINT8  s_chInClusterSectorCounter = 0;
  27.     UINT32 dwSector = 0;
  28.     UINT16 wTempSize = 0;
  29.     if  (
  30.             (pFATBPB == NULL)
  31.         ||  (pFileEntry == NULL)
  32.         ||  (pReadPoint == NULL)
  33.         ||  (pchBuffer == NULL)
  34.         )
  35.     {
  36.         /* 无效的输入 */
  37.         return FALSE;
  38.     }
  39.    
  40.     //新文件初始化
  41.     if ((*pReadPoint) == 0)
  42.     {
  43.         s_dwReadPoint = 0;                                  //初始化读写指针
  44.         s_chInClusterSectorCounter = 0;
  45.         s_dwRemainFileSize = pFileEntry->DIR_FileSize;      //初始化剩余文件大小
  46.         //获取文件的起始簇号
  47.         s_dwReadCluster = ((UINT32)(pFileEntry->DIR_FstClusHI) &lt;&lt; 16)
  48.                         | (UINT32)(pFileEntry->DIR_FstClusLO);
  49.     }
  50.    
  51.     //检查簇的合法有效性
  52.     if  (
  53.             (s_dwReadCluster>= FAT_ITEM_EOC_LBOUND)
  54.         &&  (s_dwReadCluster &lt;= FAT_ITEM_EOC_UBOUND)
  55.         )
  56.     {
  57.         if (s_dwRemainFileSize == 0)
  58.         {
  59.             //正常结束
  60.             if (pwSize != NULL)
  61.             {
  62.                 *pwSize = 0;
  63.             }
  64.             return TRUE;
  65.         }
  66.         else
  67.         {
  68.             //非正常结束
  69.             return FALSE;
  70.         }
  71.     }
  72.     else if (!(
  73.                 (s_dwReadCluster>= FAT_ITEM_LOCATED_LBOUND)
  74.             &&  (s_dwReadCluster &lt;= FAT_ITEM_LOCATED_UBOUND)
  75.             ))
  76.     {
  77.         //错误的簇信息
  78.         return FALSE;
  79.     }
  80.    
  81.     if (s_dwRemainFileSize == 0)
  82.     {
  83.         //文件读写完毕
  84.         if (pwSize != NULL)
  85.         {
  86.             *pwSize = 0;
  87.         }
  88.         return TRUE;
  89.     }
  90.    
  91.     //获取当前正在读取的簇的首扇区编号
  92.     dwSector = GET_FIRST_SECTOR_OF_CLUSTER(*pFATBPB,s_dwReadCluster)
  93.                 + s_chInClusterSectorCounter;

  94.     //读取扇区数据
  95.     if (!GET_SECTOR(dwSector,pchBuffer))
  96.     {
  97.         /* 读取错误 */
  98.         return FALSE;
  99.     }
  100.    
  101.     //更新读写指针
  102.     s_dwReadPoint++;
  103.     s_chInClusterSectorCounter++;
  104.     if (s_chInClusterSectorCounter == pFATBPB->BPB_SecPerClus)
  105.     {
  106.         // 完成了一个簇的读取
  107.         s_chInClusterSectorCounter = 0;                     //复位簇内扇区读写指针
  108.         s_dwReadCluster = Get_Next_Cluster                  //获取下一个簇
  109.                             (
  110.                                 pFATBPB,
  111.                                 s_dwReadCluster
  112.                             );
  113.     }
  114.    
  115.     // 返回当前的读写位置
  116.     (*pReadPoint) = s_dwReadPoint;
  117.     // 返回实际读取到的字节数
  118.     wTempSize = (s_dwRemainFileSize>= pFATBPB->BPB_BytePerSec) ?
  119.                 pFATBPB->BPB_BytePerSec :
  120.                 s_dwRemainFileSize;
  121.     if (pwSize != NULL)
  122.     {
  123.         *pwSize = wTempSize;
  124.     }
  125.    
  126.     /* 更新完成当前读写前的剩余大小 */
  127.     s_dwRemainFileSize -= wTempSize;
  128.                   
  129.     return TRUE;
  130. }
复制代码

[使用范例]

Example A:

  1. //显示根目录下所有TXT文件的文件名及文件内容
  2. UINT32 dwLastEntry = 0;
  3. UINT32 dwReadPoint = 0;
  4. BYTE chBuffer[512];
  5. UINT16 wSize = 0;
  6. BYTE chBuffer[sizeof(FAT32_DIR_ENTRY)];
  7. FAT32_DIR_ENTRY Entry = {0};
  8.                  
  9. while(Find_File_Extend_Name
  10.     (
  11.         (FAT_BPB *)(g_chBPBBuffer + 11),
  12.         2,
  13.         "TXT",
  14.         &dwLastEntry,
  15.         &Entry
  16.      ))
  17. {
  18.     UINT8 n = 0;
  19.     UINT8 *pchName = Entry.DIR_Name;

  20.     for (n = 0;n &lt; 11;n++)
  21.     {
  22.         while(!SERIAL_OUT(pchName[n]));
  23.     }
  24.     while(!SERIAL_OUT(10));
  25.     while(!SERIAL_OUT(13));

  26.     dwReadPoint = 0;
  27.     while(Read_File_Sectors
  28.             (
  29.                 (FAT_BPB *)(g_chBPBBuffer + 11),
  30.                 &Entry,
  31.                 &dwReadPoint,
  32.                 chBuffer,
  33.                 &wSize
  34.              ))
  35.     {
  36.         BYTE *p = chBuffer;
  37.         if (wSize == 0)
  38.         {
  39.             break;
  40.         }
  41.         while(wSize--)
  42.         {
  43.             while(!SERIAL_OUT(*p))
  44.             {
  45.                 PROC_Serial_Transmitter();
  46.             }
  47.             PROC_Serial_Transmitter();
  48.             p++;
  49.         }
  50.     }
  51.     while(!SERIAL_OUT(10));
  52.     while(!SERIAL_OUT(13));

  53. }
复制代码

出0入296汤圆

 楼主| 发表于 2008-9-24 16:45:52 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2012-8-1 21:56 编辑


[FAT32目录的访问]    ——如何从指定的目录中找到想要的文件



[原理解析]



[数据结构]

  1. /*------------------*
  2. *   常 数 宏 定 义  *
  3. *------------------*/
  4. # define ATTR_READ_ONLY         0x01
  5. # define ATTR_HIDDEN            0x02
  6. # define ATTR_SYSTEM            0x04
  7. # define ATTR_VOLUME_ID         0x08
  8. # define ATTR_DIRECTORY         0x10
  9. # define ATTR_ARCHIVE           0x20
  10. # define ATTR_LONG_NAME         (ATTR_READ_ONLY |\
  11.                                 ATTR_HIDDEN |   \
  12.                                 ATTR_SYSTEM |   \
  13.                                 ATTR_VOLUME_ID)

  14. # define FAT_ITEM_UNLOCATED         0x00000000
  15. # define FAT_ITEM_LOCATED_LBOUND    0x00000002
  16. # define FAT_ITEM_LOCATED_UBOUND    0xFFFFFFEF
  17. # define FAT_ITEM_RESERVED_LBOUND   0xFFFFFFF0
  18. # define FAT_ITEM_RESERVED_UBOUND   0xFFFFFFF6
  19. # define FAT_ITEM_BAD_CLUSTER       0xFFFFFFF7
  20. # define FAT_ITEM_EOC_LBOUND        0xFFFFFFF8
  21. # define FAT_ITEM_EOC_UBOUND        0xFFFFFFFF

  22. /*------------------*
  23. *   动 作 宏 定 义  *
  24. *------------------*/
  25. /* 计算出指定簇的第一个扇区的编号 */
  26. # define GET_FIRST_SECTOR_OF_CLUSTER(__BPB,__CLUSTER) \
  27.             (((__CLUSTER) - 2) * (__BPB).BPB_SecPerClus + GET_FAT_FIRST_DATA_SECTOR(__BPB))

  28. /********************
  29. *  用户变量类型定义 *
  30. ********************/
  31. typedef struct FAT32DirectoryEntry  FAT32_DIR_ENTRY;

  32. /********************
  33. *    结构体定义区   *
  34. ********************/
  35. struct FAT32DirectoryEntry
  36. {
  37.     BYTE    DIR_Name[11];
  38.     UINT8   DIR_Attr;
  39.     UINT8   DIR_NTRes;
  40.     UINT8   DIR_CrtTimeTenth;
  41.     UINT16  DIR_CrtTime;
  42.     UINT16  DIR_CrtDate;
  43.     UINT16  DIR_LstAccDate;
  44.     UINT16  DIR_FstClusHI;
  45.     UINT16  DIR_WrtTime;
  46.     UINT16  DIR_WrtDate;
  47.     UINT16  DIR_FstClusLO;
  48.     UINT32  DIR_FileSize;
  49. };
复制代码

[关键代码]

>>如何从目录中获取指定编号的条目信息

  1. /***********************************************************
  2. *   函数说明:  返回指定目录项数据                         *
  3. *   输入:      BPB,目录项索引,保存目录项的缓冲区指针    *
  4. *   输出:      保存目录项的缓冲区指针                     *
  5. *   调用函数:  无                                         *
  6. ***********************************************************/
  7. FAT32_DIR_ENTRY *Get_Directory_Entry_Item
  8.         (
  9.             FAT_BPB *pFATBPB,
  10.             UINT32 dwCluster,
  11.             UINT32 dwIndex,
  12.             BYTE *pchBuffer
  13.         )
  14. {
  15.     if (
  16.             (pFATBPB == NULL)
  17.         ||  (pchBuffer == NULL)
  18.         ||  (
  19.                 (dwCluster &lt; FAT_ITEM_LOCATED_LBOUND)
  20.             &&  (dwCluster> FAT_ITEM_LOCATED_UBOUND)
  21.             )
  22.        )
  23.     {
  24.         /* 无效的输入 */
  25.         return NULL;
  26.     }
  27.    
  28.     {
  29.         /* 获取根目录所在的扇区 */
  30.         BYTE   chSectorBuffer[512];
  31.         UINT32 dwDirectoryStartSector;
  32.         UINT16 wItemsPerSector;
  33.         dwDirectoryStartSector = GET_FIRST_SECTOR_OF_CLUSTER((*pFATBPB),dwCluster);
  34.         wItemsPerSector = pFATBPB->BPB_BytePerSec / sizeof(FAT32_DIR_ENTRY);

  35.         if (
  36.             !GET_SECTOR
  37.                 (
  38.                     dwDirectoryStartSector + (dwIndex / wItemsPerSector),
  39.                     chSectorBuffer
  40.                 )
  41.            )
  42.         {
  43.             /* 读取错误 */
  44.             
  45.             return NULL;
  46.         }

  47.         (*(FAT32_DIR_ENTRY *)pchBuffer) =
  48.                 ((FAT32_DIR_ENTRY *)chSectorBuffer)[dwIndex & (BIT(4) - 1)];
  49.     }
  50.    
  51.     /* 返回结果 */
  52.     return (FAT32_DIR_ENTRY *)pchBuffer;
  53. }
复制代码

>>如何从目录指定的位置开始查找指定扩展名的文件

  1. /***********************************************************
  2. *   函数说明:  从FAT32表中查找指定扩展名称的文件          *
  3. *   输入:      BPB指针,扩展名字符串,上一个有效目录入口  *
  4. *   输出:      是否找到需要的文件                         *
  5. *   调用函数:  无                                         *
  6. * -------------------------------------------------------- *
  7. *   [使用说明]                                             *
  8. *           输入的扩展名指针可以为空,这将导致直接从指定位 *
  9. *       置开始向后进行遍历输出。当上一次的目录入口地址输入 *
  10. *       为空时,则表示输出一个符合要求的结果。当找不到符合 *
  11. *       要求的结果时,输出为NULL。                         *
  12. ***********************************************************/
  13. BOOL Find_File_Extend_Name
  14.         (
  15.             FAT_BPB *pFATBPB,
  16.             UINT32 dwCluster,
  17.             BYTE *pstrFileExterndName,
  18.             UINT32 *pdwLastEntry,
  19.             FAT32_DIR_ENTRY *pDIREntryResult
  20.         )
  21. {
  22.     UINT32 dwSearchPoint = 0;
  23.     if (
  24.             (pFATBPB == NULL)
  25.         ||  (
  26.                 (pdwLastEntry == NULL)
  27.             &&  (pDIREntryResult == NULL)
  28.             )
  29.        )
  30.     {
  31.         /* 无效的输入 */
  32.         return FALSE;
  33.     }
  34.    
  35.     if (pdwLastEntry == NULL)
  36.     {
  37.         dwSearchPoint = 0;
  38.     }
  39.     else
  40.     {
  41.         dwSearchPoint = *pdwLastEntry;
  42.     }
  43.    
  44.     while(TRUE)
  45.     {
  46.         BYTE chDIREntryBuffer[sizeof(FAT32_DIR_ENTRY)];
  47.         /* 获取指定的目录入口 */
  48.         FAT32_DIR_ENTRY *pDIREntry = Get_Directory_Entry_Item
  49.                 (
  50.                     pFATBPB,
  51.                     dwCluster,
  52.                     dwSearchPoint,
  53.                     chDIREntryBuffer
  54.                 );
  55.         if (pDIREntry == NULL)
  56.         {
  57.             /* 读取目录项失败 */
  58.             return FALSE;
  59.         }
  60.         
  61.         /* 检测目录项是否有效 */
  62.         if (pDIREntry->DIR_Name[0] == 0x00)
  63.         {
  64.             /* 已经是目录项的最后内容了 */
  65.             break;
  66.         }
  67.         else if (
  68.                     (pDIREntry->DIR_Name[0] == 0xE5)
  69.                 ||  ((pDIREntry->DIR_Attr & ATTR_LONG_NAME) == ATTR_LONG_NAME)
  70.                 ||  ((pDIREntry->DIR_Attr & ATTR_DIRECTORY) == ATTR_DIRECTORY)
  71.                 )
  72.         {
  73.             /* 该目录项为空项 */
  74.             dwSearchPoint++;
  75.             continue;
  76.         }
  77.         
  78.         dwSearchPoint++;
  79.         
  80.         if (
  81.                 pDIREntry->DIR_Name[8] == pstrFileExterndName[0]
  82.             &&  pDIREntry->DIR_Name[9] == pstrFileExterndName[1]
  83.             &&  pDIREntry->DIR_Name[10] == pstrFileExterndName[2]
  84.            )
  85.         {
  86.             /* 扩展名匹配 */
  87.             
  88.             if (pdwLastEntry != NULL)
  89.             {
  90.                 *pdwLastEntry = dwSearchPoint;
  91.             }
  92.             
  93.             if (pDIREntryResult)
  94.             {
  95.                 (*pDIREntryResult) = (*pDIREntry);
  96.             }
  97.             return TRUE;
  98.         }
  99.     }
  100.    
  101.     return FALSE;
  102. }
复制代码

[使用范例]

Example A:

  1. //列出根目录下所有的TXT文件名
  2. //g_chBPBBuffer里面保存的是0扇区中的内容
  3. UINT32 dwLastEntry = 0;
  4. FAT32_DIR_ENTRY Entry = {0};
  5.                  
  6. while(Find_File_Extend_Name
  7.     (
  8.         (FAT_BPB *)(g_chBPBBuffer + 11),
  9.          2,
  10.          "TXT",
  11.          &dwLastEntry,
  12.          &Entry
  13.      ))
  14. {
  15.     UINT8 n = 0;
  16.     UINT8 *pchName = Entry.DIR_Name;
  17.    
  18.     for (n = 0;n &lt; 11;n++)
  19.     {
  20.         while(!SERIAL_OUT(pchName[n]));
  21.     }
  22.     //通过串口输出回车换行符浩
  23.     while(!SERIAL_OUT(10));
  24.     while(!SERIAL_OUT(13));
  25. }
复制代码

出0入296汤圆

 楼主| 发表于 2008-9-24 16:43:35 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2012-8-1 22:15 编辑


[磁盘分区逻辑扇区的读取]    ——如何以逻辑扇区编号来访问实际的物理扇区

    根据前面的叙述,我们知道,一个物理存储介质中可能有若干个磁盘分区,这些
磁盘分区的逻辑0扇区相对介质的物理0扇区来说,都有一个偏移量。如何在编写文件
系统的代码时,能够以磁盘分区的逻辑扇区编号来访问设备,而无需一一指名访问时
具体的物理扇区地址呢?
    解决方法很简单:所有的文件系统代码在编写时,都以磁盘分区的逻辑扇区地址
为依据,也就是相对BPB所在的扇区为逻辑0扇区,然后所有针对设备的扇区读写函数
都通过一个统一的函数接口来实现,而该接口函数实际上会针对逻辑扇区的地址进行
物理地址的偏移运算,从而根据逻辑地址得到实际的物理扇区地址。


[关键代码]

  1. //定义逻辑扇区访问接口
  2. # define GET_SECTOR(__SECTOR_NUMBER,__BUFF_ADDR) \
  3.             Get_Sector((__SECTOR_NUMBER),(__BUFF_ADDR))
复制代码
//我们假设这是一个U盘设备,host_read_10_ram()会利用SCSI函数的READ_10指令读取
//U盘的指定物理扇区
  1. /***********************************************************
  2. *   函数说明:  扇区读取接口函数                           *
  3. *   输入:      要读取的扇区编号,存放扇区的缓冲区         *
  4. *   输出:      操作是否成功                               *
  5. *   调用函数:  host_read_10_ram()                         *
  6. ***********************************************************/
  7. BOOL Get_Sector(UINT32 dwSectorNumber,BYTE *pBuffer)
  8. {
  9.     if (pBuffer == NULL)
  10.     {
  11.         return FALSE;
  12.     }
  13.    
  14.     host_read_10_ram
  15.         (
  16.             [color=blue]//还记得这个变量s_dwPartitionSectorOffset是怎么获得的么?[/color]
  17.             dwSectorNumber + s_dwPartitionSectorOffset,     
  18.             pBuffer
  19.         );

  20.     return TRUE;
  21. }
复制代码

[使用范例]

Example A:

  1. //通过接口函数读取该逻辑分区的0扇区,
  2. // 0 为逻辑扇区地址
  3. // g_chSectorBuffer是扇区缓冲区
  4. if (!GET_SECTOR(0,g_chSectorBuffer))
  5. {
  6.     //扇区读取失败的处理
  7. }
复制代码

出0入296汤圆

 楼主| 发表于 2008-9-24 16:43:28 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2012-8-1 22:20 编辑


[BIOS Parameter Block 与 FAT类型]    ——如何区分FAT12、FAT16和FAT32



<font color=blue>[原理解析]

  1. /*---------------------------------------------------------*
  2. *  [保留扇区] |  启动扇区                                  *
  3. *  Reserved   |  FSINFO                                    *
  4. *  Sectors    |  Root区备份(SECTOR 6)                    *
  5. * -------------------------------------------------------- *
  6. *  [FAT表]    |  FAT表0                                    *
  7. *             |  FAT表1                                    *
  8. *             |  ...    (BPB_NumFATs)                      *
  9. * -------------------------------------------------------- *
  10. *  [RootDir区]|  FAT16/12的根目录,FAT32不存在这个区域     *
  11. * -------------------------------------------------------- *
  12. *  [ 数据区 ] |  FAT32的根目录和数据。                     *
  13. *             |  需要说明的是,以第一个数据扇区为起始,簇  *
  14. *             |  从2开始编号。                             *
  15. *---------------------------------------------------------*/


  16. [hr][color=blue][数据结构][/color][hr]
  17. /*------------------*
  18. *   常 数 宏 定 义  *
  19. *------------------*/
  20. # define FAT_TYPE_FAT32             0x00
  21. # define FAT_TYPE_FAT16             0x01
  22. # define FAT_TYPE_FAT12             0x02
  23. # define FAT_ERROR                  0xFF

  24. /********************
  25. *  用户变量类型定义 *
  26. ********************/
  27. typedef struct BiosParameterBlock   FAT_BPB;

  28. /********************
  29. *    结构体定义区   *
  30. ********************/
  31. struct BiosParameterBlock
  32. {
  33.     /* FAT32 Structure Starting at Offset 11 */
  34.     UINT16  BPB_BytePerSec;         //每扇区的字节数通常为512
  35.     UINT8   BPB_SecPerClus;         //每簇的扇区数量
  36.     UINT16  BPB_ResvdSecCnt;        //保留扇区数
  37.     UINT8   BPB_NumFATs;            //FAT表的数量
  38.     UINT16  BPB_RootEntCnt;         //FAT12/16根目录中目录项的项数,FAT32为0
  39.     UINT16  BPB_TotSec16;           //FAT12/16系统记录卷标中扇区的总数,FAT32为0
  40.     UINT8   BPB_Media;              //磁盘介质
  41.     UINT16  BPB_FATSz16;            //FAT12/16用于保存一个FAT标所占用的扇区数
  42.     UINT16  BPB_SecPerTrk;          //每磁道的扇区数
  43.     UINT16  BPB_NumHeads;           //磁头数
  44.     UINT32  BPB_HiddSec;            //隐藏扇区
  45.     UINT32  BPB_TotSec32;           //FAT32卷中扇区的总数目
  46.    
  47.     /* FAT32 Structure Starting at Offset 36 */
  48.     UINT32  BPB_FATSz32;            //一个FAT32表中占用的扇区数
  49.     UINT16  BPB_ExtFlag;            //标志位
  50.     UINT16  BPB_FSVer;              //FAT32 version
  51.     UINT32  BPB_RootClus;           //FAT32 根目录所在簇
  52.     UINT16  BPB_FSInfo;             //FSINFO结构体在保留扇区内的扇区号
  53.     UINT16  BPB_BkBootSec;          //Boot 记录的备份在保留区内的扇区号,通常为6
  54.     BYTE    BPB_Reserved[12];       //RESERVED
  55. };
复制代码

[关键代码]

>>如何判断当前的FAT文件系统类型

  1. /*------------------*
  2. *   动 作 宏 定 义  *
  3. *------------------*/
  4. /* 获取根目录所占用的扇区数 */
  5. # define GET_ROOT_DIR_SECTOR_COUNT(__BPB)   \
  6.             (((__BPB).BPB_RootEntCnt * 32 + ((__BPB).BPB_BytePerSec - 1)) / (__BPB).BPB_BytePerSec)

  7. /* 获取FAT32文件系统中第一个数据扇区的编号(位于簇2)*/
  8. # define GET_FAT_FIRST_DATA_SECTOR(__BPB) \
  9.             (\
  10.                 (__BPB).BPB_ResvdSecCnt + (__BPB).BPB_NumFATs * \
  11.                 (\
  12.                     ((__BPB).BPB_FATSz16 != 0) ?\
  13.                     (__BPB).BPB_FATSz16 : (__BPB).BPB_FATSz32\
  14.                 ) + GET_ROOT_DIR_SECTOR_COUNT(__BPB)\
  15.             )

  16. /***********************************************************
  17. *   函数说明:  获取当前FAT的类型                          *
  18. *   输入:      FAT_BPB指针                                *
  19. *   输出:      FAT类型                                    *
  20. *   调用函数:  无                                         *
  21. ***********************************************************/
  22. UINT8 Get_FAT_Type(FAT_BPB *pFATBPB)
  23. {
  24.     if (pFATBPB == NULL)
  25.     {
  26.         return FAT_ERROR;
  27.     }
  28.    
  29.     {
  30.         UINT32 dwTotalSectors;
  31.         if (pFATBPB->BPB_TotSec16 != 0)
  32.         {
  33.             dwTotalSectors = pFATBPB->BPB_TotSec16;
  34.         }
  35.         else
  36.         {
  37.             dwTotalSectors = pFATBPB->BPB_TotSec32;
  38.         }
  39.         dwTotalSectors -= GET_FAT_FIRST_DATA_SECTOR((*pFATBPB));

  40.         dwTotalSectors /= pFATBPB->BPB_SecPerClus;
  41.         
  42.         if (dwTotalSectors &lt; 4085)
  43.         {
  44.             return FAT_TYPE_FAT12;
  45.         }
  46.         else if (dwTotalSectors &lt; 65525)
  47.         {
  48.             return FAT_TYPE_FAT16;
  49.         }
  50.     }
  51.     return FAT_TYPE_FAT32;
  52. }
复制代码

[使用范例]

Example A:

  1. //判断当前的文件类型是否为FAT32,其中g_chBPBBuffer是一个
  2. //大小为512字节的缓冲区,保存了BPB结构所在的扇区,也就是
  3. //对应磁盘分区的逻辑0扇区中的内容。由于BPB在该扇区中的起
  4. //始偏移为11,所以我们在需要访问BPB时需要加入相应的偏移
  5. //量。
  6. if (Get_FAT_Type((FAT_BPB *)(g_chBPBBuffer + 11)) != FAT_TYPE_FAT32)
  7. {
  8.     /* 检测FAT类型是否符合要求 */
  9.     return FALSE;
  10. ]
复制代码

出0入296汤圆

 楼主| 发表于 2008-9-24 16:43:12 | 显示全部楼层
本帖最后由 Gorgon_Meducer 于 2012-8-1 22:24 编辑


[磁盘引导扇区与磁盘分区表]    ——如何从硬盘(U盘)中找到我们需要的分区



[原理解析]



[数据结构]

  1. /*------------------*
  2. *   常 数 宏 定 义  *
  3. *------------------*/
  4. # define DPT_START_ADDRESS      0x01BE

  5. /*------------------*
  6. *   动 作 宏 定 义  *
  7. *------------------*/
  8. # define DPT_PARTITION_START_SECTOR(__DPT_ITEM_ADDR)    \
  9.             (__DPT_ITEM_ADDR)->RelativeSectors

  10. /********************
  11. *  用户变量类型定义 *
  12. ********************/
  13. typedef struct DiskPartitionTableItem DPT_ITEM;

  14. /********************
  15. *    结构体定义区   *
  16. ********************/
  17. struct DiskPartitionTableItem
  18. {
  19.     BYTE        ActivePartition;
  20.     BYTE        StartHead;
  21.     unsigned    StartSector     :6;
  22.     unsigned    StartCylinder   :10;
  23.     BYTE        OSIndicator;
  24.     BYTE        EndHead;
  25.     unsigned    EndSector       :6;
  26.     unsigned    EndCylinder     :10;
  27.     UINT32      RelativeSectors;
  28.     UINT32      TotalSectors;
  29. };
复制代码

[关键代码]

>>如何依次获取各个磁盘分区的信息

  1. /***********************************************************
  2. *   函数说明:  获取下一个DPT项                            *
  3. *   输入:      主引导扇区指针,分区表项指针               *
  4. *   输出:      下一个分区表项                             *
  5. *   调用函数:  无                                         *
  6. * -------------------------------------------------------- *
  7. *   [使用说明]                                             *
  8. *           当传入的分区表项指针为空(NULL)时,自动返回第 *
  9. *       一个分区表项。当传入的分区表项指针不为空时,自动返 *
  10. *       回下一个分区表项的指针,当下一个分区表项不存在时, *
  11. *       返回空(NULL)                                     *
  12. ***********************************************************/
  13. DPT_ITEM *Get_Next_Disk_Partition
  14.     (
  15.         BYTE *pchMasterBootSector,
  16.         DPT_ITEM *pdptPartition
  17.     )
  18. {
  19.     if (pchMasterBootSector == NULL)
  20.     {
  21.         /* 无效的输入 */
  22.         return NULL;
  23.     }   
  24.    
  25.     if (pdptPartition == NULL)
  26.     {
  27.         /* 要求返回第一个分区表项 */
  28.         pdptPartition = (DPT_ITEM *)(pchMasterBootSector + DPT_START_ADDRESS);
  29.     }
  30.     else
  31.     {
  32.         /* 指向下一个分区表项 */
  33.         pdptPartition += 1;
  34.     }
  35.    
  36.     /* 进行分区表项的有效性检测 */
  37.     {
  38.         UINT8 n = 0;
  39.         BOOL bIfValid = FALSE;
  40.         for (n = 0;n &lt; sizeof(DPT_ITEM);n++)
  41.         {
  42.             if (((BYTE *)pdptPartition)[n] != 0x00)
  43.             {
  44.                 bIfValid = TRUE;
  45.                 break;
  46.             }
  47.         }
  48.         if (!bIfValid)
  49.         {
  50.             /* 表项为空 */
  51.             return NULL;
  52.         }
  53.     }
  54.    
  55.     return pdptPartition;
  56. }
复制代码

[使用范例]

Example A:

  1. //从引导扇区中提取第一个磁盘分区在物理扇区中的偏移量
  2. //其中g_chSectorBuffer是一个大小为512字节的数组,保存
  3. //了磁盘第一个引导扇区的内容
  4. s_dwPartitionSectorOffset = DPT_PARTITION_START_SECTOR
  5.             (
  6.                 Get_Next_Disk_Partition(g_chSectorBuffer,NULL)
  7.             );

  8. [hr][color=blue]Example B:[/color]

  9. //从引导扇区中查找第一个类型为NTFS的磁盘分区,并返回
  10. //它在物理扇区偏移量

  11. # define OS_INDICATOR_NTFS                0x07

  12. DPT_ITEM *pDPTItem = NULL;

  13. do
  14. {
  15.     //读取新的分区信息
  16.     pDPTItem = Get_Next_Disk_Partition(g_chSectorBuffer,pDPTItem);
  17.    
  18.     if ((pDPTItem != NULL) && (pDPTItem->OSIndicator == OS_INDICATOR_NTFS))
  19.     {
  20.         s_dwPartitionSectorOffset =  DPT_PARTITION_START_SECTOR(pDPTItem);
  21.         break;
  22.     }
  23. }
  24. while(pDPTItem != NULL)

  25. if (pDPTItem != NULL)
  26. {
  27.     //找到了需要的分区,进行对应的处理
  28. }
  29. else
  30. {
  31.     //没有找到需要的分区,进行对应的处理
  32. }
复制代码

出0入0汤圆

发表于 2009-1-15 20:14:01 | 显示全部楼层
mark

出0入0汤圆

发表于 2008-12-29 13:44:25 | 显示全部楼层
先做个记号,毕业设计要做用ARM9实现这个功能

要细细研究~~~

出0入0汤圆

发表于 2008-12-24 16:23:36 | 显示全部楼层
好贴&nbsp;记号

出0入0汤圆

发表于 2008-12-24 11:39:11 | 显示全部楼层
不错,记号

出0入0汤圆

发表于 2008-12-24 08:34:34 | 显示全部楼层
mark

出0入0汤圆

发表于 2008-12-23 23:40:10 | 显示全部楼层
牛逼

出0入0汤圆

发表于 2008-12-19 08:24:42 | 显示全部楼层
最后能来个完整例子,比如用AVR写个TXT文件到SD中,就完美了.

出0入0汤圆

发表于 2008-12-19 08:05:55 | 显示全部楼层
学习

出0入0汤圆

发表于 2008-12-14 15:47:27 | 显示全部楼层
好东西,顶一顶!

出0入8汤圆

发表于 2008-12-14 15:06:02 | 显示全部楼层
我以来收藏。

出0入0汤圆

发表于 2008-9-24 23:42:55 | 显示全部楼层
顶!

傻孩子帖子的排版没得说!

出0入0汤圆

发表于 2008-9-24 21:48:56 | 显示全部楼层
沉得真快啊,帮顶一下

出0入0汤圆

发表于 2008-9-24 18:26:30 | 显示全部楼层
哦哦哦

精品啊,重点学习

佩服&nbsp;傻孩子&nbsp;

出0入296汤圆

 楼主| 发表于 2008-9-24 16:46:34 | 显示全部楼层
</font>&lt;有待添加内容>

出0入0汤圆

发表于 2008-9-25 00:20:35 | 显示全部楼层
顶&nbsp;顶&nbsp;

出0入0汤圆

发表于 2008-9-25 00:16:16 | 显示全部楼层

出0入0汤圆

发表于 2008-9-24 23:51:35 | 显示全部楼层
很详细,谢谢!



前段时间我想写读取硬盘逻辑扇区的程序,发帖问了没人回答。不过我已经把程序写好了,现在的FAT程序支持1主分区+5逻辑分区的磁盘文件搜索和读取,对于137G内的硬盘来说,已达到实用阶段^_^

出0入0汤圆

发表于 2008-9-24 23:44:15 | 显示全部楼层
好贴子一定要顶的,呵呵

出0入0汤圆

发表于 2009-3-5 13:45:54 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-3-5 19:56:24 | 显示全部楼层
好长啊!LZ厉害

出0入0汤圆

发表于 2009-3-5 20:11:45 | 显示全部楼层
关于FAT系统,MARK

出0入0汤圆

发表于 2009-3-9 17:31:42 | 显示全部楼层
1K SRAM 的单片机玩不转这个啊。

出0入296汤圆

 楼主| 发表于 2009-3-9 20:21:03 | 显示全部楼层
恩,1K Sram基本不用考虑FAT系统……非要在1K SRAM的系统上跑……我只能说
很具有表演性……
头像被屏蔽

出0入0汤圆

发表于 2009-3-10 09:11:47 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-4-7 15:16:39 | 显示全部楼层
好贴,关于文件系统

出0入0汤圆

发表于 2009-4-7 23:07:19 | 显示全部楼层
发现自己就是个 超级菜鸟啊!!!

出0入0汤圆

发表于 2009-4-10 17:35:17 | 显示全部楼层
MBR扇区的字节数是不是一定是512Bytes的?可否扩展为其它的数值?
因为我看fat手册里面是可以规定扇区字节数的,手册里面的扇区是否只是局限于该分区范围内呢?
各种资料看多了有点混乱-_- thanks.

出0入0汤圆

发表于 2009-4-12 21:23:01 | 显示全部楼层
厉害!

出0入0汤圆

发表于 2009-4-13 09:21:57 | 显示全部楼层
晕啊,怎么我看1、2楼,3、4楼,5、6楼,7,8楼的内容都是重复的?
是不是重复发了?

出0入296汤圆

 楼主| 发表于 2009-4-13 15:23:38 | 显示全部楼层
网站被攻击的时候,数据恢复导致的错误。别介意。

出0入0汤圆

发表于 2009-4-13 21:59:19 | 显示全部楼层
没介意,哈哈。

麻烦看一下40楼的问题,谢谢。

据说硬盘的一个扇区将来可能扩展到4kbytes,不知要怎样保证和现有硬盘的兼容性呢?

出0入296汤圆

 楼主| 发表于 2009-4-13 23:39:09 | 显示全部楼层
写代码的时候老老实实根据扇区大小来设置参数,而不是以默认的512字节来操作扇区就可以了。
扇区的大小应该根据BPB的内容来获取。

出0入0汤圆

发表于 2009-4-14 01:03:32 | 显示全部楼层
多谢指教。
不过BPB是VFAT FS里面才有的东西,
而我的意思是MBS(也即0柱面0磁头第一个扇区里面)是否一定是512bytes?
如果不是的话,从哪里可以得到该参数?

出0入0汤圆

发表于 2009-4-14 11:10:49 | 显示全部楼层
好资料,顶

出0入0汤圆

发表于 2009-4-14 14:12:56 | 显示全部楼层
好帖!

出0入0汤圆

发表于 2009-4-14 14:13:00 | 显示全部楼层
好帖!

出0入0汤圆

发表于 2009-4-14 14:13:24 | 显示全部楼层
好帖!

出0入0汤圆

发表于 2009-4-14 14:34:58 | 显示全部楼层
烦人啊,还有结构体的字节对齐问题。

出0入0汤圆

发表于 2009-4-23 20:09:15 | 显示全部楼层
顶    LZ强人

出0入296汤圆

 楼主| 发表于 2009-4-24 09:14:07 | 显示全部楼层
to 【46楼】 valley 微风山谷
    准确的说法是这样的:在总共512byte的主引导记录中,MBR的引导程序占了其中的前446个字
节(偏移0H~偏移1BDH),随后的64个字节(偏移1BEH~偏移1FDH)为DPT(Disk PartitionTable,硬盘
分区表),最后的两个字节“55 AA”(偏移1FEH~偏移1FFH)是分区有效结束标志。

出0入0汤圆

发表于 2009-5-24 00:53:42 | 显示全部楼层
记号,随时关注进展

出0入0汤圆

发表于 2009-6-16 11:03:18 | 显示全部楼层
好厉害呀!
请教一个问题:我有一个台电酷闪 4G U盘,用单片机分别读取 0、1、2、3、4、5扇区(512B)时,全为零,直到第6扇区时才读到非零值,而读取爱国者(aigo 4G)正常,可以读到 0 扇区的BPB记录。用WinHEX软件读这两种 U 盘的时候都正常。是不是我没有弄清楚台电酷闪 BPB存放的正确位置,或者是物理扇区和逻辑扇区的区别?

出0入296汤圆

 楼主| 发表于 2009-6-16 11:54:26 | 显示全部楼层
按道理来说BPB应该是在0扇区的……

出0入0汤圆

发表于 2009-6-16 12:19:04 | 显示全部楼层
mark一下。有时间的时候看看那。

出0入0汤圆

发表于 2009-6-18 22:07:10 | 显示全部楼层
顶一个!

出0入0汤圆

发表于 2009-6-18 23:51:56 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-6-25 21:05:21 | 显示全部楼层
做个记号,回头重点学习。

出0入0汤圆

发表于 2009-6-25 21:46:24 | 显示全部楼层
纯mark

出0入0汤圆

发表于 2009-6-26 00:51:48 | 显示全部楼层
等能看懂的时候来看...

出0入0汤圆

发表于 2009-7-24 15:11:34 | 显示全部楼层
顶!~mark!

出0入0汤圆

发表于 2009-7-24 16:20:14 | 显示全部楼层
好资料,精品

出0入0汤圆

发表于 2009-7-24 16:53:46 | 显示全部楼层
mark

出0入18汤圆

发表于 2009-7-24 20:42:15 | 显示全部楼层
浏览过了,不错,保存下来,好好看看!

出0入0汤圆

发表于 2009-11-1 00:03:59 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-11-1 16:05:04 | 显示全部楼层
傻孩子的好帖

出0入0汤圆

发表于 2009-11-9 13:43:51 | 显示全部楼层
MARK

出0入0汤圆

发表于 2009-11-21 22:36:32 | 显示全部楼层
A、硬盘物理结构和FAT文件系统解析(一)
    B、硬盘物理结构和FAT文件系统解析(二)
好多图片打不开,楼主有办法吗?

出0入0汤圆

发表于 2009-11-23 16:06:10 | 显示全部楼层
文件系统

出0入0汤圆

发表于 2009-11-23 16:18:11 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-11-23 16:21:41 | 显示全部楼层
在文件读取中
   
    dwSectorNumber = pFATBPB->BPB_ResvdSecCnt +  
                ((dwCluster * 4) / (pFATBPB->BPB_BytePerSec));
    wByteOffset = (dwCluster * 4) % (pFATBPB->BPB_BytePerSec);


怎么理解?

出0入0汤圆

发表于 2009-11-23 16:22:40 | 显示全部楼层
谢谢。MARK

出0入0汤圆

发表于 2009-11-25 00:18:10 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-11-27 09:48:47 | 显示全部楼层
mark 这几天在研究U盘FAT系统的文件创建与读写,怎样查询已用空间和分配新的存储空间貌似比较麻烦,不知道有什么好的方法

出0入0汤圆

发表于 2009-12-1 16:45:33 | 显示全部楼层
着几天正在用收藏了~

出0入0汤圆

发表于 2009-12-9 01:07:00 | 显示全部楼层
傻孩子工作没有?
有这么多时间整理资料

出0入0汤圆

发表于 2009-12-9 08:56:19 | 显示全部楼层
学习学习文件系统

出0入0汤圆

发表于 2009-12-9 08:59:52 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-22 09:27:48 | 显示全部楼层
谢谢,最近正准备做这方面的工作

出0入0汤圆

发表于 2010-1-22 09:47:36 | 显示全部楼层
mark!

出0入0汤圆

发表于 2010-1-26 15:02:33 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-26 16:57:42 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-26 17:45:57 | 显示全部楼层
顶一下。

出0入0汤圆

发表于 2010-1-26 22:00:33 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-1-29 10:00:05 | 显示全部楼层
好帖

出0入0汤圆

发表于 2010-2-3 16:05:58 | 显示全部楼层
谢谢分享

出0入0汤圆

发表于 2010-2-4 23:13:21 | 显示全部楼层
做个记号

出0入0汤圆

发表于 2010-2-5 02:27:13 | 显示全部楼层
顶一下。

出110入26汤圆

发表于 2010-2-9 14:44:00 | 显示全部楼层
继续学习…标记

出0入0汤圆

发表于 2010-2-9 20:06:16 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-2-9 20:33:10 | 显示全部楼层
记号!!!

出0入0汤圆

发表于 2010-2-26 12:26:18 | 显示全部楼层
Mark 一下,有空来学习。

出0入0汤圆

发表于 2010-3-4 16:33:44 | 显示全部楼层
有时间在看

出0入0汤圆

发表于 2010-3-4 17:43:30 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-4 20:18:19 | 显示全部楼层
MARK

出235入235汤圆

发表于 2010-3-4 21:40:07 | 显示全部楼层
mark!

出0入0汤圆

发表于 2010-3-16 16:32:50 | 显示全部楼层
好贴

出0入0汤圆

发表于 2010-3-16 19:07:00 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-23 20:45:54 | 显示全部楼层
呵呵,我只看到mark等,有多少人实际分析!都一看都懂吗?一点疑问也没有,奇怪!呵呵

出0入0汤圆

发表于 2010-3-23 21:32:48 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-29 10:50:02 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-3-29 11:23:21 | 显示全部楼层
今天才发现这个好贴,晚了,晚了

出0入0汤圆

发表于 2010-4-22 17:36:07 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-4-22 17:45:48 | 显示全部楼层
顶了
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-20 07:45

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表