鴻蒙輕內核源碼分析:文件系統LittleFS


摘要:本文先介紹下LFS文件系統結構體的結構體和全局變量,然后分析下LFS文件操作接口。

本文分享自華為雲社區《# 鴻蒙輕內核M核源碼分析系列二一 02 文件系統LittleFS》,作者:zhushy 。

LittleFS是一個小型的Flash文件系統,它結合日志結構(log-structured)文件系統和COW(copy-on-write)文件系統的思想,以日志結構存儲元數據,以COW結構存儲數據。這種特殊的存儲方式,使LittleFS具有強大的掉電恢復能力(power-loss resilience)。分配COW數據塊時LittleFS采用了名為統計損耗均衡的動態損耗均衡算法,使Flash設備的壽命得到有效保障。同時LittleFS針對資源緊缺的小型設備進行設計,具有極其有限的ROM和RAM占用,並且所有RAM的使用都通過一個可配置的固定大小緩沖區進行分配,不會隨文件系統的擴大占據更多的系統資源。當在一個資源非常緊缺的小型設備上,尋找一個具有掉電恢復能力並支持損耗均衡的Flash文件系統時,LittleFS是一個比較好的選擇。本文先介紹下LFS文件系統結構體的結構體和全局變量,然后分析下LFS文件操作接口。文中所涉及的源碼,均可以在開源站點https://gitee.com/openharmony/kernel_liteos_m 獲取。

1、LFS文件系統結構體介紹

會分2部分來介紹結構體部分,先介紹LittleFS文件系統的結構體,然后介紹LiteOS-M內核中提供的和LittleFS相關的一些結構體。

1.1 LittleFS的枚舉結構體

在openharmony/third_party/littlefs/lfs.h頭文件中定義LittleFS的枚舉、結構體,我們先簡單了解下,后文會使用到的。

枚舉lfs_type定義文件類型,了解下普通文件LFS_TYPE_REG和目錄LFS_TYPE_DIR即可。枚舉lfs_open_flags定義文件系統的打開標簽屬性信息,需要熟悉常用的只讀LFS_O_RDONLY、只寫LFS_O_WRONLY、讀寫LFS_O_RDWR等等。

// File types
enum lfs_type {
    // file types
    LFS_TYPE_REG            = 0x001,
    LFS_TYPE_DIR            = 0x002,

    // internally used types
    LFS_TYPE_SPLICE         = 0x400,
    LFS_TYPE_NAME           = 0x000,
    LFS_TYPE_STRUCT         = 0x200,
    LFS_TYPE_USERATTR       = 0x300,
    LFS_TYPE_FROM           = 0x100,
    LFS_TYPE_TAIL           = 0x600,
    LFS_TYPE_GLOBALS        = 0x700,
    LFS_TYPE_CRC            = 0x500,

    // internally used type specializations
    LFS_TYPE_CREATE         = 0x401,
    LFS_TYPE_DELETE         = 0x4ff,
    LFS_TYPE_SUPERBLOCK     = 0x0ff,
    LFS_TYPE_DIRSTRUCT      = 0x200,
    LFS_TYPE_CTZSTRUCT      = 0x202,
    LFS_TYPE_INLINESTRUCT   = 0x201,
    LFS_TYPE_SOFTTAIL       = 0x600,
    LFS_TYPE_HARDTAIL       = 0x601,
    LFS_TYPE_MOVESTATE      = 0x7ff,

    // internal chip sources
    LFS_FROM_NOOP           = 0x000,
    LFS_FROM_MOVE           = 0x101,
    LFS_FROM_USERATTRS      = 0x102,
};

// File open flags
enum lfs_open_flags {
    // open flags
    LFS_O_RDONLY = 1,         // Open a file as read only
#ifndef LFS_READONLY
    LFS_O_WRONLY = 2,         // Open a file as write only
    LFS_O_RDWR   = 3,         // Open a file as read and write
    LFS_O_CREAT  = 0x0100,    // Create a file if it does not exist
    LFS_O_EXCL   = 0x0200,    // Fail if a file already exists
    LFS_O_TRUNC  = 0x0400,    // Truncate the existing file to zero size
    LFS_O_APPEND = 0x0800,    // Move to end of file on every write
#endif

