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

摘要:本文为大家介绍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物联网社区 

 

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

推荐这些文章:

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

摘要:本文先介绍下LFS文件系统结构体的结构体和全局变量,然后分析下LFS文件操作接口。
本文分享自华为云社区《# 鸿蒙轻内核M核源码分析系列二一 02 文件系统LittleFS》,作者:zhushy 。
LittleFS是一个小型的Flash文件系统,它结合日志结构(log-structured)文件系统和COW(copy-on-write)文件系统的思想,以日志结构存储元数据,以COW结构存储数据。这种特殊的存储方式,使LittleFS具有强大的掉电恢复能力(power-loss resilience)。分配COW数据块时LittleFS采用了名为统计损耗均衡的动态损耗均衡算法,使Fla...

鸿蒙轻内核源码分析:虚拟文件系统 VFS

本文分享自华为云社区《鸿蒙轻内核M核源码分析系列二一 01 虚拟文件系统VFS》,作者:zhushy 。
VFS(Virtual File System)是文件系统的虚拟层,它不是一个实际的文件系统,而是一个异构文件系统之上的软件粘合层,为用户提供统一的类 Unix 文件操作接口。由于不同类型的文件系统接口不统一,若系统中有多个文件系统类型,访问不同的文件系统就需要使用不同的非标准接口。而通过在系统中添加 VFS 层,提供统一的抽象接口,屏蔽了底层异构类型的文件系统的差异,使得访问文件系统的系统调用不用关心底层的存储介质和文件系统类型,提高开发效率。本文先介绍下 VFS 的结构体和全局变量,...

LinkedList 源码分析

LinkedList 原理

LinkedList底层是双向链表,一个节点挂着一个节点
LinkedList不需要设定长度,不需要扩容

LinkedList 的优缺点

优点
① 往里面插入一些元素的时候不需要像ArrayList数组那样需要挪动大量的元素了,直接在链表里加入一个节点就可以了
② 如果要不断的往LinkedList中插入一些元素,大量的插入,就不需要像ArrayList那样还要去扩容了,人家就是一个链表,就是不断的把新的节点挂到链表上就可以了
缺点
① 不适合用于随机查找。比如LinkedList.get(10)这种操作,性能很低,因为需要遍历这个链表,从头开始遍历这个链...

鸿蒙轻内核M核源码分析:LibC实现之Musl LibC

摘要:本文学习了LiteOS-M内核Musl LibC的实现,特别是文件系统和内存分配释放部分。
本文分享自华为云社区《鸿蒙轻内核M核源码分析系列十九 Musl LibC》,作者:zhushy。
LiteOS-M内核LibC实现有2种,可以根据需求进行二选一,分别是musl libC和newlibc。本文先学习下Musl LibC的实现代码。文中所涉及的源码,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。LiteOS-M内核提供了和内核相关的文件系统、内存申请释放接口,其他接口可以直接使用Musl提供的。我们分别来...

Kubernetes RESTMapper源码分析

1、概述
在 kubernetes API中,我们经常使用属于 GVK 或者 GVR 来区分特定的 kubernetes 资源。其中 GVK 是 Group Version Kind 的简称,而 GVR 则是 Group Version Resource 的简称。
Kind 是 API “顶级”资源对象的类型,每个资源对象都需要 Kind 来区分它自身代表的资源类型,例如,对于一个 Pod 的例子:

apiVersion: v1
kind: Pod
metadata:
labels:
app: nfs-client-provisioner
name: nfs-client-...

Redis源码分析--主从复制

Redis源码分析--主从复制
目录Redis源码分析--主从复制一、connectWithMaster二、slaveTryPartialResynchronization:部分重同步:三、replicationResurrectCachedMaster:四、readSyncBulkPayload:参考:
一、connectWithMaster
在哪里执行?
serverCron()->replicationCron() (src/redis.c)
// src/replication.c

int connectWithMaster(void) {
int fd;

...

Linux内核分析

以前卸载笔记里的东西,现在也看不懂了,先留着备忘
/////////////linux/init.h #define __HEAD ".head.text","ax"

/////////////arch/arm/include/asm unified.h
#ifdef CONFIG_THUMB2_KERNEL

#define ARM(x...)
#define THUMB(x...) x
#define BSYM(sym) sym + 1
#else
#define ARM(x...) x
#define THUMB(x...)
#ifdef __ASSEMBLY...

Linux mlocate源码分析:updatedb

在Linux的文件查找命令中,mlocate提供的locate命令在单纯进行路径名名查找时有着显著的效率优势,因为mlocate预先对磁盘文件进行扫描并存储到一个数据库文件中,查找时只需要检索数据库而即可。本文主要对mlocate工具数据库的更新(updatedb)进行分析。
基础知识
locate命令需要安装mlocate来获得
locate命令基础用法:点此链接
mlocate的配置:点此链接。这里特别说一下 PURNE_BIND_MOUNTS,大部分文章只说这是限制搜索,没说具体意思,其实PURNE_BIND_MOUNTS=yes会跳过bind mount,至于什么是bind moun...

kernel源码(一)AT&T汇编语法

1 寄存器引用
引用寄存器要在寄存器号前面加%,如mov %eax,%ebx
2 操作数顺序
操作数排列是从源(左)到目的(右),如mov %eax,%ebx
3 常数/立即数的格式
使用立即数,要在数前面加上$,如mov $4,%ebx
符号常数直接引用 如mov value,%ebx
引用符号地址在符号前加$,如mov $value,%ebx
4 操作数长度
操作数长度用加在指令后的符号表示
b(byte),w(word),l(long) 如movw %ax,%bx
5 转移和跳转指令
绝对转移和调用指令(jmp/call)的操作数前要加上*作为前缀。
远转移指令和远调用指令的操作码为l...

kernel源码(十一)system_call.s

1 系统调用流程图

 
 
 2 源码

/*
* linux/kernel/system_call.s
*
* (C) 1991 Linus Torvalds
*/

/*
* system_call.s contains the system-call low-level handling routines.
* This also contains the timer-interrupt handler, as some of the code is
* the same. The hd- and flopppy-interru...

文章标题:鸿蒙轻内核源码分析:文件系统FatFS
文章链接:https://www.dianjilingqu.com/51153.html
本文章来源于网络,版权归原作者所有,如果本站文章侵犯了您的权益,请联系我们删除,联系邮箱:saisai#email.cn,感谢支持理解。
THE END
< <上一篇
)">
下一篇>>