Hongmeng light kernel source code analysis: file system FatFS

Abstract: This paper introduces the structure and global variables of the FatFS file system structure, and analyzes the FatFS file operation interface.

This article is shared from Huawei cloud community< Hongmeng light kernel M core source code analysis series 21 03 file system FatFS >, author: zhushy.

Fat file system is the abbreviation of File Allocation Table, which mainly includes three areas: DBR area, fat area and DATA area. Among them, each table item in the fat area records the information of the corresponding cluster in the storage device, including whether the cluster is used, the number of the next cluster in the file, whether the end of the file, etc. Fat file system has FAT12, FAT16, FAT32 and other formats. Among them, 12, 16 and 32 represent the number of bits of fat table entries in the corresponding format. Fat file system supports a variety of media, especially widely used in removable storage media (U SB flash disk, SD card, mobile hard disk, etc.), so that embedded devices maintain good compatibility with desktop systems such as Windows and Linux, and it is convenient for users to manage and operate files. LiteOS-M kernel supports fat file systems in FAT12, FAT16 and FAT32 formats. It has the characteristics of small amount of code, small resource occupation, can be cut and supports a variety of physical media. It is compatible with Windows, Linux and other systems, and supports multi device, multi partition identification and other functions. LiteOS-M kernel supports multi partition of hard disk, and fat file system can be created on primary partition and logical partition.

This paper first introduces the structure and global variables of the FatFS file system structure, and then analyzes the FatFS file operation interface. The source code involved in this article can be found on the open source site https://gitee.com/openharmony/kernel_liteos_m Get.

1. Introduction to FatFS file system structure

There will be two parts to introduce the structure part. First introduce the structure of FatFS file system, and then introduce some structures related to FatFS provided in LiteOS-M kernel.

1.1 structure of FatFs

In openharmony / third_ party/FatFS/source/ff. The structure of FatFS is defined in the H header file. Let's briefly understand it first and use it later.

First look at the relevant macro definitions, including File access mode, Format options, and so on. The file opening method is inconsistent with the POSIX file opening options and needs to be converted, which will be discussed later.

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

In openharmony/third_party/FatFs/source/ffconf.h header file defines some configuration information of FatFS. For example, the following driver and volume configuration information. For LiteOS-M, 4 volumes are supported by default. Macro definition FS_MAX_SS indicates 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

For the development board adapted to LiteOS-M kernel, the header file liteos needs to be provided when using FatFS file system_ m\board\fs\fs_ config. h. For example: 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

Next, let's look at the important structures. Structure FatFs is a FatFs file system type structure. Member variable byte FS_ When type is 0, it means it is not mounted. After mounting, it is generally FS_FAT12,FS_FAT16 or FS_FAT32; WORD id indicates the mount number of the volume. Other member variables can not be understood temporarily.

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

The structures FIL and dir are the file and directory type structures of FatFS respectively, and dir is__ The alias of the dirstream structure is usually in the file dirent.com of Musl or Newlib C library H will have typedef struct__ dirstream DIR;. Both structures contain the member variable FFOBJID obj. The FFOBJID structure contains the FATFS* fs member, which can be associated with file volume information. You don't need to care about the details of other member variables, just know the purpose of the structure.

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

Structure FILINFO is used to maintain file information, including file modification time, size, file name and other information.