    // internally used flags
#ifndef LFS_READONLY
    LFS_F_DIRTY   = 0x010000, // File does not match storage
    LFS_F_WRITING = 0x020000, // File has been written since last flush
#endif
    LFS_F_READING = 0x040000, // File has been read since last flush
#ifndef LFS_READONLY
    LFS_F_ERRED   = 0x080000, // An error occurred during write
#endif
    LFS_F_INLINE  = 0x100000, // Currently inlined in directory entry
};

結構體lfs_t是littlefs文件系統類型結構體,lfs文件系統操作接口的第一個參數一般為這個結構體。成員變量struct lfs_config *cfg下文會涉及,其他成員變量可以暫不了解。

// The littlefs filesystem type
typedef struct lfs {
    lfs_cache_t rcache;
    lfs_cache_t pcache;

    lfs_block_t root[2];
    struct lfs_mlist {
        struct lfs_mlist *next;
        uint16_t id;
        uint8_t type;
        lfs_mdir_t m;
    } *mlist;
    uint32_t seed;

    lfs_gstate_t gstate;
    lfs_gstate_t gdisk;
    lfs_gstate_t gdelta;

    struct lfs_free {
        lfs_block_t off;
        lfs_block_t size;
        lfs_block_t i;
        lfs_block_t ack;
        uint32_t *buffer;
    } free;

    const struct lfs_config *cfg;
    lfs_size_t name_max;
    lfs_size_t file_max;
    lfs_size_t attr_max;

#ifdef LFS_MIGRATE
    struct lfs1 *lfs1;
#endif
} lfs_t;

結構體lfs_file_t、lfs_dir_t分別是littlefs的文件和目錄類型結構體,暫不需要關心成員變量細節,知道結構體的用途即可。

// littlefs directory type
typedef struct lfs_dir {
    struct lfs_dir *next;
    uint16_t id;
    uint8_t type;
    lfs_mdir_t m;

    lfs_off_t pos;
    lfs_block_t head[2];
} lfs_dir_t;

// littlefs file type
typedef struct lfs_file {
    struct lfs_file *next;
    uint16_t id;
    uint8_t type;
    lfs_mdir_t m;

    struct lfs_ctz {
        lfs_block_t head;
        lfs_size_t size;
    } ctz;

    uint32_t flags;
    lfs_off_t pos;
    lfs_block_t block;
    lfs_off_t off;
    lfs_cache_t cache;

    const struct lfs_file_config *cfg;
} lfs_file_t;

結構體lfs_config用於提供初始化littlefs文件系統的一些配置。其中.read,.prog,.erase,.sync分別對應該硬件平台上的底層的讀寫\擦除\同步等接口。

  • read_size 每次讀取的字節數,可以比物理讀單元大以改善性能,這個數值決定了讀緩存的大小,但值太大會帶來更多的內存消耗。
  • prog_size 每次寫入的字節數,可以比物理寫單元大以改善性能,這個數值決定了寫緩存的大小,必須是read_size的整數倍,但值太大會帶來更多的內存消耗。
  • block_size 每個擦除塊的字節數,可以比物理擦除單元大,但此數值應盡可能小因為每個文件至少會占用一個塊。必須是prog_size的整數倍。
  • block_count 可以被擦除的塊數量,這取決於塊設備的容量及擦除塊的大小。
// Configuration provided during initialization of the littlefs
struct lfs_config {
    // Opaque user provided context that can be used to pass
    // information to the block device operations
    void *context;

    int (*read)(const struct lfs_config *c, lfs_block_t block,
            lfs_off_t off, void *buffer, lfs_size_t size);
    int (*prog)(const struct lfs_config *c, lfs_block_t block,
            lfs_off_t off, const void *buffer, lfs_size_t size);
    int (*erase)(const struct lfs_config *c, lfs_block_t block);
    int (*sync)(const struct lfs_config *c);

#ifdef LFS_THREADSAFE
    int (*lock)(const struct lfs_config *c);
    int (*unlock)(const struct lfs_config *c);
#endif

    lfs_size_t read_size;
    lfs_size_t prog_size;
    lfs_size_t block_size;
    lfs_size_t block_count;

