写点什么

鸿蒙轻内核源码分析:文件系统 FatFS

  • 2022 年 2 月 09 日
  • 本文字数:12189 字

    阅读完需:约 40 分钟

摘要:本文为大家介绍 FatFS 文件系统结构体的结构体和全局变量,并分析 FatFS 文件操作接口。


本文分享自华为云社区《鸿蒙轻内核M核源码分析系列二一 03 文件系统FatFS》,作者:zhushy。


FAT 文件系统是 File Allocation Table(文件配置表)的简称,主要包括 DBR 区、FAT 区、DATA 区三个区域。其中,FAT 区各个表项记录存储设备中对应簇的信息,包括簇是否被使用、文件下一个簇的编号、是否文件结尾等。FAT 文件系统有 FAT12、FAT16、FAT32 等多种格式,其中,12、16、32 表示对应格式中 FAT 表项的比特数。FAT 文件系统支持多种介质,特别在可移动存储介质(U 盘、SD 卡、移动硬盘等)上广泛使用,使嵌入式设备和 Windows、Linux 等桌面系统保持很好的兼容性,方便用户管理操作文件。LiteOS-M 内核支持 FAT12、FAT16 与 FAT32 三种格式的 FAT 文件系统,具有代码量小、资源占用小、可裁切、支持多种物理介质等特性,并且与 Windows、Linux 等系统保持兼容,支持多设备、多分区识别等功能。LiteOS-M 内核支持硬盘多分区,可以在主分区以及逻辑分区上创建 FAT 文件系统。


本文先介绍下 FatFS 文件系统结构体的结构体和全局变量,然后分析下 FatFS 文件操作接口。文中所涉及的源码,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m获取。

1、FatFS 文件系统结构体介绍


会分 2 部分来介绍结构体部分,先介绍 FatFS 文件系统的结构体,然后介绍 LiteOS-M 内核中提供的和 FatFS 相关的一些结构体。

1.1 FatFS 的结构体


在 openharmony/third_party/FatFs/source/ff.h 头文件中定义 FatFS 的结构体,我们先简单了解下,后文会使用到的。


先看下相关的宏定义,包含文件访问模式(File access mode),格式化选项(Format options)等等。文件打开方式和 POSIX 文件打开选项不一致,需要转换,后文会涉及。


/*--------------------------------------------------------------*//* Flags and offset address                                     *//* File access mode and open method flags (3rd argument of f_open) */#define	FA_READ				0x01#define	FA_WRITE			0x02#define	FA_OPEN_EXISTING	0x00#define	FA_CREATE_NEW		0x04#define	FA_CREATE_ALWAYS	0x08#define	FA_OPEN_ALWAYS		0x10#define	FA_OPEN_APPEND		0x30
/* Fast seek controls (2nd argument of f_lseek) */#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
/* Format options (2nd argument of f_mkfs) */#define FM_FAT 0x01#define FM_FAT32 0x02#define FM_ANY 0x07#define FM_SFD 0x08......
复制代码


在 openharmony/third_party/FatFs/source/ffconf.h 头文件中定义 FatFS 的一些配置信息。如下文的驱动和卷的配置信息等。对于 LiteOS-M,默认是支持 4 个卷。宏定义 FS_MAX_SS 表示扇区大小 sector size。


/*---------------------------------------------------------------------------// Drive/Volume Configurations/---------------------------------------------------------------------------*/
#ifndef __LITEOS_M__#define FF_VOLUMES LOSCFG_FS_FAT_VOLUMES#else#define FF_VOLUMES 4#endif/* Number of volumes (logical drives) to be used. (1-10) */
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION#define _DEFAULT_VIRVOLUEMS 4#define _MIN_CLST 0x4000#define _FLOAT_ACC 0.00000001#endif
#ifndef __LITEOS_M__#define FF_STR_VOLUME_ID 0#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"#else#define FF_STR_VOLUME_ID 2#endif#define FF_MULTI_PARTITION 1
#define FF_MIN_SS 512#ifndef __LITEOS_M__#define FF_MAX_SS 4096#else#define FF_MAX_SS FS_MAX_SS#endif
复制代码


