VFS(Virtual File System)是文件系統(tǒng)的虛擬層,它不是一個實際的文件系統(tǒng),而是一個異構(gòu)文件系統(tǒng)之上的軟件粘合層,為用戶提供統(tǒng)一的類 Unix 文件操作接口。由于不同類型的文件系統(tǒng)接口不統(tǒng)一,若系統(tǒng)中有多個文件系統(tǒng)類型,訪問不同的文件系統(tǒng)就需要使用不同的非標(biāo)準(zhǔn)接口。而通過在系統(tǒng)中添加 VFS 層,提供統(tǒng)一的抽象接口,屏蔽了底層異構(gòu)類型的文件系統(tǒng)的差異,使得訪問文件系統(tǒng)的系統(tǒng)調(diào)用不用關(guān)心底層的存儲介質(zhì)和文件系統(tǒng)類型,提高開發(fā)效率。本文先介紹下 VFS 的結(jié)構(gòu)體和全局變量,然后詳細分析下 VFS 文件操作接口。文中所涉及的源碼,均可以在開源站點 https://gitee.com/openharmony/kernel_liteos_m 獲取。
1、VFS 結(jié)構(gòu)體定義
在文件 componentsfsvfsfs_operations.h 中定義了 VFS 虛擬文件系統(tǒng)操作涉及的結(jié)構(gòu)體。⑴處的 struct MountOps 結(jié)構(gòu)體封裝了掛載相關(guān)的操作,包含掛載、卸載和文件系統(tǒng)統(tǒng)計操作。⑵處的 struct FsMap 結(jié)構(gòu)體映射文件系統(tǒng)類型及其對應(yīng)的掛載操作和文件系統(tǒng)操作,支持的文件類型包含 “fat” 和 “l(fā)ittlefs” 兩種,通過這個結(jié)構(gòu)體可以獲取對應(yīng)文件類型的掛載操作及文件系統(tǒng)操作接口。⑶處的 struct FileOps 封裝文件系統(tǒng)的操作接口,包含文件操作、目錄操作,統(tǒng)計等相應(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)部全局變量
在文件 componentsfsvfslos_fs.c 中有 2 個全局變量比較重要,⑴處定義的數(shù)組 g_fsmap 維護文件系統(tǒng)類型映射信息,數(shù)組大小為 2,支持 “fat” 和 “l(fā)ittlefs” 文件類型。⑵處的變量 g_fs 根據(jù)掛載的文件類型指向數(shù)組 g_fsmap 中的 FsMap 類型元素。⑶處的函數(shù) InitMountInfo () 會給數(shù)組 g_fsmap 進行初始化賦值。第 0 個元素維護的 “fat” 文件類型的文件系統(tǒng)映射信息,第 1 個元素維護的 “l(fā)ittlefs” 文件類型的文件系統(tǒng)映射信息。涉及到的掛載操作、文件系統(tǒng)操作變量 g_fatfsMnt、g_fatfsFops、g_lfsMnt、g_lfsFops 在對應(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)的接口,那些接口會調(diào)用 VFS 文件系統(tǒng)中操作接口。對每個接口的用途用法不再描述,快速記錄下各個操作接口。
3.1 掛載卸載操作?
掛載卸載操作包含 LOS_FsMount、LOS_FsUmount、LOS_FsUmount2 等 3 個操作。⑴處在掛載文件系統(tǒng)之前,需要初始化文件系統(tǒng)映射信息,只會操作一次。⑵處根據(jù)文件系統(tǒng)類型獲取對應(yīng)的文件類型映射信息。從這里,可以獲知,LiteOS-M 內(nèi)核只能同時支持一個文件系統(tǒng),不能只支持 fat 又支持 littlefs。⑶處對應(yīng)對應(yīng)的文件系統(tǒng)掛載接口實現(xiàn)掛載操作。其他兩個函數(shù)同樣比較簡單,自行閱讀代碼即可。
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 等等。對具體的文件類型的文件目錄操作接口進行封裝,代碼比較簡單,自行閱讀即可,部分代碼片段如下。
......
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 隨機數(shù)文件?
文件 /dev/random 可以用于產(chǎn)生隨機數(shù)。在開啟宏 LOSCFG_RANDOM_DEV 時,LiteOS-M 支持隨機數(shù)文件。從⑴處可知隨機數(shù)依賴文件~/openharmony/base/security/huks/interfaces/innerkits/huks_lite/hks_client.h 和 hks_tmp_client.c,這些文件用來產(chǎn)生隨機數(shù)。⑵處定義的 RANDOM_DEV_FD 和 RANDOM_DEV_PATH 分別是隨機數(shù)文件的文件描述符和隨機數(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 隨機 LOS_Open 和 LOS_Close
該函數(shù)打開一個文件,獲取文件描述符用于進一步操作。⑴處表示對于隨機數(shù)文件,打開的標(biāo)簽選項只能支持指定的這些,否則會返回錯誤碼。⑵處獲取標(biāo)準(zhǔn)路徑,如果獲取失敗,返回錯誤碼。⑶處比較獲取的標(biāo)準(zhǔn)路徑是否為 RANDOM_DEV_PATH,在確認(rèn)是隨機數(shù)路徑時,⑷處開始判斷。如果訪問模式為只讀,返回錯誤,如果打開選項標(biāo)簽是目錄,返回錯誤。如果不是上述錯誤情形,返回隨機數(shù)文件描述符。⑸處如果獲取的標(biāo)準(zhǔn)路徑為 “/” 或 “/dev”,則根據(jù)不同的選項,返回不同的錯誤碼。
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
......
}
對于隨機數(shù)文件,關(guān)閉時,直接返回成功,不需要額外操作。代碼片段如下:
int LOS_Close(int fd)
{
#ifdef LOSCFG_RANDOM_DEV
if (fd == RANDOM_DEV_FD) {
return FS_SUCCESS;
}
#endif
......
}
3.3.2 隨機 LOS_Read 和 LOS_Write
隨機數(shù)文件讀寫使用 LOS_Read 和 LOS_Write 接口。讀取時,⑴處先對傳入?yún)?shù)進行校驗,如果讀取字節(jié)數(shù)為 0,則返回 0;如果讀取的緩存地址為空,返回 - 1;如果讀的字節(jié)大于 1024,則使用 1024。⑵處調(diào)用 hks_generate_random () 產(chǎn)生隨機數(shù)。由于隨機數(shù)文件是只讀的,如果嘗試寫入會返回 - 1 錯誤碼。
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
......
}
審核編輯 黃宇
-
文件系統(tǒng)
+關(guān)注
關(guān)注
0文章
305瀏覽量
21033 -
源碼
+關(guān)注
關(guān)注
8文章
689瀏覽量
31449 -
vfs
+關(guān)注
關(guān)注
0文章
14瀏覽量
5544 -
鴻蒙
+關(guān)注
關(guān)注
60文章
3012瀏覽量
46149
發(fā)布評論請先 登錄
使用“tk(cbc(aes))”進行文件系統(tǒng)加密時出現(xiàn) iMX8MM CAAM 錯誤如何解決?
虛擬機數(shù)據(jù)恢復(fù)—虛擬機vmdk文件被刪除的數(shù)據(jù)恢復(fù)案例
開源鴻蒙技術(shù)大會2025丨虛擬化與容器分論壇:構(gòu)筑開源鴻蒙虛擬化技術(shù)高地
明晚8點|睿擎文件系統(tǒng)實戰(zhàn):從開發(fā)到發(fā)布全流程解析
【直播預(yù)告】下周三晚8點|睿擎文件系統(tǒng)實戰(zhàn):從開發(fā)到發(fā)布全流程解析
睿擎派文件系統(tǒng)指南:從開發(fā)到發(fā)布全流程實踐 | 技術(shù)解析
飛凌嵌入式ElfBoard-系統(tǒng)應(yīng)用編程之文件I/O
飛凌嵌入式ElfBoard-Vim編輯器之windows與ubuntu系統(tǒng)互傳文件
開源鴻蒙技術(shù)大會2025丨輕智能生態(tài)分論壇:共繪輕智能生態(tài)新藍圖
Linux三大主流文件系統(tǒng)解析
飛凌嵌入式ElfBoard ELF 1板卡-文件系統(tǒng)常用命令之磁盤管理與維護常用命令
飛凌嵌入式ElfBoard ELF 1板卡-busybox構(gòu)建根文件系統(tǒng)之文件系統(tǒng)驗證
飛凌嵌入式ElfBoard ELF 1板卡-文件系統(tǒng)簡介
服務(wù)器數(shù)據(jù)恢復(fù)—ocfs2文件系統(tǒng)被格式化為Ext4文件系統(tǒng)的數(shù)據(jù)恢復(fù)案例
Vsan數(shù)據(jù)恢復(fù)——Vsan分布式文件系統(tǒng)上虛擬機不可用的數(shù)據(jù)恢復(fù)
鴻蒙輕內(nèi)核源碼分析:虛擬文件系統(tǒng) VFS
評論