前面两篇文章分析了FAT文件系统,没有代码总感觉很空虚寂寞冷,fatfs正好是一个开源专门应对fat文件系统的代码,从这篇文章开始分析fatfs代码,“理论+实践”才是悟道的根本途径。从本篇文章开始分析fatfs文件系统,基本从两方面分析:
(1)主要函数分析。
(2)宏定义使用分析。
(1)假设一个磁盘就一个分区。
(2)只分析FAT32文件系统相关的代码。
(3)函数的大部分分析,都写入代码注释中。
为了方便分析,排除视觉障碍,已经删除了不在假设范围内代码。
/*-----------------------------------------------------------------------*/
/* Create FAT file system on the logical drive */
/*-----------------------------------------------------------------------*/
FRESULT f_mkfs (
const TCHAR* path, /* 磁盘号 */
BYTE opt, /* 格式化的类型:FAT32 */
DWORD au, /* 格式化时,要设置的簇大小,以字节为单位 */
void* work, /* 用户提供的buffer */
UINT len /* 用户提供的buffer的大小,以字节为单位 */
)
{
const UINT n_fats = 1; /* Number of FATs for FAT12/16/32 volume (1 or 2) */
const UINT n_rootdir = 512; /* Number of root directory entries for FAT12/16 volume */
static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT12/16 volume (4Ks unit) */
static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */
BYTE fmt, sys, *buf, *pte, pdrv, part;
WORD ss;
DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n;
DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */
DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */
UINT i;
int vol;
DSTATUS stat;
/*1. 获取磁盘号,进行一些必要参数的计算和分析,判断参数是否满足要求*/
/* Check mounted drive and clear work area */
vol = get_ldnumber(&path); /* 找到物理磁盘 */
if (vol < 0) return FR_INVALID_DRIVE;
if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* fat文件系统的描述,放到一个全局变量里面 */
pdrv = LD2PD(vol); /* 找到物理磁盘号 */
part = LD2PT(vol); /* 分区表,Partition (0:create as new, 1-4:get from partition table) */
/* Check physical drive status */
stat = disk_initialize(pdrv);
if (stat & STA_NOINIT) return FR_NOT_READY;
if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
/* 获取块大小,块是擦除的基本单元 */
if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Erase block to align data area */
ss = _MAX_SS; /* 默认扇区大小为512字节 */
if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */
au /= ss; /* 一个簇占多少个扇区 */
/* Get working buffer */
buf = (BYTE*)work; /* Working buffer */
sz_buf = len / ss; /* 下面写数据要用到一个buffer, 这里计算出用户提供的buffer的大小(以扇区为单位)*/
szb_buf = sz_buf * ss; /* buffer大小以字节为单位 */
if (!szb_buf) return FR_MKFS_ABORTED; /* buffer如果不足1个扇区就报错 */
/* Determine where the volume to be located (b_vol, sz_vol) */
if (_MULTI_PARTITION && part != 0) { /* 判断fatfs是否是支持多扇区的,我们不分区多扇区的玩法 */
/* Get partition information from partition table in the MBR */
if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Load MBR */
if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; /* Check if MBR is valid */
pte = buf + (MBR_Table + (part - 1) * SZ_PTE);
if (!pte[PTE_System]) return FR_MKFS_ABORTED; /* No partition? */
b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */
sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */
} else {
/* 获取这个分区总的扇区个数 */
if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR;
b_vol = (opt & FM_SFD) ? 0 : 63; /* opt=FM_FAT32,所以此时,保留扇区个数b_vol=63 */
if (sz_vol < b_vol) return FR_MKFS_ABORTED;
sz_vol -= b_vol; /* 分区总扇区数减去保留扇区 */
}
if (sz_vol < 128) return FR_MKFS_ABORTED; /* Check if volume size is >=128s */
/* Pre-determine the FAT type */
do {
if (_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */
if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */
fmt = FS_EXFAT; break;
}
}
if (au > 128) return FR_INVALID_PARAMETER; /* au现在的单位是扇区 */
if (opt & FM_FAT32) { /* FAT32 possible? */
if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */
fmt = FS_FAT32; break; /* 跳出do while()循环 */
}
}
if (!(opt & FM_FAT)) return FR_INVALID_PARAMETER; /* no-FAT? */
fmt = FS_FAT16;
} while (0);
{ /* Create an FAT12/16/32 volume */
do {
pau = au;
/* Pre-determine number of clusters and FAT sub-type */
if (fmt == FS_FAT32) { /* FAT32 volume */
if (!pau) { /* 如果簇大小用户设置为0,那么就默认设置为128K */
n = sz_vol / 0x20000; /* Volume size in unit of 128KS */
for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */
}
n_clst = sz_vol / pau; /* 计算出本分区总共的簇数 */
sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* sz_fat代表这个分区需要一个fat表需要占的扇区数 */
sz_rsv = 32; /* 保留扇区数 */
sz_dir = 0; /* No static directory */
if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED;
} else { /* FAT12/16 volume */
if (!pau) { /* au auto-selection */
n = sz_vol / 0x1000; /* Volume size in unit of 4KS */
for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */
}
n_clst = sz_vol / pau;
if (n_clst > MAX_FAT12) {
n = n_clst * 2 + 4; /* FAT size [byte] */
} else {
fmt = FS_FAT12;
n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */
}
sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */
sz_rsv = 1; /* Number of reserved sectors */
sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */
}
b_fat = b_vol + sz_rsv; /* FAT表所在的扇区号(63+32) */
b_data = b_fat + sz_fat * n_fats + sz_dir; /* 数据区所在的扇区号(FAT表的基地址号+FAT表所占的扇区数+0) */
/* Align data base to erase block boundary (for flash memory media) */
n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* 由于擦除是按块来的,所以在此进行调整:需要让保留区、fat表的要占完整的一个块 */
if (fmt == FS_FAT32) { /* FAT32: Move FAT base */
sz_rsv += n; b_fat += n; /* 单位为扇区 */
} else { /* FAT12/16: Expand FAT size */
sz_fat += n / n_fats;
}
/* Determine number of clusters and final check of validity of the FAT sub-type */
if (sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED; /* Too small volume */
n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau;
if (fmt == FS_FAT32) {
if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */
if (!au && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */
return FR_MKFS_ABORTED;
}
}
if (fmt == FS_FAT16) {
if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */
if (!au && (pau * 2) <= 64) {
au = pau * 2; continue; /* Adjust cluster size and retry */
}
if ((opt & FM_FAT32)) {
fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */
}
if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */
return FR_MKFS_ABORTED;
}
if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */
if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */
return FR_MKFS_ABORTED;
}
}
if (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED; /* Too many clusters for FAT12 */
/* Ok, it is the valid cluster configuration */
break;
} while (1);
/* 2.创建分区的DBR */
mem_set(buf, 0, ss);
mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */
st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */
buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */
st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */
buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */
st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */
if (sz_vol < 0x10000) {
st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */
} else {
st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */
}
buf[BPB_Media] = 0xF8; /* Media descriptor byte */
st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */
st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */
st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */
if (fmt == FS_FAT32) {
st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */
st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */
st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */
st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */
st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */
buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */
buf[BS_BootSig32] = 0x29; /* Extended boot signature */
mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */
} else {
st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */
st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */
buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */
buf[BS_BootSig] = 0x29; /* Extended boot signature */
mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */
}
st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */
if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the VBR sector */
/* Create FSINFO record if needed */
if (fmt == FS_FAT32) {
disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */
mem_set(buf, 0, ss);
st_dword(buf + FSI_LeadSig, 0x41615252);
st_dword(buf + FSI_StrucSig, 0x61417272);
st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */
st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */
st_word(buf + BS_55AA, 0xAA55);
disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */
disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */
}
/* 3.初始化fat表 */
mem_set(buf, 0, (UINT)szb_buf);
sect = b_fat; /* FAT start sector */
for (i = 0; i < n_fats; i++) { /* Initialize FATs each */
if (fmt == FS_FAT32) {
st_dword(buf + 0, 0xFFFFFFF8); /* 目录项0 */
st_dword(buf + 4, 0xFFFFFFFF); /* 目录项1 */
st_dword(buf + 8, 0x0FFFFFFF); /* 根目录项 */
} else {
st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */
}
nsect = sz_fat; /* Number of FAT sectors */
do { /* Fill FAT sectors */
n = (nsect > sz_buf) ? sz_buf : nsect;
if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR;
mem_set(buf, 0, ss);
sect += n; nsect -= n;
} while (nsect);
}
/* 4.把根目录所在的扇区全部清0 */
nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */
do {
n = (nsect > sz_buf) ? sz_buf : nsect;
if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR;
sect += n; nsect -= n;
} while (nsect);
}
/* 5.在分区的第0号扇区写入MBR */
if (_FS_EXFAT && fmt == FS_EXFAT) {
sys = 0x07; /* HPFS/NTFS/exFAT */
} else {
if (fmt == FS_FAT32) {
sys = 0x0C; /* FAT32X */
} else {
if (sz_vol >= 0x10000) {
sys = 0x06; /* FAT12/16 (>=64KS) */
} else {
sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 (<64KS) : FAT12 (<64KS) */
}
}
}
if (_MULTI_PARTITION && part != 0) {
/* Update system ID in the partition table */
if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Read the MBR */
buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system type */
if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it back to the MBR */
} else {
if (!(opt & FM_SFD)) {
/* Create partition table in FDISK format */
mem_set(buf, 0, ss);
st_word(buf + BS_55AA, 0xAA55); /* MBR signature */
pte = buf + MBR_Table; /* Create partition table for single partition in the drive */
/*5.1 MBR中分区表的信息*/
pte[PTE_Boot] = 0; /* Boot indicator */
pte[PTE_StHead] = 1; /* Start head */
pte[PTE_StSec] = 1; /* Start sector */
pte[PTE_StCyl] = 0; /* Start cylinder */
pte[PTE_System] = sys; /* System type */
n = (b_vol + sz_vol) / (63 * 255); /* (End CHS is incorrect) */
pte[PTE_EdHead] = 254; /* End head */
pte[PTE_EdSec] = (BYTE)(n >> 2 | 63); /* End sector */
pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */
st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */
st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */
if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */
}
}
if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR;
return FR_OK;
}
(1)格式化函数的参数。分析思考我们在Windows上格式化一个磁盘,我们可以得出格式化磁盘需要内容:
a.在“需要格式化的磁盘”右键。
b.选择要格式化的文件系统类型,FAT16/FAT32/EXTFAT等。
c.选择簇大小。
因此,格式化函数的第1个参数是磁盘的路径,根据磁盘的路径函数就能找到对应的磁盘,第2个参数也是要传入文件系统的类型,第3个参数也就是簇的大小,第4、5个参数是这个函数运行所需要的内存。
(2)格式化函数的作用。与我们之前分析FAT文件系统原理是一致的,此函数总共进行如下的操作:
a. 获取磁盘号,进行一些必要参数的计算和分析,判断参数是否满足要求。
b.创建DBR。同时创建FSINFO信息,FSINFO信息的作用表示了当前分区还可用的簇多少已经下一个可用的簇号,从而可以方便操作系统处理。
c.初始化FAT表。
d.初始化根目录区。把根目录所在的簇全部清0。
f.创建MBR,并且把当前的分区信息写入主分区字段内。
从f_mkfs我们可以看出,此函数的作用与我们之前分析的FAT文件系统是完全吻合的。