Files
erofs-freebsd-out-tree/current/report-4/2026-08-10-task2-code-review-style-assessment.md
T
2026-08-13 10:44:59 +02:00

727 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# repo22 任务 2:全面代码审查与风格优化评估
日期:2026-08-10
性质:只读评估,不含源码修改、测试实现或 CI 变更
范围:原任务中的“2. 进行全面的 code review 和 style 优化”
## 1. 执行结论
本轮以第三方社区维护者视角,对 repo22 的 FreeBSD EROFS 实现进行了新的严格
静态审查。审查逐条回应原任务 2 的三个目标:
1. 判断实现是否合理复用 FreeBSD kernel 已有能力,是否存在不必要的重写,或
反过来存在不应强行引用内核实现的场景。
2. 判断 BSD 与 Linux 的行为差异是否被正确识别,避免把 Linux 共同缺陷误记为
BSD 移植差异,也避免为追求外观一致而破坏 FreeBSD VFS、GEOM 和 errno 契约。
3. 判断两个独立仓库在文件职责、函数和变量命名、定义顺序、磁盘 ABI、注释和
style 上是否已经消除非必要差异。
结论是:repo22 的 FreeBSD API 集成和核心算法映射已有良好基础,但任务 2 尚不
能认定为全面完成。本轮确认:
| 等级 | 数量 | 结论 |
| --- | ---: | --- |
| P0 | 0 | 未发现当前静态证据可确认的立即阻断问题 |
| P1 | 2 | vnode 生命周期 UAF;未压缩 physical run/chunk 大读的巨型分配与 `uiomove()` 长度截断 |
| P2 | 4 | 目录排序、根类型、compact 加法回绕、explicit extent 首次全表扫描 |
| P3 | 6 | errno、时间、symlink、48-bit 边界、setattr、FRAGMENTS 注释/死分支 |
这些新 finding 均未在本轮动态复现,也未修复。本报告给出的测试均是后续建议,
不是已执行结果。CI 明确不在本轮范围。
任务 1 已完成的 feature 验证和手工测试记录不在本报告中重写。静态审查发现的
新风险也不应被误读为已推翻全部既有功能证据;它们需要后续按优先级复现、修复
并回归。
## 2. 审计方法与限制
### 2.1 方法
本轮采用以下互相独立的证据层:
- **源码静态比对**:逐文件映射 repo22 与 Linux 7.1.0-rc1 `fs/erofs`,比较磁盘
ABI、核心算法、函数职责、函数顺序和命名。
- **FreeBSD 契约核对**:直接检查 FreeBSD 15.0-RELEASE-p9 中 vnode、VFS、GEOM、
`uio`、pager、ACL、extattr、XZ、ZSTD 和参考文件系统实现。
- **历史检查**:确认 repo22 最终源码提交、Linux 参考导入提交、tree hash、历史
测试入口和 UDF helper 的可追溯程度。
- **机械检查**:执行 FreeBSD `checkstyle9.pl`,检查文件树、共同编译单元顺序、
死声明、硬编码旧目录和许可证标识。
- **独立复核**:对高风险 vnode 生命周期、目录排序、根类型、只读 `setattr`
许可证措辞、UDF 来源措辞、旧测试入口和 style 统计进行了二次静态复核。
### 2.2 限制
- 本轮没有启动新的 FreeBSD 动态测试批次。
- 新 finding 尚未通过 fault injection、损坏镜像、并发卸载或大 I/O 实测。
- 性能风险只根据分配和扫描路径分析,未给出吞吐、延迟或内存峰值数据。
- 许可证部分只陈述仓库可观察事实,不作法律结论。
- Linux 与 FreeBSD 是独立仓库;本报告只要求维护者可识别的相似性,不要求共享
Git 历史、构建系统或平台抽象。
- `checkstyle9.pl` 自身标记部分规则为 experimental,因此输出是审查输入,不能
自动等同为每一项都必须修改。
## 3. 可复现基线
### 3.1 BSD 目标
- repo22 HEAD`76cfb553e0951b428f9d426933a10d1fe18d2a0e`
- 最终源码变更提交:`67d3c057fbcad5adc765ab9927da511e3221ac8f`
- 当前 `src/` tree`a605af0f01f83d67c199005e775e1a47a1610036`
-`67d3c057f` 到本报告基线 HEAD`src/` tree 未再变化。
### 3.2 Linux 独立参考
- 精简参考:`/work/dev-src-linux/fs/erofs`
- 版本:Linux 7.1.0-rc1
- 本地导入提交:`8be2be573d2b191c83d760b65c554f78eb95893c`
- EROFS tree`b79b9a8b62f633e887a8b99075ff9412fabd7f83`
- `/work/linux-src/fs/erofs` 与精简参考内容及 tree hash 相同。
该 Linux 树只作为独立源码参考,不要求与 repo22 共仓。当前 `/work` 开发环境内
可以用提交和 tree hash 验证其身份;只获得独立 repo22 的社区维护者,则无法从
现有 repo22 文档唯一重建该基线,详见第 10 节。
### 3.3 FreeBSD API 参考
- 路径:`/work/dev-freebsd-releng`
- 版本:FreeBSD 15.0-RELEASE-p9
- 重点契约:`insmntque()``uiomove()``vfs_read_dirent()`、GEOM VFS、vnode pager、
POSIX.1e ACL、extattr、XZ Embedded、ZSTDIO 和参考文件系统。
### 3.4 提交卫生
创建 report-4 前,repo22 scoped tracked、staged、untracked 状态均为 0,且
`HEAD == xdm/main`。本次提交范围限定为 `current/report-4/`,不会修改既有源码、
测试、文档、issues、report-1 至 report-3 或 `current/README.md`
## 4. 文件职责映射
### 4.1 共同或语义对应文件
| repo22 | Linux `fs/erofs` | 维护者视角结论 |
| --- | --- | --- |
| `src/erofs_fs.h` | `erofs_fs.h` | 磁盘 ABI 应最严格对齐;字段布局正确性优先于平台风格 |
| `src/internal.h` | `internal.h`, `compress.h` | 内存结构和接口语义对应,FreeBSD 类型必然不同 |
| `src/super.c` | `super.c` | superblock、设备表、mount、statfs、生命周期 |
| `src/inode.c` | `inode.c` | inode 解码;Linux inode ops 由 BSD vnops 分担 |
| `src/data.c` | `data.c` | block/chunk mapping 对应;folio/iomap 改为 buffer/GEOM/uio |
| `src/dir.c` | `dir.c` | 目录结构解析对应;cookie 和 dirent 输出是 BSD 适配 |
| `src/namei.c` | `namei.c` | 两级二分搜索语义高度对应;namecache/锁为 BSD 适配 |
| `src/xattr.c` | `xattr.c` | 磁盘 xattr 解析对应;namespace、ACL、extattr API 不同 |
| `src/xattr.h` | `xattr.h` | 只应保留 FreeBSD 实际接口,当前仍有 Linux 残片 |
| `src/decompressor.c` | `decompressor.c` | dispatcher 对应;配置职责仍有非必要差异 |
| `src/decompressor_lzma.c` | `decompressor_lzma.c` | 算法对应;FreeBSD 侧私有嵌入 XZ Embedded |
| `src/decompressor_deflate.c` | `decompressor_deflate.c` | 直接调用 FreeBSD zlib,方向正确 |
| `src/decompressor_zstd.c` | `decompressor_zstd.c` | 使用内核 ZSTDIO,方向正确 |
| `src/zmap.c` | `zmap.c` | 压缩映射核心最接近 Linux,函数顺序对齐质量高 |
| `src/zdata.c` | `zdata.c` | 职责对应;同步读取与 Linux folio/pcluster pipeline 必然不同 |
| `src/Makefile` | `Makefile`, `Kconfig` | 只比较编译单元职责和顺序,不比较构建语法 |
### 4.2 Linux-only 文件,不应为了外观模拟
| Linux-only 文件 | 不在 repo22 创建空文件的理由 |
| --- | --- |
| `sysfs.c` | FreeBSD 无 Linux sysfs 模型,mount/sysctl 语义需独立设计 |
| `fileio.c` | Linux file-backed I/O 路径与当前 GEOM provider 模型不同 |
| `fscache.c` | Linux fscache/netfs 子系统在 FreeBSD 无直接对应 |
| `ishare.c` | Linux inode/xattr sharing 生命周期依赖其 VFS 内部模型 |
| `zutil.c` | Linux folio、bio、pcluster 工具层不适用于同步 BSD 读取路径 |
| `decompressor_crypto.c` | Linux Crypto API 后端在当前 FreeBSD 目标中无对应承诺 |
| `compress.h` | Linux 压缩调度、folio 和 stream 数据结构不能机械复制 |
缺少这些文件是必要平台差异,不是结构欠缺。为了目录看起来相同而新增空壳,会
制造虚假维护入口。
### 4.3 BSD-only 文件
| BSD-only 文件 | 必要性结论 |
| --- | --- |
| `src/erofs_vnops.c` | 必要。集中实现 VOP、pager、ACL、NFS file handle 和只读语义 |
| `src/lz4.c` | 必要。FreeBSD/OpenZFS LZ4 接口不匹配 EROFS raw block 和 partial 输出 |
| `src/erofs_defs.h` | 文件本身可存在,但当前含部分仅定义未使用的宏,必要性需缩减 |
## 5. Findings First
以下只列入已经过独立静态复核、或任务指定为已复核的结论。每项明确其类别,
避免把 Linux 共同问题写成 BSD 行为差异。
### 5.1 P0
无。
### 5.2 P1-1`insmntque()` 失败后继续访问已回收 vnode
- **位置**`src/inode.c:452` 调用 `insmntque()`;失败分支在
`src/inode.c:453-457` 释放 `en` 后又于 `src/inode.c:455`
`vp->v_data = NULL`
- **FreeBSD 对照**`sys/kern/vfs_subr.c:2303-2315` 在插入失败时清空
`v_data`、切换 dead ops、执行 `vgone()``vput()`
`sys/kern/vfs_subr.c:2328-2341` 明确说明 `insmntque()` 会在失败时回收 vnode
只有 `insmntque1()` 把清理留给调用者。
- **参考实现**`sys/fs/cd9660/cd9660_vfsops.c:717-721` 失败后只释放私有
inode,不再解引用 vnode。
- **Linux 对照**Linux `fs/erofs/inode.c` 使用 `iget5_locked()` /
`iget_failed()` 管理不同的 inode 生命周期,不能机械复制,但同样要求失败路径
不访问已交还对象。
- **触发与影响**:vnode 构造与强制或并发卸载竞争,进入
`MNTK_UNMOUNT`/`MNTK_UNMOUNTF` 失败路径。后续写可能成为释放后访问,并可能
清空已经复用 vnode 的 `v_data`,属于内核内存安全风险。
- **类别**BSD 独有缺陷,源于 FreeBSD vnode 所有权契约。
- **修复方向**:采用 cd9660 所示所有权模式,失败后释放私有 `en`、清空输出并
返回,不再访问 `vp`。是否改用 `insmntque1()` 必须有明确的完整清理理由。
- **建议 Markdown 手工测试**:新增并发冷 lookup/getfh 与 `umount -f` 测试,
配合 vnode 分配压力;在 INVARIANTS/WITNESS 内核下记录 panic、WITNESS、引用
计数和清理状态。建议增加仅测试用故障注入以确定性触发插入失败。
- **状态**:已静态复核;未动态复现;未修复。
### 5.3 P1-2:一般未压缩 physical run/chunk 大读可触发巨型 `M_WAITOK` 分配和 `uiomove()` 长度截断
- **位置**`src/data.c:493-500` 直接从 `uio_resid` 和映射 run 计算 `want`
`src/data.c:514-518` 把该 `size_t` 长度传入读取路径;
`src/data.c:208-245``erofs_bread_device()` 使用
`malloc(len, M_EROFS, M_WAITOK)` 分配连续缓冲;`src/data.c:521` 再把 `size_t want`
传给 `uiomove()`
- **FreeBSD 对照**`sys/sys/uio.h:95` 声明
`int uiomove(void *cp, int n, struct uio *uio)`。因此大于等于 2 GiB 的 `size_t`
长度在传参时不能被该接口正确表达,可能截断为负值或零值并造成错误或无进展。
- **Linux 对照**Linux `fs/erofs/data.c` 使用 folio/iomap 分页路径,不建立与
整个用户请求同尺寸的连续内核缓冲。该差异来自 I/O 模型,但当前无界分配不是
FreeBSD 所要求的必要差异。
- **触发与影响**:恶意或异常大 chunk/run,配合大 `VOP_READ`/`uio_resid`。影响
包括用户可控的巨型 `M_WAITOK` 连续内核分配、系统内存压力、长时间阻塞,以及
大于等于 2 GiB 时 `uiomove()` 截断或循环无进展。
- **类别**:BSD 独有缺陷,属于 I/O 分块和内核 API 宽度处理问题。
- **修复方向**:把读取固定分块到同时满足 buffer cache、`INT_MAX` 和合理内存
上限的尺寸;每轮验证 `uio_resid` 或 offset 必须前进;避免按整个 run 分配。
- **建议 Markdown 手工测试**:构造超大 chunk/run 的稀疏镜像,分别测试
`INT_MAX-1``INT_MAX``INT_MAX+1` 和大于 2 GiB 请求;记录 wired memory、
malloc failure、进度、信号中断、errno 和 mount/KLD 清理。测试必须设置资源
上限,避免把宿主机作为压力目标。
- **状态**:已静态复核;未动态复现;未修复。
### 5.4 P2-1:目录名称排序不变量未验证
- **位置**`src/dir.c:72-109` 校验 dirent 数量、`nameoff` 单调、名称长度和非法
`/`,但不比较相邻名称;`src/namei.c:55-100` 执行块内二分,
`src/namei.c:103-125` 开始跨块二分选择。
- **Linux 对照**Linux `fs/erofs/erofs_fs.h:280` 记录目录项按字典序排列;
Linux `fs/erofs/namei.c:45` 起的查找同样依赖该不变量,也未做全局排序验证。
- **触发与影响**:结构合法但名称降序、重复或跨块逆序的损坏镜像。实际存在的
名称可被静默返回为 `ENOENT`,随后建立负 namecache,使错误持续存在。
- **类别**:Linux 共同依赖的格式不变量;在 repo22“损坏结构返回
`EINTEGRITY`、不得静默 miss”的更严格政策下,是 BSD 当前一致性缺口,不应
描述成 BSD/Linux 行为差异。
- **修复方向**:对不可变目录执行一次全局严格递增验证,覆盖块内、跨块和重复
名称,并缓存结果;损坏统一返回 `EINTEGRITY`,不得写入负缓存。
- **建议 Markdown 手工测试**:构造块内降序、块内重复、跨块逆序、跨块重复四类
镜像;冷 lookup、重复 lookup、readdir 和 NFS lookup 均应返回
`EINTEGRITY`,并验证 namecache 无错误负项。
- **状态**:已静态复核;未动态复现;未修复。
### 5.5 P2-2:根 vnode 未验证为目录
- **位置**`src/super.c:759-768``erofs_root()` 只调用 `erofs_vget()`
`src/inode.c:471-475` 按 inode mode 设置 vnode 类型,并仅按 NID 设置 `VV_ROOT`
- **Linux 对照**Linux `fs/erofs/super.c:765-774` 加载 root inode 后明确执行
`S_ISDIR()`,非目录则释放并返回 `-EINVAL`
- **FreeBSD 对照**VFS mount 后续不会替 EROFS 强制验证其 `VFS_ROOT()` 返回
`VDIR`,文件系统必须自行维持该不变量。
- **触发与影响**checksum-valid、但 root inode mode 被改为普通文件、symlink
或 FIFO 的镜像可能完成挂载;挂载点后续出现 `ENOTDIR` 或异常 vnode 语义。
- **类别**:BSD 独有缺口;Linux 已显式处理。
- **修复方向**`erofs_vget()` 成功后验证 `(*vpp)->v_type == VDIR`,失败时正确
`vput()`。errno 可对齐 Linux 使用 `EINVAL`,或按 repo22 损坏政策使用
`EINTEGRITY`,但需统一文档。
- **建议 Markdown 手工测试**:分别生成 root inode 为 VREG、VLNK、VFIFO 的
checksum-valid 镜像,验证挂载失败、errno 稳定、无 mount/md/KLD 残留。
- **状态**:已静态复核;未动态复现;未修复。
### 5.6 P2-3compact index 物理块号加法先发生 32-bit 回绕
- **位置**`src/zmap.c:267`
`m->pblk = le32dec(...) + nblk;``le32dec()``nblk` 均为 32-bit 范围,表达式
在赋给 64-bit `m->pblk` 前可能已经回绕。
- **Linux 对照**Linux `fs/erofs/zmap.c:230-232` 使用同样的
`le32_to_cpu(...) + nblk` 结构,因此 Linux 参考也存在相同风险。
- **触发与影响**:基准物理块接近 `UINT32_MAX` 且 compact 索引累计 `nblk` 非零,
映射到错误的低地址,可能读到错误数据或触发后续一致性错误。
- **类别**:Linux 共同缺陷,绝不能为了源码相似而保留,也不能记成 BSD 差异。
- **修复方向**:在加法前显式提升到 64-bit,并使用 checked addition;随后按设备
块范围验证。
- **建议 Markdown 手工测试**:构造 base pblk 接近 `UINT32_MAX`、累计跨越边界的
compact 索引,验证映射不会回绕;同时测试恰好不溢出的边界和真实设备范围错误。
- **状态**:已静态复核;未动态复现;未修复。
### 5.7 P2-4explicit extent 首次初始化执行有界 O(N) 全表扫描
- **位置**`src/zmap.c:581-619``z_erofs_validate_extent_table()` 在首次初始化
时从 `index=0``en->z_extents-1` 逐项读取并验证严格递增;调用后才进入
二分映射路径。
- **Linux 对照**Linux `fs/erofs/zmap.c:501-588` 映射 explicit extent 时按需
二分读取,没有 repo22 这一首次完整预扫描。
- **触发与影响**:超大 extent 表在首次访问时产生 O(N) 元数据 I/O 和延迟,攻击
者可用格式边界内的大计数制造明显可用性压力。
- **边界说明**:循环由 `en->z_extents` 严格限制,记录位置使用 checked arithmetic
并验证 `lstart` 严格递增。当前证据不支持“无限循环”或“无边界访问”的说法。
- **类别**:BSD 独有的性能/可用性权衡,源于更严格的全表不变量验证,不是立即
correctness 失败。
- **修复方向**:先量化真实最坏情况。可评估分段验证、验证结果缓存、mount-time
预算或按二分访问范围逐步验证;任何优化都不能重新引入静默错误映射。
- **建议 Markdown 手工测试**:生成多档 extent count 的合法/末尾损坏表,测量
首次与重复 lookup/read 延迟、I/O 次数和内存;验证预算超限时 errno 和清理。
- **状态**:已静态复核;未做性能原型;未修复。
### 5.8 P3-1DEFLATE 分配失败被统一映射为 `EIO`
- **位置**`src/decompressor.c:246-257` 将所有后端非零返回统一转换为 `EIO`
`src/decompressor_deflate.c` 的分配失败因而不能保留 `ENOMEM`
- **Linux/FreeBSD 对照**:平台均区分资源耗尽和输入/设备错误;调用 FreeBSD
zlib 是正确的,但 wrapper 的返回契约过窄。
- **影响**:内存压力下诊断和上层重试策略失真。
- **类别**BSD 独有 errno 映射问题。
- **修复方向**:后端返回正 errno,dispatcher 只做必要规范化。
- **建议测试**:故障注入 DEFLATE workspace/output 分配失败,要求 `ENOMEM`;损坏
流仍应为 `EIO` 或项目约定的完整性错误。
- **状态**:已静态复核;未动态复现;未修复。
### 5.9 P3-2:负 Unix 时间被拒绝,和 Linux 语义不一致
- **位置**`src/inode.c:60-68``uint64_t seconds` 接收时间,并拒绝大于
`INT64_MAX` 的值。
- **Linux 对照**Linux `fs/erofs/inode.c:108-109` 将磁盘 extended inode 的
64-bit 时间传给 inode timeLinux 时间模型允许 1970 年前的负时间语义。
- **影响**:使用补码表达负秒值的镜像在 BSD 被视为损坏,形成跨平台兼容差异。
- **类别**BSD/Linux 行为差异,格式签名语义需先与上游规范确认。
- **修复方向**:确认磁盘字段的正式有符号语义,再采用显式 signed 解码和 FreeBSD
`timespec` 范围检查。
- **建议测试**:覆盖 `-1``INT64_MIN` 邻近值、epoch 和正最大值,在 Linux/BSD
上比较 stat 结果与 errno。
- **状态**:静态候选已复核;格式语义仍需上游确认;未修复。
### 5.10 P3-3inline symlink 未拒绝声明长度内嵌 NUL
- **位置**`src/data.c:529-533` 的 readlink 直接复用普通读取路径;
`src/data.c:493-525` 按 inode 声明长度移动字节,没有对 symlink payload 做 NUL
一致性检查。
- **Linux 对照**Linux 同样主要依赖 inode size 和 VFS symlink 语义;本项不是
用于制造表面差异,而是要求 BSD 明确损坏镜像政策。
- **影响**:声明长度内部出现 NUL 时,不同调用者可能观察截断或不一致目标。
- **类别**:损坏输入处理缺口,是否需要拒绝应与 EROFS 格式语义统一。
- **修复方向**:确认格式是否禁止嵌入 NUL;若禁止,在 inode/readlink 验证中返回
`EINTEGRITY`,且不得影响合法非 NUL 目标。
- **建议测试**inline 和非 inline symlink 分别构造中间 NUL、末尾 NUL、无 NUL
目标,比较 `readlink(2)`、namei 跟随和 NFS 行为。
- **状态**:静态候选已复核;格式语义待确认;未修复。
### 5.11 P3-448-bit end-exclusive 检查拒绝最后一个合法完整块
- **位置**`src/zmap.c:895-898` 计算 `pend = m_pa + m_plen`,随后以
`(pend >> block_bits) >= (1ULL << 48)` 拒绝映射。
- **Linux 对照**Linux 参考中的相关 48-bit 映射边界也存在同类 end-exclusive
处理风险。
- **触发与影响**:映射恰好结束在 48-bit 地址空间上界时,`pend` 是合法的
end-exclusive 值,但当前检查把最后一个完整合法块拒绝为 `EINTEGRITY`
- **类别**:Linux 共同边界缺陷,不是 BSD 差异。
- **修复方向**:校验最后一个实际字节/块,或使用 `pend > limit` 的 byte limit
比较,并单独处理零长度。
- **建议测试**:覆盖最后合法块、越界一字节、越界一块和零长度映射。
- **状态**:已静态复核;未动态复现;未修复。
### 5.12 P3-5birthtime-only `VOP_SETATTR()` 可假成功
- **位置**`src/erofs_vnops.c:252-281` 检查 mode、uid、gid、atime、mtime、flags
和 size,但不检查 `va_birthtime`,最终可能返回 0。
- **FreeBSD 对照**`VATTR_NULL()` 会把 birthtime 设为 `VNOVAL`,内核调用者可以
构造仅 birthtime 有效的请求。
- **触发与影响**:直接内核 VOP 调用可收到成功但属性未改变,违反只读 VOP 语义。
- **路径限定**:独立复核未确认普通用户态或 NFSv4 可绕过只读 mount 检查到达该
单属性路径,因此不能声称已有用户/NFS 可利用的假成功链。
- **类别**:BSD 独有 VOP 契约和未来维护问题,严重度降为 P3。
- **修复方向**birthtime 变更返回 `EROFS`;对 type、nlink、fsid、fileid、
blocksize、rdev、bytes、generation 等结构字段明确返回 `EINVAL`
- **建议测试**:扩展内核 probe,直接提交 birthtime-only 和各结构字段请求;本地
syscall 与 NFS 测试用于确认它们仍被只读层拒绝。
- **状态**:已静态复核;无已证实用户/NFS 路径;未修复。
### 5.13 P3-6FRAGMENTS 特例分支不可达,注释与支持边界失配
- **位置**`src/erofs_fs.h:56-64` 已把 `EROFS_FEATURE_INCOMPAT_FRAGMENTS` 放入
`EROFS_ALL_SUPPORTED_INCOMPAT``src/super.c:532` 因而从 `unsupported` 中清除该
bit,但 `src/super.c:533-549` 又试图在 `unsupported != 0` 时特例允许仅
FRAGMENTS 的组合。
- **Linux 对照**Linux 通过实际 fragment/packed inode 路径表达支持,不需要
这种与 supported mask 自相矛盾的特例。
- **影响**:当前分支不可达,注释声称“窄允许”但真实代码已经把 FRAGMENTS 当作
一般 supported incompat bit,误导维护者判断支持边界。
- **类别**:BSD 独有死分支和文档一致性问题。
- **修复方向**:依据真实 feature 支持政策二选一:从 supported mask 移除后保留
严格特例,或删除死分支并准确文档化支持范围。
- **建议测试**:组合测试 FRAGMENTS、XATTR_PREFIXES、PLAIN_XATTR_PFX 和
`packed_nid`,记录 mount errno 和实际 fragment inode 访问结果。
- **状态**:已静态复核;未动态复现;未修复。
### 5.14 不列为 correctness findingfragment `pstart_hi`
曾有候选认为 fragment extent 构造会丢失 `pstart_hi`。当前静态证据不足以确认该
字段在 fragment 记录格式中具有同样的高位语义,也不足以证明当前路径实际丢失
可用地址。因此本报告不把它列为 correctness finding,只保留为“与上游格式语义
确认”的待办。后续必须先取得格式规范或上游实现证据,再决定是否测试或修改。
## 6. FreeBSD 最佳实践与原生能力复用
| 领域 | 当前做法 | 评估与决策 |
| --- | --- | --- |
| DEFLATE | `src/decompressor_deflate.c:28` 起调用 FreeBSD zlib | 正确复用;应保留,修正 errno 契约即可 |
| ZSTD | `src/decompressor_zstd.c` 使用 ZSTDIO API | 正确复用;FreeBSD 无通用独立 zstd KLD,不应伪造依赖 |
| MicroLZMA | `src/decompressor_lzma.c:14-38` 私有重命名并编译 XZ Embedded | 应保留本地实现;stock `xz.ko` 未定义 `XZ_DEC_MICROLZMA`,不能强行链接 |
| LZ4 | `src/lz4.c:21` 起实现 raw/partial decoder | 应保留;FreeBSD/OpenZFS 接口含不同 framing,且不满足 EROFS partial 语义 |
| 目录输出 | `src/dir.c:160-178` 自有 cookie helper | 不能机械替换;它额外验证 cookie 严格递增和容量短读 |
| `vfs_read_dirent()` | FreeBSD `sys/kern/vfs_subr.c:6814-6830` | 可原型化为输出层 helper,但必须保留 EROFS cookie 约束并回归 NFS |
| GEOM/VFS I/O | `src/super.c:226` 建 consumer`src/data.c:250``bread()` | 符合 FreeBSD 文件系统惯用路径,不应改为 GEOM class 私有读接口 |
| vnode cache | `src/inode.c:435-460``vfs_hash_get/insert` | 总体正确;仅 `insmntque()` 失败所有权需修复 |
| pager | `src/erofs_vnops.c:434` 复用 local pager | 正确,避免重写 VM pager |
| ACL/extattr | `src/erofs_vnops.c:68` 等调用 POSIX.1e ACL/extattr API | 正确保留 BSD namespace 和权限差异 |
| CRC32C | `src/super.c:307` 使用 `calculate_crc32c()` | 正确复用 FreeBSD kernel helper |
| endian | 磁盘字段使用 `leXXtoh`,非对齐流使用 `leXXdec` | 正确;不应直接复制 Linux unaligned 宏 |
| checked arithmetic | 多处使用 compiler overflow builtin | 合理;FreeBSD 15 无更优的通用非 LinuxKPI 替代 |
### 6.1 为什么不能强行依赖 `xz.ko`
FreeBSD `sys/modules/xz/Makefile:5-20` 会编译 `xz_dec_lzma2.c` 并导出符号,但没有
定义 `XZ_DEC_MICROLZMA`。MicroLZMA 入口受该宏控制,因此 stock `xz.ko` 不提供
repo22 所需的 `xz_dec_microlzma_*` ABI。简单添加 `MODULE_DEPEND(erofs, xz, ...)`
不能解决符号缺失。
只有在 FreeBSD 正式启用并导出 MicroLZMA KPI,同时更新模块 ABI/version 后,
repo22 才应重新评估直接依赖。当前强行引用反而违反“优先复用,但不强制引用一切”
的原任务要求。
### 6.2 LZMA per-call allocation 是性能原型事项
`src/decompressor_lzma.c:55-66` 每次解压分配并释放 decoder state。Linux 使用可
复用 stream pool。当前没有证据证明它造成 correctness 错误,但并发压缩读取会
产生约 30 KiB state 的反复 malloc/free 和锁竞争。
后续应单独做 per-CPU、mount-local 或受限 pool 原型,测量并发吞吐、内存峰值、
卸载清理和字典变化。没有数据前,不应把缓存重构混入 correctness 批次。
## 7. BSD/Linux 行为差异分类
### 7.1 必要的 BSD 差异
- 正 errno,而不是 Linux 内核负 errno。
- VOP/VFS operation table、vnode lock、namecache、`vn_vget_ino()`
`vfs_hash_*` 生命周期。
- GEOM consumer、设备 vnode 和 buffer cache I/O,而不是 folio/iomap/bio。
- FreeBSD extattr user/system namespace、`extattr_check_cred()` 和 POSIX.1e ACL。
- readdir 的 `a_cookies`/`a_ncookies` ABI、synthetic dot cookie 和 NFS 恢复语义。
- `fifo_specops`、FreeBSD FID 布局、generation 与 `ESTALE` 校验。
- pager 使用 `vnode_pager_local_getpages*`
这些差异应保留,不应为了外观接近 Linux 而移除。
### 7.2 Linux 共同缺陷
- compact index 32-bit 加法回绕,见 `src/zmap.c:267` 与 Linux
`fs/erofs/zmap.c:231`
- 48-bit end-exclusive 最后合法块边界。
- 目录二分依赖排序;Linux 也未全局验证。repo22 可选择更严格验证,但不能把
上游共同不变量说成 BSD 偏差。
源码相似不是保留共同缺陷的理由。修复时应尽量沿用上游命名和 checked arithmetic
结构,并把可上游化的发现单独记录。
### 7.3 BSD 独有缺陷
- `insmntque()` 失败后的 vnode 释放后访问。
- chunk 大读的整段 `M_WAITOK` 分配和 `size_t``int``uiomove()` 传参。
- root vnode 未检查 `VDIR`
- DEFLATE errno 归一化过度。
- birthtime-only `VOP_SETATTR()` 假成功。
- FRAGMENTS supported mask 与特例注释矛盾。
### 7.4 非必要维护差异
- `erofs_fs.h` 定义顺序、常量命名和字段注释没有尽量保持 Linux 顺序。
- 压缩配置 loader 集中于 `decompressor.c`,而 Linux 分布在各 backend。
- backend 函数名使用 `lzma_decompress``deflate_decompress`
`zstd_decompress`,没有采用 Linux `z_erofs_*` 体系。
- `xattr.h``internal.h``erofs_defs.h` 中仍有失效 Linux 声明或未使用抽象。
- xattr prefix helper 命名、部分函数定义位置和空包装与 Linux 无必要不同。
### 7.5 需要原型或格式确认后再判断
- fragment extent 的 `pstart_hi` 语义。
- LZMA decoder state 缓存。
- 保留 cookie 验证的 `vfs_read_dirent()` 输出适配器。
- explicit extent 全表验证的分段/缓存策略。
- negative timestamp 与 inline symlink NUL 的正式格式语义。
## 8. 结构、命名和 style 对齐
### 8.1 正向对齐结果
- repo22 与 Linux 有 15 个共同 C/H 文件名。
- `src/namei.c` 的核心名称查找函数名称和顺序高度对应 Linux。
- `src/zmap.c` 的 13 个共享核心算法函数保持相同相对顺序,是当前对齐质量最高的
文件之一。
- `src/Makefile` 中 12 个共同编译单元保持与 Linux Makefile 相同的相对顺序。
- `erofs_vnops.c` 集中 BSD VOP glue,避免把 FreeBSD 代码散入所有 Linux 对应
算法文件,这一文件级差异是必要且有利于维护的。
### 8.2 `erofs_fs.h` 的非必要差异
BSD 与 Linux 都包含同一组核心磁盘结构,但 repo22 的结构顺序、常量命名和注释
存在明显漂移。例如 repo22 从 `src/erofs_fs.h:104` 定义 superblock,而 Linux
先定义 deviceslotdirent、chunk index、map header 和 lcluster index 的位置也有
移动。机械审查对 19 个核心 struct/union 记录到 31/171 个顺序逆序对,顺序相似度
约 81.9%。
字段 packing、offset、endian 和 `_Static_assert` 是 correctness 约束,应保留 BSD
表达;结构定义顺序、字段语义注释和非平台相关名称则应尽量恢复 Linux,以降低
未来磁盘格式同步成本。`EROFS_ALL_SUPPORTED_INCOMPAT` 与 Linux 名称不同,也应
先判断是否存在真实语义差异,再决定是否保留。
### 8.3 压缩职责和命名
repo22 把 LZMA、DEFLATE、ZSTD 配置解析集中在 `src/decompressor.c`Linux 则分别
放在对应 backend。调用 ABI 必须因 FreeBSD 内核 API 而不同,但配置 loader 的
文件职责和 `z_erofs_*` 命名并无平台限制。
建议在 correctness 修复完成后,单独评估把配置解析移回各 backend,并统一内部
命名。该重排必须保持双配置构建和全部压缩 Markdown 测试,不应与 P1/P2 修复混在
同一提交。
### 8.4 死代码和残留抽象
- `src/xattr.h:11-42` 保留注释掉的 Linux `CONFIG_*` 分支、未实现的
`erofs_xattr_handlers``struct inode``struct posix_acl` 声明。
- `src/internal.h:50-71` 保留未使用的同步解压/cache 枚举和 `erofs_buf`
`src/internal.h:243-247``erofs_is_fileio_mode()` 固定返回 false。
- `src/erofs_defs.h:5-19` 的 CRC polynomial、inode slot bits 和 range-coder 常量
多项仅定义未使用;`src/inode.c:137-139` 仍直接使用字面量 5。
- `src/super.c` 的部分空包装和函数顺序可进一步与 Linux/FreeBSD 惯例收敛,但应
放在纯维护提交中。
### 8.5 失效测试入口和 harness
- `test_all_decompress.sh:2-7` 仍标记 repo19;其 `SRCDIR` 当前未实际使用,但测试
内容不验证 repo22 KLD。
- `test_chunk_based.sh:2-17` 实际进入 repo17,后续会 clean、写镜像、卸载模块、
配置 md 和 mount,具有真实误操作其他目录和全局系统状态的风险。
- `tests/test_decompress.c``tests/test_decompress_standalone.c` 已无法通过当前语法
检查,并仍按旧参数数量调用后端接口;当前接口见 `src/internal.h:314-322`
`docs/architecture.md:242-245` 已声明这两个根脚本不能作为验收入口,但只写文档
不足以消除可执行误入口。后续应删除、移入明确的 historical 目录,或改为立即失败
并指向 `tests/TC*.md`
### 8.6 文档陈旧
- `docs/architecture.md:79``erofs_mount` 示例与当前
`src/internal.h` 结构不一致,字段如 `z_algorithmformat``fragmentoff` 已陈旧。
- `docs/architecture.md:273` 声称“零拷贝”,但 `src/data.c:244-261` 明确执行
`malloc``memcpy`
- Linux 基线只记录绝对工作区路径和版本,没有独立仓库可获取的上游 provenance。
### 8.7 style(9) 机械结果
使用 FreeBSD `tools/build/checkstyle9.pl` 0.31 对 `src/*.[ch]` 连续检查,结果稳定:
- 18 个文件;
- 48 warnings
- 1 error
- 44 个超过 80 列;
- 4 个块注释格式告警;
- 唯一 error`src/erofs_fs.h:265``return 0` 缺 FreeBSD 风格括号。
其他可见项包括 `src/internal.h:11-12``sys/param.h // MUST FIRST` 实际位于
`sys/types.h` 后,SPDX 注释形式混用,以及部分 Makefile 空白不统一。
这些结果不能机械全改。磁盘 ABI 宏、静态断言和与 Linux 保持一致的行,可能有
理由超过 80 列。建议先制定“FreeBSD style(9) 优先、磁盘 ABI/上游同步可例外”的
仓库规则,再逐批清理。
## 9. 正向成果与保留项
本轮不能只列问题。以下成果符合原任务 2 的目标,应在后续优化中保留:
- 使用 `bsd.kmod.mk`,而不是维持 Linux Kbuild 兼容层。
- GEOM consumer、设备 vnode、`bread()` 和 buffer cache 的组合符合 FreeBSD 文件
系统路径。
- `vfs_hash_*`、vnode pager、ACL、extattr、CRC32C、endian helper 和 checked
arithmetic 的总体选型正确。
- DEFLATE 和 ZSTD 直接复用可用的 FreeBSD 内核实现。
- 不强行依赖缺少 MicroLZMA ABI 的 `xz.ko`,不强行套用不兼容的 OpenZFS LZ4。
- `namei.c``zmap.c` 核心函数顺序与 Linux 高度对应。
- Linux-only 文件没有以空壳方式复制到 BSD 仓库。
- FreeBSD-only VOP glue 集中在 `erofs_vnops.c`,平台边界清晰。
- 既有动态测试已覆盖多设备、压缩映射、xattr/ACL、NFS、48-bit 和边界行为;
本报告不重写这些 task 1 证据。
## 10. 可追溯性与许可证边界
### 10.1 仓库许可证材料
repo22 的 18 个生产 `src/*.[ch]` 文件均带 SPDX 标识:13 个
`GPL-2.0-only`、4 个 `BSD-2-Clause`、1 个 `MIT``docs/erofs.5:1-23` 自身包含
完整的 BSD 两条款文本。
但 repo22 根目录没有集中式 `LICENSE``COPYING``NOTICE`、许可证构成表或来源
清单。SPDX 可以识别文件当前声明,不能单独让第三方重建整个 KLD 的来源、整体
分发边界和权利链。
建议后续增加集中式许可证和来源 manifest,明确 GPL 派生核心、MIT 磁盘 ABI、
BSD VOP wrapper、本地 LZ4 和嵌入 XZ 源的关系。本报告不作兼容性或法律结论。
### 10.2 UDF helper 来源
`src/dir.c:158-178` 明确写有“modelled after UDF”。历史版本的 `erofs_uiodir`
与 FreeBSD `sys/fs/udf/udf_vnops.c:614-639` 在结构、分支顺序和控制流上高度接近;
当前版本已增加严格 cookie 单调性、结果枚举、容量检查和不同错误传播。
现有 Git 历史不足以区分最初是直接复制、紧密改写还是参考实现,因此不得断言
侵权或具体来源类别。应由作者确认来源过程,并据此决定是否补充 FreeBSD UDF 的
版权归属或说明。
### 10.3 Linux 基线外部可复现性
在当前 `/work` 单仓环境内,Linux 参考可由导入提交
`8be2be573d2b191c83d760b65c554f78eb95893c` 和 tree
`b79b9a8b62f633e887a8b99075ff9412fabd7f83` 验证。
`docs/architecture.md:160-163` 目前只记录本地绝对路径和 Linux 7.1-rc1 字符串,
没有官方上游 URL、上游 Git commit/tag、获取命令和校验值。因此内部审查可复现,
独立社区仓库的外部复现材料仍不完整。
## 11. 第三方维护者相似度
以下评分来自文件树、共同符号顺序、磁盘 ABI 顺序、FreeBSD API、代码卫生和文档
可复现性的综合人工/机械评估。它不是客观度量,也不代表 feature 完整度:
| 维度 | 暂评分 | 说明 |
| --- | ---: | --- |
| 文件树与职责 | 8.0/10 | 共同文件清晰,Linux-only/BSD-only 边界总体合理 |
| 核心算法命名和顺序 | 8.5/10 | namei/zmap 较好,压缩后端仍有差异 |
| 磁盘 ABI 头文件可同步性 | 6.0/10 | 布局重视正确性,但顺序、命名、注释漂移明显 |
| FreeBSD API 与构建集成 | 9.0/10 | GEOM、VFS、pager、ACL、压缩库选型总体正确 |
| 代码卫生与 style(9) | 5.5/10 | 死声明、旧入口、48 warnings/1 error |
| 社区文档与基线可复现性 | 5.0/10 | 内部可验证,独立仓库 provenance 不完整 |
| **综合维护者相似度** | **7.2/10** | 带权主观评估,用于维护排序,不是兼容性结论 |
综合分采用带权主观评估:文件树与职责 15%,核心算法命名和顺序 20%,磁盘 ABI
头文件可同步性 20%FreeBSD API 与构建集成 20%,代码卫生与 style(9) 15%,社区
文档与基线可复现性 10%。计算为
`8.0*0.15 + 8.5*0.20 + 6.0*0.20 + 9.0*0.20 + 5.5*0.15 + 5.0*0.10 = 7.225`
按一位小数四舍五入为 `7.2/10`。权重反映本任务更重视算法、ABI 和平台集成,仍不
应视为客观兼容性指标。
从第三方维护者视角,当前更准确的描述是:核心算法和主要 FreeBSD 适配已对齐,
但仍存在明确的 correctness 风险、结构漂移、代码卫生和可追溯性工作。
## 12. 后续执行路线
本报告只给出路线,不执行任何修改。
### 第一批:先处理两个 P1
1. 修复 `insmntque()` 失败后的 vnode UAF。
2. 对 chunk/physical read 建立严格分块上限,修复 `uiomove(int)` 宽度问题。
3. 为两个问题分别新增 Markdown 手工测试和独立 FreeBSD 动态报告。
该批次必须最小化变更面,不能混入文件重排或 style 清理。
### 第二批:P2 correctness 与边界测试
1. 目录全局严格排序验证与 namecache 负项检查。
2. root vnode `VDIR` 验证。
3. compact pblk 64-bit checked addition。
4. 量化 explicit extent 首次 O(N) 扫描,并决定预算/缓存策略。
每项一份或一组明确 Markdown 测试,复用既有 fixture helper,但不实现 CI。
### 第三批:P3 correctness、compatibility 与 semantic/style
1. correctness/API 契约:保留原 P3 严重度,分别修正 DEFLATE 分配失败 errno、
48-bit end-exclusive 最后合法完整块边界,以及 birthtime-only `VOP_SETATTR()`
的只读语义。
2. compatibility/格式语义:确认并实现 Linux 负时间戳兼容语义;明确 inline symlink
内嵌 NUL 的格式政策并补充拒绝或兼容测试。
3. semantic/style:删除或改写不可达的 FRAGMENTS 特例分支,使注释、supported mask
和真实支持边界一致。
六项均需各自的 Markdown 手工测试或同类成组测试,不得因进入后续批次而改变本报告
中的 P3 严重度。
### 第四批:低风险卫生、文档和 provenance
1. 删除或隔离 repo17/repo19 历史脚本和失配 C harness。
2. 清理 `xattr.h``internal.h``erofs_defs.h` 的死声明和死宏。
3. 修正 architecture 文档、零拷贝表述和 Linux 基线复现信息。
4. 增加 LICENSE/COPYING、许可证构成和来源 manifest。
5. 确认 UDF helper 和本地实现的作者归属。
6. 在规则明确后处理 style(9) 的确定性问题。
### 第五批:结构重排和压缩职责对齐
1. 恢复 `erofs_fs.h` 非平台相关定义顺序、注释和名称。
2. 评估把压缩配置 loader 移回各 backend。
3. 统一内部 `z_erofs_*` 命名和函数定义顺序。
4. 删除无行为包装,调整非必要函数位置差异。
该批次风险高于普通 style,必须用双配置构建、模块加载和全部相关 Markdown 测试
证明无行为变化。
### 第六批:性能原型
1. LZMA decoder state pool/cache。
2. explicit extent 验证缓存或分段策略。
3. `vfs_read_dirent()` 输出适配器。
原型必须提供基线、压力方法、内存峰值、卸载清理和退化行为;没有数据不合并。
## 13. 原任务三个目标的覆盖矩阵
| 原任务目标 | 已确认正向证据 | 本轮发现 | 尚未验证/后续 |
| --- | --- | --- | --- |
| 1. 最佳实践与 BSD 原生能力复用 | GEOM/bread、vfs_hash、pager、ACL/extattr、CRC/endian、DEFLATE/ZSTD 复用正确;LZ4/MicroLZMA 本地实现有技术理由 | `insmntque()` 契约违反;chunk 大分配;dir helper 需保留但可原型化输出层;LZMA per-call allocation | 动态复现 P1XZ KPI、dir adapter、LZMA pool 原型 |
| 2. 正确处理 BSD/Linux 行为差异 | 正 errno、VOP/VFS、GEOM、namecache、cookie、ACL namespace、FID/generation 等必要差异清晰 | root 类型、setattr、errno、负时间;compact/48-bit/排序属于 Linux 共同问题,未误记为 BSD 差异 | negative time、symlink NUL、fragment pstart_hi 格式语义确认 |
| 3. 第三方维护者相似性 | 15 个共同文件;namei/zmap 和 Makefile 顺序质量高;未伪造 Linux-only 空文件 | erofs_fs 顺序/注释、压缩职责/命名、死代码、旧脚本、陈旧文档、style、provenance、许可证清单 | 逐批结构重排、来源确认、外部可复现基线、回归验证 |
因此,三个目标都已有实质正向成果,但目标 3 明显未达到“没有任何非必要差异”
的完成标准;目标 1 和目标 2 也因两个 P1 和若干 P2/P3 finding 需要继续工作。
## 14. 与 report-3 的关系
`current/report-3` 是任务 1 收尾时的历史快照。它关于 161 项 feature 手工验证、
构建、清理和当时已知 issue 的证据继续有效,本报告不修改也不重写这些结论。
但 report-3 中“未发现仍需消除的非必要 Linux/FreeBSD 结构、命名或职责差异”是
一个超出当时证据范围的绝对表述。当时没有保存:
- 全部文件的函数、变量和定义顺序对照;
- FreeBSD 原生 API 复用清单;
- Linux-only/BSD-only 文件逐项必要性论证;
- 死代码、失效入口、style(9)、来源和许可证审计;
- 独立仓库可重建的 Linux 基线 provenance。
因此,对任务 2 应以本报告作更精确限定:
> repo22 的核心算法和主要 FreeBSD 适配已取得良好对齐,但尚不能认定全面完成
> code review 和 style 优化;仍存在两个 P1、四个 P2、六个 P3,以及明确的结构、
> 代码卫生、style 和可追溯性工作。
该限定不回写 report-3,以保持历史报告不可变和审计链清晰。
## 15. 最终状态
- 本轮只创建 `current/report-4/`
- 未修改 `src/``tests/``docs/``issues/`、既有 report 或 CI。
- 未声称任何 finding 已修复。
- 未把静态候选误写成动态复现结果。
- 新问题进入后续计划前,应先创建对应 Markdown 手工测试规格,再由独立执行者在
FreeBSD 15 环境中复现、修复和回归。