鸿蒙轻内核M核源码分析系列之虚拟文件系统VFS
系统
VFS(Virtual File System)是文件系统的虚拟层,它不是一个实际的文件系统,而是一个异构文件系统之上的软件粘合层,为用户提供统一的类Unix文件操作接口。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

VFS(Virtual File System)是文件系统的虚拟层,它不是一个实际的文件系统,而是一个异构文件系统之上的软件粘合层,为用户提供统一的类Unix文件操作接口。由于不同类型的文件系统接口不统一,若系统中有多个文件系统类型,访问不同的文件系统就需要使用不同的非标准接口。而通过在系统中添加VFS层,提供统一的抽象接口,屏蔽了底层异构类型的文件系统的差异,使得访问文件系统的系统调用不用关心底层的存储介质和文件系统类型,提高开发效率。本文先介绍下VFS的结构体和全局变量,然后详细分析下VFS文件操作接口。文中所涉及的源码,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。

1、VFS结构体定义

在文件components\fs\vfs\fs_operations.h中定义了VFS虚拟文件系统操作涉及的结构体。⑴处的struct MountOps结构体封装了挂载相关的操作,包含挂载、卸载和文件系统统计操作。⑵处的struct FsMap结构体映射文件系统类型及其对应的挂载操作和文件系统操作,支持的文件类型包含“fat”和“littlefs”两种,通过这个结构体可以获取对应文件类型的挂载操作及文件系统操作接口。⑶处的struct FileOps封装文件系统的操作接口,包含文件操作、目录操作,统计等相应的接口。

  1. ⑴  struct MountOps { 
  2.         int (*Mount)(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, 
  3.             const void *data); 
  4.         int (*Umount)(const char* target); 
  5.         int (*Umount2)(const char* target, int flag); 
  6.         int (*Statfs)(const char *path, struct statfs *buf); 
  7.     }; 
  8.  
  9. ⑵  struct FsMap { 
  10.         const char *fileSystemtype; 
  11.         const struct MountOps *fsMops; 
  12.         const struct FileOps *fsFops; 
  13.     }; 
  14.  
  15. ⑶  struct FileOps { 
  16.         int (*Open)(const char *path, int openFlag, ...); 
  17.         int (*Close)(int fd); 
  18.         int (*Unlink)(const char *fileName); 
  19.         int (*Rmdir)(const char *dirName); 
  20.         int (*Mkdir)(const char *dirName, mode_t mode); 
  21.         struct dirent *(*Readdir)(DIR *dir); 
  22.         DIR *(*Opendir)(const char *dirName); 
  23.         int (*Closedir)(DIR *dir); 
  24.         int (*Read)(int fd, void *buf, size_t len); 
  25.         int (*Write)(int fd, const void *buf, size_t len); 
  26.         off_t (*Seek)(int fd, off_t offset, int whence); 
  27.         int (*Getattr)(const char *path, struct stat *buf); 
  28.         int (*Rename)(const char *oldName, const char *newName); 
  29.         int (*Fsync)(int fd); 
  30.         int (*Fstat)(int fd, struct stat *buf); 
  31.         int (*Stat)(const char *path, struct stat *buf); 
  32.         int (*Ftruncate)(int fd, off_t length); 
  33.     }; 

2、VFS重要的内部全局变量