对于适配 LiteOS-M 内核的开发板,使用 FatFS 文件系统时,需要提供头文件 liteos_m\board\fs\fs_config.h,例如:openharmony\device\qemu\arm_mps2_an386\liteos_m\board\fs\fs_config.h。


#define FF_VOLUME_STRS "system", "inner", "update", "user"#define FS_MAX_SS      512
#define FAT_MAX_OPEN_FILES 50
复制代码


接下来看看重要的结构体。结构体 FATFS 是 FatFS 文件系统类型结构体。成员变量 BYTE fs_type 等 0 时表示未挂载,挂载后一般取值为 FS_FAT12、FS_FAT16 或 FS_FAT32;WORD id 表示卷的挂载编号。 其他成员变量可以暂不了解。


/* Filesystem object structure (FATFS) */
typedef struct { BYTE fs_type; /* Filesystem type (0:not mounted) */ BYTE pdrv; /* Associated physical drive */ BYTE n_fats; /* Number of FATs (1 or 2) */ BYTE wflag; /* win[] flag (b0:dirty) */ BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ WORD id; /* Volume mount ID */ WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ WORD csize; /* Cluster size [sectors] */#if FF_MAX_SS != FF_MIN_SS size_t ssize; /* Sector size (512, 1024, 2048 or 4096) */#endif#if FF_USE_LFN WCHAR* lfnbuf; /* LFN working buffer */#endif#if FF_FS_REENTRANT FF_SYNC_t sobj; /* Identifier of sync object */#endif#if !FF_FS_READONLY DWORD last_clst; /* Last allocated cluster */ DWORD free_clst; /* Number of free clusters */#endif#if FF_FS_RPATH DWORD cdir; /* Current directory start cluster (0:root) */#endif DWORD n_fatent; /* Number of FAT entries, = number of clusters + 2 */ DWORD fsize; /* Sectors per FAT */ LBA_t volbase; /* Volume base sector */ LBA_t fatbase; /* FAT base sector */ LBA_t dirbase; /* Root directory base sector/cluster */ LBA_t database; /* Data base sector */ LBA_t winsect; /* Current sector appearing in the win[] */ BYTE* win; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION DWORD st_clst; DWORD ct_clst; BYTE vir_flag; /* Flag of Virtual Filesystem Object, b0 : 1 for virtual Fatfs object, 0 for reality Fatfs object */ BYTE vir_avail; DWORD vir_amount; VOID* parent_fs; /* Point to the reality Fatfs object, only available in virtual Fatfs object */ CHAR namelabel[_MAX_ENTRYLENGTH + 1]; /* The name label point to the each virtual Fatfs object ,only available in virtual Fatfs obj */ VOID** child_fs; /* Point to the child Fatfs object ,only available in reality Fatfs object */#endif#ifndef __LITEOS_M__ int fs_uid; int fs_gid; mode_t fs_mode;#endif unsigned short fs_dmask; unsigned short fs_fmask;} FATFS;
复制代码


结构体 FIL、DIR 分别是 FatFS 的文件和目录类型结构体,DIR 是__dirstream 结构体的别名,一般在 Musl 或 Newlib C 库的文件 dirent.h 会有 typedef struct __dirstream DIR;。这两个结构体都包含 FFOBJID obj 这个成员变量,FFOBJID 结构体体包含 FATFS* fs 成员,可以关联文件卷信息。暂不需要关心其他成员变量细节,知道结构体的用途即可。


/* Object ID and allocation information (FFOBJID) */
typedef struct { FATFS* fs; /* Pointer to the hosting volume of this object */ WORD id; /* Hosting volume mount ID */ BYTE attr; /* Object attribute */ BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */ DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */ FSIZE_t objsize; /* Object size (valid when sclust != 0) */#if FF_FS_LOCK UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */#endif} FFOBJID;
/* File object structure (FIL) */
typedef struct { FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ BYTE flag; /* File status flags */ BYTE err; /* Abort flag (error code) */ FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */ LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */#if !FF_FS_READONLY LBA_t dir_sect; /* Sector number containing the directory entry */ BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */#endif#if FF_USE_FASTSEEK DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */#endif#if !FF_FS_TINY BYTE* buf; /* File private data read/write window */#endif#ifndef __LITEOS_M__ LOS_DL_LIST fp_entry;#endif} FIL;/* Directory object structure (DIR) */
struct __dirstream { FFOBJID obj; /* Object identifier */ DWORD dptr; /* Current read/write offset */ DWORD clust; /* Current cluster */ LBA_t sect; /* Current sector (0:Read operation has terminated) */ BYTE* dir; /* Pointer to the directory item in the win[] */ BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */#if FF_USE_LFN DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */#endif#if FF_USE_FIND const TCHAR* pat; /* Pointer to the name matching pattern */#endif#ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION BYTE atrootdir;#endif};
复制代码


结构体 FILINFO 用于维护文件信息,包含文件修改时间,大小和文件名等信息。


/* File information structure (FILINFO) */
typedef struct { FSIZE_t fsize; /* File size */ WORD fdate; /* Modified date */ WORD ftime; /* Modified time */ BYTE fattrib; /* File attribute */#if FF_USE_LFN TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */#else TCHAR fname[12 + 1]; /* File name */#endif DWORD sclst;#ifndef __LITEOS_M__ LOS_DL_LIST fp_list;#endif} FILINFO;
复制代码


1.2 LiteOS-M FatFS 的结构体


我们来看下在文件 components\fs\fatfs\fatfs.c 里定义的结构体。结构体 FatHandleStruct 维护文件相关的信息,该结构体非常简单,在 FIL 的基础上增加了是否使用成员变量。


typedef struct {    UINT8 useFlag;    FIL fil;} FatHandleStruct;
复制代码


2、LiteOS-M FatFS 的重要全局变量及操作


了解下文件 components\fs\fatfs\fatfs.c 中定义的常用全局变量。⑴处的 g_handle 数组维护文件信息,默认支持的文件数目为 FAT_MAX_OPEN_FILES;g_dir 数组维护目录信息,默认支持的目录数目为 FAT_MAX_OPEN_DIRS。 ⑵处的 g_fatfs 数组维护每个卷的的文件系统信息,默认文件卷数目 FF_VOLUMES 为 4 个。和文件卷相关的变量还有 g_volPath 数组维护每个卷的字符串路径,g_volWriteEnable 数组维护每个卷是否可写。⑶处的 g_workBuffer 维护每个扇区的缓存。⑷处开始的 g_fileNum、g_dirNum 分别是文件和目录打开的数目;struct dirent g_retValue 是目录项结构体变量,用于函数 fatfs_readdir();pthread_mutex_t g_fsMutex 是互斥锁变量;⑸处开始的挂载操作变量 g_fatfsMnt、文件操作操作全局变量 g_fatfsFops,在虚拟文件系统中被使用。


⑴  static FatHandleStruct g_handle[FAT_MAX_OPEN_FILES] = {0};    static DIR g_dir[FAT_MAX_OPEN_DIRS] = {0};⑵  static FATFS g_fatfs[FF_VOLUMES] = {0};⑶  static UINT8 g_workBuffer[FF_MAX_SS];⑷  static UINT32 g_fileNum = 0;    static UINT32 g_dirNum = 0;    static struct dirent g_retValue;    static pthread_mutex_t g_fsMutex = PTHREAD_MUTEX_INITIALIZER;
static const char * const g_volPath[FF_VOLUMES] = {FF_VOLUME_STRS}; static BOOL g_volWriteEnable[FF_VOLUMES] = {FALSE}; ......⑸ struct MountOps g_fatfsMnt = { .Mount = fatfs_mount, .Umount = fatfs_umount, .Umount2 = fatfs_umount2, .Statfs = fatfs_statfs, };
struct FileOps g_fatfsFops = { .Mkdir = fatfs_mkdir, .Unlink = fatfs_unlink, .Rmdir = fatfs_rmdir, .Opendir = fatfs_opendir, .Readdir = fatfs_readdir, .Closedir = fatfs_closedir, .Open = fatfs_open, .Close = fatfs_close, .Write = fatfs_write, .Read = fatfs_read, .Seek = fatfs_lseek, .Rename = fatfs_rename, .Getattr = fatfs_stat, .Fsync = fatfs_fsync, .Fstat = fatfs_fstat, };
复制代码


下文继续介绍下和这些变量相关的内部操作接口。

2.1 文件系统互斥锁


FatFS 文件系统使用的是超时加锁。函数 FsLock()中,⑴处获取系统实时时间,⑵处设置 15 秒超时,FS_LOCK_TIMEOUT_SEC 默认为 15 秒。⑶处对互斥量进行加锁,超时后不会再对互斥量加锁。函数 FsUnlock()用于解锁。


static int FsLock(void){    INT32 ret = 0;    struct timespec absTimeout = {0};    if (osKernelGetState() != osKernelRunning) {        return ret;    }⑴  ret = clock_gettime(CLOCK_REALTIME, &absTimeout);    if (ret != 0) {        PRINTK("clock gettime err 0x%x!\r\n", errno);        return errno;    }⑵  absTimeout.tv_sec += FS_LOCK_TIMEOUT_SEC;⑶  ret = pthread_mutex_timedlock(&g_fsMutex, &absTimeout);    return ret;}
static void FsUnlock(void){ if (osKernelGetState() != osKernelRunning) { return; } (void)pthread_mutex_unlock(&g_fsMutex);}
复制代码


2.2 判断文件描述符有效性


函数 IsValidFd()用于判断文件描述符的是否有效,如果文件描述符超出有效范围,或者文件未使用状态,返回 false,否则返回 true。


static bool IsValidFd(int fd){    if ((fd < 0) || (fd >= FAT_MAX_OPEN_FILES) || (g_handle[fd].useFlag == 0)) {        return false;    }    return true;}
复制代码


2.3 切换驱动器


函数 FsChangeDrive()根据传入的路径切换驱动器(盘符)。字符串数组 tmpPath 用于保存驱动器名称,其中驱动器名称最大值 FS_DRIVE_NAME_MAX_LEN。⑵处处理路径长度大于驱动器名称长度的情况。⑶处从路径中获取驱动器名称,然后调用接口 f_chdrive()切换驱动器。在文件操作接口中,会调用该函数来切换驱动器,如 fatfs_open、fatfs_unlink、fatfs_stat、fatfs_mkdir、fatfs_opendir、fatfs_rmdir、fatfs_rename 和 fatfs_statfs,这些函数的参数涉及文件路径 char *path 或者目录 char *dirName。


static int FsChangeDrive(const char *path){    INT32 res;⑴  CHAR tmpPath[FS_DRIVE_NAME_MAX_LEN] = { "/" }; /* the max name length of different parts is 16 */    errno_t retErr;    UINT16 pathLen;    pathLen = strlen((char const *)path);    /* make sure the path begin with "/", the path like /xxx/yyy/... */⑵  if (pathLen >= (FS_DRIVE_NAME_MAX_LEN - 1)) {        /* 2: except first flag "/" and last end flag */        pathLen = FS_DRIVE_NAME_MAX_LEN - 2;    }
⑶ retErr = strncpy_s(tmpPath + 1, (FS_DRIVE_NAME_MAX_LEN - 1), (char const *)path, pathLen); if (retErr != EOK) { return FS_FAILURE; }
res = f_chdrive(tmpPath); if (res != FR_OK) { return FS_FAILURE; }
return FS_SUCCESS;}
复制代码


2.4 匹配文件卷


函数 FsPartitionMatch()根据传入的文件路径获取对应的卷索引,在挂载、卸载、格式化等接口中使用。⑴处如果传入的是卷名称,获取顶级目录名称,即路径的第一级目录,前后不包含路径分隔符/。⑵如果传入的是路径名称,获取顶级路径,截止到第一个分隔符/。否则执行⑶,赋值路径中的名称。然后遍历每一个卷,如果获取的顶级目录名称等于卷名称,则返回对应的卷数组索引。否则返回 FS_FAILURE。


static int FsPartitionMatch(const char *path, int flag){    INT32 ret;    UINT32 index;    CHAR tmpName[FF_MAX_LFN] = {0};
if (path == NULL) { return FS_FAILURE; }
switch ((UINT32)flag & NAME_MASK) { case VOLUME_NAME:⑴ ret = sscanf_s(path, "/%[^/]", tmpName, FF_MAX_LFN); if (ret <= 0) { return FS_FAILURE; } break; case PATH_NAME:⑵ ret = sscanf_s(path, "%[^/]", tmpName, FF_MAX_LFN); if (ret <= 0) { return FS_FAILURE; } break; case PART_NAME: default:⑶ ret = strcpy_s(tmpName, FF_MAX_LFN, path); if (ret != EOK) { return FS_FAILURE; } }
for (index = 0; index < FF_VOLUMES; index++) {⑷ if (strcmp(tmpName, g_volPath[index]) == 0) { return index; } } return FS_FAILURE;}
复制代码


2.5 判断文件卷是否可写


函数 FsCheckByPath()、FsCheckByID()用于判断文件卷是否可写,传递参数不同。前者传入的是文件路径,转化为卷索引后判断。后者传入的是挂载编号,遍历每一个卷,判断相应卷的编号与传入参数是否相等。


static bool FsCheckByPath(const char *path){    INT32 index;
index = FsPartitionMatch(path, PATH_NAME); if (index == FS_FAILURE) { return FS_FAILURE; }
return g_volWriteEnable[index];}
static bool FsCheckByID(int id){ INT32 index;
for (index = 0; index < FF_VOLUMES; index++) { if (g_fatfs[index].id == id) { return g_volWriteEnable[index]; } } return false;}
复制代码


2.6 标签转换


函数 FatFsGetMode()用于把 POSIX 格式的文件打开标签转换为 FatFS 文件系统格式的文件打开标签。FatfsErrno()把 FatFS 文件系统格式的错误号转换为 POSIX 格式的错误号。


static unsigned int FatFsGetMode(int oflags){    UINT32 fmode = FA_READ;
if ((UINT32)oflags & O_WRONLY) { fmode |= FA_WRITE; }
if (((UINT32)oflags & O_ACCMODE) & O_RDWR) { fmode |= FA_WRITE; } /* Creates a new file if the file is not existing, otherwise, just open it. */ if ((UINT32)oflags & O_CREAT) { fmode |= FA_OPEN_ALWAYS; /* Creates a new file. If the file already exists, the function shall fail. */ if ((UINT32)oflags & O_EXCL) { fmode |= FA_CREATE_NEW; } } /* Creates a new file. If the file already exists, its length shall be truncated to 0. */ if ((UINT32)oflags & O_TRUNC) { fmode |= FA_CREATE_ALWAYS; }
return fmode;}
static int FatfsErrno(int result){ INT32 status = 0;
if (result < 0) { return result; }
/* FatFs errno to Libc errno */ switch (result) { case FR_OK: break;
case FR_NO_FILE: case FR_NO_PATH: case FR_NO_FILESYSTEM: status = ENOENT; break; ...... default: status = result; break; }
return status;}
复制代码


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 文件操作接口。时间仓促和能力关系,如有失误,欢迎指正。感谢阅读,如有任何问题、建议,都可以博客下留言给我,谢谢。

参考资料


  • HarmonyOS Device>文档指南>基础能力-FatFS


更多学习内容,请关注IoT物联网社区


点击关注,第一时间了解华为云新鲜技术~

发布于: 刚刚阅读数: 2
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
鸿蒙轻内核源码分析:文件系统FatFS