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

42 KiB
Raw Blame History

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 HEAD76cfb553e0951b428f9d426933a10d1fe18d2a0e
  • 最终源码变更提交:67d3c057fbcad5adc765ab9927da511e3221ac8f
  • 当前 src/ treea605af0f01f83d67c199005e775e1a47a1610036
  • 67d3c057f 到本报告基线 HEADsrc/ tree 未再变化。

3.2 Linux 独立参考

  • 精简参考:/work/dev-src-linux/fs/erofs
  • 版本:Linux 7.1.0-rc1
  • 本地导入提交:8be2be573d2b191c83d760b65c554f78eb95893c
  • EROFS treeb79b9a8b62f633e887a8b99075ff9412fabd7f83
  • /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-1insmntque() 失败后继续访问已回收 vnode

  • 位置src/inode.c:452 调用 insmntque();失败分支在 src/inode.c:453-457 释放 en 后又于 src/inode.c:455vp->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-245erofs_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-1INT_MAXINT_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-768erofs_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-619z_erofs_validate_extent_table() 在首次初始化 时从 index=0en->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-68uint64_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 范围检查。
  • 建议测试:覆盖 -1INT64_MIN 邻近值、epoch 和正最大值,在 Linux/BSD 上比较 stat 结果与 errno。
  • 状态:静态候选已复核;格式语义仍需上游确认;未修复。
  • 位置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_INCOMPATsrc/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 建 consumersrc/data.c:250bread() 符合 FreeBSD 文件系统惯用路径,不应改为 GEOM class 私有读接口
vnode cache src/inode.c:435-460vfs_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_tintuiomove() 传参。
  • 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_decompressdeflate_decompresszstd_decompress,没有采用 Linux z_erofs_* 体系。
  • xattr.hinternal.herofs_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.cLinux 则分别 放在对应 backend。调用 ABI 必须因 FreeBSD 内核 API 而不同,但配置 loader 的 文件职责和 z_erofs_* 命名并无平台限制。

建议在 correctness 修复完成后,单独评估把配置解析移回各 backend,并统一内部 命名。该重排必须保持双配置构建和全部压缩 Markdown 测试,不应与 P1/P2 修复混在 同一提交。

8.4 死代码和残留抽象

  • src/xattr.h:11-42 保留注释掉的 Linux CONFIG_* 分支、未实现的 erofs_xattr_handlersstruct inodestruct posix_acl 声明。
  • src/internal.h:50-71 保留未使用的同步解压/cache 枚举和 erofs_buf src/internal.h:243-247erofs_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.ctests/test_decompress_standalone.c 已无法通过当前语法 检查,并仍按旧参数数量调用后端接口;当前接口见 src/internal.h:314-322

docs/architecture.md:242-245 已声明这两个根脚本不能作为验收入口,但只写文档 不足以消除可执行误入口。后续应删除、移入明确的 historical 目录,或改为立即失败 并指向 tests/TC*.md

8.6 文档陈旧

  • docs/architecture.md:79erofs_mount 示例与当前 src/internal.h 结构不一致,字段如 z_algorithmformatfragmentoff 已陈旧。
  • docs/architecture.md:273 声称“零拷贝”,但 src/data.c:244-261 明确执行 mallocmemcpy
  • Linux 基线只记录绝对工作区路径和版本,没有独立仓库可获取的上游 provenance。

8.7 style(9) 机械结果

使用 FreeBSD tools/build/checkstyle9.pl 0.31 对 src/*.[ch] 连续检查,结果稳定:

  • 18 个文件;
  • 48 warnings
  • 1 error
  • 44 个超过 80 列;
  • 4 个块注释格式告警;
  • 唯一 errorsrc/erofs_fs.h:265return 0 缺 FreeBSD 风格括号。

其他可见项包括 src/internal.h:11-12sys/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.czmap.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 个 MITdocs/erofs.5:1-23 自身包含 完整的 BSD 两条款文本。

但 repo22 根目录没有集中式 LICENSECOPYINGNOTICE、许可证构成表或来源 清单。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.hinternal.herofs_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 环境中复现、修复和回归。