在文件components\fs\vfs\los_fs.c中有2个全局变量比较重要,⑴处定义的数组g_fsmap维护文件系统类型映射信息,数组大小为2,支持"fat"和"littlefs"文件类型。⑵处的变量g_fs根据挂载的文件类型指向数组g_fsmap中的FsMap类型元素。⑶处的函数InitMountInfo()会给数组g_fsmap进行初始化赋值。第0个元素维护的"fat"文件类型的文件系统映射信息,第1个元素维护的"littlefs"文件类型的文件系统映射信息。涉及到的挂载操作、文件系统操作变量g_fatfsMnt、g_fatfsFops、g_lfsMnt、g_lfsFops在对应的文件系统文件中定义。⑷处的函数MountFindfs()用于根据文件类型从数组中获取文件映射信息。

  1. ⑴  static struct FsMap g_fsmap[MAX_FILESYSTEM_LEN] = {0}; 
  2. ⑵  static struct FsMap *g_fs = NULL
  3.  
  4. ⑶  static void InitMountInfo(void) 
  5.     { 
  6.     #if (LOSCFG_SUPPORT_FATFS == 1) 
  7.         extern struct MountOps g_fatfsMnt; 
  8.         extern struct FileOps g_fatfsFops; 
  9.         g_fsmap[0].fileSystemtype = strdup("fat"); 
  10.         g_fsmap[0].fsMops = &g_fatfsMnt; 
  11.         g_fsmap[0].fsFops = &g_fatfsFops; 
  12.     #endif 
  13.     #if (LOSCFG_SUPPORT_LITTLEFS == 1) 
  14.         extern struct MountOps g_lfsMnt; 
  15.         extern struct FileOps g_lfsFops; 
  16.         g_fsmap[1].fileSystemtype = strdup("littlefs"); 
  17.         g_fsmap[1].fsMops = &g_lfsMnt; 
  18.         g_fsmap[1].fsFops = &g_lfsFops; 
  19.     #endif 
  20.     } 
  21.  
  22. ⑷  static struct FsMap *MountFindfs(const char *fileSystemtype) 
  23.     { 
  24.         struct FsMap *m = NULL
  25.  
  26.         for (int i = 0; i < MAX_FILESYSTEM_LEN; i++) { 
  27.             m = &(g_fsmap[i]); 
  28.             if (m->fileSystemtype && strcmp(fileSystemtype, m->fileSystemtype) == 0) { 
  29.                 return m; 
  30.             } 
  31.         } 
  32.  
  33.         return NULL
  34.     } 

3、VFS相关的操作接口

在之前的系列文章《鸿蒙轻内核M核源码分析系列十九 Musl LibC》中介绍了相关的接口,那些接口会调用VFS文件系统中操作接口。对每个接口的用途用法不再描述,快速记录下各个操作接口。

3.1 挂载卸载操作

挂载卸载操作包含LOS_FsMount、LOS_FsUmount、LOS_FsUmount2等3个操作。⑴处在挂载文件系统之前,需要初始化文件系统映射信息,只会操作一次。⑵处根据文件系统类型获取对应的文件类型映射信息。从这里,可以获知,LiteOS-M内核只能同时支持一个文件系统,不能只支持fat又支持littlefs。⑶处对应对应的文件系统挂载接口实现挂载操作。其他两个函数同样比较简单,自行阅读代码即可。

  1.    int LOS_FsMount(const char *source, const char *target, 
  2.                     const char *filesystemtype, unsigned long mountflags, 
  3.                     const void *data) 
  4.     { 
  5.         static int initFlag = 0; 
  6.  
  7. ⑴      if (initFlag == 0) { 
  8.             InitMountInfo(); 
  9.             initFlag = 1; 
  10.         } 
  11.  
  12. ⑵      g_fs = MountFindfs(filesystemtype); 
  13.         if (g_fs == NULL) { 
  14.             errno = ENODEV; 
  15.             return FS_FAILURE; 
  16.         } 
  17.  
  18.         if (g_fs->fsMops == NULL || g_fs->fsMops->Mount == NULL) { 
  19.             errno = ENOSYS; 
  20.             return FS_FAILURE; 
  21.         } 
  22.  
  23. ⑶      return g_fs->fsMops->Mount(source, target, filesystemtype, mountflags, data); 
  24.     } 
  25.  
  26.     int LOS_FsUmount(const char *target) 
  27.     { 
  28.         if (g_fs == NULL) { 
  29.             errno = ENODEV; 
  30.             return FS_FAILURE; 
  31.         } 
  32.         if (g_fs->fsMops == NULL || g_fs->fsMops->Umount == NULL) { 
  33.             errno = ENOSYS; 
  34.             return FS_FAILURE; 
  35.         } 
  36.         return g_fs->fsMops->Umount(target); 
  37.     } 
  38.  
  39.     int LOS_FsUmount2(const char *target, int flag) 
  40.     { 
  41.         if (g_fs == NULL) { 
  42.             errno = ENODEV; 
  43.             return FS_FAILURE; 
  44.         } 
  45.         if (g_fs->fsMops == NULL || g_fs->fsMops->Umount2 == NULL) { 
  46.             errno = ENOSYS; 
  47.             return FS_FAILURE; 
  48.         } 
  49.         return g_fs->fsMops->Umount2(target, flag); 
  50.     } 

3.2 文件目录操作