/* 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 structure of liteos-m FatFs

Let's look at the file components \ FS \ FatFs \ FatFs Structure defined in C. The structure FatHandleStruct maintains the information related to the file. The structure is very simple and adds whether to use member variables on the basis of FIL.

typedef struct {
    UINT8 useFlag;
    FIL fil;
} FatHandleStruct;

2. Important global variables and operations of LiteOS-M FatFS

Learn about the file components \ FS \ FatFs \ FatFs Common global variables defined in C. (1) g at_ Handle array maintains file information. The number of files supported by default is FAT_MAX_OPEN_FILES; g_dir array maintains directory information. The default number of supported directories is FAT_MAX_OPEN_DIRS. (2) g at_ FatFs array maintains the file system information of each volume. The default number of file volumes is FF_ There are 4 volumes. Other variables related to file volumes are g_volPath array maintains the string path of each volume, G_ The volwriteenable array maintains whether each volume is writable. (3) g at_ Workbuffer maintains the cache of each sector. (4) g starting at_ fileNum,g_dirNum is the number of files and directories opened respectively; struct dirent g_retValue is the variable of the directory item structure, which is used in the function fatfs_readdir(); pthread_mutex_t g_fsMutex is a mutex variable; (5) start of variable loading_ Fatfsmnt, file operation global variable G_ Fatfsfoss, which is used in virtual file systems.

⑴  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,
    };

The following continues to introduce the internal operation interfaces related to these variables.

2.1 file system Mutex

The FatFS file system uses timeout locking. In the function FsLock(), the system real-time time time is obtained at (1), and the 15 second timeout is set at (2), FS_LOCK_TIMEOUT_SEC defaults to 15 seconds. (3) lock the mutex at. After the timeout, the mutex will not be locked again. The function FsUnlock() is used to unlock.

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 judging the validity of file descriptor

The function IsValidFd() is used to judge whether the file descriptor is valid. If the file descriptor exceeds the valid range or the file is not in use, it returns false, otherwise it returns 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 switching driver

The path of the incoming drive () is switched according to the drive letter of the incoming drive (). The string array tmpPath is used to save the drive name, where the maximum value of the drive name is FS_DRIVE_NAME_MAX_LEN. (2) handle the case where the length of the path is greater than the length of the drive name. Get the name of the drive from the path, then call the interface f_. Chdrive() switches the drive. In the file operation interface, this function will be called to switch drives, such as fatfs_open,fatfs_unlink,fatfs_stat,fatfs_mkdir,fatfs_opendir,fatfs_rmdir,fatfs_rename and fatfs_statfs. The parameters of these functions involve the file path char *path or directory 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 matching file volumes

The function FsPartitionMatch() obtains the corresponding volume index according to the incoming file path and is used in mount, unmount, format and other interfaces. (1) if the volume name is passed in, obtain the top-level directory name, that is, the first level directory of the path, without the path separator /. (2) if the path name is passed in, get the top-level path until the first separator /. Otherwise, execute (3) and assign the name in the path. Then traverse each volume. If the obtained top-level directory name is equal to the volume name, the corresponding volume array index is returned. Otherwise, FS is returned_ 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 judge whether the file volume is writable

The functions FsCheckByPath() and FsCheckByID() are used to judge whether the file volume is writable. The transfer parameters are different. The former passes in the file path, which is determined after being converted into the volume index. The latter passes in the mount number. Traverse each volume to determine whether the number of the corresponding volume is equal to the incoming parameters.

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 label conversion

The function FatFsGetMode() is used to convert the file opening label in POSIX format to the file opening label in FatFS file system format. FatfsErrno() converts the error number in the FatFS file system format to the error number in POSIX format.

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. File system operation interface of LiteOS-M FATFS

Quickly record each operation interface, and the purpose and usage of each interface will not be described. You can refer to the previous series of articles, "Hongmeng light kernel M core source code analysis series 19 Musl LibC", which introduces the relevant interfaces. Those interfaces will call the operation interface in the VFS file system, and then further call the FatFS file operation interface.

3.1 mount and unload operations

Take a look at the mount operation first. FatFS supports the re mount operation. If the mount option contains MS_ When remount, the function Remount() will be called to mount again. In the function Remount(), FsPartitionMatch() is called at (1) to obtain the volume index. If the volume is not mounted at (2), it is not allowed to mount again, and the error code is returned. (3) set whether the corresponding volume is readable or writable at. From this point of view, re mounting is mainly to update the readability and writability of the volume.

Look at the mount function fatfs_mount(), start to judge the validity of the parameter at (4), which cannot be empty, the file system type must be "fat", call FsPartitionMatch() at (5) to obtain the volume index, and return the error code if the volume has been mounted at (6). (7) call f at_ Mount() implements mount, and the third parameter 1 indicates mount immediately. (8) set whether the corresponding volume is readable or writable.

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;
}

Next, look at the uninstall operation. Function FatFs_ In umount(), first perform basic checks such as parameter validity and whether to mount, and call function f at ⑴_ Check openlock() to determine whether there are open files or directories in the volume to be unloaded, and call f at (2)_ Mount(), the first parameter is NULL, which means to unmount the file system specified by the target;, The third parameter 0 indicates that mounting is not required. If the unloading is wrong, convert the corresponding error code. (3) if the Disk access window for Directory is not empty, perform the corresponding release operation. (4) set the element corresponding to the file volume array to zero.

The function CloseAll() traverses every open file and directory to close according to the file volume number. Function fatfs_umount2(), the supported uninstall options indicated at (5) are: MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW. (6) in the case of forced unloading, first close the open files and directories, and then execute (7) to realize the unloading operation.

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 file directory operation interface

The file directory operation interface contains fatfs_mkdir,fatfs_unlink,fatfs_rmdir,fatfs_readdir,fatfs_closedir,fatfs_open,fatfs_close, etc. will further call the file directory operation interface of FatFS for encapsulation. The code is relatively simple and can be read by yourself. Some code fragments are as follows.

......
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;
}
......

Summary

This paper introduces the structure and global variables of FatFS, the operation interface of global variables, and analyzes the operation interface of FatFS file. Hurry of time is related to ability. If there is any mistake, you are welcome to correct it. Thank you for reading. If you have any questions and suggestions, you can leave a message to me under the blog. Thank you.

reference material

  • Harmonyos device > documentation Guide > basic capabilities - FatFS

Please pay more attention to learning content IoT IOT community

 

Click follow to learn about Huawei's new cloud technology for the first time~

Keywords: liteos

Added by coowboy on Thu, 10 Feb 2022 23:07:15 +0200