    int32_t block_cycles;
    lfs_size_t cache_size;
    lfs_size_t lookahead_size;
    void *read_buffer;
    void *prog_buffer;
    void *lookahead_buffer;
    lfs_size_t name_max;
    lfs_size_t file_max;
    lfs_size_t attr_max;
    lfs_size_t metadata_max;
};

結構體lfs_info用於維護文件信息,包含文件類型,大小和文件名信息。

// File info structure
struct lfs_info {
    // Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
    uint8_t type;

    // Size of the file, only valid for REG files. Limited to 32-bits.
    lfs_size_t size;

    // Name of the file stored as a null-terminated string. Limited to
    // LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to
    // reduce RAM. LFS_NAME_MAX is stored in superblock and must be
    // respected by other littlefs drivers.
    char name[LFS_NAME_MAX+1];
};

1.2 LiteOS-M LittleFS的結構體

我們來看下在文件components\fs\littlefs\lfs_api.h里定義的幾個結構體。結構體LittleFsHandleStruct維護文件相關的信息,該結構體的成員包含是否使用,文件路徑和lfs文件系統類型結構體lfs_t *lfsHandle和文件類型結構體lfs_file_t file。類似的,結構體FileDirInfo維護目錄相關的信息,該結構體成員包含包含是否使用,目錄名稱和lfs文件系統類型結構體lfs_t *lfsHandle和目錄類型結構體lfs_dir_t dir。另外一個結構體FileOpInfo維護文件操作信息。

typedef struct {
    uint8_t useFlag;
    const char *pathName;
    lfs_t *lfsHandle;
    lfs_file_t file;
} LittleFsHandleStruct;

struct FileOpInfo {
    uint8_t useFlag;
    const struct FileOps *fsVops;
    char *dirName;
    lfs_t lfsInfo;
};

typedef struct {
    uint8_t useFlag;
    char *dirName;
    lfs_t *lfsHandle;
    lfs_dir_t dir;
} FileDirInfo;

2、LiteOS-M LittleFS的重要全局變量及操作

了解下文件components\fs\littlefs\lfs_api.c定義的常用全局變量。⑴處的g_lfsDir數組維護目錄信息,默認支持的目錄數目為LFS_MAX_OPEN_DIRS,等於10。⑵處的g_fsOp數組維護針對每個掛載點的文件操作信息,默認掛載點數目LOSCFG_LFS_MAX_MOUNT_SIZE為3個。⑶處的g_handle數組維護文件信息,默認支持文件的數量LITTLE_FS_MAX_OPEN_FILES為100個。⑷處開始的struct dirent g_nameValue是目錄項結構體變量,用於函數LfsReaddir();pthread_mutex_t g_FslocalMutex是互斥鎖變量;g_littlefsMntName是掛載點名稱數組。⑸處開始的掛載操作變量g_lfsMnt、文件操作操作全局變量g_lfsFops在虛擬文件系統中被使用。

⑴  FileDirInfo g_lfsDir[LFS_MAX_OPEN_DIRS] = {0};

⑵  struct FileOpInfo g_fsOp[LOSCFG_LFS_MAX_MOUNT_SIZE] = {0};
⑶  static LittleFsHandleStruct g_handle[LITTLE_FS_MAX_OPEN_FILES] = {0};
⑷  struct dirent g_nameValue;
    static pthread_mutex_t g_FslocalMutex = PTHREAD_MUTEX_INITIALIZER;
    static const char *g_littlefsMntName[LOSCFG_LFS_MAX_MOUNT_SIZE] = {"/a", "/b", "/c"};
    ......
⑸  const struct MountOps g_lfsMnt = {
        .Mount = LfsMount,
        .Umount = LfsUmount,
    };

    const struct FileOps g_lfsFops = {
        .Mkdir = LfsMkdir,
        .Unlink = LfsUnlink,
        .Rmdir = LfsRmdir,
        .Opendir = LfsOpendir,
        .Readdir = LfsReaddir,
        .Closedir = LfsClosedir,
        .Open = LfsOpen,
        .Close = LfsClose,
        .Write = LfsWrite,
        .Read = LfsRead,
        .Seek = LfsSeek,
        .Rename = LfsRename,
        .Getattr = LfsStat,
        .Fsync = LfsFsync,
        .Fstat = LfsFstat,
    };