VFS封装的文件目录操作接口包含LOS_Open、LOS_Close、LOS_Read、LOS_Write、LOS_Opendir、LOS_Readdir、LOS_Closedir等等。对具体的文件类型的文件目录操作接口进行封装,代码比较简单,自行阅读即可,部分代码片段如下:

  1. ...... 
  2.  
  3. int LOS_Unlink(const char *path) 
  4.     if (g_fs == NULL) { 
  5.         errno = ENODEV; 
  6.         return FS_FAILURE; 
  7.     } 
  8.     if (g_fs->fsFops == NULL || g_fs->fsFops->Unlink == NULL) { 
  9.         errno = ENOSYS; 
  10.         return FS_FAILURE; 
  11.     } 
  12.     return g_fs->fsFops->Unlink(path); 
  13.  
  14. int LOS_Fstat(int fd, struct stat *buf) 
  15.     if (g_fs == NULL) { 
  16.         errno = ENODEV; 
  17.         return FS_FAILURE; 
  18.     } 
  19.     if (g_fs->fsFops == NULL || g_fs->fsFops->Fstat == NULL) { 
  20.         errno = ENOSYS; 
  21.         return FS_FAILURE; 
  22.     } 
  23.     return g_fs->fsFops->Fstat(fd, buf); 
  24.  
  25. ...... 
  26.  
  27. int LOS_Mkdir(const char *path, mode_t mode) 
  28.     if (g_fs == NULL) { 
  29.         errno = ENODEV; 
  30.         return FS_FAILURE; 
  31.     } 
  32.     if (g_fs->fsFops == NULL || g_fs->fsFops->Mkdir == NULL) { 
  33.         errno = ENOSYS; 
  34.         return FS_FAILURE; 
  35.     } 
  36.     return g_fs->fsFops->Mkdir(path, mode); 
  37.  
  38. DIR *LOS_Opendir(const char *dirName) 
  39.     if (g_fs == NULL) { 
  40.         errno = ENODEV; 
  41.         return NULL
  42.     } 
  43.     if (g_fs->fsFops == NULL || g_fs->fsFops->Opendir == NULL) { 
  44.         errno = ENOSYS; 
  45.         return NULL
  46.     } 
  47.     return g_fs->fsFops->Opendir(dirName); 
  48. ...... 

3.3 随机数文件

文件/dev/random可以用于产生随机数。在开启宏LOSCFG_RANDOM_DEV时,LiteOS-M支持随机数文件。从⑴处可知随机数依赖文件~/openharmony/base/security/huks/interfaces/innerkits/huks_lite/hks_client.h和hks_tmp_client.c,这些文件用来产生随机数。⑵处定义的RANDOM_DEV_FD和RANDOM_DEV_PATH分别是随机数文件的文件描述符和随机数文件路径。

  1.     #ifdef LOSCFG_RANDOM_DEV 
  2. ⑴  #include "hks_client.h" 
  3. ⑵  #define RANDOM_DEV_FD  CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS 
  4.     #define RANDOM_DEV_PATH  "/dev/random" 
  5.     #endif 

3.3.1 随机LOS_Open和LOS_Close

该函数打开一个文件,获取文件描述符用于进一步操作。⑴处表示对于随机数文件,打开的标签选项只能支持指定的这些,否则会返回错误码。⑵处获取标准路径,如果获取失败,返回错误码。⑶处比较获取的标准路径是否为RANDOM_DEV_PATH,在确认是随机数路径时,⑷处开始判断。如果访问模式为只读,返回错误,如果打开选项标签是目录,返回错误。如果不是上述错误情形,返回随机数文件描述符。⑸处如果获取的标准路径为“/”或“/dev”,则根据不同的选项,返回不同的错误码。

  1. int LOS_Open(const char *path, int oflag, ...) 
  2. #ifdef LOSCFG_RANDOM_DEV 
  3.     unsigned flags = O_RDONLY | O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_LARGEFILE | O_TRUNC | O_EXCL | O_DIRECTORY; 
  4. ⑴  if ((unsigned)oflag & ~flags) { 
  5.         errno = EINVAL; 
  6.         return FS_FAILURE; 
  7.     } 
  8.  
  9.     size_t pathLen = strlen(path) + 1; 
  10.     char *canonicalPath = (char *)malloc(pathLen); 
  11.     if (!canonicalPath) { 
  12.         errno = ENOMEM; 
  13.         return FS_FAILURE; 
  14.     } 
  15. ⑵  if (GetCanonicalPath(NULL, path, canonicalPath, pathLen) == 0) { 
  16.         FREE_AND_SET_NULL(canonicalPath); 
  17.         errno = ENOMEM; 
  18.         return FS_FAILURE; 
  19.     } 
  20.  
  21. ⑶  if (strcmp(canonicalPath, RANDOM_DEV_PATH) == 0) { 
  22.         FREE_AND_SET_NULL(canonicalPath); 
  23. ⑷      if ((O_ACCMODE & (unsigned)oflag) != O_RDONLY) { 
  24.             errno = EPERM; 
  25.             return FS_FAILURE; 
  26.         } 
  27.         if ((unsigned)oflag & O_DIRECTORY) { 
  28.             errno = ENOTDIR; 
  29.             return FS_FAILURE; 
  30.         } 
  31.         return RANDOM_DEV_FD; 
  32.     } 
  33. ⑸  if (strcmp(canonicalPath, "/") == 0 || strcmp(canonicalPath, "/dev") == 0) { 
  34.         FREE_AND_SET_NULL(canonicalPath); 
  35.         if ((unsigned)oflag & O_DIRECTORY) { 
  36.             errno = EPERM; 
  37.             return FS_FAILURE; 
  38.         } 
  39.         errno = EISDIR; 
  40.         return FS_FAILURE; 
  41.     } 
  42.     FREE_AND_SET_NULL(canonicalPath); 
  43. #endif 
  44. ...... 

