当前位置:   article > 正文

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

fat entries
摘要:本文为大家介绍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文件操作接口。文中所涉及的源码,均可以在开源站点kernel_liteos_m: LiteOS kernel for devices with few resources, such as the MCU | 适用于MCU等各种资源极小设备的LiteOS内核 获取。

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

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

1.1 FatFS的结构体

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

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

  1. /*--------------------------------------------------------------*/
  2. /* Flags and offset address */
  3. /* File access mode and open method flags (3rd argument of f_open) */
  4. #define FA_READ 0x01
  5. #define FA_WRITE 0x02
  6. #define FA_OPEN_EXISTING 0x00
  7. #define FA_CREATE_NEW 0x04
  8. #define FA_CREATE_ALWAYS 0x08
  9. #define FA_OPEN_ALWAYS 0x10
  10. #define FA_OPEN_APPEND 0x30
  11. /* Fast seek controls (2nd argument of f_lseek) */
  12. #define CREATE_LINKMAP ((FSIZE_t)0 - 1)
  13. /* Format options (2nd argument of f_mkfs) */
  14. #define FM_FAT 0x01
  15. #define FM_FAT32 0x02
  16. #define FM_ANY 0x07
  17. #define FM_SFD 0x08
  18. ......

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

  1. /*---------------------------------------------------------------------------/
  2. / Drive/Volume Configurations
  3. /---------------------------------------------------------------------------*/
  4. #ifndef __LITEOS_M__
  5. #define FF_VOLUMES LOSCFG_FS_FAT_VOLUMES
  6. #else
  7. #define FF_VOLUMES 4
  8. #endif
  9. /* Number of volumes (logical drives) to be used. (1-10) */
  10. #ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
  11. #define _DEFAULT_VIRVOLUEMS 4
  12. #define _MIN_CLST 0x4000
  13. #define _FLOAT_ACC 0.00000001
  14. #endif
  15. #ifndef __LITEOS_M__
  16. #define FF_STR_VOLUME_ID 0
  17. #define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
  18. #else
  19. #define FF_STR_VOLUME_ID 2
  20. #endif
  21. #define FF_MULTI_PARTITION 1
  22. #define FF_MIN_SS 512
  23. #ifndef __LITEOS_M__
  24. #define FF_MAX_SS 4096
  25. #else
  26. #define FF_MAX_SS FS_MAX_SS
  27. #endif

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

  1. #define FF_VOLUME_STRS "system", "inner", "update", "user"
  2. #define FS_MAX_SS 512
  3. #define FAT_MAX_OPEN_FILES 50

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

  1. /* Filesystem object structure (FATFS) */
  2. typedef struct {
  3. BYTE fs_type; /* Filesystem type (0:not mounted) */
  4. BYTE pdrv; /* Associated physical drive */
  5. BYTE n_fats; /* Number of FATs (1 or 2) */
  6. BYTE wflag; /* win[] flag (b0:dirty) */
  7. BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
  8. WORD id; /* Volume mount ID */
  9. WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
  10. WORD csize; /* Cluster size [sectors] */
  11. #if FF_MAX_SS != FF_MIN_SS
  12. size_t ssize; /* Sector size (512, 1024, 2048 or 4096) */
  13. #endif
  14. #if FF_USE_LFN
  15. WCHAR* lfnbuf; /* LFN working buffer */
  16. #endif
  17. #if FF_FS_REENTRANT
  18. FF_SYNC_t sobj; /* Identifier of sync object */
  19. #endif
  20. #if !FF_FS_READONLY
  21. DWORD last_clst; /* Last allocated cluster */
  22. DWORD free_clst; /* Number of free clusters */
  23. #endif
  24. #if FF_FS_RPATH
  25. DWORD cdir; /* Current directory start cluster (0:root) */
  26. #endif
  27. DWORD n_fatent; /* Number of FAT entries, = number of clusters + 2 */
  28. DWORD fsize; /* Sectors per FAT */
  29. LBA_t volbase; /* Volume base sector */
  30. LBA_t fatbase; /* FAT base sector */
  31. LBA_t dirbase; /* Root directory base sector/cluster */
  32. LBA_t database; /* Data base sector */
  33. LBA_t winsect; /* Current sector appearing in the win[] */
  34. BYTE* win; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
  35. #ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
  36. DWORD st_clst;
  37. DWORD ct_clst;
  38. BYTE vir_flag; /* Flag of Virtual Filesystem Object, b0 : 1 for virtual Fatfs object, 0 for reality Fatfs object */
  39. BYTE vir_avail;
  40. DWORD vir_amount;
  41. VOID* parent_fs; /* Point to the reality Fatfs object, only available in virtual Fatfs object */
  42. CHAR namelabel[_MAX_ENTRYLENGTH + 1]; /* The name label point to the each virtual Fatfs object ,only available in virtual Fatfs obj */
  43. VOID** child_fs; /* Point to the child Fatfs object ,only available in reality Fatfs object */
  44. #endif
  45. #ifndef __LITEOS_M__
  46. int fs_uid;
  47. int fs_gid;
  48. mode_t fs_mode;
  49. #endif
  50. unsigned short fs_dmask;
  51. unsigned short fs_fmask;
  52. } FATFS;

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

  1. /* Object ID and allocation information (FFOBJID) */
  2. typedef struct {
  3. FATFS* fs; /* Pointer to the hosting volume of this object */
  4. WORD id; /* Hosting volume mount ID */
  5. BYTE attr; /* Object attribute */
  6. BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
  7. DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
  8. FSIZE_t objsize; /* Object size (valid when sclust != 0) */
  9. #if FF_FS_LOCK
  10. UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
  11. #endif
  12. } FFOBJID;
  13. /* File object structure (FIL) */
  14. typedef struct {
  15. FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
  16. BYTE flag; /* File status flags */
  17. BYTE err; /* Abort flag (error code) */
  18. FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
  19. DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
  20. LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
  21. #if !FF_FS_READONLY
  22. LBA_t dir_sect; /* Sector number containing the directory entry */
  23. BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */
  24. #endif
  25. #if FF_USE_FASTSEEK
  26. DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
  27. #endif
  28. #if !FF_FS_TINY
  29. BYTE* buf; /* File private data read/write window */
  30. #endif
  31. #ifndef __LITEOS_M__
  32. LOS_DL_LIST fp_entry;
  33. #endif
  34. } FIL;
  35. /* Directory object structure (DIR) */
  36. struct __dirstream {
  37. FFOBJID obj; /* Object identifier */
  38. DWORD dptr; /* Current read/write offset */
  39. DWORD clust; /* Current cluster */
  40. LBA_t sect; /* Current sector (0:Read operation has terminated) */
  41. BYTE* dir; /* Pointer to the directory item in the win[] */
  42. BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
  43. #if FF_USE_LFN
  44. DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
  45. #endif
  46. #if FF_USE_FIND
  47. const TCHAR* pat; /* Pointer to the name matching pattern */
  48. #endif
  49. #ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
  50. BYTE atrootdir;
  51. #endif
  52. };

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

  1. /* File information structure (FILINFO) */
  2. typedef struct {
  3. FSIZE_t fsize; /* File size */
  4. WORD fdate; /* Modified date */
  5. WORD ftime; /* Modified time */
  6. BYTE fattrib; /* File attribute */
  7. #if FF_USE_LFN
  8. TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
  9. TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
  10. #else
  11. TCHAR fname[12 + 1]; /* File name */
  12. #endif
  13. DWORD sclst;
  14. #ifndef __LITEOS_M__
  15. LOS_DL_LIST fp_list;
  16. #endif
  17. } FILINFO;