下文繼續介紹下和這些變量相關的內部操作接口。

2.1 目錄信息數組操作

GetFreeDir()設置目錄信息數組元素信息。參數dirName為目錄名稱。遍歷目錄信息數組,遍歷到第一個未使用的元素標記其為已使用狀態,設置目錄名稱,返回目錄信息元素指針地址。如果遍歷失敗,返回NULL。函數FreeDirInfo()為函數GetFreeDir()的反向操作,根據目錄名稱設置對應的數組元素為未使用狀態,並把GetFreeDir設置為NULL。

函數CheckDirIsOpen()用於檢測目錄是否已經打開。如果目錄信息數組中記錄着對應的目錄信息,則標志着該目錄已經打開。

FileDirInfo *GetFreeDir(const char *dirName)
{
    pthread_mutex_lock(&g_FslocalMutex);
    for (int i = 0; i < LFS_MAX_OPEN_DIRS; i++) {
        if (g_lfsDir[i].useFlag == 0) {
            g_lfsDir[i].useFlag = 1;
            g_lfsDir[i].dirName = strdup(dirName);
            pthread_mutex_unlock(&g_FslocalMutex);
            return &(g_lfsDir[i]);
        }
    }
    pthread_mutex_unlock(&g_FslocalMutex);
    return NULL;
}

void FreeDirInfo(const char *dirName)
{
    pthread_mutex_lock(&g_FslocalMutex);
    for (int i = 0; i < LFS_MAX_OPEN_DIRS; i++) {
        if (g_lfsDir[i].useFlag == 1 && strcmp(g_lfsDir[i].dirName, dirName) == 0) {
            g_lfsDir[i].useFlag = 0;
            if (g_lfsDir[i].dirName) {
                free(g_lfsDir[i].dirName);
                g_lfsDir[i].dirName = NULL;
            }
            pthread_mutex_unlock(&g_FslocalMutex);
        }
    }
    pthread_mutex_unlock(&g_FslocalMutex);
}

BOOL CheckDirIsOpen(const char *dirName)
{
    pthread_mutex_lock(&g_FslocalMutex);
    for (int i = 0; i < LFS_MAX_OPEN_DIRS; i++) {
        if (g_lfsDir[i].useFlag == 1) {
            if (strcmp(g_lfsDir[i].dirName, dirName) == 0) {
                pthread_mutex_unlock(&g_FslocalMutex);
                return TRUE;
            }
        }
    }
    pthread_mutex_unlock(&g_FslocalMutex);
    return FALSE;
}

2.2 文件信息數組操作

函數LfsAllocFd()設置文件信息數組元素信息。參數fileName為文件路徑信息,傳出參數fd為文件描述符即數組索引。遍歷文件信息數組,遍歷到第一個未使用的元素標記其為已使用狀態,設置文件路徑信息,把數組索引賦值給文件描述符fd,返回文件信息元素指針地址。如果遍歷失敗,返回NULL。函數LfsFreeFd()為函數LfsAllocFd()的反向操作,根據文件描述符設置對應的數組元素為未使用狀態,並把路徑信息等設置為NULL。

函數CheckFileIsOpen()用於檢測文件是否已經打開,文件如果打開過,則表示獲取過該文件的文件描述符,根據對應的fd文件描述符,可以對文件進行更多的操作。如果文件信息數組中記錄着對應的文件路徑信息,則標志着該文件已經打開。函數LfsFdIsValid()用於判斷文件描述符是否有效。

LittleFsHandleStruct *LfsAllocFd(const char *fileName, int *fd)
{
    pthread_mutex_lock(&g_FslocalMutex);
    for (int i = 0; i < LITTLE_FS_MAX_OPEN_FILES; i++) {
        if (g_handle[i].useFlag == 0) {
            *fd = i;
            g_handle[i].useFlag = 1;
            g_handle[i].pathName = strdup(fileName);
            pthread_mutex_unlock(&g_FslocalMutex);
            return &(g_handle[i]);
        }
    }
    pthread_mutex_unlock(&g_FslocalMutex);
    *fd = INVALID_FD;
    return NULL;
}

