我可以不吃啊 发表于 2024-8-14 16:19:45

鸿蒙轻内核M核源码分析系列二一 03 文件系统LittleFS

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.useFlag == 0) {
            *fd = i;
            g_handle.useFlag = 1;
            g_handle.pathName = strdup(fileName);
            pthread_mutex_unlock(&g_FslocalMutex);
            return &(g_handle);
      }
    }
    pthread_mutex_unlock(&g_FslocalMutex);
    *fd = INVALID_FD;
    return NULL;
}

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

    if (g_handle.lfsHandle != NULL) {
      g_handle.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.useFlag == 1) {
            if (strcmp(g_handle.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.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.useFlag == 0 && strcmp(target, g_littlefsMntName) == 0) {
            g_fsOp.useFlag = 1;
            g_fsOp.fsVops = fileOps;
            g_fsOp.dirName = strdup(target);
            pthread_mutex_unlock(&g_FslocalMutex);
            return &(g_fsOp);
      }
    }

    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 = 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.useFlag == 1) {
            if (g_fsOp.dirName && strcmp(target, g_fsOp.dirName) == 0) {
                *mountIndex = i;
                pthread_mutex_unlock(&g_FslocalMutex);
                return &(g_fsOp);
            }
      }
    }

    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.useFlag == 1 && g_fsOp.dirName != NULL) {
      g_fsOp.useFlag = 0;
      free(g_fsOp.dirName);
      g_fsOp.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.useFlag == 1) {
            if (g_fsOp.dirName && strcmp(target, g_fsOp.dirName) == 0) {
                g_fsOp.useFlag = 0;
                free(g_fsOp.dirName);
                g_fsOp.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 == '/') {
            break;
      }
      len++;
    }

    return len;
}

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

    pthread_mutex_lock(&g_FslocalMutex);
    for (int i = 0; i < LOSCFG_LFS_MAX_MOUNT_SIZE; i++) {
⑵      if (g_fsOp.useFlag == 1) {
            (void)strncpy_s(tmpName, LITTLEFS_MAX_LFN_LEN, pathName, len);
⑶          if (strcmp(tmpName, g_fsOp.dirName) == 0) {
                *fileOpInfo = &(g_fsOp);
                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;
}
......

小结

本文先容了LFS的结构体和全局变量,全局变量的操作接口,分析了下LFS文件操作接口。
如果各人想更加深入的学习 OpenHarmony 开辟的内容,不妨可以参考以下相关学习文档进行学习,助你快速提拔自己:
OpenHarmony 开辟情况搭建:https://qr18.cn/CgxrRy

https://img-blog.csdnimg.cn/direct/4dfe2f31a14b46239f7ae59808df5c90.png
《OpenHarmony源码分析》:https://qr18.cn/CgxrRy



[*]搭建开辟情况
[*]Windows 开辟情况的搭建
[*]Ubuntu 开辟情况搭建
[*]Linux 与 Windows 之间的文件共享
[*]……
https://img-blog.csdnimg.cn/direct/bef6f1bd585c4848a00e2d58ad3b9986.png
系统架构分析:https://qr18.cn/CgxrRy



[*]构建子系统
[*]启动流程
[*]子系统
[*]分布式任务调度子系统
[*]分布式通讯子系统
[*]驱动子系统
[*]……
https://img-blog.csdnimg.cn/direct/fa917c13583648ffb3d99bbecfba40d1.png
OpenHarmony 装备开辟学习手册:https://qr18.cn/CgxrRy

https://img-blog.csdnimg.cn/direct/bfc166b706774e13b603b9e268e99f12.png
OpenHarmony口试题(内含参考答案):https://qr18.cn/CgxrRy

https://img-blog.csdnimg.cn/direct/c9eba9c31e4647b7ad505234a33a0ddb.png
写在最后



[*]如果你觉得这篇内容对你还蛮有资助,我想邀请你帮我三个小忙:
[*]点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
[*]关注小编,同时可以期待后续文章ing
页: [1]
查看完整版本: 鸿蒙轻内核M核源码分析系列二一 03 文件系统LittleFS