1.2 LiteOS-M FatFS的结构体

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

  1. typedef struct {
  2. UINT8 useFlag;
  3. FIL fil;
  4. } 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,在虚拟文件系统中被使用。

  1. ⑴ static FatHandleStruct g_handle[FAT_MAX_OPEN_FILES] = {0};
  2. static DIR g_dir[FAT_MAX_OPEN_DIRS] = {0};
  3. ⑵ static FATFS g_fatfs[FF_VOLUMES] = {0};
  4. ⑶ static UINT8 g_workBuffer[FF_MAX_SS];
  5. ⑷ static UINT32 g_fileNum = 0;
  6. static UINT32 g_dirNum = 0;
  7. static struct dirent g_retValue;
  8. static pthread_mutex_t g_fsMutex = PTHREAD_MUTEX_INITIALIZER;
  9. static const char * const g_volPath[FF_VOLUMES] = {FF_VOLUME_STRS};
  10. static BOOL g_volWriteEnable[FF_VOLUMES] = {FALSE};
  11. ......
  12. ⑸ struct MountOps g_fatfsMnt = {
  13. .Mount = fatfs_mount,
  14. .Umount = fatfs_umount,
  15. .Umount2 = fatfs_umount2,
  16. .Statfs = fatfs_statfs,
  17. };
  18. struct FileOps g_fatfsFops = {
  19. .Mkdir = fatfs_mkdir,
  20. .Unlink = fatfs_unlink,
  21. .Rmdir = fatfs_rmdir,
  22. .Opendir = fatfs_opendir,
  23. .Readdir = fatfs_readdir,
  24. .Closedir = fatfs_closedir,
  25. .Open = fatfs_open,
  26. .Close = fatfs_close,
  27. .Write = fatfs_write,
  28. .Read = fatfs_read,
  29. .Seek = fatfs_lseek,
  30. .Rename = fatfs_rename,
  31. .Getattr = fatfs_stat,
  32. .Fsync = fatfs_fsync,
  33. .Fstat = fatfs_fstat,
  34. };

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

