FATFS f_mount函数详细解析
f_mount
函数是 FatFs 文件系统库中的一个关键函数,主要用于挂载或卸载一个逻辑卷(文件系统)。它的作用是将一个文件系统对象与一个逻辑驱动器关联起来,使得后续的文件操作能够在该驱动器上进行。
FRESULT f_mount (
FATFS* fs, /* 指向文件系统对象的指针 (如果为 NULL 表示卸载) */
const TCHAR* path, /* 要挂载或卸载的逻辑驱动器号 */
BYTE opt /* 挂载选项:0 = 延迟挂载,1 = 立即挂载 */
)
{
FATFS *cfs;
int vol;
FRESULT res;
const TCHAR *rp = path;
/* 获取逻辑驱动器号 */
vol = get_ldnumber(&rp);
if (vol < 0) return FR_INVALID_DRIVE; /* 如果驱动器号无效,返回错误代码 */
cfs = FatFs[vol]; /* 获取当前卷的文件系统对象指针 */
if (cfs) { /* 如果当前卷已经注册了文件系统对象,执行卸载操作 */
FatFs[vol] = 0; /* 将当前卷的文件系统对象置空 */
#if FF_FS_LOCK
clear_share(cfs); /* 清除与该卷相关的文件锁 */
#endif
#if FF_FS_REENTRANT /* 如果启用了可重入性支持 */
ff_mutex_delete(vol); /* 删除卷的互斥锁 */
#endif
cfs->fs_type = 0; /* 将文件系统类型设置为0,表示该文件系统对象已失效 */
}
if (fs) { /* 如果提供了新的文件系统对象 fs,则注册该对象 */
fs->pdrv = LD2PD(vol); /* 设置卷的物理驱动器号 */
#if FF_FS_REENTRANT /* 如果启用了可重入性支持 */
fs->ldrv = (BYTE)vol; /* 设置卷的逻辑驱动器号 */
if (!ff_mutex_create(vol)) return FR_INT_ERR; /* 创建卷的互斥锁 */
#if FF_FS_LOCK
if (SysLock == 0) { /* 如果系统锁还未创建,则创建系统锁 */
if (!ff_mutex_create(FF_VOLUMES)) {
ff_mutex_delete(vol); /* 创建失败时删除卷的互斥锁 */
return FR_INT_ERR; /* 返回内部错误 */
}
SysLock = 1; /* 设置系统锁已就绪的标志 */
}
#endif
#endif
fs->fs_type = 0; /* 将新文件系统对象的类型设置为 0(无效状态) */
FatFs[vol] = fs; /* 将新文件系统对象注册到对应的逻辑驱动器号 */
}
if (opt == 0) return FR_OK; /* 如果 opt 为 0,表示不立即挂载,返回成功 */
res = mount_volume(&path, &fs, 0); /* 强制挂载该卷 */
LEAVE_FF(fs, res); /* 返回挂载结果 */
}
在代码中可以看到,核心的代码就是mount_volume
static FRESULT mount_volume ( /* FR_OK(0): 成功,!=0: 发生错误 */
const TCHAR** path, /* 指向路径名称的指针(含驱动器号) */
FATFS** rfs, /* 指向找到的文件系统对象的指针 */
BYTE mode /* 要检查的访问模式(是否需要写保护) */
)
{
int vol; /* 逻辑驱动器号 */
FATFS *fs; /* 文件系统对象指针 */
DSTATUS stat; /* 物理驱动器状态 */
LBA_t bsect; /* 起始扇区号 */
DWORD tsect, sysect, fasize, nclst, szbfat; /* 用于存储文件系统的相关信息 */
WORD nrsv; /* 保留扇区数 */
UINT fmt; /* 文件系统格式 (FAT12, FAT16, FAT32, exFAT) */
/* 获取逻辑驱动器号 */
*rfs = 0; /* 初始化返回的文件系统对象指针为 NULL */
vol = get_ldnumber(path); /* 从路径中获取逻辑驱动器号 */
if (vol < 0) return FR_INVALID_DRIVE; /* 如果驱动器号无效,返回错误 */
/* 检查文件系统对象是否有效 */
fs = FatFs[vol]; /* 获取当前逻辑卷的文件系统对象 */
if (!fs) return FR_NOT_ENABLED; /* 如果文件系统对象不可用,返回错误 */
#if FF_FS_REENTRANT
if (!lock_volume(fs, 1)) return FR_TIMEOUT; /* 锁定该卷以确保多线程环境下的安全性 */
#endif
*rfs = fs; /* 返回文件系统对象指针 */
mode &= (BYTE)~FA_READ; /* 检查是否需要写访问模式 */
if (fs->fs_type != 0) { /* 如果文件系统已经挂载 */
stat = disk_status(fs->pdrv); /* 获取物理驱动器状态 */
if (!(stat & STA_NOINIT)) { /* 检查驱动器是否已经初始化 */
if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* 检查写保护 */
return FR_WRITE_PROTECTED; /* 如果写保护,返回错误 */
}
return FR_OK; /* 文件系统对象有效,返回成功 */
}
}
/* 文件系统对象无效,尝试挂载该卷 */
fs->fs_type = 0; /* 将文件系统对象标记为无效 */
stat = disk_initialize(fs->pdrv); /* 初始化物理驱动器 */
if (stat & STA_NOINIT) { /* 检查初始化是否成功 */
return FR_NOT_READY; /* 驱动器未准备好,返回错误 */
}
if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* 检查写保护 */
return FR_WRITE_PROTECTED;
}
#if FF_MAX_SS != FF_MIN_SS /* 如果支持多种扇区大小 */
if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR; /* 获取扇区大小 */
if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; /* 扇区大小不合法 */
#endif
/* 查找 FAT 卷 */
fmt = find_volume(fs, LD2PT(vol)); /* 尝试识别文件系统格式 */
if (fmt == 4) return FR_DISK_ERR; /* 如果发生磁盘 I/O 错误,返回错误 */
if (fmt >= 2) return FR_NO_FILESYSTEM; /* 没有找到有效的 FAT 文件系统,返回错误 */
bsect = fs->winsect; /* 存储文件系统的起始扇区 */
/* 初始化 FAT 文件系统对象 */
#if FF_FS_EXFAT
if (fmt == 1) { /* 如果是 exFAT 文件系统 */
QWORD maxlba;
DWORD so, cv, bcl, i;
/* 检查 exFAT 的 BPB (BIOS Parameter Block) */
for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* 检查填充区是否为零 */
if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; /* 如果不是,返回错误 */
if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* 检查 exFAT 版本是否为 1.0 */
/* 检查扇区大小是否正确 */
if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) {
return FR_NO_FILESYSTEM;
}
maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* 获取卷的最后一个 LBA 扇区 */
if (!FF_LBA64 && maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* 超过 32 位 LBA 限制,返回错误 */
fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* 获取 FAT 区大小 */
fs->n_fats = fs->win[BPB_NumFATsEx]; /* 获取 FAT 数量 */
if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* 仅支持单 FAT */
fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* 获取簇大小 */
if (fs->csize == 0) return FR_NO_FILESYSTEM; /* 簇大小无效,返回错误 */
nclst = ld_dword(fs->win + BPB_NumClusEx); /* 获取簇的总数 */
if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* 超过最大簇数,返回错误 */
fs->n_fatent = nclst + 2; /* 设置 FAT 表条目数 */
/* 设置卷的边界和限制 */
fs->volbase = bsect;
fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); /* 数据区起始扇区 */
fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); /* FAT 区起始扇区 */
if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* 检查卷大小是否足够 */
fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); /* 根目录起始簇 */
/* 检查位图是否连续 */
so = i = 0;
for (;;) { /* 在根目录中查找位图条目 */
if (i == 0) {
if (so >= fs->csize) return FR_NO_FILESYSTEM; /* 没有找到位图条目,返回错误 */
if (move_window(fs, clst2sect(fs, (DWORD)fs->dirbase) + so) != FR_OK) return FR_DISK_ERR;
so++;
}
if (fs->win[i] == ET_BITMAP) break; /* 找到位图条目 */
i = (i + SZDIRE) % SS(fs); /* 继续寻找下一个条目 */
}
bcl = ld_dword(fs->win + i + 20); /* 获取位图所在的簇 */
if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; /* 无效簇号,返回错误 */
fs->bitbase = fs->database + fs->csize * (bcl - 2); /* 位图起始扇区 */
/* 检查位图是否连续 */
for (;;) {
if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR;
cv = ld_dword(fs->win + bcl % (SS(fs) / 4) * 4); /* 读取 FAT 条目 */
if (cv == 0xFFFFFFFF) break; /* 如果到达最后一个条目,退出循环 */
if (cv != ++bcl) return FR_NO_FILESYSTEM; /* 位图不是连续的,返回错误 */
}
#if !FF_FS_READONLY
fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* 初始化簇分配信息 */
#endif
fmt = FS_EXFAT; /* 设置为 exFAT 文件系统 */
} else
#endif /* FF_FS_EXFAT */
{
/* FAT12/16/32 文件系统初始化 */
if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* 检查扇区大小是否匹配 */
fasize = ld_word(fs->win + BPB_FATSz16); /* 获取 FAT 区大小 */
if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); /* 如果是 FAT32,使用更大的大小字段 */
fs->fsize = fasize;
fs->n_fats = fs->win[BPB_NumFATs]; /* 获取 FAT 区数量 */
if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* FAT 数量必须为 1 或 2 */
fs->csize = fs->win[BPB_SecPerClus]; /* 获取每簇扇区数 */
if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* 检查簇大小是否为 2 的幂 */
fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* 获取根目录项数 */
if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* 检查根目录项数是否对齐 */
tsect = ld_word(fs->win + BPB_TotSec16); /* 获取卷的总扇区数 */
if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);
nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* 获取保留扇区数 */
if (nrsv == 0) return FR_NO_FILESYSTEM; /* 保留扇区数不能为 0 */
/* 确定 FAT 文件系统类型(FAT12、FAT16、FAT32) */
sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* 计算系统区大小 */
if (tsect < sysect) return FR_NO_FILESYSTEM; /* 扇区数不足,返回错误 */
nclst = (tsect - sysect) / fs->csize; /* 计算簇的数量 */
if (nclst == 0) return FR_NO_FILESYSTEM; /* 无效簇数,返回错误 */
fmt = 0;
if (nclst <= MAX_FAT32) fmt = FS_FAT32; /* 符合 FAT32 标准 */
if (nclst <= MAX_FAT16) fmt = FS_FAT16; /* 符合 FAT16 标准 */
if (nclst <= MAX_FAT12) fmt = FS_FAT12; /* 符合 FAT12 标准 */
if (fmt == 0) return FR_NO_FILESYSTEM; /* 无法识别文件系统类型 */
/* 设置文件系统对象的边界和限制 */
fs->n_fatent = nclst + 2; /* FAT 表中的条目数 */
fs->volbase = bsect; /* 卷的起始扇区 */
fs->fatbase = bsect + nrsv; /* FAT 区的起始扇区 */
fs->database = bsect + sysect; /* 数据区的起始扇区 */
if (fmt == FS_FAT32) { /* 如果是 FAT32 */
if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* 检查 FAT32 版本 */
fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* 根目录起始簇 */
szbfat = fs->n_fatent * 4; /* 计算 FAT 大小 */
} else {
fs->dirbase = fs->fatbase + fasize; /* 根目录的起始扇区 */
szbfat = (fmt == FS_FAT16) ? /* 计算 FAT 大小 */
fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
}
if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* FAT 大小不足,返回错误 */
#if !FF_FS_READONLY
/* 获取 FSInfo 信息(如果可用) */
fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* 初始化簇分配信息 */
fs->fsi_flag = 0x80;
#if (FF_FS_NOFSINFO & 3) != 3
if (fmt == FS_FAT32 && ld_word(fs->win + BPB_FSInfo32) == 1 && move_window(fs, bsect + 1) == FR_OK) {
/* 检查并加载 FSInfo 数据 */
fs->fsi_flag = 0;
if (ld_word(fs->win + BS_55AA) == 0xAA55 && ld_dword(fs->win + FSI_LeadSig) == 0x41615252
&& ld_dword(fs->win + FSI_StrucSig) == 0x61417272) {
#if (FF_FS_NOFSINFO & 1) == 0
fs->free_clst = ld_dword(fs->win + FSI_Free_Count);
#endif
#if (FF_FS_NOFSINFO & 2) == 0
fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free);
#endif
}
}
#endif /* (FF_FS_NOFSINFO & 3) != 3 */
#endif /* !FF_FS_READONLY */
}
fs->fs_type = (BYTE)fmt; /* 设置文件系统类型 */
fs->id = ++Fsid; /* 增加文件系统的挂载 ID */
#if FF_USE_LFN == 1
fs->lfnbuf = LfnBuf; /* 为长文件名分配缓冲区 */
#if FF_FS_EXFAT
fs->dirbuf = DirBuf; /* 为 exFAT 分配目录缓冲区 */
#endif
#endif
#if FF_FS_RPATH != 0
fs->cdir = 0; /* 初始化当前目录为根目录 */
#endif
#if FF_FS_LOCK
clear_share(fs); /* 清除文件锁 */
#endif
return FR_OK; /* 挂载成功,返回 FR_OK */
}