# ark_vfs **Repository Path**: xw19981010/ark_vfs ## Basic Information - **Project Name**: ark_vfs - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-22 - **Last Updated**: 2026-10-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ark_vfs **[简体中文]** | [English](README_en.md) 一个下午就能移植完的虚拟文件系统。 没有操作系统,没有 RTOS 头文件,没有堆,不用 `malloc`、`pthread`、`errno`。两个 `.c` 文件加三个头, 挂载点、路径和 fd 表在 Cortex-M 上、在 RISC-V 板子上、在你主机的单元测试里行为完全一致。 ```c #include "ark_vfs.h" #include "ark_vfs_ramfs.h" ark_vfs_init(NULL); /* 不需要任何钩子 */ ark_vfs_mount("ram", "/", ark_ramfs_fs(), NULL, 0, NULL); ark_vfs_write_all("/etc/greeting", "hello", 5); ark_vfs_read_all("/etc/greeting", buf, sizeof buf, &n); ``` 整个思路就这一句:中间是一个小、可预期的核心,所有碰真实硬件的东西全部推到两个接口后面。 ``` 应用 | open / read / write / stat / chmod / chown / readdir / rename / mkdir / unlink v ark_vfs 核心 ......... 挂载表、路径归一化、fd 表 (本仓库) | +--> fsdrv ....... 字节 -> 文件:ramfs、littlefs、FAT、devfs | +--> backend ..... 裸字节存储:NOR Flash、SD 卡、PC 上的一个文件 ``` ## 四条承诺 这是本库立下并守住的规则。哪个改动破坏了其中一条,那个改动就是错的。 1. **零依赖。** 只 include `` 和 ``。不用 libc 的字符串函数,连 `memcpy` 都不用—— `src/ark_vfs_priv.h` 里约 110 行本地实现。这里没有一处链接 libc。(编译器仍可能把逐字节拷贝循环 换成对**它自己**的 `memcpy` 的调用;那是工具链的运行时,不是你引入的依赖。见 [docs/architecture.md §10](docs/architecture.md#10-how-the-promises-are-checked)。) 2. **无堆。** 每张表都是静态定长数组。`ARK_VFS_MAX_MOUNTS` 个挂载点、`ARK_VFS_MAX_FDS` 个打开文件, 这就是上限。表满返回 `ARK_E_FULL`,不会自己长大,也不会在之后某处才失败。 3. **不隐藏阻塞。** 库里没有任何 sleep、等待或让出。如果两个线程可能同时碰 fd 表,通过 `ark_vfs_hooks_t` 交进一把锁;如果别的什么都不会在跑,传 `NULL`,加锁代码就一行都不进映像。 4. **文件系统是插件。** 核心永远不知道底下是什么存储。设备节点表(`/dev`)、只读 ROM 块、完整的 FAT 驱动,都只是 `ark_vfs_fsdrv_t` 的实现,核心一视同仁。 ## 目录布局 ``` include/ark_vfs.h 公开 API——类型、fsdrv/backend 契约、错误码 include/ark_vfs_config.h 上限;放到 include 路径更前面即可覆盖 include/ark_vfs_ramfs.h 自带的 RAM 文件系统 src/ark_vfs_core.c 挂载表、路径、fd 表、分发 src/ark_vfs_ramfs.c 一个文件里的完整文件系统(约 750 行) src/ark_vfs_priv.h 内部辅助(不用 libc) tests/ 主机测试:161 项,无框架、无 mock ports/ 现成移植(svcrtos / arkrtos 等) docs/ 内部实现、驱动作者指南、移植指南 ``` ## 文档 | 文档 | 给谁看 | |---|---| | [docs/architecture.md](docs/architecture.md) | 核心怎么工作:分层、静态表、路径模型、fd 表、加锁、错误、生命周期,以及它刻意不做什么 | | [docs/fsdrv.md](docs/fsdrv.md) | 怎么写一个文件系统:回调契约、错误规则、目录、后端,附一个完整的 `romfs` 实例 | | [docs/porting.md](docs/porting.md) | 怎么放到你的板子上:上限、后端、钩子、构建配方(GCC / AC6 / AC5 / IAR)、实测占用 | | [CHANGELOG.md](CHANGELOG.md) | 改了什么,以及版本为什么是 0.1.0 | ## 路径行为与 Linux 一致 `ark_vfs_normalize()` 折叠 `//`、`.`,并把 `..` 作用于它之前的部分,所以 `/a/b/../c` 真的就是 `/a/c`。 越过根目录的 `..` 会**如实报错** `ARK_E_LOOP`,而不是被折进 `/`:逃出自己根目录的路径是调用方的 bug, 悄悄"修好"它只会让错误的代码继续错下去。 挂载点查找是最长前缀优先,且带路径分量边界检查,所以 `/mnt/nor` 上的挂载会遮蔽它下面所有的 `/`, 而 `/mnt/norette` 不会被误判成落在 `/mnt/nor` 里。 ## 文件系统必须遵守的契约 交给 fsdrv 的每一个路径**都已经归一化,且相对它自己的挂载根**:没有前导 `/`,没有 `.` 或 `..`, 没有重复分隔符,挂载根本身是 `""`。 这就是契约的全部。也正因为如此,文件系统驱动永远不需要知道它被挂在哪、不用拼路径、不用解析任何东西—— 核心只做一次,且只在一个地方做。 支持哪个回调就实现哪个,其余的留 `NULL`——核心会把缺失的回调变成 `ARK_E_NOSYS`,而不是崩掉。 `src/ark_vfs_ramfs.c` 就是范例:在没有分配器的前提下,约 750 行实现了全部回调。 ## 移植:三步 1. **存储(可选)。** 填一个 `ark_vfs_backend_t`,给出 `read`、`write`、`erase` 和容量。如果你的文件系统 自带状态(RAM、ROM、一张设备表),这一步整个跳过。 2. **上限。** 把 `include/ark_vfs_config.h` 复制到 include 路径更前面,把表按你的板子定尺寸。没有别的要配。 3. **线程(可选)。** 如果中断或任务可能并发进来,提供 `ark_vfs_hooks_t.lock/unlock`;单上下文系统留 `NULL`。 然后把 `ark_vfs_core.c`(加上你用到的文件系统)加进构建。 这里没有 `#include "FreeRTOS.h"` 要满足,没有 config 结构体要填,除了"先调 `ark_vfs_init()`"之外没有初始化顺序。 ## 错误码 所有错误都是负数,且都在 `ark_vfs.h` 里有说明: | 错误码 | 含义 | |---|---| | `ARK_E_NOENT` | 没有挂载点覆盖这个路径,或文件不存在(`readdir` 的"没有更多条目"也是它) | | `ARK_E_INVAL` | NULL、空路径、错误的 seek | | `ARK_E_FULL` | fd 表、挂载表或文件系统存储已满 | | `ARK_E_BADF` | fd 未打开,或访问模式不允许这个调用 | | `ARK_E_ROFS` | 只读挂载 | | `ARK_E_LOOP` | 路径越过了自己的根 | | `ARK_E_NOSYS` | 这个文件系统没有实现该调用 | | ... | 完整清单见 `ark_vfs_error_name()` | 它们刻意**不是** `errno` 值:本库运行在 `errno` 不存在的地方,而且它绝不能在一个有 `errno` 的主机上改变含义。 需要 POSIX 错误号,就在移植层做映射。 ## 测试 ```sh sh tests/run_host_tests.sh # 核心 + ramfs:161 项 sh ports/svcrtos/tests/run_port_tests.sh # SVCrtOS 端口:70 项 sh ports/arkrtos/tests/run_port_tests.sh # Ark RTOS 端口:70 项 ``` 共 301 项,没有测试框架:在目标板上跑的代码,在同一份主机测试里跑。退出码就是失败项数,所以能直接接进 CI。 全部以 `-Wall -Wextra -Werror` 编译。 核心测试套件一个 mock 都没有。端口测试套件按定义必须伪造另一侧——它的 mock 实现的是**成文的**内核契约 (blk 调用要么全成要么全不成、bringup 幂等、设备按槽位索引),并且假芯片会拒绝非对齐擦除和写未擦除字节。 一个偷偷做取整或截断的移植能骗过宽松的假对象,但会在板上翻车。 ## 状态 版本 0.1.0。 | 部分 | 状态 | |---|---| | 核心、ramfs | 完成,161 项主机检查全绿 | | SVCrtOS 端口(`ports/svcrtos/`) | 作为端口已完成,70 项主机检查全绿;**并已在真机上运行**(见下) | | Ark RTOS 端口(`ports/arkrtos/`) | 作为端口已完成,70 项主机检查全绿(真机验证随 Ark RTOS 进行) | 端口既对着内核成文的契约验过,也在真机上验过。在 STM32F427(Cortex-M4,带一个 littlefs 下的 NOR 卷)上, 内核用本库的 ramfs 作 `/`、devfs 作 `/dev` 拉起命名空间,在 `/mnt/nor` 挂载一个掉电后仍在的 littlefs 卷, 列出并读回一个**掉电前写入**的文件,坏路径上也拿回了真实的 `ARK_E_*` 错误码(把目录当文件读、`..` 越过根、 设备拒绝该调用)。偏移、擦除对齐、错误映射、`/dev` 语义都已覆盖;**真并发没有**——锁钩子只验过嵌套配平, 没有验过两个任务在板上抢。 ## 许可证 MIT,见 `LICENSE`。