2.1 文件系统互斥锁

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

  1. static int FsLock(void)
  2. {
  3. INT32 ret = 0;
  4. struct timespec absTimeout = {0};
  5. if (osKernelGetState() != osKernelRunning) {
  6. return ret;
  7. }
  8. ⑴ ret = clock_gettime(CLOCK_REALTIME, &absTimeout);
  9. if (ret != 0) {
  10. PRINTK("clock gettime err 0x%x!\r\n", errno);
  11. return errno;
  12. }
  13. ⑵ absTimeout.tv_sec += FS_LOCK_TIMEOUT_SEC;
  14. ⑶ ret = pthread_mutex_timedlock(&g_fsMutex, &absTimeout);
  15. return ret;
  16. }
  17. static void FsUnlock(void)
  18. {
  19. if (osKernelGetState() != osKernelRunning) {
  20. return;
  21. }
  22. (void)pthread_mutex_unlock(&g_fsMutex);
  23. }

2.2 判断文件描述符有效性

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

  1. static bool IsValidFd(int fd)
  2. {
  3. if ((fd < 0) || (fd >= FAT_MAX_OPEN_FILES) || (g_handle[fd].useFlag == 0)) {
  4. return false;
  5. }
  6. return true;
  7. }

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。

  1. static int FsChangeDrive(const char *path)
  2. {
  3. INT32 res;
  4. ⑴ CHAR tmpPath[FS_DRIVE_NAME_MAX_LEN] = { "/" }; /* the max name length of different parts is 16 */
  5. errno_t retErr;
  6. UINT16 pathLen;
  7. pathLen = strlen((char const *)path);
  8. /* make sure the path begin with "/", the path like /xxx/yyy/... */
  9. if (pathLen >= (FS_DRIVE_NAME_MAX_LEN - 1)) {
  10. /* 2: except first flag "/" and last end flag */
  11. pathLen = FS_DRIVE_NAME_MAX_LEN - 2;
  12. }
  13. ⑶ retErr = strncpy_s(tmpPath + 1, (FS_DRIVE_NAME_MAX_LEN - 1), (char const *)path, pathLen);
  14. if (retErr != EOK) {
  15. return FS_FAILURE;
  16. }
  17. res = f_chdrive(tmpPath);
  18. if (res != FR_OK) {
  19. return FS_FAILURE;
  20. }
  21. return FS_SUCCESS;
  22. }

