鴻蒙輕內(nèi)核M核源碼分析系列之虛擬文件系統(tǒng)VFS
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
VFS(Virtual File System)是文件系統(tǒng)的虛擬層,它不是一個(gè)實(shí)際的文件系統(tǒng),而是一個(gè)異構(gòu)文件系統(tǒng)之上的軟件粘合層,為用戶提供統(tǒng)一的類Unix文件操作接口。由于不同類型的文件系統(tǒng)接口不統(tǒng)一,若系統(tǒng)中有多個(gè)文件系統(tǒng)類型,訪問(wèn)不同的文件系統(tǒng)就需要使用不同的非標(biāo)準(zhǔn)接口。而通過(guò)在系統(tǒng)中添加VFS層,提供統(tǒng)一的抽象接口,屏蔽了底層異構(gòu)類型的文件系統(tǒng)的差異,使得訪問(wèn)文件系統(tǒng)的系統(tǒng)調(diào)用不用關(guān)心底層的存儲(chǔ)介質(zhì)和文件系統(tǒng)類型,提高開(kāi)發(fā)效率。本文先介紹下VFS的結(jié)構(gòu)體和全局變量,然后詳細(xì)分析下VFS文件操作接口。文中所涉及的源碼,均可以在開(kāi)源站點(diǎn)https://gitee.com/openharmony/kernel_liteos_m 獲取。
1、VFS結(jié)構(gòu)體定義
在文件components\fs\vfs\fs_operations.h中定義了VFS虛擬文件系統(tǒng)操作涉及的結(jié)構(gòu)體。⑴處的struct MountOps結(jié)構(gòu)體封裝了掛載相關(guān)的操作,包含掛載、卸載和文件系統(tǒng)統(tǒng)計(jì)操作。⑵處的struct FsMap結(jié)構(gòu)體映射文件系統(tǒng)類型及其對(duì)應(yīng)的掛載操作和文件系統(tǒng)操作,支持的文件類型包含“fat”和“littlefs”兩種,通過(guò)這個(gè)結(jié)構(gòu)體可以獲取對(duì)應(yīng)文件類型的掛載操作及文件系統(tǒng)操作接口。⑶處的struct FileOps封裝文件系統(tǒng)的操作接口,包含文件操作、目錄操作,統(tǒng)計(jì)等相應(yīng)的接口。
- ⑴ struct MountOps {
- int (*Mount)(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags,
- const void *data);
- int (*Umount)(const char* target);
- int (*Umount2)(const char* target, int flag);
- int (*Statfs)(const char *path, struct statfs *buf);
- };
- ⑵ struct FsMap {
- const char *fileSystemtype;
- const struct MountOps *fsMops;
- const struct FileOps *fsFops;
- };
- ⑶ struct FileOps {
- int (*Open)(const char *path, int openFlag, ...);
- int (*Close)(int fd);
- int (*Unlink)(const char *fileName);
- int (*Rmdir)(const char *dirName);
- int (*Mkdir)(const char *dirName, mode_t mode);
- struct dirent *(*Readdir)(DIR *dir);
- DIR *(*Opendir)(const char *dirName);
- int (*Closedir)(DIR *dir);
- int (*Read)(int fd, void *buf, size_t len);
- int (*Write)(int fd, const void *buf, size_t len);
- off_t (*Seek)(int fd, off_t offset, int whence);
- int (*Getattr)(const char *path, struct stat *buf);
- int (*Rename)(const char *oldName, const char *newName);
- int (*Fsync)(int fd);
- int (*Fstat)(int fd, struct stat *buf);
- int (*Stat)(const char *path, struct stat *buf);
- int (*Ftruncate)(int fd, off_t length);
- };
2、VFS重要的內(nèi)部全局變量
在文件components\fs\vfs\los_fs.c中有2個(gè)全局變量比較重要,⑴處定義的數(shù)組g_fsmap維護(hù)文件系統(tǒng)類型映射信息,數(shù)組大小為2,支持"fat"和"littlefs"文件類型。⑵處的變量g_fs根據(jù)掛載的文件類型指向數(shù)組g_fsmap中的FsMap類型元素。⑶處的函數(shù)InitMountInfo()會(huì)給數(shù)組g_fsmap進(jìn)行初始化賦值。第0個(gè)元素維護(hù)的"fat"文件類型的文件系統(tǒng)映射信息,第1個(gè)元素維護(hù)的"littlefs"文件類型的文件系統(tǒng)映射信息。涉及到的掛載操作、文件系統(tǒng)操作變量g_fatfsMnt、g_fatfsFops、g_lfsMnt、g_lfsFops在對(duì)應(yīng)的文件系統(tǒng)文件中定義。⑷處的函數(shù)MountFindfs()用于根據(jù)文件類型從數(shù)組中獲取文件映射信息。
- ⑴ static struct FsMap g_fsmap[MAX_FILESYSTEM_LEN] = {0};
- ⑵ static struct FsMap *g_fs = NULL;
- ⑶ static void InitMountInfo(void)
- {
- #if (LOSCFG_SUPPORT_FATFS == 1)
- extern struct MountOps g_fatfsMnt;
- extern struct FileOps g_fatfsFops;
- g_fsmap[0].fileSystemtype = strdup("fat");
- g_fsmap[0].fsMops = &g_fatfsMnt;
- g_fsmap[0].fsFops = &g_fatfsFops;
- #endif
- #if (LOSCFG_SUPPORT_LITTLEFS == 1)
- extern struct MountOps g_lfsMnt;
- extern struct FileOps g_lfsFops;
- g_fsmap[1].fileSystemtype = strdup("littlefs");
- g_fsmap[1].fsMops = &g_lfsMnt;
- g_fsmap[1].fsFops = &g_lfsFops;
- #endif
- }
- ⑷ static struct FsMap *MountFindfs(const char *fileSystemtype)
- {
- struct FsMap *m = NULL;
- for (int i = 0; i < MAX_FILESYSTEM_LEN; i++) {
- m = &(g_fsmap[i]);
- if (m->fileSystemtype && strcmp(fileSystemtype, m->fileSystemtype) == 0) {
- return m;
- }
- }
- return NULL;
- }
3、VFS相關(guān)的操作接口
在之前的系列文章《鴻蒙輕內(nèi)核M核源碼分析系列十九 Musl LibC》中介紹了相關(guān)的接口,那些接口會(huì)調(diào)用VFS文件系統(tǒng)中操作接口。對(duì)每個(gè)接口的用途用法不再描述,快速記錄下各個(gè)操作接口。
3.1 掛載卸載操作
掛載卸載操作包含LOS_FsMount、LOS_FsUmount、LOS_FsUmount2等3個(gè)操作。⑴處在掛載文件系統(tǒng)之前,需要初始化文件系統(tǒng)映射信息,只會(huì)操作一次。⑵處根據(jù)文件系統(tǒng)類型獲取對(duì)應(yīng)的文件類型映射信息。從這里,可以獲知,LiteOS-M內(nèi)核只能同時(shí)支持一個(gè)文件系統(tǒng),不能只支持fat又支持littlefs。⑶處對(duì)應(yīng)對(duì)應(yīng)的文件系統(tǒng)掛載接口實(shí)現(xiàn)掛載操作。其他兩個(gè)函數(shù)同樣比較簡(jiǎn)單,自行閱讀代碼即可。
- int LOS_FsMount(const char *source, const char *target,
- const char *filesystemtype, unsigned long mountflags,
- const void *data)
- {
- static int initFlag = 0;
- ⑴ if (initFlag == 0) {
- InitMountInfo();
- initFlag = 1;
- }
- ⑵ g_fs = MountFindfs(filesystemtype);
- if (g_fs == NULL) {
- errno = ENODEV;
- return FS_FAILURE;
- }
- if (g_fs->fsMops == NULL || g_fs->fsMops->Mount == NULL) {
- errno = ENOSYS;
- return FS_FAILURE;
- }
- ⑶ return g_fs->fsMops->Mount(source, target, filesystemtype, mountflags, data);
- }
- int LOS_FsUmount(const char *target)
- {
- if (g_fs == NULL) {
- errno = ENODEV;
- return FS_FAILURE;
- }
- if (g_fs->fsMops == NULL || g_fs->fsMops->Umount == NULL) {
- errno = ENOSYS;
- return FS_FAILURE;
- }
- return g_fs->fsMops->Umount(target);
- }
- int LOS_FsUmount2(const char *target, int flag)
- {
- if (g_fs == NULL) {
- errno = ENODEV;
- return FS_FAILURE;
- }
- if (g_fs->fsMops == NULL || g_fs->fsMops->Umount2 == NULL) {
- errno = ENOSYS;
- return FS_FAILURE;
- }
- return g_fs->fsMops->Umount2(target, flag);
- }
3.2 文件目錄操作
VFS封裝的文件目錄操作接口包含LOS_Open、LOS_Close、LOS_Read、LOS_Write、LOS_Opendir、LOS_Readdir、LOS_Closedir等等。對(duì)具體的文件類型的文件目錄操作接口進(jìn)行封裝,代碼比較簡(jiǎn)單,自行閱讀即可,部分代碼片段如下:
- ......
- int LOS_Unlink(const char *path)
- {
- if (g_fs == NULL) {
- errno = ENODEV;
- return FS_FAILURE;
- }
- if (g_fs->fsFops == NULL || g_fs->fsFops->Unlink == NULL) {
- errno = ENOSYS;
- return FS_FAILURE;
- }
- return g_fs->fsFops->Unlink(path);
- }
- int LOS_Fstat(int fd, struct stat *buf)
- {
- if (g_fs == NULL) {
- errno = ENODEV;
- return FS_FAILURE;
- }
- if (g_fs->fsFops == NULL || g_fs->fsFops->Fstat == NULL) {
- errno = ENOSYS;
- return FS_FAILURE;
- }
- return g_fs->fsFops->Fstat(fd, buf);
- }
- ......
- int LOS_Mkdir(const char *path, mode_t mode)
- {
- if (g_fs == NULL) {
- errno = ENODEV;
- return FS_FAILURE;
- }
- if (g_fs->fsFops == NULL || g_fs->fsFops->Mkdir == NULL) {
- errno = ENOSYS;
- return FS_FAILURE;
- }
- return g_fs->fsFops->Mkdir(path, mode);
- }
- DIR *LOS_Opendir(const char *dirName)
- {
- if (g_fs == NULL) {
- errno = ENODEV;
- return NULL;
- }
- if (g_fs->fsFops == NULL || g_fs->fsFops->Opendir == NULL) {
- errno = ENOSYS;
- return NULL;
- }
- return g_fs->fsFops->Opendir(dirName);
- }
- ......
3.3 隨機(jī)數(shù)文件
文件/dev/random可以用于產(chǎn)生隨機(jī)數(shù)。在開(kāi)啟宏LOSCFG_RANDOM_DEV時(shí),LiteOS-M支持隨機(jī)數(shù)文件。從⑴處可知隨機(jī)數(shù)依賴文件~/openharmony/base/security/huks/interfaces/innerkits/huks_lite/hks_client.h和hks_tmp_client.c,這些文件用來(lái)產(chǎn)生隨機(jī)數(shù)。⑵處定義的RANDOM_DEV_FD和RANDOM_DEV_PATH分別是隨機(jī)數(shù)文件的文件描述符和隨機(jī)數(shù)文件路徑。
- #ifdef LOSCFG_RANDOM_DEV
- ⑴ #include "hks_client.h"
- ⑵ #define RANDOM_DEV_FD CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS
- #define RANDOM_DEV_PATH "/dev/random"
- #endif
3.3.1 隨機(jī)LOS_Open和LOS_Close
該函數(shù)打開(kāi)一個(gè)文件,獲取文件描述符用于進(jìn)一步操作。⑴處表示對(duì)于隨機(jī)數(shù)文件,打開(kāi)的標(biāo)簽選項(xiàng)只能支持指定的這些,否則會(huì)返回錯(cuò)誤碼。⑵處獲取標(biāo)準(zhǔn)路徑,如果獲取失敗,返回錯(cuò)誤碼。⑶處比較獲取的標(biāo)準(zhǔn)路徑是否為RANDOM_DEV_PATH,在確認(rèn)是隨機(jī)數(shù)路徑時(shí),⑷處開(kāi)始判斷。如果訪問(wèn)模式為只讀,返回錯(cuò)誤,如果打開(kāi)選項(xiàng)標(biāo)簽是目錄,返回錯(cuò)誤。如果不是上述錯(cuò)誤情形,返回隨機(jī)數(shù)文件描述符。⑸處如果獲取的標(biāo)準(zhǔn)路徑為“/”或“/dev”,則根據(jù)不同的選項(xiàng),返回不同的錯(cuò)誤碼。
- int LOS_Open(const char *path, int oflag, ...)
- {
- #ifdef LOSCFG_RANDOM_DEV
- unsigned flags = O_RDONLY | O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_LARGEFILE | O_TRUNC | O_EXCL | O_DIRECTORY;
- ⑴ if ((unsigned)oflag & ~flags) {
- errno = EINVAL;
- return FS_FAILURE;
- }
- size_t pathLen = strlen(path) + 1;
- char *canonicalPath = (char *)malloc(pathLen);
- if (!canonicalPath) {
- errno = ENOMEM;
- return FS_FAILURE;
- }
- ⑵ if (GetCanonicalPath(NULL, path, canonicalPath, pathLen) == 0) {
- FREE_AND_SET_NULL(canonicalPath);
- errno = ENOMEM;
- return FS_FAILURE;
- }
- ⑶ if (strcmp(canonicalPath, RANDOM_DEV_PATH) == 0) {
- FREE_AND_SET_NULL(canonicalPath);
- ⑷ if ((O_ACCMODE & (unsigned)oflag) != O_RDONLY) {
- errno = EPERM;
- return FS_FAILURE;
- }
- if ((unsigned)oflag & O_DIRECTORY) {
- errno = ENOTDIR;
- return FS_FAILURE;
- }
- return RANDOM_DEV_FD;
- }
- ⑸ if (strcmp(canonicalPath, "/") == 0 || strcmp(canonicalPath, "/dev") == 0) {
- FREE_AND_SET_NULL(canonicalPath);
- if ((unsigned)oflag & O_DIRECTORY) {
- errno = EPERM;
- return FS_FAILURE;
- }
- errno = EISDIR;
- return FS_FAILURE;
- }
- FREE_AND_SET_NULL(canonicalPath);
- #endif
- ......
- }
對(duì)于隨機(jī)數(shù)文件,關(guān)閉時(shí),直接返回成功,不需要額外操作。代碼片段如下:
- int LOS_Close(int fd)
- {
- #ifdef LOSCFG_RANDOM_DEV
- if (fd == RANDOM_DEV_FD) {
- return FS_SUCCESS;
- }
- #endif
- ......
- }
3.3.2 隨機(jī)LOS_Read和LOS_Write
隨機(jī)數(shù)文件讀寫(xiě)使用LOS_Read和LOS_Write接口。讀取時(shí),⑴處先對(duì)傳入?yún)?shù)進(jìn)行校驗(yàn),如果讀取字節(jié)數(shù)為0,則返回0;如果讀取的緩存地址為空,返回-1;如果讀的字節(jié)大于1024,則使用1024。⑵處調(diào)用hks_generate_random()產(chǎn)生隨機(jī)數(shù)。由于隨機(jī)數(shù)文件是只讀的,如果嘗試寫(xiě)入會(huì)返回-1錯(cuò)誤碼。
- ssize_t LOS_Read(int fd, void *buf, size_t nbyte)
- {
- #ifdef LOSCFG_RANDOM_DEV
- if (fd == RANDOM_DEV_FD) {
- ⑴ if (nbyte == 0) {
- return FS_SUCCESS;
- }
- if (buf == NULL) {
- errno = EINVAL;
- return FS_FAILURE;
- }
- if (nbyte > 1024) { /* 1024, max random_size */
- nbyte = 1024; /* hks_generate_random: random_size must <= 1024 */
- }
- struct hks_blob key = {HKS_BLOB_TYPE_RAW, (uint8_t *)buf, nbyte};
- ⑵ if (hks_generate_random(&key) != 0) {
- errno = EIO;
- return FS_FAILURE;
- }
- return (ssize_t)nbyte;
- }
- #endif
- ......
- }
- ssize_t LOS_Write(int fd, const void *buf, size_t nbyte)
- {
- #ifdef LOSCFG_RANDOM_DEV
- if (fd == RANDOM_DEV_FD) {
- errno = EBADF; /* "/dev/random" is readonly */
- return FS_FAILURE;
- }
- #endif
- ......
- }
小結(jié)
本文介紹了VFS的結(jié)構(gòu)體和全局變量,分析了下VFS文件操作接口,對(duì)于隨機(jī)數(shù)文件也進(jìn)行了分析。
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)




