static void LfsFreeFd(int fd)
{
    pthread_mutex_lock(&g_FslocalMutex);
    g_handle[fd].useFlag = 0;
    if (g_handle[fd].pathName != NULL) {
        free((void *)g_handle[fd].pathName);
        g_handle[fd].pathName = NULL;
    }

    if (g_handle[fd].lfsHandle != NULL) {
        g_handle[fd].lfsHandle = NULL;
    }
    pthread_mutex_unlock(&g_FslocalMutex);
}

BOOL CheckFileIsOpen(const char *fileName)
{
    pthread_mutex_lock(&g_FslocalMutex);
    for (int i = 0; i < LITTLE_FS_MAX_OPEN_FILES; i++) {
        if (g_handle[i].useFlag == 1) {
            if (strcmp(g_handle[i].pathName, fileName) == 0) {
                pthread_mutex_unlock(&g_FslocalMutex);
                return TRUE;
            }
        }
    }
    pthread_mutex_unlock(&g_FslocalMutex);
    return FALSE;
}

static BOOL LfsFdIsValid(int fd)
{
    if (fd >= LITTLE_FS_MAX_OPEN_FILES || fd < 0) {
        return FALSE;
    }
    if (g_handle[fd].lfsHandle == NULL) {
        return FALSE;
    }
    return TRUE;
}

2.3 掛載點文件操作信息相關操作

函數AllocMountRes()用於設置掛載點文件操作信息。參數target為掛載點名稱,參數fileOps為文件操作信息。遍歷每個掛載點,如果遍歷到的掛載點未使用,並且掛載點名稱相等,則設置其使用標記為已使用,設置目錄名稱,設置文件操作信息,然后返回文件操作信息指針。如果沒有遍歷到,返回NULL。掛載點數組g_littlefsMntName的元素默認為/a,/b,/c等,可以使用函數SetDefaultMountPath()設置指定位置的掛載點名稱。

struct FileOpInfo *AllocMountRes(const char* target, const struct FileOps *fileOps)
{
    pthread_mutex_lock(&g_FslocalMutex);
    for (int i = 0; i < LOSCFG_LFS_MAX_MOUNT_SIZE; i++) {
        if (g_fsOp[i].useFlag == 0 && strcmp(target, g_littlefsMntName[i]) == 0) {
            g_fsOp[i].useFlag = 1;
            g_fsOp[i].fsVops = fileOps;
            g_fsOp[i].dirName = strdup(target);
            pthread_mutex_unlock(&g_FslocalMutex);
            return &(g_fsOp[i]);
        }
    }

    pthread_mutex_unlock(&g_FslocalMutex);
    return NULL;
}

int SetDefaultMountPath(int pathNameIndex, const char* target)
{
    if (pathNameIndex >= LOSCFG_LFS_MAX_MOUNT_SIZE) {
        return VFS_ERROR;
    }

    pthread_mutex_lock(&g_FslocalMutex);
    g_littlefsMntName[pathNameIndex] = strdup(target);
    pthread_mutex_unlock(&g_FslocalMutex);
    return VFS_OK;
}

函數GetMountRes()用於獲取給定掛載點在掛載點文件操作信息數組中的索引值。參數target為掛載點名稱,參數mountIndex用於輸出文件操作信息數組索引值。遍歷每個掛載點,如果遍歷到的掛載點已使用,並且掛載點名稱相等,則返回相應的數組索引,否則返回NULL。

struct FileOpInfo *GetMountRes(const char *target, int *mountIndex)
{
    pthread_mutex_lock(&g_FslocalMutex);
    for (int i = 0; i < LOSCFG_LFS_MAX_MOUNT_SIZE; i++) {
        if (g_fsOp[i].useFlag == 1) {
            if (g_fsOp[i].dirName && strcmp(target, g_fsOp[i].dirName) == 0) {
                *mountIndex = i;
                pthread_mutex_unlock(&g_FslocalMutex);
                return &(g_fsOp[i]);
            }
        }
    }

    pthread_mutex_unlock(&g_FslocalMutex);
    return NULL;
}