2.4 匹配文件卷

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

  1. static int FsPartitionMatch(const char *path, int flag)
  2. {
  3. INT32 ret;
  4. UINT32 index;
  5. CHAR tmpName[FF_MAX_LFN] = {0};
  6. if (path == NULL) {
  7. return FS_FAILURE;
  8. }
  9. switch ((UINT32)flag & NAME_MASK) {
  10. case VOLUME_NAME:
  11. ⑴ ret = sscanf_s(path, "/%[^/]", tmpName, FF_MAX_LFN);
  12. if (ret <= 0) {
  13. return FS_FAILURE;
  14. }
  15. break;
  16. case PATH_NAME:
  17. ⑵ ret = sscanf_s(path, "%[^/]", tmpName, FF_MAX_LFN);
  18. if (ret <= 0) {
  19. return FS_FAILURE;
  20. }
  21. break;
  22. case PART_NAME:
  23. default:
  24. ⑶ ret = strcpy_s(tmpName, FF_MAX_LFN, path);
  25. if (ret != EOK) {
  26. return FS_FAILURE;
  27. }
  28. }
  29. for (index = 0; index < FF_VOLUMES; index++) {
  30. if (strcmp(tmpName, g_volPath[index]) == 0) {
  31. return index;
  32. }
  33. }
  34. return FS_FAILURE;
  35. }

2.5 判断文件卷是否可写

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

  1. static bool FsCheckByPath(const char *path)
  2. {
  3. INT32 index;
  4. index = FsPartitionMatch(path, PATH_NAME);
  5. if (index == FS_FAILURE) {
  6. return FS_FAILURE;
  7. }
  8. return g_volWriteEnable[index];
  9. }
  10. static bool FsCheckByID(int id)
  11. {
  12. INT32 index;
  13. for (index = 0; index < FF_VOLUMES; index++) {
  14. if (g_fatfs[index].id == id) {
  15. return g_volWriteEnable[index];
  16. }
  17. }
  18. return false;
  19. }

2.6 标签转换

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

  1. static unsigned int FatFsGetMode(int oflags)
  2. {
  3. UINT32 fmode = FA_READ;
  4. if ((UINT32)oflags & O_WRONLY) {
  5. fmode |= FA_WRITE;
  6. }
  7. if (((UINT32)oflags & O_ACCMODE) & O_RDWR) {
  8. fmode |= FA_WRITE;
  9. }
  10. /* Creates a new file if the file is not existing, otherwise, just open it. */
  11. if ((UINT32)oflags & O_CREAT) {
  12. fmode |= FA_OPEN_ALWAYS;
  13. /* Creates a new file. If the file already exists, the function shall fail. */
  14. if ((UINT32)oflags & O_EXCL) {
  15. fmode |= FA_CREATE_NEW;
  16. }
  17. }
  18. /* Creates a new file. If the file already exists, its length shall be truncated to 0. */
  19. if ((UINT32)oflags & O_TRUNC) {
  20. fmode |= FA_CREATE_ALWAYS;
  21. }
  22. return fmode;
  23. }
  24. static int FatfsErrno(int result)
  25. {
  26. INT32 status = 0;
  27. if (result < 0) {
  28. return result;
  29. }
  30. /* FatFs errno to Libc errno */
  31. switch (result) {
  32. case FR_OK:
  33. break;
  34. case FR_NO_FILE:
  35. case FR_NO_PATH:
  36. case FR_NO_FILESYSTEM:
  37. status = ENOENT;
  38. break;
  39. ......
  40. default:
  41. status = result;
  42. break;
  43. }
  44. return status;
  45. }

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表示立即挂载。⑻设置对应的卷是否可读可写标记。

  1. static int Remount(const char *path, unsigned long mountflags)
  2. {
  3. INT32 index;
  4. index = FsPartitionMatch(path, PART_NAME);
  5. if (index == FS_FAILURE) {
  6. PRINTK("Wrong volume path!\r\n");
  7. errno = ENOENT;
  8. return FS_FAILURE;
  9. }
  10. /* remount is not allowed when the device is not mounted. */
  11. if (g_fatfs[index].fs_type == 0) {
  12. errno = EINVAL;
  13. return FS_FAILURE;
  14. }
  15. ⑶ g_volWriteEnable[index] = (mountflags & MS_RDONLY) ? FALSE : TRUE;
  16. return FS_SUCCESS;
  17. }
  18. ......
  19. int fatfs_mount(const char *source, const char *target,
  20. const char *filesystemtype, unsigned long mountflags,
  21. const void *data)
  22. {
  23. INT32 index;
  24. FRESULT res;
  25. INT32 ret;
  26. if ((target == NULL) || (filesystemtype == NULL)) {
  27. errno = EFAULT;
  28. return FS_FAILURE;
  29. }
  30. ret = FsLock();
  31. if (ret != 0) {
  32. errno = ret;
  33. return FS_FAILURE;
  34. }
  35. if (mountflags & MS_REMOUNT) {
  36. ret = Remount(target, mountflags);
  37. goto OUT;
  38. }
  39. if (strcmp(filesystemtype, "fat") != 0) {
  40. errno = ENODEV;
  41. ret = FS_FAILURE;
  42. goto OUT;
  43. }
  44. index = FsPartitionMatch(target, VOLUME_NAME);
  45. if (index == FS_FAILURE) {
  46. errno = ENODEV;
  47. ret = FS_FAILURE;
  48. goto OUT;
  49. }
  50. /* If the volume has been mounted */
  51. if (g_fatfs[index].fs_type != 0) {
  52. errno = EBUSY;
  53. ret = FS_FAILURE;
  54. goto OUT;
  55. }
  56. ⑺ res = f_mount(&g_fatfs[index], target, 1);
  57. if (res != FR_OK) {
  58. errno = FatfsErrno(res);
  59. ret = FS_FAILURE;
  60. goto OUT;
  61. }
  62. ⑻ g_volWriteEnable[index] = (mountflags & MS_RDONLY) ? FALSE : TRUE;
  63. ret = FS_SUCCESS;
  64. OUT:
  65. FsUnlock();
  66. return ret;
  67. }

