【FATFS】f_mount函数详细解析

 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 */
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值