函數FreeMountResByIndex()屬於函數AllocMountRes()的反向操作,用於釋放掛載點文件操作信息。傳入參數mountIndex對應的文件操作信息標記為未使用狀態,釋放掛載點名稱占用的內存。函數FreeMountRes()實現的功能一樣,傳入參數為掛載點名稱。遍歷每一個掛載點,如果存在和傳入參數相同的掛載點,則進行釋放。

int FreeMountResByIndex(int mountIndex)
{
    if (mountIndex < 0 || mountIndex >= LOSCFG_LFS_MAX_MOUNT_SIZE) {
        return VFS_ERROR;
    }

    pthread_mutex_lock(&g_FslocalMutex);
    if (g_fsOp[mountIndex].useFlag == 1 && g_fsOp[mountIndex].dirName != NULL) {
        g_fsOp[mountIndex].useFlag = 0;
        free(g_fsOp[mountIndex].dirName);
        g_fsOp[mountIndex].dirName = NULL;
    }
    pthread_mutex_unlock(&g_FslocalMutex);

    return VFS_OK;
}

int FreeMountRes(const char *target)
{
    pthread_mutex_lock(&g_FslocalMutex);
    for (int i = 0; i < LOSCFG_LFS_MAX_MOUNT_SIZE; i++) {
        if (g_fsOp[i].useFlag == 1) {
            if (g_fsOp[i].dirName && strcmp(target, g_fsOp[i].dirName) == 0) {
                g_fsOp[i].useFlag = 0;
                free(g_fsOp[i].dirName);
                g_fsOp[i].dirName = NULL;
                pthread_mutex_unlock(&g_FslocalMutex);
                return VFS_OK;
            }
        }
    }

    pthread_mutex_unlock(&g_FslocalMutex);
    return VFS_ERROR;
}

2.4 路徑是否已掛載CheckPathIsMounted

函數CheckPathIsMounted()用於檢查給定的路徑是否已經掛載,如果掛載上把對應掛載點的文件操作信息由參數struct FileOpInfo **fileOpInfo輸出。⑴處先獲取路徑的第一級目錄的長度。⑵處遍歷每一個掛載點的文件操作數組,如果文件操作處於使用狀態,則執行⑶比對相應的掛載點名稱和路徑的第一級目錄名稱是否相等。如果相等,則輸出文件操作信息,並返回TRUE。否則返回FALSE。

int GetFirstLevelPathLen(const char *pathName)
{
    int len = 1;
    for (int i = 1; i < strlen(pathName) + 1; i++) {
        if (pathName[i] == '/') {
            break;
        }
        len++;
    }

    return len;
}

BOOL CheckPathIsMounted(const char *pathName, struct FileOpInfo **fileOpInfo)
{
    char tmpName[LITTLEFS_MAX_LFN_LEN] = {0};
⑴  int len = GetFirstLevelPathLen(pathName);

    pthread_mutex_lock(&g_FslocalMutex);
    for (int i = 0; i < LOSCFG_LFS_MAX_MOUNT_SIZE; i++) {
⑵      if (g_fsOp[i].useFlag == 1) {
            (void)strncpy_s(tmpName, LITTLEFS_MAX_LFN_LEN, pathName, len);
⑶          if (strcmp(tmpName, g_fsOp[i].dirName) == 0) {
                *fileOpInfo = &(g_fsOp[i]);
                pthread_mutex_unlock(&g_FslocalMutex);
                return TRUE;
            }
        }
    }
    pthread_mutex_unlock(&g_FslocalMutex);
    return FALSE;
}

3、LiteOS-M LittleFS的文件系統操作接口

快速記錄下各個操作接口,對每個接口的用途用法不再描述。可以參考之前的系列文章,《鴻蒙輕內核M核源碼分析系列十九 Musl LibC》中介紹了相關的接口,那些接口會調用VFS文件系統中操作接口,然后進一步調用LFS文件操作接口。

3.1 掛載LfsMount和卸載LfsUmounts操作

掛載卸載操作包含LfsMount、LfsUmounts等2個操作。對於函數LfsMount(),需要注意下參數const void *data,這個需要是struct lfs_config指針類型變量。⑴處在掛載文件系統之前,對輸入參數進行檢測。⑵處判斷是否已經掛載,不允許重復掛載。⑶處設置掛載點信息,⑷處調用LFS的函數實現掛載,如果掛載失敗,則執行⑸嘗試格式化,然后重新掛載。