对于随机数文件,关闭时,直接返回成功,不需要额外操作。代码片段如下:

  1. int LOS_Close(int fd) 
  2. #ifdef LOSCFG_RANDOM_DEV 
  3.     if (fd == RANDOM_DEV_FD) { 
  4.         return FS_SUCCESS; 
  5.     } 
  6. #endif 
  7. ...... 

3.3.2 随机LOS_Read和LOS_Write

随机数文件读写使用LOS_Read和LOS_Write接口。读取时,⑴处先对传入参数进行校验,如果读取字节数为0,则返回0;如果读取的缓存地址为空,返回-1;如果读的字节大于1024,则使用1024。⑵处调用hks_generate_random()产生随机数。由于随机数文件是只读的,如果尝试写入会返回-1错误码。

  1. ssize_t LOS_Read(int fd, void *buf, size_t nbyte) 
  2. #ifdef LOSCFG_RANDOM_DEV 
  3.     if (fd == RANDOM_DEV_FD) { 
  4. ⑴      if (nbyte == 0) { 
  5.             return FS_SUCCESS; 
  6.         } 
  7.         if (buf == NULL) { 
  8.             errno = EINVAL; 
  9.             return FS_FAILURE; 
  10.         } 
  11.         if (nbyte > 1024) { /* 1024, max random_size */ 
  12.             nbyte = 1024; /* hks_generate_random: random_size must <= 1024 */ 
  13.         } 
  14.         struct hks_blob key = {HKS_BLOB_TYPE_RAW, (uint8_t *)buf, nbyte}; 
  15. ⑵      if (hks_generate_random(&key) != 0) { 
  16.             errno = EIO; 
  17.             return FS_FAILURE; 
  18.         } 
  19.         return (ssize_t)nbyte; 
  20.     } 
  21. #endif 
  22. ...... 
  23.  
  24. ssize_t LOS_Write(int fd, const void *buf, size_t nbyte) 
  25. #ifdef LOSCFG_RANDOM_DEV 
  26.     if (fd == RANDOM_DEV_FD) { 
  27.         errno = EBADF; /* "/dev/random" is readonly */ 
  28.         return FS_FAILURE; 
  29.     } 
  30. #endif 
  31. ...... 

小结

本文介绍了VFS的结构体和全局变量,分析了下VFS文件操作接口,对于随机数文件也进行了分析。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

 

责任编辑:jianghua 来源: 鸿蒙社区

同话题下的热门内容

Linux教程系列:Linux 中的 Watch 命令示例所有Chrome内核浏览器:删除默认搜索引擎的功能都不复存在使用 Python 脚本轻松剪辑/分割大视频微软发布 Office Build 14907.2 预览版:改进协作文档编辑,Excel 下拉列表自动完成电脑装哪个系统好,Windows 7还是Windows 10?看完这个就知道了我们一起玩转 Grep 指令微软发布全新 Linux DirectX 驱动程序,升级 Windows 11/10 WSL 子系统内核微软发布紧急带外更新:修复1月Windows更新诸多问题

编辑推荐

HarmonyOS 2.0鸿蒙第二期开发者Beta公测申请指南谷歌“断供”华为!中国真写不出操作系统?这可能是讲分布式系统最到位的一篇文章实锤!华为操作系统“鸿蒙”最早将于今秋面市!微软最强命令行工具发布!强势霸榜GitHub
我收藏的内容
点赞
收藏

51CTO技术栈视频号