接下来,看下卸载操作。函数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。⑹处处理强制卸载的情形,会首先关闭打开的文件和目录,然后再去执行⑺实现卸载操作。

  1. int fatfs_umount(const char *target)
  2. {
  3. FRESULT res;
  4. INT32 ret;
  5. INT32 index;
  6. if (target == NULL) {
  7. errno = EFAULT;
  8. return FS_FAILURE;
  9. }
  10. ret = FsLock();
  11. if (ret != 0) {
  12. errno = ret;
  13. return FS_FAILURE;
  14. }
  15. index = FsPartitionMatch(target, VOLUME_NAME);
  16. if (index == FS_FAILURE) {
  17. errno = ENOENT;
  18. ret = FS_FAILURE;
  19. goto OUT;
  20. }
  21. /* The volume is not mounted */
  22. if (g_fatfs[index].fs_type == 0) {
  23. errno = EINVAL;
  24. ret = FS_FAILURE;
  25. goto OUT;
  26. }
  27. /* umount is not allowed when a file or diretory is opened. */
  28. if (f_checkopenlock(index) != FR_OK) {
  29. errno = EBUSY;
  30. ret = FS_FAILURE;
  31. goto OUT;
  32. }
  33. ⑵ res = f_mount((FATFS *)NULL, target, 0);
  34. if (res != FR_OK) {
  35. errno = FatfsErrno(res);
  36. ret = FS_FAILURE;
  37. goto OUT;
  38. }
  39. if (g_fatfs[index].win != NULL) {
  40. ff_memfree(g_fatfs[index].win);
  41. }
  42. ⑷ (void)memset_s(&g_fatfs[index], sizeof(FATFS), 0x0, sizeof(FATFS));
  43. ret = FS_SUCCESS;
  44. OUT:
  45. FsUnlock();
  46. return ret;
  47. }
  48. static int CloseAll(int index)
  49. {
  50. INT32 i;
  51. FRESULT res;
  52. for (i = 0; i < FAT_MAX_OPEN_FILES; i++) {
  53. if (g_fileNum <= 0) {
  54. break;
  55. }
  56. if ((g_handle[i].useFlag == 1) && (g_handle[i].fil.obj.fs == &g_fatfs[index])) {
  57. res = f_close(&g_handle[i].fil);
  58. if (res != FR_OK) {
  59. errno = FatfsErrno(res);
  60. return FS_FAILURE;
  61. }
  62. (void)memset_s(&g_handle[i], sizeof(FatHandleStruct), 0x0, sizeof(FatHandleStruct));
  63. g_fileNum--;
  64. }
  65. }
  66. for (i = 0; i < FAT_MAX_OPEN_DIRS; i++) {
  67. if (g_dirNum <= 0) {
  68. break;
  69. }
  70. if (g_dir[i].obj.fs == &g_fatfs[index]) {
  71. res = f_closedir(&g_dir[i]);
  72. if (res != FR_OK) {
  73. errno = FatfsErrno(res);
  74. return FS_FAILURE;
  75. }
  76. (void)memset_s(&g_dir[i], sizeof(DIR), 0x0, sizeof(DIR));
  77. g_dirNum--;
  78. }
  79. }
  80. return FS_SUCCESS;
  81. }
  82. int fatfs_umount2(const char *target, int flag)
  83. {
  84. INT32 index;
  85. INT32 ret;
  86. UINT32 flags;
  87. FRESULT res;
  88. if (target == NULL) {
  89. errno = EFAULT;
  90. return FS_FAILURE;
  91. }
  92. ⑸ flags = MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW;
  93. if ((UINT32)flag & ~flags) {
  94. errno = EINVAL;
  95. return FS_FAILURE;
  96. }
  97. ret = FsLock();
  98. if (ret != 0) {
  99. errno = ret;
  100. return FS_FAILURE;
  101. }
  102. index = FsPartitionMatch(target, VOLUME_NAME);
  103. if (index == FS_FAILURE) {
  104. errno = ENOENT;
  105. ret = FS_FAILURE;
  106. goto OUT;
  107. }
  108. /* The volume is not mounted */
  109. if (g_fatfs[index].fs_type == 0) {
  110. errno = EINVAL;
  111. ret = FS_FAILURE;
  112. goto OUT;
  113. }
  114. if ((UINT32)flag & MNT_FORCE) {
  115. ret = CloseAll(index);
  116. if (ret != FS_SUCCESS) {
  117. goto OUT;
  118. }
  119. }
  120. ⑺ res = f_mount((FATFS *)NULL, target, 0);
  121. if (res != FR_OK) {
  122. errno = FatfsErrno(res);
  123. ret = FS_FAILURE;
  124. goto OUT;
  125. }
  126. if (g_fatfs[index].win != NULL) {
  127. ff_memfree(g_fatfs[index].win);
  128. }
  129. (void)memset_s(&g_fatfs[index], sizeof(FATFS), 0x0, sizeof(FATFS));
  130. ret = FS_SUCCESS;
  131. OUT:
  132. FsUnlock();
  133. return ret;
  134. }

3.2 文件目录操作接口

文件目录操作接口包含fatfs_mkdir、fatfs_unlink、fatfs_rmdir、fatfs_readdir、fatfs_closedir、fatfs_open、fatfs_close等等,会进一步调用FatFS的文件目录操作接口进行封装,代码比较简单,自行阅读即可,部分代码片段如下。

  1. ......
  2. int fatfs_close(int fd)
  3. {
  4. FRESULT res;
  5. INT32 ret;
  6. ret = FsLock();
  7. if (ret != 0) {
  8. errno = ret;
  9. return FS_FAILURE;
  10. }
  11. if (!IsValidFd(fd)) {
  12. FsUnlock();
  13. errno = EBADF;
  14. return FS_FAILURE;
  15. }
  16. if (g_handle[fd].fil.obj.fs == NULL) {
  17. FsUnlock();
  18. errno = ENOENT;
  19. return FS_FAILURE;
  20. }
  21. res = f_close(&g_handle[fd].fil);
  22. if (res != FR_OK) {
  23. PRINTK("FAT close err 0x%x!\r\n", res);
  24. FsUnlock();
  25. errno = FatfsErrno(res);
  26. return FS_FAILURE;
  27. }
  28. #if !FF_FS_TINY
  29. if (g_handle[fd].fil.buf != NULL) {
  30. (void)ff_memfree(g_handle[fd].fil.buf);
  31. }
  32. #endif
  33. (void)memset_s(&g_handle[fd], sizeof(FatHandleStruct), 0x0, sizeof(FatHandleStruct));
  34. if (g_fileNum > 0) {
  35. g_fileNum--;
  36. }
  37. FsUnlock();
  38. return FS_SUCCESS;
  39. }
  40. ......

小结

本文介绍了FatFS的结构体和全局变量,全局变量的操作接口,分析了下FatFS文件操作接口。时间仓促和能力关系,如有失误,欢迎指正。感谢阅读,如有任何问题、建议,都可以博客下留言给我,谢谢。

参考资料

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

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

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

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号