對於函數LfsUmount(),⑹處根據掛載點獲取文件操作信息和掛載點索引值。⑺處調用LFS函數實現卸載,然后執行⑻釋放掛載點文件操作信息。

int LfsMount(const char *source, const char *target, const char *fileSystemType, unsigned long mountflags,
    const void *data)
{
    int ret;
    struct FileOpInfo *fileOpInfo = NULL;

⑴  if (target == NULL || fileSystemType == NULL || data == NULL) {
        errno = EFAULT;
        ret = VFS_ERROR;
        goto errout;
    }

    if (strcmp(fileSystemType, "littlefs") != 0) {
        errno = ENODEV;
        ret = VFS_ERROR;
        goto errout;
    }

⑵  if (CheckPathIsMounted(target, &fileOpInfo)) {
        errno = EBUSY;
        ret = VFS_ERROR;
        goto errout;
    }

    // select free mount resource
⑶  fileOpInfo = AllocMountRes(target, &g_lfsFops);
    if (fileOpInfo == NULL) {
        errno = ENODEV;
        ret = VFS_ERROR;
        goto errout;
    }

⑷  ret = lfs_mount(&(fileOpInfo->lfsInfo), (struct lfs_config*)data);
    if (ret != 0) {
⑸      ret = lfs_format(&(fileOpInfo->lfsInfo), (struct lfs_config*)data);
        if (ret == 0) {
            ret = lfs_mount(&(fileOpInfo->lfsInfo), (struct lfs_config*)data);
        }
    }

    if (ret != 0) {
        errno = LittlefsErrno(ret);
        ret = VFS_ERROR;
    }

errout:
    return ret;
}

int LfsUmount(const char *target)
{
    int ret;
    int mountIndex = -1;
    struct FileOpInfo *fileOpInfo = NULL;

    if (target == NULL) {
        errno = EFAULT;
        return VFS_ERROR;
    }

⑹  fileOpInfo = GetMountRes(target, &mountIndex);
    if (fileOpInfo == NULL) {
        errno = ENOENT;
        return VFS_ERROR;
    }

⑺  ret = lfs_unmount(&(fileOpInfo->lfsInfo));
    if (ret != 0) {
        errno = LittlefsErrno(ret);
        ret = VFS_ERROR;
    }

⑻  (void)FreeMountResByIndex(mountIndex);
    return ret;
}

3.2 文件目錄操作接口

文件目錄操作接口包含LfsMkdir、LfsUnlink、LfsRmdir、LfsReaddir、LfsClosedir、LfsOpen、LfsClose等等,會進一步調用LFS的文件目錄操作接口進行封裝,代碼比較簡單,自行閱讀即可,部分代碼片段如下。

......
int LfsUnlink(const char *fileName)
{
    int ret;
    struct FileOpInfo *fileOpInfo = NULL;

    if (fileName == NULL) {
        errno = EFAULT;
        return VFS_ERROR;
    }

    if (CheckPathIsMounted(fileName, &fileOpInfo) == FALSE || fileOpInfo == NULL) {
        errno = ENOENT;
        return VFS_ERROR;
    }

    ret = lfs_remove(&(fileOpInfo->lfsInfo), fileName);
    if (ret != 0) {
        errno = LittlefsErrno(ret);
        ret = VFS_ERROR;
    }

    return ret;
}

int LfsMkdir(const char *dirName, mode_t mode)
{
    int ret;
    struct FileOpInfo *fileOpInfo = NULL;

    if (dirName == NULL) {
        errno = EFAULT;
        return VFS_ERROR;
    }

    if (CheckPathIsMounted(dirName, &fileOpInfo) == FALSE || fileOpInfo == NULL) {
        errno = ENOENT;
        return VFS_ERROR;
    }

    ret = lfs_mkdir(&(fileOpInfo->lfsInfo), dirName);
    if (ret != 0) {
        errno = LittlefsErrno(ret);
        ret = VFS_ERROR;
    }

    return ret;
}
......

參考資料

  • HarmonyOS Device>文檔指南>基礎能力-LittleFS

 

點擊關注,第一時間了解華為雲新鮮技術~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM