鸿蒙轻内核M核源码分析系列二一 05 文件系统FatFS 原创 精华
鸿蒙轻内核M核源码分析系列二一 05 文件系统FatFS
【本文正在参与优质创作者激励】
3、LiteOS-M FATFS的文件系统操作接口
快速记录下各个操作接口,对每个接口的用途用法不再描述。可以参考之前的系列文章,《鸿蒙轻内核M核源码分析系列十九 Musl LibC》中介绍了相关的接口,那些接口会调用VFS文件系统中操作接口,然后进一步调用FatFS文件操作接口。
3.1 挂载和卸载操作
先看下挂载操作,FatFS支持重新挂载操作。如果挂载选项包含MS_REMOUNT时,会调用函数Remount()重新挂载。函数Remount()中,⑴处调用FsPartitionMatch()获取卷索引,⑵处如果卷未被挂载,不允许重新挂载,返回错误码。⑶处设置对应的卷是否可读可写标记。由此看来,重新挂载,主要是更新卷的可读可写能力。
看下挂载函数fatfs_mount(),⑷处开始判断参数有效性,不能为空,文件系统类型必须为“fat”,⑸处调用FsPartitionMatch()获取卷索引,⑹处如果卷已经被挂载,则返回错误码。⑺处调用f_mount()实现挂载,第3个参数1表示立即挂载。⑻设置对应的卷是否可读可写标记。
static int Remount(const char *path, unsigned long mountflags)
{
INT32 index;
⑴ index = FsPartitionMatch(path, PART_NAME);
if (index == FS_FAILURE) {
PRINTK("Wrong volume path!\r\n");
errno = ENOENT;
return FS_FAILURE;
}
/* remount is not allowed when the device is not mounted. */
⑵ if (g_fatfs[index].fs_type == 0) {
errno = EINVAL;
return FS_FAILURE;
}
⑶ g_volWriteEnable[index] = (mountflags & MS_RDONLY) ? FALSE : TRUE;
return FS_SUCCESS;
}
......
int fatfs_mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data)
{
INT32 index;
FRESULT res;
INT32 ret;
⑷ if ((target == NULL) || (filesystemtype == NULL)) {
errno = EFAULT;
return FS_FAILURE;
}
ret = FsLock();
if (ret != 0) {
errno = ret;
return FS_FAILURE;
}
if (mountflags & MS_REMOUNT) {
ret = Remount(target, mountflags);
goto OUT;
}
if (strcmp(filesystemtype, "fat") != 0) {
errno = ENODEV;
ret = FS_FAILURE;
goto OUT;
}
⑸ index = FsPartitionMatch(target, VOLUME_NAME);
if (index == FS_FAILURE) {
errno = ENODEV;
ret = FS_FAILURE;
goto OUT;
}
/* If the volume has been mounted */
⑹ if (g_fatfs[index].fs_type != 0) {
errno = EBUSY;
ret = FS_FAILURE;
goto OUT;
}
⑺ res = f_mount(&g_fatfs[index], target, 1);
if (res != FR_OK) {
errno = FatfsErrno(res);
ret = FS_FAILURE;
goto OUT;
}
⑻ g_volWriteEnable[index] = (mountflags & MS_RDONLY) ? FALSE : TRUE;
ret = FS_SUCCESS;
OUT:
FsUnlock();
return ret;
}
接下来,看下卸载操作。函数fatfs_umount()中,先进行参数有效性,是否挂载等基础检查,⑴处调用函数f_checkopenlock()来判断要卸载的卷中是否有打开的文件或目录,⑵处调用f_mount(),第一个参数为NULL,表示卸载target指定的文件系统;,第3个参数0表示不需要挂载。如果卸载错误,转换相应的错误码。⑶处如果磁盘访问窗口(Disk access window for Directory)不为空,执行相应的释放操作。⑷处把文件卷数组对应的元素置零。
函数CloseAll()根据文件卷编号,遍历每一个打开的文件和目录进行关闭。函数fatfs_umount2()中,⑸处表示支持的卸载选项有:MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW。⑹处处理强制卸载的情形,会首先关闭打开的文件和目录,然后再去执行⑺实现卸载操作。
int fatfs_umount(const char *target)
{
FRESULT res;
INT32 ret;
INT32 index;
if (target == NULL) {
errno = EFAULT;
return FS_FAILURE;
}
ret = FsLock();
if (ret != 0) {
errno = ret;
return FS_FAILURE;
}
index = FsPartitionMatch(target, VOLUME_NAME);
if (index == FS_FAILURE) {
errno = ENOENT;
ret = FS_FAILURE;
goto OUT;
}
/* The volume is not mounted */
if (g_fatfs[index].fs_type == 0) {
errno = EINVAL;
ret = FS_FAILURE;
goto OUT;
}
/* umount is not allowed when a file or diretory is opened. */
⑴ if (f_checkopenlock(index) != FR_OK) {
errno = EBUSY;
ret = FS_FAILURE;
goto OUT;
}
⑵ res = f_mount((FATFS *)NULL, target, 0);
if (res != FR_OK) {
errno = FatfsErrno(res);
ret = FS_FAILURE;
goto OUT;
}
⑶ if (g_fatfs[index].win != NULL) {
ff_memfree(g_fatfs[index].win);
}
⑷ (void)memset_s(&g_fatfs[index], sizeof(FATFS), 0x0, sizeof(FATFS));
ret = FS_SUCCESS;
OUT:
FsUnlock();
return ret;
}
static int CloseAll(int index)
{
INT32 i;
FRESULT res;
for (i = 0; i < FAT_MAX_OPEN_FILES; i++) {
if (g_fileNum <= 0) {
break;
}
if ((g_handle[i].useFlag == 1) && (g_handle[i].fil.obj.fs == &g_fatfs[index])) {
res = f_close(&g_handle[i].fil);
if (res != FR_OK) {
errno = FatfsErrno(res);
return FS_FAILURE;
}
(void)memset_s(&g_handle[i], sizeof(FatHandleStruct), 0x0, sizeof(FatHandleStruct));
g_fileNum--;
}
}
for (i = 0; i < FAT_MAX_OPEN_DIRS; i++) {
if (g_dirNum <= 0) {
break;
}
if (g_dir[i].obj.fs == &g_fatfs[index]) {
res = f_closedir(&g_dir[i]);
if (res != FR_OK) {
errno = FatfsErrno(res);
return FS_FAILURE;
}
(void)memset_s(&g_dir[i], sizeof(DIR), 0x0, sizeof(DIR));
g_dirNum--;
}
}
return FS_SUCCESS;
}
int fatfs_umount2(const char *target, int flag)
{
INT32 index;
INT32 ret;
UINT32 flags;
FRESULT res;
if (target == NULL) {
errno = EFAULT;
return FS_FAILURE;
}
⑸ flags = MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW;
if ((UINT32)flag & ~flags) {
errno = EINVAL;
return FS_FAILURE;
}
ret = FsLock();
if (ret != 0) {
errno = ret;
return FS_FAILURE;
}
index = FsPartitionMatch(target, VOLUME_NAME);
if (index == FS_FAILURE) {
errno = ENOENT;
ret = FS_FAILURE;
goto OUT;
}
/* The volume is not mounted */
if (g_fatfs[index].fs_type == 0) {
errno = EINVAL;
ret = FS_FAILURE;
goto OUT;
}
⑹ if ((UINT32)flag & MNT_FORCE) {
ret = CloseAll(index);
if (ret != FS_SUCCESS) {
goto OUT;
}
}
⑺ res = f_mount((FATFS *)NULL, target, 0);
if (res != FR_OK) {
errno = FatfsErrno(res);
ret = FS_FAILURE;
goto OUT;
}
if (g_fatfs[index].win != NULL) {
ff_memfree(g_fatfs[index].win);
}
(void)memset_s(&g_fatfs[index], sizeof(FATFS), 0x0, sizeof(FATFS));
ret = FS_SUCCESS;
OUT:
FsUnlock();
return ret;
}
3.2 文件目录操作接口
文件目录操作接口包含fatfs_mkdir、fatfs_unlink、fatfs_rmdir、fatfs_readdir、fatfs_closedir、fatfs_open、fatfs_close等等,会进一步调用FatFS的文件目录操作接口进行封装,代码比较简单,自行阅读即可,部分代码片段如下。
......
int fatfs_close(int fd)
{
FRESULT res;
INT32 ret;
ret = FsLock();
if (ret != 0) {
errno = ret;
return FS_FAILURE;
}
if (!IsValidFd(fd)) {
FsUnlock();
errno = EBADF;
return FS_FAILURE;
}
if (g_handle[fd].fil.obj.fs == NULL) {
FsUnlock();
errno = ENOENT;
return FS_FAILURE;
}
res = f_close(&g_handle[fd].fil);
if (res != FR_OK) {
PRINTK("FAT close err 0x%x!\r\n", res);
FsUnlock();
errno = FatfsErrno(res);
return FS_FAILURE;
}
#if !FF_FS_TINY
if (g_handle[fd].fil.buf != NULL) {
(void)ff_memfree(g_handle[fd].fil.buf);
}
#endif
(void)memset_s(&g_handle[fd], sizeof(FatHandleStruct), 0x0, sizeof(FatHandleStruct));
if (g_fileNum > 0) {
g_fileNum--;
}
FsUnlock();
return FS_SUCCESS;
}
......
小结
本文介绍了FatFS的结构体和全局变量,全局变量的操作接口,分析了下FatFS文件操作接口。时间仓促和能力关系,如有失误,欢迎指正。感谢阅读,如有任何问题、建议,都可以博客下留言给我,谢谢。
参考资料
【本文正在参与优质创作者激励】