test code v1

This commit is contained in:
2026-08-13 10:48:49 +02:00
parent f3b1165f19
commit f6aeadb22d
300 changed files with 0 additions and 31306 deletions
View File
-90
View File
@@ -1,90 +0,0 @@
# EROFS for FreeBSD
## Build Kernel Module
Build on FreeBSD 15 amd64 with a matching FreeBSD source tree. The default
source path is `/usr/src`:
```sh
WITH_ZSTDIO=0 ./build.sh
```
Use `FREEBSD_SRC=/path/to/freebsd-src` when the source tree is elsewhere. The
script is a native FreeBSD wrapper around `src/Makefile` and `bsd.kmod.mk`; the
Makefile is the authoritative module name, source list, architecture gate, and
per-source flag definition. Each run rebuilds `build/obj` and writes
`build/erofs.ko`.
Only `MACHINE_ARCH=amd64` is currently qualified. Other architectures are
rejected explicitly instead of inheriting amd64 ABI flags. Build ZSTD support
with:
```sh
WITH_ZSTDIO=1 ./build.sh
```
The enabled module references the FreeBSD kernel ZSTD API and therefore
requires a running kernel built with `options ZSTDIO`. `WITH_ZSTDIO=0` builds a
module without those references and rejects ZSTD-compressed images at mount.
## Mount
EROFS is read-only. Mount a single-device image with:
```sh
mount -t erofs -o ro /dev/md0 /mnt/erofs
```
For an image with external blob devices, map every one-based on-disk device
slot explicitly with `device.<slot>=<provider>`:
```sh
mount -t erofs -o ro \
-o device.2=/dev/md92 \
-o device.1=/dev/md91 \
/dev/md90 /mnt/erofs
```
Slot names make the mapping independent of option order. Every external slot
must be present exactly once; assigning the same GEOM provider to multiple
slots is rejected. The same names and values may be passed directly as
`nmount(2)` iovec entries.
There is no repo-local `mount_erofs` binary; FreeBSD's generic `/sbin/mount`
frontend passes these distinct option names through to `nmount(2)`. The driver
forces every successful mount read-only, so even `-o rw` produces a read-only
mount rather than enabling writes.
If an image has a device table but no `device.<slot>` options are supplied,
the primary provider is treated as a Linux-compatible flatdev image. It must
contain the external ranges at their declared `uniaddr` block offsets, for
example a deterministic concatenation of the primary image and its blobs.
## Qualified Semantics
- Compact/extended metadata, Linux device-number decode, plain/inline/chunk
data, and LZ4/MicroLZMA/DEFLATE/ZSTD compressed reads.
- Inline tails are confined to the inode metadata block and declared image or
metabox backing bounds.
- Directory lookup/readdir share strict validation while accepting Linux-style
nonzero unused bytes after the final name NUL.
- Compressed `st_blocks` reflects the inode's on-disk compressed block count;
uncompressed and chunk files retain logical block rounding.
- FreeBSD 15 local vnode pager sync/async entry points are used for real mmap
faults.
- NFS export uses full 64-bit NIDs and a generation derived from the
superblock seed and inode metadata. Replacing metadata changes the generation
and makes old handles stale; a metadata-identical, payload-only replacement
is not guaranteed to return `ESTALE`.
See `docs/features.md` for the bounded feature claim and
`tests/results/manual/2026-08-09T1804Z-final-review-independent/manual-test-report.md`
for the final-review evidence.
## Test Fixtures
`tests/prepare_directory_fixtures.sh` creates the deterministic TC048/141/148
directory fixtures. `tests/prepare_error_fixtures.sh` and
`tests/erofs_fixture.py` create and self-check the structured TC002/086/087/102
and TC112-TC116/119 fixtures. Generated trees, images, overlays, and `build/`
outputs are test artifacts and are not committed.
-68
View File
@@ -1,68 +0,0 @@
#!/bin/sh
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
FREEBSD_SRC="${FREEBSD_SRC:-/usr/src}"
FS_SRC="${SCRIPT_DIR}/src"
BUILD_DIR="${SCRIPT_DIR}/build"
WORK_DIR="${BUILD_DIR}/obj"
MAKE="${MAKE:-make}"
WITH_ZSTDIO="${WITH_ZSTDIO:-0}"
NM_UNDEF=""
cleanup() {
[ -z "${NM_UNDEF}" ] || rm -f "${NM_UNDEF}"
}
trap cleanup EXIT
trap 'cleanup; exit 1' HUP INT TERM
fail() {
echo "ERROR: $*" >&2
exit 1
}
make_kmod() {
MAKEOBJDIR="${WORK_DIR}" "${MAKE}" -C "${FS_SRC}" \
SRCTOP="${FREEBSD_SRC}" SYSDIR="${FREEBSD_SRC}/sys" \
WITH_ZSTDIO="${WITH_ZSTDIO}" "$@"
}
[ "$(uname -s)" = "FreeBSD" ] ||
fail "build.sh requires a native FreeBSD build host"
[ -d "${FREEBSD_SRC}/sys" ] ||
fail "FreeBSD source tree not found: ${FREEBSD_SRC}"
[ -d "${FS_SRC}" ] || fail "Source directory not found: ${FS_SRC}"
command -v "${MAKE}" >/dev/null 2>&1 || fail "make is required"
command -v awk >/dev/null 2>&1 || fail "awk is required"
command -v nm >/dev/null 2>&1 || fail "nm is required"
[ -f "${FREEBSD_SRC}/sys/tools/vnode_if.awk" ] ||
fail "vnode_if.awk not found in ${FREEBSD_SRC}"
[ -f "${FREEBSD_SRC}/sys/kern/vnode_if.src" ] ||
fail "vnode_if.src not found in ${FREEBSD_SRC}"
case "${WITH_ZSTDIO}" in
0|1) ;;
*) fail "WITH_ZSTDIO must be 0 or 1" ;;
esac
rm -rf "${WORK_DIR}"
mkdir -p "${WORK_DIR}"
KMOD="$(make_kmod -V PROG)"
[ -n "${KMOD}" ] || fail "src/Makefile did not define a module output"
echo "==> Building repo22 ${KMOD}"
make_kmod all
[ -f "${WORK_DIR}/${KMOD}" ] || fail "module was not produced"
NM_UNDEF="$(mktemp "${WORK_DIR}/nm-undef.XXXXXX")" ||
fail "could not create nm output file"
if ! nm -u "${WORK_DIR}/${KMOD}" >"${NM_UNDEF}"; then
fail "nm failed while checking ${KMOD}"
fi
if ! awk '$NF == "bcmp" { found = 1 } END { exit found }' "${NM_UNDEF}"; then
fail "${KMOD} contains an unresolved bcmp reference"
fi
cp -f "${WORK_DIR}/${KMOD}" "${BUILD_DIR}/"
echo "==> SUCCESS: ${BUILD_DIR}/${KMOD}"
-13
View File
@@ -1,13 +0,0 @@
# repo22 当前状态报告索引
- [report-1](report-1/README.md):第一次总体进度报告,保留
`573aaab7ee0b47cb6aed7f76c52c68dd4041326b` 创建时的原始内容。
- [report-2](report-2/README.md):第二次总体进度报告,按
`381349b3847209a67d669264ac54a50a3599915c` 快照汇总正式批次、动态结果、
issue、风险和下一步。
- [report-3](report-3/README.md):第三次总体进度报告,以
`12351cbb0a593474f4fcef68ffc5218d9c99dd46` 为最终验证快照,汇总完成项、
160 PASS/1 PARTIAL、独立审查结论、剩余搁置项和后续可选计划。
后续总体状态报告统一使用 `current/report-N/` 目录。逐项动态证据位于
`tests/results/manual/`,问题触发、分析和解决状态位于 `issues/`
@@ -1,160 +0,0 @@
# repo22 当前总体进度
更新时间:2026-08-09 UTC
状态报告代码基线:`cd0e985b5ac54a4b7acb7042422329ad1729fb3e`
本报告记录的是上述基线上的阶段性状态。测试结果、问题状态和统计会随后续
提交继续更新,不能将本文视为 TC001-TC156 已完成全量回归的声明。
## 目标与执行范围
repo22 的目标是在 FreeBSD 15 amd64 上提供只读 EROFS 内核模块,并以可复现
fixture、真实 KLD、真实 mount/read/errno 和完整清理证据验证声明的功能边界。
当前执行约束如下:
- 只修改和提交 `repo-community/repo22`fixture、VM overlay、KLD 和原始日志仅
放在 `/work/build` 或 guest 临时目录。
- 不建设或使用 CI、runner、自动 PASS wrapper;每个 TC 必须逐份对照对应
Markdown 手工执行。
- host 侧 `mkfs.erofs``dump.erofs``fsck.erofs` 和静态源码审查只作为 fixture
或分析证据,不能替代 FreeBSD 内核动态结果。
- 每批测试形成独立 manual report,记录 commit、构建配置、KLD SHA256、guest
版本、命令、可观察输出或 errno、fixture hash 和清理状态。
- 每批完成后提交并 push 到 `xdm/main`;并行批次若使远端前进,则 fetch、rebase
自己的 test-only 提交后正常 push,禁止 force push。
## 已完成实现与审查
当前源码已覆盖 superblock、compact/extended inode、plain/inline/chunk 数据、
目录/namecache、xattr/ACL/metabox、LZ4/MicroLZMA/DEFLATE/ZSTD、fragment、
multi-device、NFS export、pager 和多项 fail-closed 边界检查。功能声明的准确边界
`docs/features.md` 为准。
主要 feature 批次包括:
- `29a215dd`POSIX ACL 与完整 xattr namespace 集成。
- `545d35bd`xattr、long prefix、shared xattr 和 metabox 边界加固。
- `96e22cc`cold nested namei 与目录结构校验修复。
- `4ef680df``78182686`:真实 multi-device、slot mapping、flatdev 和 extent
映射审查修复。
- `2354b448`:NFS export、稳定句柄和相关 vnode 路径。
- `c208bf1f`:压缩映射 P0 收口,包括多算法、partial reference 和 extent 路径。
- `9a3604fb`metadata/VFS 语义收口,包括 inline bounds、special `rdev`、pager、
namei、NFS generation 和真实压缩占用统计。
关键收尾与工程对齐提交:
| Commit | 已完成内容 |
|---|---|
| `c881f656` | 修复 48-bit superblock union、`OFF_MAX`、大 hole 内存和大目录索引等最终 metadata 边界;建立 TC150-TC153 与三份未决 issue。 |
| `4d51ca9e` | 停止跟踪 `build/obj`、生成头和架构 symlink,避免构建产物进入正式提交。 |
| `89594551` | 将 KLD 构建布局、源文件命名、架构门控和 `WITH_ZSTDIO` 方式与 FreeBSD/Linux 上游职责对齐。 |
| `2f2ac5a8` | 更新 README、architecture、refactoring plan/status,使构建、BSD API 差异和功能边界与源码一致。 |
| `077b37ab` | 删除会伪造覆盖或无条件 PASS 的旧 runner/wrapper,加入字段断言的 fixture transformer 与 FreeBSD probe,修订关键 TC 文档。 |
| `cd0e985b` | 完成最终 review findingper-inode NFS generation、显式 extent 物理地址溢出检查、compact/extended timestamp 校验,并直接动态执行 TC154-TC156。 |
上述工作也包含多轮 code review 和 style review:源文件职责、函数顺序和磁盘 ABI
尽量贴近 Linux EROFSFreeBSD vnode、pager、NFS、GEOM 和 `dev_t` 差异采用
FreeBSD 15 原生接口;磁盘长度、加法、移位和 signed/unsigned 转换按 fail-closed
原则处理。
## 已有动态验证
`tests/results/manual/` 中保留了各 feature 在对应历史 commit 上的真实 FreeBSD 15
动态证据。它们证明对应变更批次曾执行内核路径,但不能替代当前 final source 的
TC001-TC156 全量重跑。
| 批次 | 主要动态证据 | 当前解释 |
|---|---|---|
| 压缩 | compact/full LZ4、legacy full index、partial reference、MicroLZMA、DEFLATE、ZSTD、fragment、ztailpacking 和多种边界 source/hash compare | 历史 feature 证据存在;G5/G6 尚需在本轮 exact KLD 重跑。 |
| xattr/ACL/metabox | inline/shared/long-prefix xattr、ACL、metabox carrier 和 corruption errno | 历史 feature 证据存在;G4 尚未启动。 |
| directory/namei/pager | cold nested lookup、目录 padding、special `rdev`、真实 mmap/page fault 和 inline bounds | 历史 feature 证据存在;G1/G3 将按当前文档重跑。 |
| multi-device | 外部 provider、显式 slot、flatdev、unified address、fragment 和 orphan/error 路径 | 历史 feature 证据存在;G7 尚未启动。 |
| NFS | getfh/fhopen、同镜像重挂、同 md 替换、ESTALE、background/stress cleanup | 历史 feature 证据存在;G8 尚未启动。 |
| final review | TC150-TC152 动态 PASSTC153 明确 SHELVED | 结果来自 `c881f656` 前后的 final-review 批次,不是 156 项全量 PASS。 |
当前 final source `cd0e985b` 的直接动态证据是
`tests/results/manual/2026-08-09T0557Z-review-fixes/manual-test-report.md`
- FreeBSD 15.0-RELEASE-p8 amd64 上 `WITH_ZSTDIO=0` KLD SHA256 为
`d6e755c7a75dd5043b603d58215ae3e7c8f29318b31dc1885aa9bb070d6e57a5`
- `WITH_ZSTDIO=1` KLD SHA256 为
`b442f9f05af1d1a76801690c83c9a40ec6fc739adb0663e7dacaf837eeb2ecf3`
- TC154、TC155、TC156 均在该 final source 上直接动态 PASS,分别覆盖 per-inode
NFS generation、explicit extent `pa + plen` overflow 和 inode timestamp
validation。
- 该批结束时 EROFS mount、KLD 和 md provider 均已清理。
## 本轮八组全量回归
TC 编号范围为 TC001-TC156,共 156 项。以下集合经计数检查为总数 156、重复 0、
遗漏 0。
| 组 | 精确集合 | 数量 | 当前状态 |
|---|---|---:|---|
| G1 | TC001, TC007, TC009, TC011-TC014, TC019-TC040, TC147, TC150, TC151 | 32 | 已启动;正在修订文档和确定性 fixture,尚未形成 final-source 32 项结论。 |
| G2 | TC002, TC008, TC010, TC015-TC018, TC112-TC116, TC119 | 13 | 已启动;使用独立 worktree/VM,尚未形成 13 项结论。 |
| G3 | TC041-TC066, TC141, TC148, TC149, TC153 | 30 | 未启动。 |
| G4 | TC005, TC067-TC083, TC117, TC134-TC140, TC142 | 27 | 未启动。 |
| G5 | TC003, TC084-TC092 | 10 | 未启动。 |
| G6 | TC004, TC102-TC110, TC143-TC146, TC155 | 15 | 未启动。 |
| G7 | TC006, TC093-TC101, TC118 | 11 | 未启动。 |
| G8 | TC111, TC120-TC133, TC152, TC154, TC156 | 18 | 未启动。 |
G1/G2 的“已启动”只表示执行代理、工作目录和任务集合已经建立,不表示其中任何
TC 已在本轮计为 PASS。G8 中 TC154/TC156、G6 中 TC155 虽已有 `cd0e985b` 直接
证据,仍需由对应全量组在最终统计中明确引用或按组要求重跑,不能导致重复计数。
## 已完成与未完成
已完成:
- 核心 feature 实现、针对性 code review、FreeBSD 15 API 对齐和构建布局收口。
- 多个历史 feature 批次的真实 KLD 动态验证与清理记录。
- 当前 final source 的双配置构建和 TC154-TC156 直接动态验证。
- 移除旧 CI 风格 runner/wrapper,建立“逐 Markdown 手工执行”的验收规则。
- 三项已知验证缺口均有详细 issue,没有把未执行或环境受限结果标成 PASS。
未完成:
- TC001-TC156 在同一 final-source 基线族上的八组全量手工回归与最终汇总。
- G1/G2 的文档修订、fixture 固化、exact KLD 执行、逐 TC 报告和 push。
- G3-G8 的启动、独立环境分配、执行和结果提交。
- TC010 所需 16 TiB 级合格 provider、TC153 大目录内核路径、explicit extent
mapped compressed payload 的正向 fixture。
## Issue 索引
检查时 `issues/` 只有以下三份已跟踪文档,没有未提交 issue 文件或真实 issue
改动,因此本状态提交不修改 `issues/`
| Issue | 状态 | 影响 feature | Shelved 原因 |
|---|---|---|---|
| `issues/TC010-48bit-statfs-large-provider.md` | SHELVED - environment unavailable | 48-bit block count、primary provider size validation、`statfs(2)`/`df` 总量和 root/block union 选择 | 非零 `blocks_hi` 在 4 KiB block 下至少要求 16 TiB GEOM provider;当前 vnode md 流程没有已验证、可安全清理的合格 provider。小 provider 拒绝和 TC150 fallback 都不能替代正向结果。 |
| `issues/TC153-large-directory-block-index-validation.md` | SHELVED - kernel validation incomplete | 超大目录二分索引、cold lookup、`EINTEGRITY` 传播和 negative namecache 正确性 | 已有可复现 FLAT_PLAIN fixture,但尚未在 FreeBSD fixed/pre-fix KLD 上完成冷查找对照;host 生成和静态审查不足以计 PASS。 |
| `issues/extent-metadata-fixture-unavailable.md` | SHELVED - positive fixture unavailable | explicit compressed extent parser、4/8/16/32-byte record、物理/逻辑高位、binary search、partial reference/fragment 映射 | erofs-utils 1.8.6 和已试 1.9.3 均不生成真实 mapped explicit-record payload。TC152 hole 与 TC155 overflow 只覆盖部分 header/negative 分支,不能替代正向压缩数据读取。 |
## 风险与依赖
- 并行组共享远端 `xdm/main`,每批 push 前必须重新 fetch/rebase 并确认没有覆盖其他
组的 TC、helper、report 或 issue。
- FreeBSD VM、SSH 端口、qcow overlay、md unit 和 mount point 必须按组隔离;任何
guest 残留都会污染后续动态证据。
- erofs-utils 的格式生成能力限制 explicit extent 等正向 fixture,必要时只能使用
字段自检、CRC32C-aware 的 structured transformer,不能手工猜偏移。
- 48-bit 大 provider、memory pressure、NFS stress 和性能 TC 对环境容量及稳定性要求
较高,环境不足时必须使用 `SHELVED/ISSUE``ENVIRONMENT-UNAVAILABLE`,不能
降低判据。
- 历史报告跨多个 commit。最终统计必须只接受对应 exact KLD 的本轮动态结果,或
明确标注尚未重跑,禁止把旧 HEAD 结果直接升级为全量 PASS。
## 下一步
1. 完成 G1/G2 文档和 fixture 修订,构建各自 exact KLD,逐 TC 手工执行并提交报告。
2. 每个先完成的组 fetch/rebase 最新 `xdm/main`,严格 pathspec 提交并正常 push。
3. 启动 G3/G4,随后按环境和依赖启动 G5-G8;压缩、multi-device、NFS 使用隔离 VM。
4. 对任何 kernel behavior failure 立即停止 PASS 计数,新增或更新有事实依据的 issue。
5. 八组结束后汇总 156 项,检查总数、重复、遗漏、KLD/image/source hash 和全部清理
证据,再发布 final-source 全量结论。
-8
View File
@@ -1,8 +0,0 @@
# repo22 当前状态索引
- [2026-08-09 总体进度](2026-08-09-overall-progress.md):基线
`cd0e985b5ac54a4b7acb7042422329ad1729fb3e` 上的实现、历史动态证据、八组
全量回归分工、未决 issue、风险和下一步。
此目录记录阶段性状态;测试结果会随后续 commit 更新。正式逐项证据位于
`tests/results/manual/`,未决验证缺口位于 `issues/`
@@ -1,123 +0,0 @@
# repo22 第二次总体进度报告
更新时间:2026-08-09 UTC
权威统计快照:`381349b3847209a67d669264ac54a50a3599915c`
(该提交是当次 `xdm/main` 上完成 G3 报告后的快照)。
本报告只统计该快照已经提交的实现、正式批次和动态证据。TC060 源码修复与 G5
压缩回归正在并行进行,但在形成完整报告并提交前不提前计为 PASS,也不改变下述
冻结统计。
## 总体结论
- feature 实现、源码 code review、style review、FreeBSD 15 API 对齐和双配置构建
布局已经完成阶段性收口。
- 正式八组回归中,G1、G2、G3、G4 共执行 103 项:102 PASS1 项
`TC060 KERNEL-FAIL` 正在修复。
- final review-fix 阶段还直接动态执行了 TC154-TC156,三项均 PASS。它们是三项
额外且不与前述 103 项重叠的 TC,因此当前共有 106 个不同 TC 具备动态结果,
其中 105 PASS、1 项待修。
- TC154-TC156 已分配给 G8,但先行 PASS 不能重复计入“已完成 G8”,也不能把 G8
标成已执行完毕。G8 尚有 TC111、TC131-TC133 未执行。
- TC010 与 TC153 已通过真实大 sparse provider 的 FreeBSD 内核路径验证并转为
RESOLVED。explicit extent 的 mapped compressed payload 正向 fixture 仍未获得,
对应 issue 尚未解决。
## 实现与审查
repo22 已完成以下阶段性工程工作:
- 实现并审查 superblock、compact/extended inode、plain/inline/chunk 数据、目录与
namecache、xattr/ACL/metabox、多算法压缩、fragment、multi-device、NFS export、
vnode pager 和 fail-closed 元数据边界。
- 对照 Linux EROFS 的磁盘 ABI、职责拆分和命名以降低长期维护偏差,同时保持
FreeBSD vnode、GEOM、pager、NFS、ACL 和 `dev_t` 语义为行为权威。
- 完成 overflow、长度、偏移、移位、signed/unsigned、目录大索引、48-bit union、
NFS generation、special vnode 和 timestamp 等多轮独立 code review 修复。
- 清理被跟踪的构建产物,统一 FreeBSD KLD 源文件布局、amd64 门控和
`WITH_ZSTDIO=0/1` 构建方式;当前构建仍以 kernel `-Werror` 为门槛。
- 删除会替代人工判定或无条件给出 PASS 的旧 CI 风格 runner/wrapper,保留可复现
fixture transformer 与原生 syscall/probe 工具。当前任务不实现 CI。
## 正式八组状态
八组精确集合总计 156 项,彼此无重复、无遗漏。
| 组 | 精确集合 | 数量 | 快照状态 |
|---|---|---:|---|
| G1 | TC001, TC007, TC009, TC011-TC014, TC019-TC040, TC147, TC150, TC151 | 32 | 32 PASS,正式批次完成。 |
| G2 | TC002, TC008, TC010, TC015-TC018, TC112-TC116, TC119 | 13 | 13 PASS,正式批次完成。 |
| G3 | TC041-TC066, TC141, TC148, TC149, TC152, TC153 | 31 | 31 已执行;30 PASSTC060 KERNEL-FAIL。 |
| G4 | TC005, TC067-TC083, TC117, TC134-TC140, TC142 | 27 | 27 PASS,正式批次完成。 |
| G5 | TC003, TC004, TC084-TC092, TC102-TC110, TC143-TC146 | 24 | 压缩回归正在进行,尚无正式整组结论。 |
| G6 | TC006, TC093-TC101, TC118 | 11 | 尚未执行。 |
| G7 | TC120-TC130 | 11 | 尚未执行。 |
| G8 | TC111, TC131-TC133, TC154-TC156 | 7 | 正式整组未完成;TC154-TC156 已先行 PASSTC111、TC131-TC133 尚未执行。 |
正式批次证据:
- [G1 报告](../../tests/results/manual/2026-08-09T0710Z-g1/manual-test-report.md)
- [G2 报告](../../tests/results/manual/2026-08-09T0710Z-g2/manual-test-report.md)
- [G3 报告](../../tests/results/manual/2026-08-09T1059Z-g3/manual-test-report.md)
- [G4 报告](../../tests/results/manual/2026-08-09T0839Z-g4/manual-test-report.md)
- [review-fix 报告](../../tests/results/manual/2026-08-09T0557Z-review-fixes/manual-test-report.md)
## 统计口径
正式批次已执行数为 `32 + 13 + 31 + 27 = 103`PASS 数为
`32 + 13 + 30 + 27 = 102`,另有 TC060 一项 KERNEL-FAIL。
TC154-TC156 来自 review-fix 阶段的 exact-source FreeBSD 动态运行,并未包含在
G1-G4 的 103 项中。因此按“不同 TC 是否已有动态结果”计数,应增加三项,得到
106 个 TC 有动态结果、105 PASS、1 待修。按“正式组是否完成”计数时,三项仍属于
尚未完成的 G8,不能再次增加 PASS 数或宣称 G8 已完成。
## 大 Provider 验证
- TC010 使用逻辑长度 `17592193667072` 字节的 qualified sparse vnode provider
动态挂载了 `blocks_hi=1` 的 48-bit EROFS,并验证 `df` 的 64-bit 总量无截断。
issue 已 RESOLVED。
- TC153 使用逻辑长度 `8796093091840` 字节的 sparse GEOM provider,令超大
FLAT_PLAIN 目录进入 `2147483648` 最终块索引的真实 kernel lookup 路径;固定 KLD
在 cold lookup 和 readdir 后均返回 `EINTEGRITY`,没有错误缓存为 `ENOENT`
issue 已 RESOLVED。
- explicit extent 已动态覆盖 hole record、header 选择和部分负向分支,但仍缺少
mapped compressed payload 以及完整 record variant 的正向读取证据,不能关闭 issue。
## Issue 索引
| Issue | 当前状态 | 结论 |
|---|---|---|
| [TC060 standard pathconf](../../issues/TC060-pathconf-standard-values.md) | OPEN - KERNEL-FAIL | `_PC_NO_TRUNC``_PC_CHOWN_RESTRICTED` 返回 `EINVAL`;源码修复和复测正在进行。 |
| [TC010 48-bit statfs](../../issues/TC010-48bit-statfs-large-provider.md) | RESOLVED - qualified sparse vnode provider validated | qualified 16 TiB 级 sparse provider 已完成真实 mount/statfs/df 验证。 |
| [TC153 large directory index](../../issues/TC153-large-directory-block-index-validation.md) | RESOLVED - exact-source kernel validation passed | qualified 多 TiB sparse provider 已完成 exact-source 大索引 kernel lookup 验证。 |
| [Explicit extent fixture](../../issues/extent-metadata-fixture-unavailable.md) | SHELVED - positive fixture unavailable | issue 未解决;尚无使用 explicit-record parser 的 mapped compressed payload 正向 fixture。 |
## 当前风险
- TC060 是已确认的 FreeBSD kernel behavior failure,修复必须保持 NAME_MAX、PATH_MAX、
FILESIZEBITS、LINK_MAX、ACL 查询和未知键 errno 不回归,并完成 mount/md/KLD 清理。
- G5 同时覆盖 LZ4、MicroLZMA、DEFLATE、ZSTD、ztailpacking、partial reference、
explicit metadata 等多条压缩路径,fixture 资格和 `WITH_ZSTDIO` 配置差异仍是当前
最大执行风险。
- explicit extent 缺少上游工具可生成的 mapped positive fixture;静态 ABI 对照、
hole record 和 malformed case 不能替代真实压缩 payload 读取。
- G6 multi-device、G7 边界与压力类测试、G8 NFS/manual 页面仍需隔离 VM、精确 KLD、
direct syscall/数据比较和完整清理,历史 feature 报告不能自动升级为本轮结果。
- 并行 worktree 共享 `xdm/main`。每次 push 前必须 fetch/rebase,严格 pathspec,禁止
force pushguest mount、md、KLD 和临时 provider 残留必须清零。
## 下一步
1. 完成 TC060 根因修复,重跑全部 pathconf 键与基础 mount/read smoke,更新 issue
和 dated manual report。
2. 完成 G5 的 24 项压缩回归,提交 exact KLD、fixture hash、errno/数据比较与清理
证据。
3. 依次完成 G6 11 项、G7 11 项和 G8 剩余 TC111、TC131-TC133;在 G8 汇总中引用
或按要求复跑 TC154-TC156,但禁止重复计数。
4. 聚合 `TEST-COVERAGE-MATRIX`,审计 156 项总数、组间重复、遗漏、动态证据来源和
未解决 issue。
5. 完成最终独立 source review,检查 lock/resource/error path;执行
`WITH_ZSTDIO=0/1` 双配置 exact-source build,并做 `xdm/main`、remote/HEAD、
tracked clean 和 guest 清理的最终 audit。
-8
View File
@@ -1,8 +0,0 @@
# repo22 第二次状态报告索引
- [第二次总体进度报告](2026-08-09-overall-progress.md):以
`381349b3847209a67d669264ac54a50a3599915c` 为统计快照,汇总实现与审查、
八组回归、动态结果计数、issue、风险和后续工作。
本目录只记录阶段性总体状态。各 TC 的命令、hash、errno、dmesg 和清理证据以
`tests/results/manual/` 中的对应报告为准。
@@ -1,124 +0,0 @@
# repo22 第三次总体进度报告
更新时间:2026-08-09 UTC
权威验证快照:`12351cbb0a593474f4fcef68ffc5218d9c99dd46`
最终源码基线:`fcc85b93d5f8fd9671686bd68bf3b086c8bd25cf`
## 总体结论
本轮要求的 feature 全面验证、FreeBSD/Linux 行为差异审查、code review 和
style/可维护性优化已经完成。161 个可执行 Markdown 测试均已执行并形成受控
手工证据,最终为 160 PASS、1 PARTIAL、0 FAIL、0 KERNEL-FAIL、0 ENV。
唯一未完全关闭的是 TC146 的 explicit compressed extent mapped-payload 正向
fixture。该问题已按用户授权详细记录并搁置,不是已观察到的 kernel failure
也不影响其他 160 个 TC 的结论。本轮目标因此完成,剩余工作属于已批准搁置项
或后续可选增强。
CI 和自动化 kernel-test harness 不在本轮范围内,未实现也未宣称完成。
## 已完成内容
- 完成 TC001-TC161 的规格、执行报告和覆盖矩阵机械审计;TC000 只作为模板。
- G1-G8 的 156 个表格行覆盖 TC001-TC156,恰好一次,无重复、无遗漏。
- 完成 TC157-TC161 独立回归,覆盖 explicit extent 全局顺序、48-bit compressed
block count、special vnode 组合 setattr、dot-omitted `OFF_MAX` cookie 和 `nm`
失败传播。
- TC010 的 16 TiB-plus statfs、TC060 的 FreeBSD pathconf、TC153 的超大目录索引
均已动态验证并转为 RESOLVED。
- 完成 superblock、inode、data、directory/namecache、xattr/ACL/metabox、压缩、
multi-device、NFS、pager、错误路径和边界算术的多轮独立 review。
- 保留 Linux EROFS 的磁盘 ABI、命名、函数顺序和职责拆分作为维护参照,同时以
FreeBSD vnode、GEOM、pager、NFS、ACL、`dev_t` 和 errno 语义为行为权威。
- 修正 README 的 NFS stale 承诺:generation 来自 superblock seed 和 inode
metadatametadata-identical payload-only replacement 不保证 `ESTALE`
- 清理 repo22 内全部 ignored build/object/image/repro/Python bytecode 产物;最终
guest 的 EROFS mount、md、EROFS KLD 和 DTrace KLD 计数均为零。
## 最终验证统计
| 状态 | 数量 | 说明 |
| --- | ---: | --- |
| PASS | 160 | 具备对应 dated manual report 的 FreeBSD 15 手工证据 |
| PARTIAL | 1 | TC146HEAD2/interlaced PASSexplicit mapped payload 未完成 |
| FAIL / KERNEL-FAIL | 0 | 无开放 kernel 行为失败 |
| ENV | 0 | 无环境阻塞 |
| SHELVED test case | 0 | TC146 的子项不另计为 TC |
G1-G8 证据来自多个源码提交,不能表述为所有旧 TC 都运行在最终 KLD 上。最终
`fcc85b93d` 基线接受了 TC157-TC161、相关回归以及双配置 build/load/mount smoke。
## 最终构建
FreeBSD 15 kernel `-Werror` 双配置均成功:
| 配置 | KLD SHA256 |
| --- | --- |
| `WITH_ZSTDIO=0` | `15fda9d334132cd81769ce4dff4f8411a6e2b4cf7531c352530d0de85f42a2d2` |
| `WITH_ZSTDIO=1` | `23782dc0ce7da188807d35020bf2d8c6044b796c5c9a8746b398ba784cd6ad4e` |
TC161 的私有 `nm` shim 正确使构建失败且不发布新 KLD;移除 shim 后使用
`/usr/bin/nm` 的正常重建 exit 0。两个最终 KLD 均完成独立加载和只读挂载 smoke。
## Code Review 结论
最终独立复核未发现 P0/P1。已修复的最后一批问题包括:
- 16/32-byte explicit extent table 全局严格顺序验证;
- extended compressed inode 的完整 48-bit `blocks_hi` 解码;
- special vnode size 与其他 setattr 字段组合时的 false success
- dot-omitted 目录在 `OFF_MAX` 的 cookie/offset overflow
- `build.sh``nm` 自身失败的可靠传播;
- NFS generation 文档与真实实现边界不一致。
从社区第三方维护者视角,未发现仍需消除的非必要 Linux/FreeBSD 结构、命名或
职责差异。保留的差异均来自 FreeBSD kernel API、锁、provider、pager、NFS 或
权限模型要求。
## 未完成与搁置
唯一批准搁置项是
[explicit mapped extent payload](../../issues/extent-metadata-fixture-unavailable.md)
- erofs-utils 1.8.6 不能生成或独立验证该新格式;
- 已尝试工具能力扫描、`--max-extent-bytes`、structured conversion、ABI/control
flow 对照以及多类负向 fixture
- TC157 已证明 16/32-byte unordered table fail closed,但不能替代 mapped payload
的 source-identical 正向读取;
- 首个非零 `lstart` 和极大 extent-count 性能边界也保留为可选覆盖风险。
## 后续执行计划
1. 等待可生成并独立验证 explicit mapped-payload 的上游工具或可信 fixture。
2. 获得 fixture 后补充 4/8/16/32-byte 正向 record、partial reference、high words、
binary-search transition 和 final fragment 动态覆盖,使 TC146 从 PARTIAL 转 PASS。
3. 如后续项目另行启动 CI,再基于现有 Markdown 断言设计自动化;不得把当前手工
PASS 记录直接转换成无条件自动 PASS。
4. 后续源码变更继续执行受影响 TC、双配置 build、module smoke、scoped Git hygiene
和独立 review,不要求无差别重跑与改动无关的全部历史环境。
## 相关提交
正式八组手工验证:
- G1 `9ae22009f23a65320730072a780998e80aa9b728`
- G2 `aed7b68b79a5bd4115913f8361821c86fd58d2e2`
- G3 `381349b3847209a67d669264ac54a50a3599915c`
- G4 `f383bbbbff301a6bde18894f03ab88a8c0cc885a`
- G5 `c566d8ac6bf8e801082bbccf108451f6ee46ad40`
- G6 `f11fff5b8e8050e1017ed86f0bcf71042b2b45aa`
- G7 `f3ab2096fea3942317deb83fa051c84d3c124ec2`
- G8 `6f336d0a7387c8f110b19abad6e96f3ea17dba2e`
最终源码与验证收口:
- `f27b524a35b1c960be7e81884f9b928d9ac907e9`metadata boundary 修复。
- `67d3c057fbcad5adc765ab9927da511e3221ac8f`explicit extent header 解码修复。
- `fcc85b93d5f8fd9671686bd68bf3b086c8bd25cf`TC157-TC161 规格与 helper。
- `12351cbb0a593474f4fcef68ffc5218d9c99dd46`:最终手工报告、覆盖、issue 和 README 汇总。
逐项证据见
`tests/results/manual/2026-08-09T1804Z-final-review-independent/manual-test-report.md`
最终统计口径见 `docs/TEST_REPORT.md``tests/TEST-COVERAGE-MATRIX.md`
-9
View File
@@ -1,9 +0,0 @@
# repo22 第三次状态报告索引
- [第三次总体进度报告](2026-08-09-overall-progress.md):以
`12351cbb0a593474f4fcef68ffc5218d9c99dd46` 为最终验证快照,汇总已完成
feature 验证、code/style review、最终构建、剩余风险和后续计划。
本目录只记录本轮收尾状态。逐项命令、hash、errno 和清理证据以
`tests/results/manual/` 为准,issue 的触发、分析、尝试和验收条件以
`issues/` 为准。report-1 和 report-2 保留为历史快照,不因本报告改写。
@@ -1,726 +0,0 @@
# 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 环境中复现、修复和回归。
-41
View File
@@ -1,41 +0,0 @@
# report-4:任务 2 代码审查与风格评估
本目录记录 2026-08-10 对 repo22 原任务中“任务 2:进行全面的 code review
和 style 优化”的只读调查结果。
本次工作只做静态评估、源码契约核对、Linux/FreeBSD 严格比对、历史检查和
机械 style 检查,不修改生产源码、测试、既有文档、issues 或 CI。报告中的新
finding 尚未在 FreeBSD 虚拟机中动态复现,也尚未修复。
## 审计基线
- BSD 目标:`repo-community/repo22`,提交
`76cfb553e0951b428f9d426933a10d1fe18d2a0e`
- 最终源码变更提交:`67d3c057fbcad5adc765ab9927da511e3221ac8f`
- repo22 `src/` tree`a605af0f01f83d67c199005e775e1a47a1610036`
- Linux 独立参考:`/work/dev-src-linux/fs/erofs`Linux 7.1.0-rc1
- Linux 本地导入提交:`8be2be573d2b191c83d760b65c554f78eb95893c`
- Linux EROFS tree`b79b9a8b62f633e887a8b99075ff9412fabd7f83`
- FreeBSD API 参考:`/work/dev-freebsd-releng`15.0-RELEASE-p9
## 结论摘要
- 任务 1 的 161 项 feature 验证结论不在本报告中重写,也不因本报告自动失效。
- 任务 2 尚不能认定已经全面完成。本轮确认 `P0=0``P1=2``P2=4`
`P3=6`,另有若干非 correctness 的结构、style、来源与性能原型工作。
- FreeBSD 原生 API 和构建集成总体合理,强行复用并不适用于 LZ4、MicroLZMA
和目录 cookie helper。
- 核心算法映射质量较高,但磁盘 ABI 头文件顺序、压缩职责、死代码、历史测试
入口、文档、style(9) 和外部可复现性仍有明确维护债务。
- 综合维护者相似度暂评为 `7.2/10`。该分数仅用于排序维护工作,不是客观质量
或兼容性指标。
## 文档索引
- [任务 2 详细代码审查与风格评估](2026-08-10-task2-code-review-style-assessment.md)
## 与既有报告的关系
`current/report-1``current/report-3` 保持历史快照。本目录不修改它们。
report-3 的 feature 验证结果继续作为任务 1 证据;其中“没有非必要差异”的绝对
表述,由本次更完整的结构、style、来源和许可证审计作更精确的限定。
View File
View File
-97
View File
@@ -1,97 +0,0 @@
# Manual Test Evidence
## Final Status
repo22 has 161 executable manual test cases, TC001-TC161, plus the non-executable
TC000 template. The canonical dated reports produce:
| Status | Count |
| --- | ---: |
| PASS | 160 |
| PARTIAL | 1 |
| FAIL / KERNEL-FAIL | 0 |
| ENVIRONMENT-UNAVAILABLE | 0 |
| SHELVED test case | 0 |
TC146 is the single PARTIAL case. Its HEAD2 and interlaced paths pass, while
the explicit mapped-payload positive fixture remains unavailable. That
shelved subitem is not counted as a second test case.
## Evidence Policy
Authoritative evidence consists of the numbered Markdown procedures,
deterministic field-asserting fixture helpers, native syscall/kernel probes,
and dated `tests/results/manual/*/manual-test-report.md` reports. Reports record
the exact source, module or fixture hashes, FreeBSD kernel behavior, errno, and
cleanup state.
Host-only fixture generation, static ABI review, and userspace erofs-utils
output are supporting evidence. They do not replace a required FreeBSD kernel
result. The suite is manual-only; no CI pipeline or automated kernel-test
harness is implemented or claimed.
## Canonical Runs
The final status uses these non-overlapping report groups for TC001-TC156:
| Group | Exact scope | Result |
| --- | --- | --- |
| G1 | 32 IDs | 32 PASS |
| G2 | 13 IDs | 13 PASS |
| G3 | 31 IDs | 31 PASS after the dated TC060 fixed-source rerun |
| G4 | 27 IDs | 27 PASS |
| G5 | 24 IDs | 23 PASS, TC146 PARTIAL |
| G6 | 11 IDs | 11 PASS |
| G7 | 11 IDs | 11 PASS |
| G8 | 7 IDs | 7 PASS |
The G1-G8 tables contain exactly 156 rows and 156 unique IDs, with no missing
or duplicate TC from TC001-TC156. TC060's original G3 KERNEL-FAIL is historical
and is superseded by
`tests/results/manual/2026-08-09T1120Z-tc060-fix/manual-test-report.md`.
TC153 passed in the final G3 run and its issue is resolved.
TC157-TC161 are recorded in
`tests/results/manual/2026-08-09T1804Z-final-review-independent/manual-test-report.md`.
All five pass on exact source baseline
`fcc85b93d5f8fd9671686bd68bf3b086c8bd25cf`.
## TC111 Coverage Audit
A bounded audit of `tests/TC[0-9][0-9][0-9]-*.md` found 162 rows and 162 unique
IDs, TC000-TC161, with no missing or duplicate ID. Excluding TC000 leaves 161
executable specifications.
The audit also cross-checked the eight canonical group tables and the five
final-review rows. Final arithmetic is `155 PASS + 1 PARTIAL + 5 PASS`, or
160 PASS and one PARTIAL.
## Final Build Qualification
The final independent run rebuilt both configurations after the TC161 failing
`nm` shim and used `/usr/bin/nm` for the post-shim build:
| Configuration | KLD SHA256 |
| --- | --- |
| `WITH_ZSTDIO=0` | `15fda9d334132cd81769ce4dff4f8411a6e2b4cf7531c352530d0de85f42a2d2` |
| `WITH_ZSTDIO=1` | `23782dc0ce7da188807d35020bf2d8c6044b796c5c9a8746b398ba784cd6ad4e` |
Both KLDs loaded, mounted a qualified image read-only, unmounted, detached the
md provider, and unloaded. Final guest mount, md, EROFS KLD, and DTrace KLD
counts were zero.
G1-G8 evidence was collected across multiple source commits. It is not claimed
that all historical tests ran on the final KLD. The final code baseline was
independently exercised by TC157-TC161 and the affected dual-build,
module-load, and mount smoke.
## Issue Status
- TC010: RESOLVED by a real 16 TiB-plus sparse-provider statfs run.
- TC060: RESOLVED by the FreeBSD pathconf fix and exact-source rerun.
- TC153: RESOLVED by the multi-TiB Layout 0 directory lookup run.
- Explicit mapped payload: SHELVED as a detailed fixture/tooling limitation;
TC146 remains PARTIAL.
See `issues/README.md` for the current index and the individual files for
triggers, analysis, attempts, results, feature impact, and acceptance criteria.
-283
View File
@@ -1,283 +0,0 @@
# repo22 架构设计
## 设计目标
repo22 提供尽量贴近 Linux `fs/erofs` 职责划分的 FreeBSD 15
只读实现,同时对 FreeBSD vnode、GEOM、pager、`dev_t` 和 NFS FID 语义做
必要适配。
## 核心原则
1. **Linux 对齐**:函数命名、文件组织、代码排序尽可能匹配 Linux 版本
2. **最小抽象**:避免不必要的封装层
3. **安全优先**:保留 repo19 的所有安全修复
4. **可维护性**:便于社区维护和与上游同步
## 文件组织
```
src/
├── super.c - 超级块、挂载、VFS 集成
├── inode.c - inode 读取和 vnode 管理
├── data.c - 数据块映射和解压缩
├── namei.c - 路径查找(二分搜索)
├── dir.c - 目录遍历和输出
├── xattr.c - 扩展属性和 ACL
├── erofs_vnops.c - VFS vnode 操作实现
├── decompressor.c - 压缩配置解析和统一调度
├── lz4.c - FreeBSD 有界 LZ4 后端
├── decompressor_lzma.c - MicroLZMA 后端
├── decompressor_deflate.c - DEFLATE 后端
├── decompressor_zstd.c - 可选 ZSTDIO 后端
├── zmap.c - 压缩逻辑块映射
├── zdata.c - 压缩数据读取
├── internal.h - 内存结构和内部 API
├── erofs_fs.h - 磁盘格式定义
├── xattr.h - 扩展属性接口
└── erofs_defs.h - 常量定义
```
## 分层架构
```
┌─────────────────────────────────────┐
│ VFS 层 (FreeBSD kernel) │
└─────────────┬───────────────────────┘
┌─────────────▼───────────────────────┐
│ VFS 接口层 │
│ - erofs_vnops.c │
│ - super.c (mount/unmount/root) │
└─────────────┬───────────────────────┘
┌─────────────▼───────────────────────┐
│ 文件系统逻辑层 │
│ - inode.c (erofs_read_inode) │
│ - namei.c (erofs_namei) │
│ - dir.c (erofs_readdir_block) │
│ - xattr.c (erofs_getxattr) │
└─────────────┬───────────────────────┘
┌─────────────▼───────────────────────┐
│ 数据访问层 │
│ - data.c (erofs_map_blocks) │
│ - data.c (erofs_read_data) │
│ - zmap.c / zdata.c │
│ - decompressor.c (z_erofs_decompress) │
└─────────────┬───────────────────────┘
┌─────────────▼───────────────────────┐
│ 块 I/O 层 │
│ - erofs_bread/erofs_brelse │
└─────────────────────────────────────┘
```
## 核心数据结构
### erofs_mount (内存中的文件系统状态)
```c
struct erofs_mount {
struct mount *mnt; // FreeBSD mount 结构
struct vnode *devvp; // 块设备 vnode
struct g_consumer *cp; // GEOM consumer
uint32_t block_size; // 块大小
uint64_t root_nid; // 根目录 NID
uint32_t feature_compat; // 特性标志
uint32_t feature_incompat;
struct erofs_sb_lz4_info lz4; // LZ4 参数
struct erofs_deviceslot *devs; // 设备表
struct erofs_xattr_prefix_item *xattr_prefixes; // xattr 前缀表
};
```
### erofs_node (内存中的 inode)
```c
struct erofs_node {
struct vnode *vnode; // 关联的 vnode
uint64_t nid; // 节点 ID
uint64_t size; // 文件大小
uint8_t datalayout; // 数据布局类型
// 压缩相关
uint8_t z_algorithmformat;
uint8_t z_lclusterbits;
// Chunk-based 相关
uint16_t chunkformat;
uint8_t chunkbits;
// Fragment 相关
uint32_t fragmentoff;
bool fragment;
};
```
## 关键实现细节
### 1. 数据布局支持
支持 4 种数据布局:
- **FLAT_PLAIN**: 连续块
- **FLAT_INLINE**: 最后一个逻辑块位于 inode metadata block 内,且受声明
image/metabox bounds 约束
- **CHUNK_BASED**: 固定大小 chunk,支持稀疏文件
- **COMPRESSED**: 可变大小压缩 cluster
### 2. 压缩算法
支持 4 种压缩算法及未压缩 transform:
- LZ4 (LZ4HC)
- LZMA
- DEFLATE
- ZSTD
- 未压缩
### 3. 高级特性
-**ztailpacking**: 压缩文件尾部内联
-**fragments**: 已验证的 fragment-backed 压缩文件与 metabox carrier
- ⚠️ **dedupe**: 仅声明已验证的 fragment/partial-reference 形式,不宣称
覆盖所有未来编码
-**xattr_prefixes**: 共享 xattr 前缀表
-**device_table**: 多设备支持
-**metabox**: 每 inode 元数据盒
### 4. 安全机制
repo22 的边界策略:
- 所有指针操作前检查边界
- 所有算术运算检查溢出
- 所有分配检查大小合理性
- 递归深度限制
- 设备 ID 和块地址验证
## 与 Linux 版本的差异
### 对照基线
本轮维护逐文件对照工作区中的 `/work/dev-src-linux/fs/erofs`。该目录是导入的
Linux 7.1-rc1 EROFS 参考快照;对照不依赖 repo22 与 Linux 树具有共同 Git
历史。`/work/linux-src/fs/erofs` 中对应文件与该精简快照字节一致,但不是本轮
文件映射的依据。
### 必要差异(FreeBSD 适配)
1. **内存分配**:使用 `malloc(..., M_EROFS, ...)` 而非 `kmalloc()`
2. **块 I/O**:通过 GEOM consumer 和 FreeBSD vnode/buffer 接口读取 provider
3. **VFS 接口**`erofs_vnops.c` 实现 FreeBSD `vop_vector`,不照搬 Linux
`inode_operations`、folio 或 iomap 接口
4. **错误约定**:内核入口返回正的 FreeBSD errnoLinux 负 errno 或
`ERR_PTR` 仅作为算法对照,不能机械移植
5. **压缩后端**BSD 调度器跨编译单元调用 `internal.h` 中的简单后端 API
Linux 使用 `struct z_erofs_decompressor` 和不同的内存/页面生命周期
6. **LZ4 文件职责**BSD 保留独立 `lz4.c` 有界解码器;Linux LZ4 路径位于
`decompressor.c` 并依赖 Linux 内核 LZ4/page API
7. **平台特性**Linux `sysfs.c``fileio.c``fscache.c``ishare.c`
`zutil.c` 没有无条件对应物,不为文件外观引入空包装
8. **构建架构**:当前只验证 FreeBSD 15 amd64Makefile 明确拒绝其他
`MACHINE_ARCH`
### 保持一致的部分
- 静态目录 helper 使用 Linux 名称 `find_target_dirent`
- LZMA、DEFLATE、ZSTD 后端使用 Linux 文件名 `decompressor_*.c`
- Makefile 先列 metadata/VFS 文件,再列压缩调度、映射和后端文件
- 跨文件后端声明集中在 `internal.h`,不在调用方手写 `extern`
- `erofs_fs.h` 的磁盘格式定义和核心目录/映射算法按 Linux 语义核对
## 代码规范
### 命名约定
- 公共函数:`erofs_<module>_<action>`
- 静态函数:描述性名称,无固定前缀
- 宏:`EROFS_*` 全大写
- 结构体:`struct erofs_*`
### 函数排序(每个文件)
1. 辅助函数(static
2. 核心逻辑函数
3. VFS 接口函数
4. 模块注册/清理(仅 super.c)
### 错误处理
```c
int erofs_function(...)
{
int error = 0;
void *buf = NULL;
// 操作...
if () {
error = EINVAL;
goto fail;
}
// ...
return 0;
fail:
if (buf)
erofs_brelse(buf);
return error;
}
```
## 测试策略
### 单元测试
不存在受支持的用户态单元测试入口。旧的 `src/Makefile.test` 从错误目录引用
`test_decompress.c`,并尝试把内核解压源码按不匹配的用户态 ABI 链接;该入口已
删除,不能作为可运行测试或 feature 验证证据。
旧的 `test_super.c``test_inode.c` 只重复了测试文件中的公式,并未调用
内核生产解析路径,其中 inode harness 还引用过已删除的磁盘字段。它们已退役,
不得作为 feature 验证证据。superblock、inode、pager 和错误路径必须使用
`TC*.md` 中的确定性镜像,经 FreeBSD 内核模块实际挂载或访问验证。
### 集成测试
仓库根目录遗留的 `test_all_decompress.sh``test_chunk_based.sh` 包含其他
repo 的硬编码路径,不能作为 repo22 的测试入口或验收证据。受支持的验证方式是
直接执行 `tests/TC*.md` 中记录的 FreeBSD 15 内核步骤,并将命令、errno、哈希和
清理状态写入 `tests/results/manual/` 下的日期报告。
### VM 测试
- 挂载真实镜像
- 文件读取验证
- 性能基准测试
## 维护指南
### 同步上游 Linux 变更
1. 选定明确的 Linux `fs/erofs` 快照;当前工作区基线为
`/work/dev-src-linux/fs/erofs`
2. 逐文件识别修改,不假设两个实现共享提交历史
3. 检查是否为磁盘格式变更(`erofs_fs.h`)或 Linux 专属 VFS/page API
4. 只移植语义上适用的算法,并保留 FreeBSD errno、锁、GEOM 和 vnode 约定
5. 运行双配置构建、模块加载和真实镜像挂载测试
### 添加新特性
1.`erofs_fs.h` 添加磁盘格式定义
2.`internal.h` 添加内存结构
3. 实现解析逻辑(data.c/inode.c
4. 添加确定性 fixture 和对应的 `TC*.md` 内核测试
5. 更新文档
## 性能考虑
- **零拷贝**:直接从缓冲区缓存读取
- **延迟加载**:仅在需要时读取 inode 元数据
- **缓存友好**:利用 FreeBSD 的 vnode 缓存
- **批量操作**:目录读取一次性处理多个条目
## 已知限制
- 不支持写操作(只读文件系统)
- 不支持 FUSE 模式
- 构建和运行时验证目前仅覆盖 FreeBSD 15 amd64
- 不实现 Linux file-backed、fscache、page-cache sharing 或 sysfs 控制面
-65
View File
@@ -1,65 +0,0 @@
# repo-pre-2 Capability Record
## Status Vocabulary
This document separates inherited pre1 claims from fresh pre2 validation:
- `INHERITED CLAIM`: documented by the unchanged pre1 snapshot; not re-tested
for pre2.
- `STATIC GATE`: visible in tracked source or build declarations; runtime
behavior was not exercised.
- `NOT IMPLEMENTED`: explicitly excluded by inherited project documentation.
- `NOT CLAIMED`: inherited documentation deliberately limits the support
claim.
- `NOT RUN`: no build or runtime validation was performed for repo-pre-2.
`INHERITED CLAIM` is provenance, not a new PASS result. Existing reports under
`tests/results/manual/` describe earlier pre1 work and must not be cited as a
fresh repo-pre-2 execution.
## Snapshot Evidence
```text
repo-pre-1 source tree: 6a5a1a49d4a4e08f6ef1155de240500e63a15365
repo-pre-2 initial tree: 6a5a1a49d4a4e08f6ef1155de240500e63a15365
src-linux reference tree: b79b9a8b62f633e887a8b99075ff9412fabd7f83
runtime validation: NOT RUN
```
The inherited claims below are transcribed at category level from `README.md`
and `docs/features.md`. They are not an independent feature review.
## Capability Matrix
| Area | Inherited pre1 claim or static declaration | Pre2 status |
| --- | --- | --- |
| Mount and metadata | Read-only mount/unmount, `statfs`, superblock checksum and bounds, compact/extended inodes, 48-bit fields | INHERITED CLAIM; NOT RUN |
| Plain data | Flat plain, flat inline, tail bounds, and logical block accounting | INHERITED CLAIM; NOT RUN |
| Chunk and devices | Chunk indexes, holes, device tables, explicit devices, and flatdev mapping | INHERITED CLAIM; NOT RUN |
| Compressed data | LZ4, MicroLZMA, DEFLATE, full/compact indexes, partial references, HEAD2, ztailpacking, and qualified fragment forms | INHERITED CLAIM; NOT RUN |
| ZSTD | Source is always listed; `WITH_ZSTDIO=1` adds `ZSTDIO`, while the default is `0` | STATIC GATE; runtime NOT RUN |
| Directories and vnode operations | Lookup, readdir, cookies, namecache, access, readlink, pathconf, pager integration, and read-only mutation rejection | INHERITED CLAIM; NOT RUN |
| Xattrs and ACLs | Inline/shared namespaces, metabox storage, long/packed prefixes, and POSIX ACL reads | INHERITED CLAIM; NOT RUN |
| NFS export | 64-bit NIDs, generation handling, and malformed/stale handle validation | INHERITED CLAIM; NOT RUN |
| Build target | `src/Makefile` rejects architectures other than `amd64` | STATIC GATE; build NOT RUN |
| Write support | Writable filesystem operations | NOT IMPLEMENTED |
| `VOP_BMAP` | Explicitly unsupported in inherited documentation | NOT IMPLEMENTED |
| Complete future-format parity | All future incompat features and every dedupe encoding | NOT CLAIMED |
| Explicit compressed-extent positive payload | Inherited documentation records only partial coverage | NOT CLAIMED; NOT RUN |
| Performance guarantees | Manual observations are not a performance contract | NOT CLAIMED |
## Interpretation Rules
1. Source presence, feature-bit definitions, or build selection do not prove a
runtime capability.
2. A pre1 manual report does not become a pre2 PASS merely because the initial
trees match.
3. Phase 1 mechanical edits must not expand or reduce this capability matrix.
4. A future status change requires recorded evidence from an actually executed
validation step.
## Current Validation Declaration
No build, QEMU execution, test script, smoke test, mount, malformed-image
probe, or performance measurement was run for this initialization. The current
repo-pre-2 runtime validation result is therefore `NOT RUN` in every category.
-271
View File
@@ -1,271 +0,0 @@
.\" Copyright (c) 2026
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd August 9, 2026
.Dt EROFS 5
.Os
.Sh NAME
.Nm erofs
.Nd Enhanced Read-Only File System
.Sh SYNOPSIS
To mount an
.Nm
volume:
.Bd -literal -offset indent
mount -t erofs /dev/da0 /mnt
.Ed
.Sh DESCRIPTION
The
.Nm
driver provides read-only support for the Enhanced Read-Only File System
(EROFS), a modern compressed read-only filesystem designed for space
efficiency and performance.
EROFS is widely used in Linux distributions and mobile systems for root
filesystems, firmware images, and container layers.
.Pp
The
.Fx
implementation supports multiple compression algorithms, various data layouts,
extended attributes, and multi-device configurations.
.Pp
The currently qualified build target is
.Fx 15
on
.Sy amd64 .
The module Makefile rejects other architectures because they have not been
validated against this implementation's kernel ABI.
.Sh FEATURES
.Ss Inode Types
The
.Nm
driver supports both compact and extended inode formats:
.Bl -bullet -compact
.It
Compact inodes (32 bytes) for typical files
.It
Extended inodes (64 bytes) with extended metadata
.It
Special handling for single-link files
.It
Inline data (tailpacking) for small files
.El
.Ss Data Layouts
.Bl -bullet -compact
.It
.Sy FLAT_PLAIN :
Uncompressed contiguous data
.It
.Sy FLAT_INLINE :
Uncompressed data with inline tail
.It
.Sy Chunk-based :
Fixed-size chunks for multi-device support
.It
.Sy Compressed :
LZ4, DEFLATE, zstd, or LZMA compressed data with pcluster mapping
.El
.Ss Compression Algorithms
.Bl -tag -width "MicroLZMA"
.It Sy LZ4
Fast decompression for general-purpose use, including ztailpacking
(compressed tail in inode metadata)
.It Sy DEFLATE
Standard compression with good compression ratio
.It Sy zstd
High compression ratio when the module is built with
.Va WITH_ZSTDIO=1 .
ZSTD support is disabled by default
.Pq Va WITH_ZSTDIO=0 ,
and an enabled module requires a kernel built with
.Cd "options ZSTDIO" .
.It Sy LZMA/MicroLZMA
Maximum compression ratio for space-constrained environments
.El
.Ss Extended Attributes
.Bl -bullet -compact
.It
Shared extended attributes with metabox container support
.It
Inline extended attributes
.It
Long-prefix extended attribute support
.It
Packed prefix table support
.It
POSIX ACL support (access and default ACLs)
.El
.Pp
User namespace attributes are exposed without prefix; system namespace
attributes retain their full qualified names (trusted.*, security.*).
.Pp
Linux POSIX access/default ACL xattrs are decoded into
.Fx
POSIX.1e ACLs when present.
ACL and xattr mutation remains read-only.
.Ss Advanced Features
.Bl -bullet -compact
.It
Superblock CRC32C verification
.It
48-bit block count and root nid support
.It
Device table for multi-device volumes
.It
Qualified fragment-backed compressed files and metabox carriers
.It
VFS hash integration and namecache support
.It
Directory entry optimization (dot_omitted handling)
.It
NFS export with stable superblock-seeded per-inode file-handle generations
.It
.Fx
local vnode pager support for read-only mappings
.El
.Sh MOUNT OPTIONS
The
.Nm
filesystem supports standard read-only mount options and the following
filesystem-specific option:
.Bl -tag -width "device.N=/dev/mdN"
.It Cm device.N= Ns Pa path
Map one-based on-disk external device slot
.Ar N
to the disk provider at
.Ar path .
The slot number is part of the option name, so option order has no effect.
Every declared external slot must be supplied exactly once when external blob
providers are used.
Reusing the primary provider or one external provider for
multiple slots is rejected.
.El
.Pp
There is no filesystem-specific
.Pa /sbin/mount_erofs
utility in this repository.
Use the generic
.Xr mount 8
frontend with
.Fl t Cm erofs ;
it passes the filesystem-specific option names to
.Xr nmount 2 .
.Pp
If the image has a device table and no
.Cm device.N
options are supplied, the primary provider is treated as a flatdev image.
It
must contain every declared device range at its on-disk unified block address.
The driver forces every successful mount read-only.
An explicit
.Fl o Cm rw
request therefore still produces a read-only mount; it does not enable writes
and is not rejected solely because
.Cm rw
was requested.
Mutating vnode operations fail with
.Er EROFS .
.Sh EXAMPLES
Mount an EROFS image from a disk device:
.Bd -literal -offset indent
mount -t erofs -o ro /dev/da0s1 /mnt
.Ed
.Pp
Mount an EROFS image from a regular file using
.Xr mdconfig 8 :
.Bd -literal -offset indent
mdconfig -a -t vnode -f rootfs.img -u 0
mount -t erofs -o ro /dev/md0 /mnt
.Ed
.Pp
Mount a split image with two external blob slots.
The deliberately reversed
option order demonstrates that slot mapping is deterministic:
.Bd -literal -offset indent
mdconfig -a -t vnode -f primary.img -u 90
mdconfig -a -t vnode -f blob1.img -u 91
mdconfig -a -t vnode -f blob2.img -u 92
mount -t erofs -o ro -o device.2=/dev/md92 \
-o device.1=/dev/md91 /dev/md90 /mnt
.Ed
.Pp
Mount a flatdev image containing the primary image followed by all declared
unified device ranges:
.Bd -literal -offset indent
mdconfig -a -t vnode -f combined-flatdev.img -u 90
mount -t erofs -o ro /dev/md90 /mnt
.Ed
.Pp
Unmount an EROFS filesystem:
.Bd -literal -offset indent
umount /mnt
.Ed
.Sh DIAGNOSTICS
Error messages are logged via
.Xr printf 9
when filesystem inconsistencies are detected, such as:
.Bl -bullet -compact
.It
Invalid superblock magic number
.It
Superblock CRC32C checksum mismatch
.It
Unsupported compression algorithm
.It
Invalid inode format
.It
Invalid or unrepresentable inode timestamps
.It
Corrupted directory entries
.It
Compressed extent address arithmetic overflow
.It
Inline data crossing its inode metadata block or declared backing bounds
.It
Missing, short, or orphaned external providers
.It
Malformed or overlapping device-table ranges
.El
.Sh SEE ALSO
.Xr nmount 2 ,
.Xr mdconfig 8 ,
.Xr mount 8 ,
.Xr umount 8 ,
.Xr printf 9
.Sh HISTORY
EROFS was originally developed for Linux by Huawei in 2019.
The
.Fx
implementation first appeared in 2026.
.Sh AUTHORS
.An Ruicheng Pan
.Sh BUGS
.Bl -bullet -compact
.It
The driver is read-only and does not claim support for every future EROFS
incompat feature or every dedupe encoding.
.It
There is no automated kernel regression harness; the repository records
.Fx 15
manual-test procedures and results.
.El
-77
View File
@@ -1,77 +0,0 @@
# EROFS Feature Status
This list describes behavior implemented and manually qualified in the
FreeBSD 15 module. It is not a claim of complete Linux EROFS feature parity.
## Filesystem and Metadata
- Read-only mount/unmount and `statfs`.
- Superblock CRC32C, declared image/media bounds, 48-bit block/root-NID decode.
- Compact and extended inodes.
- Correct compact non-directory `I_NLINK_1` semantics and directory
`dot_omitted` semantics.
- Linux `new_decode_dev` major/minor decoding followed by FreeBSD `makedev()`.
- Superblock-seeded, inode-metadata-derived vnode/NFS generation synchronized
with `va_gen`; metadata-identical payload replacement is outside the stale
handle guarantee.
- NFS export, 64-bit NID file handles, stale-generation and malformed-FID
validation.
## Data Layouts
- `FLAT_PLAIN` and `FLAT_INLINE` reads.
- Inline tails constrained to the inode metadata block and declared backing
bounds.
- Chunk-based files, chunk indexes, holes, device tables, explicit devices,
and flatdev mapping.
- LZ4, MicroLZMA, DEFLATE, and ZSTD compressed reads.
- Full and compact indexes, partial references, HEAD2/interlaced records,
ztailpacking, and fragment-backed compressed data used by qualified images.
- Compressed `va_bytes`/`st_blocks` from the complete 48-bit on-disk compressed
block count; plain, inline, and chunk files retain logical block rounding.
## Vnode and Directory Operations
- `vget`, root lookup, `vfs_hash`, namecache, and `vn_vget_ino` integration.
- `lookup`, `readdir`, stable restart cookies, cold nested lookup, and
Linux-compatible nonzero final-name padding.
- Shared strict validation for dirent arrays, `nameoff`, block bounds, and
illegal names.
- `getattr`, access checks, `readlink`, `pathconf`, and read-only mutation
rejection, including combined special-vnode setattr requests.
- FreeBSD 15 `vnode_pager_local_getpages` and compatible async pager entry;
`VOP_BMAP` remains explicitly unsupported.
## Xattrs and ACLs
- Inline and shared `user.*`, `trusted.*`, and `security.*` attributes.
- Shared attributes in primary metadata and metabox containers.
- Long-prefix and packed-prefix-table lookup.
- FreeBSD user/system namespace exposure and POSIX access/default ACL reads.
- Read-only xattr/ACL mutation behavior.
## Documentation and Validation
- `erofs(5)` manual page.
- Markdown manual tests TC001-TC161, with unresolved cases tracked in
`issues/`.
- FreeBSD 15 manual reports with fixture hashes and cleanup evidence.
- Final explicit-extent ordering, 48-bit allocation, special setattr,
`OFF_MAX` directory-cookie, and build-tool failure checks.
## Build Qualification
- FreeBSD 15 `amd64` is the only currently validated and accepted target;
`src/Makefile` rejects other architectures.
- ZSTD support is disabled by default (`WITH_ZSTDIO=0`). Opt-in builds use
`WITH_ZSTDIO=1` and require a kernel built with `options ZSTDIO`.
## Not Implemented or Not Claimed
- Writable filesystem operations.
- A CI pipeline or automated kernel-test harness.
- Positive mapped-payload execution of the explicit compressed-extent format;
TC146 remains PARTIAL and the fixture limitation is tracked under `issues/`.
- Blanket support for every future EROFS incompat feature or every dedupe
encoding; only the explicitly qualified fragment/metabox forms are claimed.
- Performance guarantees from the manual throughput observations.
-65
View File
@@ -1,65 +0,0 @@
# repo-pre-2 Maintenance Baseline
## Snapshot Identity
`repo-pre-2` was created from the tracked `repo-pre-1` tree without copying
working-tree-only files or build artifacts.
```text
planning baseline commit: ed47f1b5583a229e372f488b9492d1f6234aae98
snapshot creation parent: 109fe74d3fcef107db5869be1d030f4f12d1cfa6
snapshot creation commit: 73fa57924b549387400eeeb85354bbbbe770a962
repo-pre-1 source tree: 6a5a1a49d4a4e08f6ef1155de240500e63a15365
repo-pre-2 snapshot tree: 6a5a1a49d4a4e08f6ef1155de240500e63a15365
Linux comparison tree: b79b9a8b62f633e887a8b99075ff9412fabd7f83
```
The matching source and snapshot tree IDs establish byte-for-byte content,
file-mode, and path equivalence at snapshot creation. `repo-pre-1` and
`src-linux` are read-only references for pre2 work.
## Phase Scope
Pre2 phase 1 is L0 mechanical maintenance alignment. It permits only the
separately reviewed changes listed below:
1. Remove stale Linux-only declaration fragments from `src/xattr.h` while
preserving every active FreeBSD interface.
2. Remove six specified, unreferenced private constants from
`src/erofs_defs.h`.
3. Rename the private checksum helper in `src/super.c` without changing its
signature, body, call position, or behavior.
Each source change must remain an independent commit. This phase does not add
features, fix behavior, move responsibilities between files, reorder data
structures, alter ABI, or broadly reformat code.
## Required FreeBSD Differences
Linux and FreeBSD EROFS remain independent implementations. The following
platform boundaries are intentional and must not be replaced merely to make
the source look more similar:
- FreeBSD VFS, vnode, mount, namecache, pager, and NFS export interfaces.
- FreeBSD buffer/cache, GEOM provider, device I/O, locking, and allocation
APIs.
- FreeBSD xattr, ACL, errno, kernel-module, and `bsd.kmod.mk` conventions.
- FreeBSD build gates, including the current amd64 restriction and optional
`ZSTDIO` integration.
Linux naming and layout should be followed only when doing so preserves these
native contracts and makes cross-repository review easier.
## Excluded Work
This phase does not address vnode ownership after `insmntque()` failure,
large-run allocation and `uiomove()` limits, compact pblk validation, 48-bit
boundaries, decompressor descriptors, backend ABI, typed errno, xattr parsing,
ACL behavior, file splitting, build-system alignment, CI, or test tooling.
## Validation Status
Only static snapshot and Git allowlist checks were performed for the
initialization commits. No build, QEMU run, test script, smoke test, mount, or
runtime feature check was performed. All runtime capabilities therefore remain
`NOT RUN` for repo-pre-2 until a later, explicitly authorized validation phase.
-83
View File
@@ -1,83 +0,0 @@
# repo-pre-3 Maintenance Baseline
## Snapshot Identity
`repo-pre-3` was created exclusively from the Git-tracked `repo-pre-2` tree.
Untracked files, ignored files, build outputs, and working-tree-only content
were not copied.
```text
snapshot source commit: 034f409841d2edaf7f140fc7475a357c924a5930
snapshot creation commit: 865a702341a82e61ab5ca1d5708a28005277cc0a
repo-pre-2 source tree: c28e015c74f4d012dfd070f887fabf75d4d4cb16
repo-pre-3 snapshot tree: c28e015c74f4d012dfd070f887fabf75d4d4cb16
tracked files per tree: 281
```
The matching tree IDs establish path, file-mode, and blob equivalence at
snapshot creation. `repo-pre-2`, `repo-pre-1`, and `src-linux` are read-only
references for pre3 work.
## Phase Scope
Pre3 is an L1 mechanical naming-alignment stage. It contains four independent
source tasks:
1. Rename `erofs_init_xattr_prefixes` and
`erofs_cleanup_xattr_prefixes` to the Linux-like lifecycle names
`erofs_xattr_prefixes_init` and `erofs_xattr_prefixes_cleanup`.
2. Rename the private ACL helper `erofs_getacl` to `erofs_get_acl`.
3. Rename `erofs_close_device` to `erofs_release_device_info` while retaining
its FreeBSD implementation.
4. Rename the private mount-state destructor `erofs_free_mount` to
`erofs_sb_free` while retaining its complete FreeBSD cleanup body.
Only function names and their closed sets of declarations, definitions, and
call sites may change. Signatures, behavior, control flow, ordering, locking,
ownership, error handling, and file responsibilities must remain unchanged.
## Required FreeBSD Differences
- Xattr-prefix helpers continue to use `struct erofs_mount *`, not Linux
`struct super_block *`.
- ACL handling continues to use FreeBSD `struct vnode *`, `struct acl *`, and
integer errno conventions.
- Device teardown continues to release GEOM consumers, vnode references, and
cdev references in the existing order and under the existing locking rules.
It must not adopt the Linux callback signature or ownership model.
- Mount-state teardown continues to release all FreeBSD GEOM and private
filesystem state. A Linux-like name does not make the implementations or
lifecycle contracts interchangeable.
## Excluded Work
Pre3 does not include feature changes, correctness fixes, ABI changes, codec
work, xattr or ACL behavior changes, function reordering, structure layout
changes, file splitting, build-system changes, formatting cleanup, or repairs
to earlier planning and snapshot material.
## Git Workflow
The snapshot, this baseline document, and each of the four rename batches are
separate commits. Every commit must be pushed immediately to `xdm main` and
must not be squashed with another batch. Before each commit, its staged paths
must be checked against the task-specific allowlist. After each push, local
`HEAD`, `xdm/main`, and remote `main` must identify the same commit.
## Validation Status
Only static snapshot-equivalence, path-allowlist, Git-state, and documentation
checks were performed for pre3 initialization.
```text
build: NOT RUN
QEMU: NOT RUN
test scripts: NOT RUN
smoke tests: NOT RUN
mount/runtime: NOT RUN
feature validation: NOT RUN
```
No compile-time or runtime PASS is claimed by this document. Existing reports
copied from `repo-pre-2` are historical material and are not fresh validation
of `repo-pre-3`.
-84
View File
@@ -1,84 +0,0 @@
# Pre3 Smoke Test Report - 2026-08-12
## Result
The `repo-pre-3` plain/LZ4 differential smoke test **PASSed** within the scope documented below.
| Field | Value |
|---|---|
| Run ID | `20260812T110037.575683Z-693361-ac21acee` |
| Process exit code | `0` |
| Guest return code | `0` |
| Runner stages | `12/12 passed` |
| Elapsed time | `18m51s` |
| DUT outer commit | `1a2361bfae2f6b4e7fc4c89d97d05f44553a5ba7` (`1a2361b`) |
| DUT path tree | `902a32fbfc1274d4404174346953d19e41c61f1f` |
| DUT content SHA256 | `07aa856f0c0d9d0d084dbcb5d743cf4831a7f2ab884b51941db7d1840b248c60` |
| KLD SHA256 | `59650f03029afc31658dcf3386105d001122d7702b8144e7c5925d9d9ed4accb` |
| Base image SHA256 | `67f359621f23a1d745f0889370cbb99a096cee3e99a0b2f3bb18fc7a91bf6fef` |
| Base image mode | `0444`, unchanged before and after |
Primary evidence:
- [result.json](/work/tests-dev/erofsstress/demo-result/repo-pre-3-smoke-20260812/20260812T110037.575683Z-693361-ac21acee/result.json)
- [summary.txt](/work/tests-dev/erofsstress/demo-result/repo-pre-3-smoke-20260812/20260812T110037.575683Z-693361-ac21acee/summary.txt)
- [guest evidence directory](/work/tests-dev/erofsstress/demo-result/repo-pre-3-smoke-20260812/20260812T110037.575683Z-693361-ac21acee/guest-evidence)
- [guest-result.json](/work/tests-dev/erofsstress/demo-result/repo-pre-3-smoke-20260812/20260812T110037.575683Z-693361-ac21acee/guest-evidence/guest-result.json)
## Invocation
```sh
./test.sh --dut /work/erofs-freebsd-pre/repo-pre-3 \
--base-image /work/tests-dev/lfs/freebsd-15-dev-src-20260811.bp \
--duration 10 \
--output demo-result/repo-pre-3-smoke-20260812
```
Evidence root:
```text
/work/tests-dev/erofsstress/demo-result/repo-pre-3-smoke-20260812/20260812T110037.575683Z-693361-ac21acee/
```
## PASS Scope
The successful run covered:
- FreeBSD guest boot and SSH readiness.
- DUT build with `WITH_ZSTDIO=0` and loading the exact generated `erofs.ko`.
- Creation and use of plain and LZ4 EROFS fixtures.
- Attach, mount, differential workload, unmount, and fixture cleanup.
- Four workers per fixture for five seconds each.
- Differential random, aligned, unaligned, full-file, and EOF reads.
- Directory enumeration, symbolic-link reads, and `fadvise` operations.
- Host and guest resource cleanup after the workload.
The guest-side stages and fixture identities are recorded in [stages.txt](/work/tests-dev/erofsstress/demo-result/repo-pre-3-smoke-20260812/20260812T110037.575683Z-693361-ac21acee/guest-evidence/stages.txt) and [scenarios.txt](/work/tests-dev/erofsstress/demo-result/repo-pre-3-smoke-20260812/20260812T110037.575683Z-693361-ac21acee/guest-evidence/scenarios.txt). Per-worker evidence is available in the same guest evidence directory.
## SKIPPED And Non-Claims
The following tests were **SKIPPED** and have no execution evidence in this run:
| Test | Area | Status |
|---|---|---|
| `TC079` | xattr prefix initialization, lookup, and cleanup | `SKIPPED` |
| `TC138` | ACL parsing and error paths | `SKIPPED` |
| `TC099` | multidevice mount and release paths | `SKIPPED` |
This report therefore does **not** claim that xattr prefix handling, ACL behavior, multidevice behavior, ZSTD, LZMA, DEFLATE, or the full feature/regression suite passed. The PASS result is limited to the plain/LZ4 differential smoke scope listed above.
## Integrity And Cleanup
- The DUT content hash, commit, and tree were identical before and after the run; the test did not modify `repo-pre-3`.
- The read-only base image remained mode `0444` with the same SHA256 before and after the run.
- New QEMU PID `693836` exited gracefully.
- Test port `10000` was released.
- The qcow2 overlay was deleted and the port lock became available.
- Guard PID `26318` and guard port `9222` retained the same process identity, start time, and command hash.
- The generated result directory under `tests-dev` is evidence only and was not committed.
## Honesty Review
The post-run honesty audit **PASSed**. It found no concealed source fix, result substitution, or exaggerated coverage claim. The guest log filename `repo22-build.txt` and related `repo22` stage labels are inherited labels in the test harness; they do not identify the DUT. The DUT is independently bound to `repo-pre-3` by the recorded commit, path tree, content hash, archived source, and generated KLD hash.
The complete machine-readable record remains authoritative for details not reproduced here: [result.json](/work/tests-dev/erofsstress/demo-result/repo-pre-3-smoke-20260812/20260812T110037.575683Z-693361-ac21acee/result.json).
-80
View File
@@ -1,80 +0,0 @@
# Pre5 Extraction Baseline
## Repository Baseline
- Outer starting commit: `6421919003ecfab5317dfa70959ab81cd9bbc990`
- Starting `repo-pre-5` tree: `f99462eee8898af209332ef202f8da07f2dd567e`
- Starting `repo-pre-5/src/super.c` blob: `c150d795df42ae4136fbd6e74c3b88903341b05a`
- Execution target: the existing `repo-pre-5` directory
- Source allowlist for the later extraction commits: `repo-pre-5/src/super.c`
The fixed tree and blob identifiers above were read from Git before this
document was created. Pre5 works directly on `repo-pre-5`; it does not create
another snapshot.
## Stage Scope
Pre5 is an L2-S private cleanup responsibility extraction stage. It has two
source tasks:
1. Extract the existing extra-device cleanup from `erofs_sb_free()` into
`static void erofs_free_dev_context(struct erofs_mount *em)`.
2. Extract the existing metabox and packed-inode cleanup from
`erofs_sb_free()` into
`static void erofs_drop_internal_inodes(struct erofs_mount *em)`.
Both tasks are statement movement only. They must preserve FreeBSD GEOM,
vnode, allocation, and ownership semantics. They must not change behavior,
ABI, features, error handling, locking, logging, conditions, release calls, or
object ownership.
## Protected Paths
The following paths must remain unchanged during Pre5 execution:
- `repo-pre-3/`
- `repo-pre-2/`
- `repo-pre-1/`
- `src-linux/`
- `planning/reject/`
- existing reports and other planning stages
Except for this baseline document, no path outside
`repo-pre-5/src/super.c` belongs in the Pre5 source commits.
## Cleanup-Order Invariant
The effective cleanup order must remain exactly:
```text
xattr prefixes
-> metabox
-> packed inode
-> extra devices in reverse order
-> primary device
-> mount state
```
The extraction must not add pointer clearing, conditions, assertions, logs,
locks, return values, error handling, or header declarations.
## Commit and Push Discipline
Each Pre5 commit must contain one planned logical task and must be pushed
immediately to `xdm main`. The commits must not be squashed. Execution must
stop if a push fails or if local, tracking, and remote commit identities
diverge.
## Validation Status at Baseline
| Validation item | Status |
|---|---|
| Static Git baseline capture | RECORDED |
| Source extraction tasks | NOT RUN |
| Build with `WITH_ZSTDIO=0` | NOT RUN |
| Build with `WITH_ZSTDIO=1` | NOT RUN |
| QEMU smoke testing | NOT RUN |
| Manual or automated tests | NOT RUN |
No build, QEMU run, feature validation, regression test, or runtime result is
claimed by this document.
-268
View File
@@ -1,268 +0,0 @@
# Pre5 Completion Report
Date: 2026-08-12
## Conclusion
| Area | Result | Notes |
|---|---|---|
| Planned source extraction | PASS | Both private cleanup helpers are present in `repo-pre-5/src/super.c`. |
| Static correctness review | PASS | No High or Medium findings; statement order and FreeBSD cleanup behavior are preserved. |
| Commit recoverability | PASS after correction | The initial split commits were non-destructively reverted and replaced by one independently revertible atomic source commit. |
| `WITH_ZSTDIO=0` build | PASS | Exit code 0, zero warnings, zero errors. |
| Module load | NOT RUN | The produced `erofs.ko` was not loaded. |
| QEMU functional testing | NOT RUN | No functional or regression test was run for Pre5. |
| Pre5 smoke test | DEFERRED | Deferred for a combined Pre5/Pre6 smoke run. |
Pre5 is complete for its planned source changes, static review, and required
build gate. This report does not claim runtime, feature, regression, or smoke
test coverage.
The final Pre5 source commit is
`4535cccaf1ca1837f0a2d1cca526e5f138a23c6d`. This report is created after that
commit and, when committed, its documentation commit will therefore follow the
final source commit.
## Scope
Pre5 directly modified `repo-pre-5`; no new repository snapshot was created.
The starting planning commit was:
```text
6421919003ecfab5317dfa70959ab81cd9bbc990
docs: plan repo-pre-5 cleanup extraction phase
```
The baseline documentation commit was:
```text
0666800fa529a8869757da85f2e69b8e1dd2c9f6
docs: record pre5 extraction baseline
```
The baseline recorded the following source identity:
```text
repo-pre-5/src/super.c blob:
c150d795df42ae4136fbd6e74c3b88903341b05a
```
The source scope was limited to extracting two existing cleanup regions from
`erofs_sb_free()` into private helpers. No feature, ABI, error handling,
locking, logging, ownership, or cleanup policy change was intended.
## Final Implementation
The final implementation is in [`src/super.c`](../src/super.c).
### Extra-device cleanup
`erofs_free_dev_context()` is a `static void` helper with exactly one
definition and one call. It contains only the existing extra-device cleanup:
```c
static void
erofs_free_dev_context(struct erofs_mount *em)
{
unsigned int i;
if (em->devs != NULL) {
for (i = em->extra_devices; i > 0; --i)
erofs_release_device_info(&em->devs[i - 1]);
free(em->devs, M_EROFS);
}
}
```
The reverse close order is unchanged. The primary device remains outside this
helper and is still released separately by `erofs_sb_free()`.
### Internal-inode cleanup
`erofs_drop_internal_inodes()` is a `static void` helper with exactly one
definition and one call. It contains only the existing metabox and packed
inode releases:
```c
static void
erofs_drop_internal_inodes(struct erofs_mount *em)
{
if (em->metabox_en != NULL)
free(em->metabox_en, M_EROFS);
if (em->packed_inode != NULL)
free(em->packed_inode, M_EROFS);
}
```
This remains a FreeBSD allocation cleanup path. It does not copy Linux
`iput()` or Linux inode-lifecycle semantics.
### Preserved cleanup order
The effective cleanup order remains:
```text
xattr prefixes
-> metabox
-> packed inode
-> extra devices in reverse order
-> primary device
-> mount state
```
The final caller is:
```c
static void
erofs_sb_free(struct erofs_mount *em)
{
if (em == NULL)
return;
erofs_xattr_prefixes_cleanup(em);
erofs_drop_internal_inodes(em);
erofs_free_dev_context(em);
erofs_release_device_info(&em->dif0);
free(em, M_EROFS);
}
```
Static expansion of both helpers produces the baseline statement sequence.
No condition, loop direction, release function, pointer clearing, lock, errno,
log message, return value, or ownership rule was added or changed.
## Commit Timeline
| Commit | Purpose | Result |
|---|---|---|
| `6421919` | Establish the Pre5 execution plan | Baseline planning point. |
| `0666800` | Record the source baseline and validation status | Documentation only. |
| `52dca2e` | Initially extract `erofs_free_dev_context()` | Static behavior correct. |
| `e9b9fb2` | Initially extract `erofs_drop_internal_inodes()` | Static behavior correct. |
| `c406813` | Revert `e9b9fb2` | Non-destructively removed the second helper first. |
| `20733bd` | Revert `52dca2e` | Restored the exact baseline `super.c` blob. |
| `4535ccc` | Atomically introduce both cleanup helpers | Final source implementation. |
The initial two source commits were behaviorally correct. The static audit
found a commit-organization defect: after `e9b9fb2`, the earlier `52dca2e`
could not be independently reverted from the final HEAD without conflict
because both commits edited the same tightly coupled `erofs_sb_free()` region.
No history rewrite or destructive reset was used. The correction was:
1. Revert `e9b9fb2` with `c406813`.
2. Revert `52dca2e` with `20733bd`.
3. Confirm that `super.c` returned to baseline blob
`c150d795df42ae4136fbd6e74c3b88903341b05a`.
4. Reintroduce both helpers in atomic commit `4535ccc`.
The final `super.c` blob is:
```text
03b92560bb5ef01f322b0d052f84bc3c577ef829
```
This is byte-for-byte identical to the correct source state at `e9b9fb2`.
The final commit `4535ccc` can be reverted without conflict and restores the
baseline blob.
The original plan preferred one source task per commit. That rule was adjusted
because the two extractions share one cleanup sequence and one caller region;
separate commits weakened independent rollback despite preserving behavior.
One atomic source commit provides a clearer and mechanically verifiable
rollback boundary without changing the planned source result.
## Static Review Evidence
The final independent static review result was PASS with no High or Medium
findings.
Verified properties:
- Only `repo-pre-5/src/super.c` differs from the source baseline.
- Both helpers are `static void` and each has one definition and one call.
- Extra devices are still released in reverse order before `em->devs` is
freed.
- Metabox cleanup still precedes packed-inode cleanup.
- The primary device remains a separate release after the extra-device
context.
- The mount state remains the final allocation released.
- Expanding both helpers recovers the original cleanup statement sequence.
- No header, exported interface, ABI, feature, condition, lock, errno, log, or
ownership change was introduced.
- No deferred correctness, codec, descriptor, formatting, or feature work was
mixed into Pre5.
- `repo-pre-1/`, `repo-pre-2/`, `repo-pre-3/`, `src-linux/`, planning files,
reject records, and earlier reports remained protected from source changes.
The source result therefore satisfies the intended L2-S responsibility
extraction while preserving the necessary FreeBSD cleanup implementation.
## Build Evidence
The required build gate was executed in the existing FreeBSD 15 VM with PID
`26318`. No new VM was started for this build.
Command:
```sh
FREEBSD_SRC=/root/pre5-build-gate-20260812T125645Z/freebsd-src \
WITH_ZSTDIO=0 ./build.sh
```
Result:
| Evidence | Value |
|---|---|
| Exit code | `0` |
| Compiler warnings | `0` |
| Compiler errors | `0` |
| Build log | `repo-pre-5/build/pre5-zstdio0-20260812T125645Z/build.log` |
| Module | `repo-pre-5/build/pre5-zstdio0-20260812T125645Z/erofs.ko` |
| Module size | `78,248` bytes |
| Module SHA256 | `0a2928a711715a22dfe243a536f514395acd52af3cec5515bd4bb003e42a14ac` |
The build artifacts are under the ignored `repo-pre-5/build/` directory and
must not be committed. The module was not loaded, and the build result alone
does not establish runtime behavior.
## Not Run And Deferred
| Item | Status | Meaning |
|---|---|---|
| Load produced `erofs.ko` | NOT RUN | Module load and unload behavior were not checked. |
| QEMU functional testing | NOT RUN | No functional test VM run was performed for Pre5. |
| Feature or regression suite | NOT RUN | No feature-completeness claim is made. |
| Pre5 standalone smoke test | DEFERRED | Deferred by user direction and risk assessment. |
| Combined Pre5/Pre6 smoke test | REQUIRED LATER | Must be performed after Pre6 before claiming runtime coverage. |
The Pre5 changes only extract existing private cleanup statements, and the
static review plus build gate found no source or compiler issue requiring an
immediate standalone smoke run. To avoid duplicating a relatively expensive VM
cycle, the smoke test is deferred and will be combined with Pre6.
The combined Pre5/Pre6 smoke run must cover at least:
1. Plain and LZ4 basic build, module load, mount, read, readdir, unmount, and
cleanup.
2. `TC099` multi-device success, missing-device failure, and device cleanup.
3. A packed-inode/metabox fixture mount and unmount path that exercises
internal-inode cleanup.
None of these deferred checks is claimed as passed by this report.
## Residual Risk
- The compiled module has not been loaded, so loader, symbol-resolution, and
unload behavior remain unverified for the final source commit.
- Failure and partial-initialization paths have only been reviewed statically.
- Multi-device cleanup has not been exercised at runtime after extracting
`erofs_free_dev_context()`.
- Packed-inode and metabox cleanup has not been exercised at runtime after
extracting `erofs_drop_internal_inodes()`.
- `WITH_ZSTDIO=1` was not built in this stage.
- No QEMU smoke, feature suite, stress test, or regression test has been run
specifically against the final Pre5 source state.
These risks are accepted for the current stage and are carried into the
combined Pre5/Pre6 smoke gate. Until that gate runs, Pre5 should be described
as static-review PASS and `WITH_ZSTDIO=0` build PASS, not runtime PASS.
-99
View File
@@ -1,99 +0,0 @@
# Pre6 Initialization Baseline
## Repository Baseline
- Outer starting commit: `bfed0431693e7798bdecacd78b7e6258c2d93c5a`
- Starting `repo-pre-6` tree: `b4a40164394b378adc947d6ad14dc7c845e79b2c`
- Starting `repo-pre-6/src/super.c` blob: `03b92560bb5ef01f322b0d052f84bc3c577ef829`
- Execution target: the existing `repo-pre-6` directory
- Source allowlist for later extraction commits: `repo-pre-6/src/super.c`
The tree and blob identities above were read from Git before this document was
created. Pre6 works directly on `repo-pre-6`; it does not create another source
snapshot.
## Stage Scope
Pre6 is an L2-I private mount-initialization responsibility extraction stage.
It contains three helper tasks:
1. Extract one decoded external-device initialization block into
`static int erofs_init_device(...)`.
2. Extract packed-carrier initialization into
`static int erofs_init_packed_inode(struct erofs_mount *em)`.
3. Extract metabox initialization into
`static int erofs_init_metabox_inode(struct erofs_mount *em)`.
These tasks move existing statements into private helpers. They must not change
features, behavior, public interfaces, error handling, allocation, cleanup,
logging, locking, GEOM operations, or object ownership.
## Source Commit Strategy
The source work is delivered in two commits:
1. One independent commit for `erofs_init_device()`.
2. One atomic commit for `erofs_init_packed_inode()` and
`erofs_init_metabox_inode()`.
Packed and metabox initialization remain one ownership unit because their
blocks are adjacent and a fragment-backed metabox can depend on the packed
carrier. No intermediate commit may contain only one internal-inode helper.
Each commit, including this baseline commit, must be pushed immediately to
`xdm main`. Execution must stop if local HEAD, `xdm/main`, and remote `main`
do not agree after a push.
## Protected Paths
The following paths must remain unchanged during Pre6 execution:
- `repo-pre-1/`
- `repo-pre-2/`
- `repo-pre-3/`
- `repo-pre-5/`
- `src-linux/`
- `planning/reject/`
- `1-code-similarity-review/`
- existing planning documents and reports
Except for Pre6 documents under `repo-pre-6/docs/`, no path outside
`repo-pre-6/src/super.c` belongs in the planned source commits.
## Behavior-Preservation Invariants
- Preserve the order of every moved statement and every helper call.
- Preserve all conditions, loop direction, values, types, casts, and return
values.
- Preserve every errno and log message without translation or normalization.
- Preserve allocations, frees, NULL assignments, and failure paths.
- Preserve the current owner and lifetime of every buffer, device, inode, and
mount resource.
- Keep `erofs_open_device()` unchanged as the FreeBSD GEOM open transaction.
- Keep external-device slot lookup and ascending iteration in
`erofs_scan_devices()`.
- Keep packed inode initialization before metabox inode initialization.
- Keep `erofs_mountfs()` as the common failure owner that calls
`erofs_sb_free()`.
- Add no rollback, validation, cleanup, logging, locking, or defensive changes.
- Keep all new helpers private and add no header declarations.
If an extraction requires a behavior, ownership, ordering, or errno change,
execution must stop instead of expanding Pre6 scope.
## Validation Status at Baseline
| Validation item | Status |
|---|---|
| Static Git baseline capture | RECORDED |
| Device initialization extraction | NOT RUN |
| Packed and metabox initialization extraction | NOT RUN |
| Static equivalence and independent revert gates | NOT RUN |
| Build with `WITH_ZSTDIO=0` | NOT RUN |
| Plain/LZ4 smoke test | NOT RUN |
| Complete `TC099` multi-device smoke test | NOT RUN |
| Positive `TC142` metabox smoke test | NOT RUN |
| Other build, QEMU, or runtime tests | NOT RUN |
No source extraction, build, QEMU run, feature validation, regression test, or
runtime result is claimed by this baseline document.
-55
View File
@@ -1,55 +0,0 @@
# Pre7 Helper-Alignment Baseline
## Repository Baseline
- Execution-start commit: `3f5f783b9f316af6d3887421251cc79909aed934`
- Starting `repo-pre-7` tree: `909ffaa0f586a51933f4220a8fe67d963d5b1dff`
- Starting `repo-pre-6` tree: `909ffaa0f586a51933f4220a8fe67d963d5b1dff`
- Starting `repo-pre-7/src/internal.h` blob: `253a30a595f9740ce97de865cc6f255f8eda2b74`
- Starting `repo-pre-7/src/inode.c` blob: `c753c14fdc48ae53e6dd2554cb9dc4a5b8a6c569`
- Starting `repo-pre-7/src/xattr.c` blob: `cef4db2a8d03be028c0344941cea60def9cd84ce`
- Execution target: the existing `repo-pre-7` directory
The tracked `repo-pre-7` baseline is an exact snapshot of `repo-pre-6` at the
execution-start commit. The identities above were read from Git objects at
that commit, not inferred from the live worktree.
Uncommitted changes were already present in `repo-pre-7/src/internal.h` and
`repo-pre-7/src/inode.c` when this document was written. This baseline does
not assess, validate, or claim completion of those changes.
## Exact Source Scope
Pre7 contains exactly two source tasks:
1. Rename the private inode-location helper from
`erofs_nid_to_offset()` to `erofs_iloc()` in
`repo-pre-7/src/internal.h` and `repo-pre-7/src/inode.c`. This is an
identifier-only alignment; the FreeBSD signature, implementation,
callers, metabox handling, overflow checks, and failure behavior remain
unchanged.
2. Extract the private `erofs_xattr_prefix()` mapping helper from
`erofs_xattr_namespace_prefix()` in `repo-pre-7/src/xattr.c`. The existing
wrapper retains FreeBSD namespace validation, namespace matching, errno
selection, and all existing get/list call sites.
No other source file, helper cleanup, behavior change, public interface
change, formatting pass, or deferred planning item belongs in Pre7.
## Validation Status
Per the current execution instruction, this baseline-recording step performs
no build or test activity. Historical Pre6 evidence is not a Pre7 result.
| Validation item | Status |
|---|---|
| Pre7 source implementation validation | NOT RUN |
| Static reference and behavior-matrix checks | NOT RUN |
| Independent source-commit revert checks | NOT RUN |
| `WITH_ZSTDIO=0` build | NOT RUN |
| QEMU smoke testing | NOT RUN |
| Manual or automated runtime tests | NOT RUN |
| CI testing | SKIPPED |
This is an implementation baseline only. It is not a completion report and
does not claim that either Pre7 source task is complete or correct.
-86
View File
@@ -1,86 +0,0 @@
# Pre8 Responsibility-Alignment Baseline
## Repository Identity
The following identities were read directly from Git objects at execution-start
commit `475b62437f89a8a98e0e3a1d1628481768525cb2`:
```text
execution-start commit: 475b62437f89a8a98e0e3a1d1628481768525cb2
execution-start tree: 3f0bc6be8324446440ce578f03d892c95f73591c
repo-pre-7 tree: b8f9af2cd55225c4348b79ff5910ae6fc83cd517
repo-pre-8 tree: b8f9af2cd55225c4348b79ff5910ae6fc83cd517
```
The equal `repo-pre-7` and `repo-pre-8` tree identities establish that
`repo-pre-8` is an exact tracked snapshot of `repo-pre-7` at the execution
start. This relationship is based on Git object identities, not a live
worktree comparison.
Relevant source identities at the same commit are:
```text
mode blob path
100644 e06822e363d9122a39256494bde7d12cfea1f7c9 repo-pre-8/src/decompressor.c
100644 ff595fe009679a4e950eb3743b2f152fac4aad31 repo-pre-8/src/decompressor_lzma.c
100644 f3e48cd2016608aaf84ed5e80151782a6c5513f7 repo-pre-8/src/decompressor_deflate.c
100644 23756af263ceb148ca50fbb8cd1ae80ba3fac4cd repo-pre-8/src/decompressor_zstd.c
100644 cac9ee15f81a1607a03fbab3466cf89c588872c8 repo-pre-8/src/internal.h
100644 2257a2299b3bcf3ab15978d723843f26d0b4b346 repo-pre-8/src/xattr.c
```
These values define the immutable comparison baseline for Pre8. Concurrent
or later worktree changes are not assessed by this document.
## Execution Scope
Pre8 is limited to the following private responsibility-alignment tasks:
1. Move `z_erofs_load_lzma_config()` from the core decompressor file to the
LZMA backend without changing its name, signature, validation order,
state updates, or error behavior.
2. Move `z_erofs_load_deflate_config()` to the DEFLATE backend with its
existing format checks, window-bit limits, state updates, and errors
preserved.
3. Move `z_erofs_load_zstd_config()` to the ZSTD backend while preserving the
availability check, configuration checks, window-log limit, diagnostics,
and `WITH_ZSTDIO` behavior.
4. Replace the duplicated inline and shared xattr traversal paths with shared
private iterator helpers while preserving lookup priority, list order,
namespace and errno timing, validation differences, output accounting, and
buffer ownership.
The source allowlist is restricted to the six files identified above. This
baseline file is the only documentation path used by the baseline step.
Pre8 does not include superblock extraction, a new `compress.h` contract,
descriptor tables, decode callback ABI changes, codec primitive renaming,
typed errno conversion, Linux page or folio abstractions, broad function
reordering, or any item already recorded under `planning/reject/`.
## Initial Validation Status
This document records the execution baseline only. No implementation,
correctness, build, or runtime conclusion is made here.
| Validation item | Initial status |
|---|---|
| Pre8 source implementation | NOT RUN |
| Source allowlist and protected-path audit | NOT RUN |
| `git diff --check` | NOT RUN |
| Loader definition, declaration, reference, and body-equivalence checks | NOT RUN |
| Xattr iterator invariant and behavior review | NOT RUN |
| Direct-parent restoration checks for source commits | NOT RUN |
| Final-HEAD inverse-patch compatibility checks | NOT RUN |
| `WITH_ZSTDIO=0` build | NOT RUN |
| `WITH_ZSTDIO=1` build | NOT RUN |
| Basic plain/LZ4 QEMU mount, read, readdir, and unmount smoke | NOT RUN |
| Pre7 deferred QEMU coverage | NOT RUN |
| Pre8 xattr QEMU coverage | NOT RUN |
| Pre8 codec QEMU coverage | NOT RUN |
| QEMU cleanup and zero-residual-state checks | NOT RUN |
| Completion and evidence-honesty review | NOT RUN |
Historical evidence from an earlier snapshot is not a Pre8 result. Every
status above remains `NOT RUN` until supported by fresh evidence from the
final Pre8 DUT. This file must not be interpreted as a completion report.
-314
View File
@@ -1,314 +0,0 @@
# Pre8 Completion and Validation
## Final Verdict
Pre8 overall status is **FAIL**.
The required implementation, static review, revert review, build matrix, basic
plain/LZ4 smoke, and selected test suite were all executed. The selected suite
did not pass its required gate. In particular, TC004 produced a definite DUT
failure while twelve other DUT verdicts were blocked by test-harness defects or
prior test residue. Under the status definition in `planning/pre8`, a required
action that ran and failed a gate is `FAIL`, not `PASS` or `PARTIAL`.
This overall verdict does not erase the narrower successful results:
| Validation layer | Verdict | Scope |
| --- | --- | --- |
| Source implementation | PASS | Planned loader placement and xattr iterator work is present in the final source tree. |
| Static source review | PASS | Allowlist, token-equivalence, references, xattr invariants, and protected paths passed review. |
| Revert and history review | PASS | Accepted replacement commits passed their defined direct-parent and final-HEAD checks; historical failures remain disclosed. |
| Four build configurations | PASS | `default`, `debug`, `zstdio0`, and `zstdio1` built and passed module load/unload smoke. |
| Basic plain/LZ4 smoke | PASS | Independent run `20260812T210345.913633Z-770842-a2868dd9`. |
| Selected 19-case suite | FAIL | Automation: 5 PASS / 9 FAIL / 4 ERROR / 1 NOT_RUN. Audited DUT: 6 PASS / 1 FAIL / 12 BLOCKED. |
| Pre8 overall | **FAIL** | A required selected-suite gate ran and failed. |
No claim is made that Pre8, repo22, the LZMA issue, or the test harness was
fixed by this validation work.
## Final DUT Identity
The final repository identity used by both selected-suite and basic-smoke
evidence is:
```text
parent repository commit: d1f5b686e8945d06b6e8c0a0188b2262de0b4ac2
parent repository tree: 19f450085cad49d06f6fe05d180313f13d4347f0
repo-pre-8 tree: 497736695fcc4285760b24c5954c34d32b8af49f
repo-pre-8/src tree: cbf93a19df8d1ce2fb66e2aeb7276a4ea6d4dcc2
```
Selected-suite DUT archive:
```text
bbcfd8a6a28faba3f7c5c7e827bde5578774be6e08b83d05485d58ee16a7fb6f
```
Basic-smoke DUT archive:
```text
402459ba6a6f55696d06474f7494a69ef1a5fe49063d565cacadece37e8b9ce1
```
The different archive hashes belong to different runners and archive
procedures. Both reports bind their run to the same final repository commit
and `repo-pre-8/src` tree.
## Source Delivery
### Accepted implementation
The final accepted source changes are:
1. `3d28d96fc48753231b1899fce6189e586cf988c6`
(`erofs: unify inline and shared xattr iteration`)
2. `aac1412056f9ae7269f7c0f85c0233ade8cc662a`
(`erofs: move codec config loaders atomically`)
The xattr commit introduces common private inline/shared iteration while
preserving the reviewed lookup order, list order, namespace and errno timing,
inline/shared validation differences, buffer ownership, and output accounting.
The atomic loader replacement moves the LZMA, DEFLATE, and ZSTD configuration
loaders to their backend files. The loader bodies remain token-equivalent to
the baseline, dispatch calls remain in the core, decode functions are not
changed, and no Linux page/folio or descriptor ABI was introduced.
### Superseded split-loader history
The following pushed commits remain in history and must not be described as
accepted independent delivery units:
| Commit | Historical final-HEAD inverse result | Final disposition |
| --- | --- | --- |
| `dd7f2483d468ec4397a863c7a871391ea1bc2c4c` | FAIL: conflict in `decompressor.c`; `internal.h` auto-merged as staged content | Superseded by `aac1412`; failure permanently retained. |
| `0dba0ea223bee8626b8cd3371562f87ed975a99f` | FAIL: conflict in `decompressor.c`; `internal.h` auto-merged as staged content | Superseded by `aac1412`; failure permanently retained. |
| `7c47f6d` | PASS | Superseded with the split-loader group by `aac1412`. |
All three split commits passed direct-parent restoration at the point where
they were introduced. That fact does not overwrite the two final-HEAD inverse
failures. The split group was reverted by `96f041f`, then replayed as the one
accepted atomic commit `aac1412`. Commit `3d28d96` was not superseded.
### Static and revert verdict
Final static and revert status is **PASS**:
- the implementation remained within the planned source allowlist;
- protected trees and unrelated paths were unchanged;
- all three moved loader bodies matched their baseline implementations;
- declaration, definition, and dispatch-call closure was correct;
- the ZSTD availability and `windowlog > 10` rejection order was preserved;
- codec decode bodies were unchanged;
- the xattr iterator behavior and ownership matrix passed independent review;
- `aac1412` restored its direct parent exactly when reverted;
- `aac1412` independently reverted from final HEAD without changing xattr;
- `3d28d96` independently reverted from final HEAD without changing the
accepted loader layout;
- final `repo-pre-8/src` matched the reviewed functional source tree;
- `git diff --check` and worktree cleanliness checks passed at static handoff.
The detailed history record is
`repo-pre-8/docs/pre8-loader-history-correction.md`.
## Build Validation
The selected-suite runner built four module configurations. Every
configuration passed build completion, SHA256 recording, unresolved-symbol
checks, and an actual `kldload`/`kldunload` smoke before case execution.
| Configuration | Verdict | SHA256 | Size |
| --- | --- | --- | --- |
| `default` | PASS | `348cb1f5a91d47e1412747d285c795c49375a61a66cfb3d35d22f38ecfde6ea0` | 47,272 bytes |
| `debug` | PASS | `274189804511d249b34fbb3c223e9525b07c7c6266ec39d016ad2f33185001c7` | **UNKNOWN**; the harness did not persist size evidence before VM destruction |
| `zstdio0` | PASS | `348cb1f5a91d47e1412747d285c795c49375a61a66cfb3d35d22f38ecfde6ea0` | 47,272 bytes; byte-identical to `default` |
| `zstdio1` | PASS | `6cdb8e01fdac7805cb7ae12a9d1804b00d3e5cf37ef8b2f583137d36ee70426e` | 51,368 bytes |
The missing debug size is not reconstructed or inferred. No KLD or other
binary was committed.
## Basic Plain/LZ4 Smoke
Independent basic smoke run:
```text
run ID: 20260812T210345.913633Z-770842-a2868dd9
automation: PASS
runner exit: 0
DUT result: PASS for plain and LZ4 basic differential scope
guest: FreeBSD 15.0-RELEASE-p8 amd64
KLD SHA256: 527fe80ad7307d0200cfc4fec84c6fb86b914e89889e4a42ff0613fc24465015
KLD size: 47,272 bytes
```
All twelve runner stages passed. The run created a deterministic corpus with
256 regular files, 9 symlinks, and 43 directories. Both the plain and LZ4
images mounted read-only, passed a 307-entry differential preflight, completed
all four workers, and unmounted cleanly. The test QEMU exited gracefully, its
overlay and large temporary data were removed, and ports 10000 and 10001 were
released.
This result covers only plain/LZ4 basic mount, traversal, read, differential,
unmount, and cleanup behavior. It does not override the selected-suite
failures or blocked verdicts.
Evidence:
- `/work/tests-dev/erofsstress/demo-result/repo-pre-8-smoke-20260812/20260812T210345.913633Z-770842-a2868dd9/audit-report.md`
- `/work/tests-dev/erofsstress/demo-result/repo-pre-8-smoke-20260812/20260812T210345.913633Z-770842-a2868dd9/result.json`
- tests-dev commit `374b8e3ab4b0df24d6761802b70c694e2896d5fd`
## Selected Suite
Primary run:
```text
run ID: pre8-required-20260812T194817Z
cases: 19
automation: 5 PASS / 9 FAIL / 4 ERROR / 1 NOT_RUN
runner exit: 3
duration: 1892.44 seconds
audited DUT: 6 PASS / 1 FAIL / 12 BLOCKED
```
The automation status is the runner's result. The DUT status is a separate
evidence audit. An automation failure caused by a harness defect is not changed
to automation PASS. Likewise, a positive command observed before a missing
negative check does not make the full DUT case PASS.
### Per-case matrix
| Case | Automation | DUT | Audited observation |
| --- | --- | --- | --- |
| TC005 | FAIL | BLOCKED | Inline listing and both requested user xattr values succeeded. The missing-xattr `ENOATTR` assertion was never invoked because the truss output directory did not exist. |
| TC013 | PASS | PASS | Invalid NID returned `EINTEGRITY` (`ERR#97`) twice; mount, unmount, and zero-state checks passed. |
| TC079 | PASS | PASS | Packed user and trusted prefix values matched expected data and cleanup passed. |
| TC080 | FAIL | BLOCKED | Four packed-prefix values were correct. The missing-suffix `ENOATTR` assertion was not invoked because the truss output directory was absent. |
| TC082 | FAIL | BLOCKED | ACL bytes and ordering were observed, but automation compared spaced hex against an unspaced prefix; the user-namespace negative assertion also was not invoked. |
| TC067 | FAIL | PASS | Both shared files, the local xattr, and listing matched the fixture. Automation incorrectly expected one additional trailing `!`. |
| TC068 | FAIL | BLOCKED | Shared listing succeeded. The nonexistent lookup was not invoked because truss could not create its output file. |
| TC069 | PASS | PASS | Three shared xattrs were listed and all exact values were read; cleanup passed. |
| TC081 | PASS | PASS | Metabox shared, shared-prefix, and packed-prefix xattrs were listed and read from both files; cleanup passed. |
| TC117 | FAIL | BLOCKED | Both corrupt fixtures mounted and valid local shared data remained readable. Corrupt-entry errno assertions were not invoked because truss failed first. |
| TC135 | PASS | PASS | Metabox, packed, and primary-prefix fallback values matched expected data. |
| TC138 | FAIL | BLOCKED | Valid unordered and empty-header ACLs were read. Three malformed ACL assertions were not invoked because the scratch directory was absent. |
| TC140 | FAIL | BLOCKED | Positive compressed and fragment metabox carrier hashes and xattrs passed. Four negative mount commands were not invoked because truss failed opening its trace. |
| TC142 | FAIL | BLOCKED | Positive fragment-backed compressed metabox hash and xattrs passed. Four negative mount commands were not invoked for the same reason. |
| TC004 | ERROR | **FAIL** | LZMA image mounted, but SHA256 of the mounted 8 MiB `level.dat` timed out after 30 seconds. The process remained running and left the mount and `md0` busy. |
| TC084 | ERROR; follow-up FAIL | BLOCKED | Primary run was contaminated by TC004 residue. Fresh follow-up mounted LZ4 and found the target, but guest `dump.erofs` was absent before content verification. |
| TC102 | ERROR; follow-up FAIL | BLOCKED | Primary run was contaminated by TC004 residue. Fresh follow-up mounted DEFLATE, but missing guest `dump.erofs` prevented algorithm and content verification. |
| TC105 | ERROR; follow-up FAIL | BLOCKED | Primary run was contaminated by TC004 residue. Fresh follow-up mounted ZSTD with `zstdio1`, but missing guest `dump.erofs` prevented verification. |
| TC145 | NOT_RUN; follow-up ERROR | BLOCKED | Both zstdio modules built and initial load smoke passed. The follow-up queried and unloaded module name `erofs` instead of the loaded artifact name, so functional mount/read gates were not reached. |
### Definite DUT failure
TC004 is a definite observed DUT failure for this test run:
1. `lzma-level6.erofs` attached as `md0`.
2. The read-only EROFS mount succeeded.
3. The mounted `level.dat` existed.
4. SHA256 of the source file completed and returned
`ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461`.
5. SHA256 of the mounted 8 MiB file did not finish within 30 seconds.
6. The timed-out `sha256` process remained alive.
7. Unmount and `mdconfig -d` returned `Device busy`.
This evidence does **not** prove that Pre8 introduced the problem. No matching
Pre7 or pre-change comparison run with the same image, guest, module build,
and command is available. The issue is recorded separately in
`issues/pre8-lzma-read-timeout.md` without an attribution claim.
### Codec follow-up
Fresh follow-up run:
```text
run ID: pre8-codec-followup-20260812T202059Z
cases: TC084, TC102, TC105, TC145
automation: 3 FAIL / 1 ERROR
runner exit: 1
duration: 1136.92 seconds
audited DUT: 4 BLOCKED
```
The follow-up isolated these cases from TC004 residue, but it did not produce
codec functional PASS verdicts. TC084, TC102, and TC105 were blocked by absent
guest `dump.erofs`. TC145 was blocked by incorrect module-name lookup/unload
and cleanup behavior in the harness. These results remain FAIL/ERROR in
automation and BLOCKED for the DUT.
The harness findings are recorded in
`issues/pre8-test-harness-blockers.md`. No harness modification is part of
Pre8 source delivery.
Selected-suite evidence:
- `/work/tests-dev/demo-result/repo-pre-8-smoke-20260812/summary.md`
- `/work/tests-dev/demo-result/repo-pre-8-smoke-20260812/summary.json`
- `/work/tests-dev/demo-result/repo-pre-8-smoke-20260812/pre8-required-20260812T194817Z/summary.json`
- `/work/tests-dev/demo-result/repo-pre-8-smoke-20260812/pre8-required-20260812T194817Z/evidence/`
- `/work/tests-dev/demo-result/repo-pre-8-smoke-20260812/pre8-codec-followup-20260812T202059Z/summary.json`
- `/work/tests-dev/demo-result/repo-pre-8-smoke-20260812/pre8-codec-followup-20260812T202059Z/evidence/`
- tests-dev commit `46e6840f1b8918414af76c9a5731322c697eee8b`
## Fixtures and Immutable Inputs
The common base image was not modified:
```text
path: /work/tests-dev/lfs/freebsd-15-dev-src-20260811.bp
format: qcow2
mode: 0444
size: 16,515,530,752 bytes
SHA256: 67f359621f23a1d745f0889370cbb99a096cee3e99a0b2f3bb18fc7a91bf6fef
```
Selected primary fixture deployment identities:
| Group | Manifest SHA256 | Checksum-list SHA256 |
| --- | --- | --- |
| G1 | `9ffe9e695a6544cb010a5d4ba1e0c2c09d7455e6d7e28cc81da54d2dd954f2f2` | `db416004ff6b00ad286e50da506168b35a544a7da5619042225198843e4c741c` |
| G4 | `6200842757dae30f3a5b0683d726d339a24e55dff98cd084597462289a9308aa` | `229c84c5436cf9b5b659ded375097fba13621c20a2cf2d845f146df737836622` |
| G5 | `c9540baafb53b3783233c3950f4d8548c28585cd32335dadc14db613692a8978` | `d08138b1bd51579cbf5461a73abce9d7fddc9cdb8ace1329c9f1581d41e1095f` |
Follow-up G5 deployment identities:
```text
manifest: 9be85e48115f2dd2cae058f2a4ea7438512f28e647c30476d049d6b8025b02fd
checksum list: 7513425c98400f641523083bf0d06b2d548aa3163dfcdfe980dca2b0e1900acd
```
Deployment hashes can include run-specific metadata. Image, source, generator,
and checksum evidence remains beneath each run's `fixture-evidence/` and
per-case evidence directories.
## Cleanup and Guard Integrity
- The selected primary and follow-up QEMUs were force-destroyed by the runner
after their final errors.
- Their run-owned overlays were deleted.
- Ports 10000 and 10001 were released and closed.
- The independent basic-smoke QEMU shut down gracefully and its overlay was
removed.
- Guard PID 26318 retained its identity and TCP port 9222 remained active.
- Base-image SHA256, mode, and size were unchanged before and after the runs.
- The DUT commit and source tree remained unchanged and clean.
- No DUT source, repo22 source, base image, test source, fixture generator, or
`oldtests` content was modified during validation.
Cleanup of the disposable VM does not change TC004's case-level cleanup result:
the case itself failed to terminate its read process and could not unmount or
detach `md0`. VM destruction only removed the run environment afterward.
## Final Handoff
Pre8 delivers the planned source-responsibility changes and passes static,
revert, build, and basic plain/LZ4 smoke validation. It does not satisfy the
full required validation gate because the selected suite contains one definite
DUT failure and twelve blocked DUT cases. The honest final status is therefore
**FAIL**.
Open follow-up records:
- `issues/pre8-lzma-read-timeout.md`
- `issues/pre8-test-harness-blockers.md`
-74
View File
@@ -1,74 +0,0 @@
# Pre8 Loader History Correction
## Scope
This record corrects the commit organization of the Pre8 codec configuration
loader moves. It does not report a source behavior defect and does not rewrite,
amend, squash, or hide any pushed commit.
The affected historical commits are:
```text
dd7f2483d468ec4397a863c7a871391ea1bc2c4c LZMA loader move
0dba0ea223bee8626b8cd3371562f87ed975a99f DEFLATE loader move
7c47f6d ZSTD loader move
3d28d96fc48753231b1899fce6189e586cf988c6 xattr iterator change
```
## Recorded Results
Each of the three loader commits restores its direct parent's complete tree
when reverted at the commit where it was introduced. Those direct-parent
revert checks are `PASS`.
When each commit is reverted independently from final functional commit
`3d28d96`, the results are:
| Commit | Final-HEAD independent revert | Result |
|---|---|---|
| `dd7f248` | Unmerged conflict in `decompressor.c`; `internal.h` auto-merged as a staged modification | FAIL |
| `0dba0ea` | Unmerged conflict in `decompressor.c`; `internal.h` auto-merged as a staged modification | FAIL |
| `7c47f6d` | Applies without conflict | PASS |
| `3d28d96` | Applies without conflict | PASS |
The two failures are caused by overlapping patch context in `decompressor.c`.
In both cases, `internal.h` is automatically merged as a staged modification,
not left as an unmerged conflict. This is a commit organization problem, not
evidence of a codec implementation or xattr behavior problem.
The `dd7f248` and `0dba0ea` final-HEAD results remain permanently recorded as
`FAIL`. The `7c47f6d` result remains `PASS`, but that commit is superseded with
the other two loader commits so the loader responsibility change has one
atomic acceptance unit.
## Correction Strategy
The three split loader commits will be reverted in reverse order without
rewriting history. Their exact combined five-path change will then be replayed
as one atomic replacement commit. The replacement is limited to:
```text
repo-pre-8/src/decompressor.c
repo-pre-8/src/decompressor_lzma.c
repo-pre-8/src/decompressor_deflate.c
repo-pre-8/src/decompressor_zstd.c
repo-pre-8/src/internal.h
```
Commit `3d28d96` remains accepted and is not superseded. Its `xattr.c` content
must remain unchanged through the correction.
The new acceptance objects are:
1. the atomic replacement loader commit, which must restore its direct parent
exactly when reverted and must independently revert from final `HEAD`
without affecting xattr content; and
2. existing xattr commit `3d28d96`, which must independently revert from final
`HEAD` without affecting the final loader layout.
## Validation Status
This document records static Git evidence only. No build or QEMU test has been
run for this history correction. Build and runtime validation remain
`NOT RUN` until later Pre8 validation produces fresh evidence from the final
DUT.
-191
View File
@@ -1,191 +0,0 @@
# Pre9 LZMA Diagnostic Implementation Baseline
## Baseline Meaning
The Pre9 planning phase ended when the planning documents were committed at
`d1ee2e05b82b5e8a8927861f1e981710532f2f23`. This file starts the
implementation phase and records identities only. It does not claim that the
TC004 root cause is known, that Pre8 introduced the observed timeout, or that
any source or test-harness fix has been selected or implemented.
All values below were read from Git objects or the named evidence files on
2026-08-13 UTC. Historical Pre8 evidence is context for controlled Pre9
diagnosis, not a Pre9 test result.
## Main Repository Identity
Repository: `/work/erofs-freebsd-pre`
```text
branch: main
execution-start commit: d1ee2e05b82b5e8a8927861f1e981710532f2f23
execution-start tree: 95a55ff912d007ca40bd578057e8a4cc6a21bbd9
xdm/main: d1ee2e05b82b5e8a8927861f1e981710532f2f23
remote xdm main: d1ee2e05b82b5e8a8927861f1e981710532f2f23
initial dirty entries: 0
```
Snapshot identities at the execution-start commit:
```text
snapshot subtree src tree
repo-pre-7 b8f9af2cd55225c4348b79ff5910ae6fc83cd517 dc203534d7b5721905f8538026e4c59346106020
repo-pre-8 1e24c992a5b071e6fdabfdc42d342b6ed5a91cf5 cbf93a19df8d1ce2fb66e2aeb7276a4ea6d4dcc2
repo-pre-9 1e24c992a5b071e6fdabfdc42d342b6ed5a91cf5 cbf93a19df8d1ce2fb66e2aeb7276a4ea6d4dcc2
```
The equal Pre8 and Pre9 object identities establish that Pre9 starts as an
exact tracked snapshot of Pre8. Pre7 is intentionally different and remains
the earlier controlled comparison point.
## Conditional Source Allowlist Identity
The primary conditional allowlist is `zdata.c`, `decompressor_lzma.c`, and
`internal.h`. The remaining three files require the additional decision gates
defined by the plan. Their baseline modes and blobs are:
```text
gate mode blob path
primary 100644 54c1c88e5c368e7b5f6a16188c48406767850821 repo-pre-9/src/zdata.c
primary 100644 688acd704e28daa46369fa2e63e895bce5262774 repo-pre-9/src/decompressor_lzma.c
primary 100644 9821fe3a8b5fcaaeb3939d191f15e9ce637cc8d1 repo-pre-9/src/internal.h
extra 100644 ca5d33cd42145159d71e153f115128ac75258708 repo-pre-9/src/inode.c
extra 100644 b233f138d036a2b37394946b382034955dc2a1df repo-pre-9/src/erofs_vnops.c
extra 100644 51170741a0fb5eced814db898c1a233e8a23088a repo-pre-9/src/zmap.c
```
This identity table authorizes no source change by itself. Source edits remain
conditional on controlled evidence and the applicable decision gate.
## tests-dev and Base Image Identity
The external test repository was inspected without modification:
```text
repository: /work/tests-dev
branch: main
current commit: 374b8e3ab4b0df24d6761802b70c694e2896d5fd
current tree: c438e72ebe94a6f10801999bfbdd36b43134d471
xdm/main: 374b8e3ab4b0df24d6761802b70c694e2896d5fd
initial dirty entries: 27 untracked paths
dirty inventory SHA256: bca9ca20e85698d04dad820a11bd0bbb117492c967d6fd338e7acf61bb8fa957
```
The pre-existing tests-dev dirty inventory was:
```text
demo-result/FINAL-REPORT.md
demo-result/audit-checklist.md
demo-result/audit-findings.md
demo-result/current-status.md
demo-result/fix-plan.md
demo-result/fix-verification-report.json
demo-result/progress-report.md
demo-result/repo-pre-6-smoke-20260812/
demo-result/repo22-full-20260811T065312Z/
demo-result/repo22-full-20260811T071634Z/
demo-result/repo22-full-20260811T073716Z/
demo-result/repo22-full-20260811T082907Z/
demo-result/repo22-full-20260811T095621Z/
demo-result/test-execution-summary.md
erofsstress/demo-result/repo-pre-3-smoke-20260812/
erofsstress/demo-result/repo-pre-6-smoke-20260812/
guest/setup-build-env.sh
lfs/FreeBSD-15.0-RELEASE-src.txz
scripts/check-kernel-src.sh
scripts/complete-setup-and-test.py
scripts/install-kernel-sources.py
scripts/manual-fix.sh
scripts/quick-install-src.py
scripts/run-final-test.sh
scripts/setup-build-env-simple.py
scripts/setup-build-env.py
scripts/simple-install-kernel-src.sh
```
These paths are not owned by this baseline step and must not be staged,
modified, or removed as part of it.
Immutable base image identity:
```text
path: /work/tests-dev/lfs/freebsd-15-dev-src-20260811.bp
format: QEMU QCOW2 Image (v3)
mode: 0444
size: 16,515,530,752 bytes
SHA256: 67f359621f23a1d745f0889370cbb99a096cee3e99a0b2f3bb18fc7a91bf6fef
```
The base image is an input only. It must not be modified during Pre9 testing.
## Historical TC004 Evidence Identity
The prior observation is preserved by tests-dev evidence commit
`46e6840f1b8918414af76c9a5731322c697eee8b`, tree
`7178de49f3f429796d6de71a910d71bfccd5745a`, with subject
`evidence: record repo-pre-8 required smoke`.
```text
run ID: pre8-required-20260812T194817Z
case: TC004 - LZMA Compressed File Read
historical DUT commit: d1f5b686e8945d06b6e8c0a0188b2262de0b4ac2
historical DUT src tree: cbf93a19df8d1ce2fb66e2aeb7276a4ea6d4dcc2
DUT archive SHA256: bbcfd8a6a28faba3f7c5c7e827bde5578774be6e08b83d05485d58ee16a7fb6f
module SHA256: 348cb1f5a91d47e1412747d285c795c49375a61a66cfb3d35d22f38ecfde6ea0
fixture image: G5/images/lzma-level6.erofs
fixture image SHA256: 32107a084b27362a093768b88746c37c2e99a74b9f1301a9d4046988479defe9
source file: G5/sources/levels/level.dat
source file SHA256: ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461
deployment manifest: c9540baafb53b3783233c3950f4d8548c28585cd32335dadc14db613692a8978
fixture checksum hash: d08138b1bd51579cbf5461a73abce9d7fddc9cdb8ace1329c9f1581d41e1095f
TC004 evidence SHA256: de6982a11fa368fbaad7d837586daa899789cd4cd8b9f698974a3ef4e251811f
```
Evidence paths:
```text
/work/tests-dev/demo-result/repo-pre-8-smoke-20260812/pre8-required-20260812T194817Z/summary.json
/work/tests-dev/demo-result/repo-pre-8-smoke-20260812/pre8-required-20260812T194817Z/evidence/TC004/TC004_evidence.json
/work/tests-dev/demo-result/repo-pre-8-smoke-20260812/summary.md
/work/erofs-freebsd-pre/issues/pre8-lzma-read-timeout.md
```
The historical automation verdict was `ERROR` and the audited DUT verdict was
`FAIL`: mount and lookup succeeded, but hashing the mounted 8 MiB `level.dat`
did not finish within 30 seconds. PID 95693 remained active, and the mount and
`md0` stayed busy until the disposable VM was destroyed. Attribution remains
`UNKNOWN`. These facts neither prove a Pre8 regression nor select a Pre9 fix.
## Initial Implementation Status
| Item | Status |
|---|---|
| Pre9 planning | COMPLETE |
| Pre9 implementation baseline | RECORDED |
| tests-dev TC004 prerequisite corrections | NOT RUN |
| tests-dev remaining full-suite prerequisites | NOT RUN |
| Controlled Pre7 reproduction | NOT RUN |
| Controlled Pre8 reproduction | NOT RUN |
| Controlled unchanged Pre9 reproduction | NOT RUN |
| Manual SSH diagnosis | NOT RUN |
| Hypothesis decision gates | NOT RUN |
| Source decision | NOT RUN |
| Optional diagnostic instrumentation | NOT RUN |
| Pre9 source implementation | NOT RUN |
| Source static review | NOT RUN |
| Source direct-parent revert gate | NOT RUN |
| Source final-HEAD inverse gate | NOT RUN |
| `WITH_ZSTDIO=0` build | NOT RUN |
| `WITH_ZSTDIO=1` build | NOT RUN |
| TC004 | NOT RUN |
| Extended LZMA matrix | NOT RUN |
| Plain/LZ4 smoke | NOT RUN |
| Full regression | NOT RUN |
| QEMU cleanup and zero-residual-state checks | NOT RUN |
| Pre9 completion and evidence-honesty review | NOT RUN |
| Pre9 overall | NOT RUN |
No diagnostic command, tests-dev prerequisite, controlled comparison, source
change, build, QEMU run, or manual SSH investigation has been performed for
Pre9 at this baseline. Fresh evidence is required before any status above can
advance or any root-cause or repair claim can be made.
-132
View File
@@ -1,132 +0,0 @@
# Pre9 decoded extent cache final manual validation
Date: 2026-08-13
## Scope
This run validated the mount-scoped decoded LZMA extent cache at enclosing
repository commit `cdcf276d12169a672d2de42996532a470ac061d9`.
Controlled identities:
- `repo-pre-9` tree: `9d7eaf63a1c5d07320af3ef39930fc0a3530dd11`
- `repo-pre-9/src` tree: `e657e8a63b097b061670f75ca01947a568ef33d5`
- guest-built KLD SHA-256:
`473a6205f43905972f2417609d43f67ab2cb51ea79bc1716fffa5c1914ff85af`
- LZMA image SHA-256:
`32107a084b27362a093768b88746c37c2e99a74b9f1301a9d4046988479defe9`
- LZMA source SHA-256:
`ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461`
The existing fresh QEMU process on host port 10030 was reused. No second VM
was created. The guest used FreeBSD 15.0-RELEASE-p8 under QEMU TCG with 6 GiB
RAM and four virtual CPUs. The KLD was built in the guest with
`make WITH_ZSTDIO=0`.
The VM used the existing base image
`/work/tests-dev/lfs/freebsd-15-dev-src-20260811.bp`. Its previously audited
SHA-256 is
`67f359621f23a1d745f0889370cbb99a096cee3e99a0b2f3bb18fc7a91bf6fef`.
This run relied on that established integrity record and did not repeat the
approximately 16 GiB image hash, so base-image hashing did not block the final
validation.
## Verdict
| Area | Verdict | Result |
| --- | --- | --- |
| Guest build, KLD load and fixture mounts | PASS | Build returned zero, `kldload` returned zero and both LZMA and LZ4 images mounted read-only. |
| LZMA single-read correctness | PASS | 4 KiB, 16 KiB, 64 KiB and 1 MiB single `pread()` calls returned the requested bytes with `errno=0`; every output range matched the source range SHA-256. |
| LZMA full-file correctness | PASS | Cold and warm full SHA-256 runs returned zero and matched the 8 MiB source hash. |
| LZMA liveness and performance | PASS | Cold full SHA completed in 2.16 seconds and warm full SHA in 1.44 seconds, both below the 120-second host hard limit. |
| LZMA concurrency | PASS | Two simultaneous full SHA processes both returned zero and the expected hash; guest real times were 22.52 and 21.97 seconds. |
| Non-LZMA regression | PASS | The 8 MiB LZ4 fixture returned the expected SHA-256 in 9.03 seconds. |
| Cleanup and guard integrity | PASS | Test QEMU PID 812999 exited, port 10030 closed, its overlay was deleted, and guard PID 26318/port 9222 remained alive and reachable. |
Overall verdict: **PASS for the required final manual smoke scope**.
## Single `pread()` evidence
Each probe program performs one target-file `pread()`. Truss confirmed one
target call at offset zero for every requested length. Dynamic-loader reads are
not counted.
| Length | Returned | Probe command wall time | Output/source range hash | Verdict |
| ---: | ---: | ---: | --- | --- |
| 4 KiB | 4 KiB | 2.696 s | `1c3c59331a4849514667bbc51c078327577b98f75a62996bbc2fe310b014dd79` | PASS |
| 16 KiB | 16 KiB | 2.140 s | `2433ce7c2ea151f487a319447ce4fe14e1a2c2af9f0e5113fe47fc74b6138eee` | PASS |
| 64 KiB | 64 KiB | 1.369 s | `2bb87afd3b9fab420cbe55b782d69a7342150e8816a35efacf22d2ab3012815d` | PASS |
| 1 MiB | 1 MiB | 3.838 s | `52e806509e5dfeb523904f167fca765d001ea749351ecf51738e61a5352a1ba3` | PASS |
These wall times include SSH and truss overhead and are not pure kernel read
times.
## Full-file SHA evidence
The 8 MiB LZMA source hash is:
`ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461`
| Run | Exit | Guest real time | Captured hash | Verdict |
| --- | ---: | ---: | --- | --- |
| Cold, fresh mount | 0 | 2.16 s | expected hash | PASS |
| Warm, same mount | 0 | 1.44 s | expected hash | PASS |
| Concurrent process 1 | 0 | 22.52 s | expected hash | PASS |
| Concurrent process 2 | 0 | 21.97 s | expected hash | PASS |
The concurrency result shows substantial contention compared with a single
reader, but it completed correctly and stayed well inside the 120-second hard
limit. This run does not claim concurrency performance is optimized.
## Baseline comparison
The preceding controlled Pre7/Pre8/Pre9 run sampled each full SHA after about
eight seconds. Each process was still inside the LZMA decompression path and
had advanced only about 1.0-1.25 MiB; no final hash was captured. That report
therefore classified full-read liveness as failed and correctness as blocked.
With the mount-scoped decoded extent cache, the same 8 MiB fixture completed
with the correct hash in 2.16 seconds cold and 1.44 seconds warm. This closes
the previous full-file correctness block for this fixture and demonstrates a
material liveness improvement. It does not establish a precise speedup ratio,
because the baseline process was terminated at the observation point rather
than allowed to finish.
## Non-LZMA result
The LZ4 compact-64k image contained `compressed.bin` with expected SHA-256:
`3ff012b76087c4da65ce0b69813a76f47ea367e95782edf8b8cd6cd3ec4d1880`
The mounted file produced the same hash, returned zero and completed in 9.03
seconds of guest real time.
## Kernel and cleanup observations
No EROFS panic, assertion, mount error or decompression error was found in the
captured dmesg tail. The image emitted pre-existing root-filesystem directory
warnings during boot; they occurred before the test KLD was loaded and are not
attributed to EROFS.
All test-specific mounts and md providers were explicitly detached before VM
shutdown. A final generic cleanup command contained an awk quoting error, but
it ran only after the explicit LZMA and LZ4 unmount/md detach operations had
already returned zero. Host-side cleanup independently confirmed:
- PID 812999 absent;
- port 10030 closed;
- test overlay absent;
- guard PID 26318 alive;
- guard port 9222 open.
## Uncovered cases
This is a targeted final smoke validation, not a full feature suite. It did not
exercise partial-reference LZMA, metadata tailpacking, fragments, multi-device
images, forced unmount under active I/O, repeated mount/unmount under memory
pressure, or deliberate allocation failure. Those remain separate regression
and stress-test work.
Concise machine-readable evidence is stored in
`docs/pre9-manual-evidence/pre9-cache-final-result.json`. Target syscall lines
are stored in `docs/pre9-manual-evidence/pre9-cache-final-probes.txt`.
-158
View File
@@ -1,158 +0,0 @@
# Pre9 controlled manual QEMU evidence audit
Date: 2026-08-13
## Scope and evidence status
This report audits the already completed manual run at
`/work/tests-dev/temp/pre9-manual-20260813-run3/`. No QEMU test was rerun.
The long-lived guard VM (PID 26318, host port 9222) was not touched.
The automated TC004 implementation remains `PENDING_FIX/BLOCKED` in
`/work/tests-dev/fix-todo/tc004-automation.md`. Run3 is a manual substitute;
it is not evidence that TC004 automation passes.
Run history:
- Run1: infrastructure failure; no retained result directory is available.
- Run2: `INFRA`. Guest SSH authentication failed for Pre7 and Pre8, then the
host runner was interrupted while preparing Pre9. It produced no DUT result.
- Run3: valid manual run. All three DUTs built, loaded, mounted, executed the
bounded probes, produced a live SHA stack sample, and cleaned up.
## Verdict
| Question | Verdict | Evidence |
| --- | --- | --- |
| 4 KiB, 16 KiB, 64 KiB and 1 MiB single-syscall read correctness | PASS | Every version returned the requested byte count, `errno=0`, matched the source-range SHA-256, exited zero, and has exactly one target-file `pread()` in truss. |
| First 1 MiB sequential `dd` read | PASS | Every version returned zero and copied 1 MiB within the 20-second bound. This is a bounded smoke check, not proof of one 1 MiB kernel read. |
| Full-file SHA correctness | BLOCKED | No version retained a hash or an exit status for the background SHA. `sha_after` is empty and process disappearance is not correctness evidence. |
| Full-file read liveness/performance | FAIL | After an eight-second observation delay, SHA was still running in the LZMA decode path and had advanced only about 1.0-1.25 MiB. This is unacceptable for the 8 MiB smoke fixture and reproduces across all three versions. |
| Pre7 to Pre8/Pre9 regression attribution | PASS: no Pre9 regression demonstrated | Pre8 and Pre9 have the same `src` tree and identical KLD hash. Pre7 has a different source tree/KLD but shows the same symptom and stack. The evidence attributes the issue to shared behavior, not a Pre9-only change. |
Overall verdict: **PARTIAL**. Bounded reads are correct, but the full-file
correctness verdict is blocked and full-file liveness fails.
## Runner semantics audit
The runner creates a fresh qcow2 overlay per version over the read-only base,
archives the selected repository subtree, builds the KLD in the guest, mounts
the same LZMA fixture, and runs four probes. Each probe invokes a C program that
contains one target-file `pread()` and writes the returned bytes to a file.
The runner then hashes that file and compares it with bytes read from the
uncompressed source fixture.
Important field meanings:
- `commit`: Git tree object for the version directory at the enclosing repo
HEAD. It is not a standalone commit ID.
- `src_tree`: Git tree object for that version's `src` directory.
- `probes[].elapsed`: host wall time for the complete SSH command, including
SSH and truss overhead. It is not pure kernel decompression time.
- `metadata.hash_match`: equality between the mounted output-range hash and
the uncompressed source-range hash.
- `signal=0`: runner shorthand for probe return code zero. It is not a signal
collected through `waitpid()`.
- `dd_1m`: a userspace `dd bs=1m count=1` result. It does not establish the
size or count of VOP/kernel reads.
- `sha_pid`: PID printed after starting a background `sha256` command.
- `diagnostic_sample`: process table, kernel stack, descriptor offset, mount,
md device and dmesg captured after an unconditional eight-second sleep.
- `sha_after`: process status followed by `sha.out` and `sha.err`. Empty output
means neither a process row nor captured SHA/error output was available.
- `status=PARTIAL`: runner-generated fallback when any bounded probe succeeds.
It does not mean full SHA correctness passed.
The full SHA was not run under `timeout`. The runner waited eight seconds,
sampled it, sent TERM, slept two seconds, then attempted KILL, and finally read
the output files. No start/end timestamp or exit status was recorded. Therefore
the exact SHA lifetime is unknown; the only defensible timing statement is that
it was still active approximately eight seconds after launch.
`sha_after` contains only `,state=,command=` for all versions. The subsequent
KILL reports `No such process`. This establishes only that the sampled PID no
longer existed after TERM plus the two-second delay. It does not distinguish a
successful completion from TERM handling, and the absent `sha.out` means no
hash can be validated. Process disappearance must not be reported as PASS.
## Controlled identities and setup
The common fixture hashes were:
- Image: `32107a084b27362a093768b88746c37c2e99a74b9f1301a9d4046988479defe9`
- Source: `ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461`
| Version | Repository tree | `src` tree | KLD SHA-256 | Build/load/mount |
| --- | --- | --- | --- | --- |
| Pre7 | `b8f9af2cd55225c4348b79ff5910ae6fc83cd517` | `dc203534d7b5721905f8538026e4c59346106020` | `33a52f2f16a94afda0501d305b238678b96c71e420d4b8bbcbeec6ffcdf1aae5` | PASS/PASS/PASS |
| Pre8 | `1e24c992a5b071e6fdabfdc42d342b6ed5a91cf5` | `cbf93a19df8d1ce2fb66e2aeb7276a4ea6d4dcc2` | `348cb1f5a91d47e1412747d285c795c49375a61a66cfb3d35d22f38ecfde6ea0` | PASS/PASS/PASS |
| Pre9 | `dc0c01157b5c925684bd77c57ed6703904af7453` | `cbf93a19df8d1ce2fb66e2aeb7276a4ea6d4dcc2` | `348cb1f5a91d47e1412747d285c795c49375a61a66cfb3d35d22f38ecfde6ea0` | PASS/PASS/PASS |
Pre8 and Pre9 are runtime-identical for this test according to both the source
tree and the produced KLD hash.
## Single-syscall probes
All probes used offset zero. Times below include SSH/truss overhead.
| Version | Length | Returned | Hash match | Elapsed | Exactly one target `pread()` |
| --- | ---: | ---: | --- | ---: | --- |
| Pre7 | 4 KiB | 4 KiB | yes | 1.299 s | yes |
| Pre7 | 16 KiB | 16 KiB | yes | 1.291 s | yes |
| Pre7 | 64 KiB | 64 KiB | yes | 1.438 s | yes |
| Pre7 | 1 MiB | 1 MiB | yes | 1.432 s | yes |
| Pre8 | 4 KiB | 4 KiB | yes | 1.216 s | yes |
| Pre8 | 16 KiB | 16 KiB | yes | 1.327 s | yes |
| Pre8 | 64 KiB | 64 KiB | yes | 1.430 s | yes |
| Pre8 | 1 MiB | 1 MiB | yes | 1.442 s | yes |
| Pre9 | 4 KiB | 4 KiB | yes | 1.791 s | yes |
| Pre9 | 16 KiB | 16 KiB | yes | 1.341 s | yes |
| Pre9 | 64 KiB | 64 KiB | yes | 1.374 s | yes |
| Pre9 | 1 MiB | 1 MiB | yes | 1.737 s | yes |
The target-file truss lines are preserved in
`pre9-manual-evidence/probe-summary.txt`. Dynamic-loader `pread()` calls are
not counted as target-file calls.
The 1 MiB `dd` results were:
- Pre7: 1 MiB in 0.195526 s.
- Pre8: 1 MiB in 0.179484 s.
- Pre9: 1 MiB in 0.367417 s.
These values are not stable enough for version performance ranking, but all
three bounded operations completed.
## Full SHA sample
| Version | State at sample | File offset | Kernel stack | Final hash |
| --- | --- | ---: | --- | --- |
| Pre7 | running (`RC`) | 1,245,184 | `lzma2_lzma -> erofs_xz_dec_microlzma_run -> lzma_decompress -> z_erofs_decompress -> z_erofs_do_read -> z_erofs_read_uio` | absent |
| Pre8 | running (`RC`) | 1,150,976 | same | absent |
| Pre9 | running (`RC`) | 1,044,480 | same | absent |
The stack is direct evidence of active CPU-side decompression, not an EROFS
lock wait. It does not by itself prove why decompression is slow. Combined with
the static review, the leading explanation remains repeated full mapped-extent
decompression as `z_erofs_read_uio()` segments reads at `MAXPHYS`. There is no
direct evidence here of an XZ infinite loop or an EROFS lock deadlock.
## Infrastructure integrity and cleanup
The base image SHA-256 before and after run3 was identical and matched the
expected value:
`67f359621f23a1d745f0889370cbb99a096cee3e99a0b2f3bb18fc7a91bf6fef`
For each version, `umount`, `mdconfig -d` and `kldunload` returned zero. Each
QEMU PID was no longer alive and each overlay was deleted. The run3 result set
therefore supports successful test-VM cleanup. This report does not infer the
state of the unrelated long-lived guard VM and did not inspect or modify it.
## Required follow-up
A supplemental manual test is still required if full-file correctness must be
closed. It should run one full SHA with an explicit wall-clock deadline, record
start/end timestamps and exit status, capture `sha.out` before cleanup, compare
the hash to the source fixture, and separately record whether TERM/KILL was
used. Until then, full-file SHA correctness remains `BLOCKED`.
-142
View File
@@ -1,142 +0,0 @@
# Pre9 LZMA decoded extent cache
## Decision
Pre7, Pre8, and Pre9 all reproduced the same LZMA read-liveness symptom. The
manual evidence shows that a single large read completes quickly while a full
file read made up of successive small reads remains in the LZMA decode path.
The source path creates and destroys a decoded extent for every mapped read;
the existing FreeBSD buffer cache only retains compressed block buffers.
A cache local to `z_erofs_read_uio()` would not cross the VOP/read boundary
between successive small user reads. Commit `61f4709` therefore kept one
decoded extent on each compressed-file vnode. Review found that retained
memory could then grow with the number of open vnodes. This follow-up moves
the cache to `struct erofs_mount`: each mount retains at most one decoded
extent until unmount, so ordinary open-file count cannot expand the cache.
A single entry can thrash between files and concurrent readers; that is an
accepted first-stage tradeoff.
The cache is deliberately limited to complete, non-partial MicroLZMA extents.
Partial references keep the existing path because their decoded length and
reference semantics are different. Other codecs are also unchanged in this
phase: the measured failure is LZMA-specific, and broadening the change would
make the validation and regression attribution less precise.
## Data and lifecycle
`struct erofs_zextent_cache` stores the decoded allocation and the mapping
identity needed to prove that it can be reused:
```c
struct erofs_zextent_cache {
void *data;
erofs_nid_t m_nid;
erofs_off_t m_pa;
erofs_off_t m_la;
uint64_t m_plen;
uint64_t m_llen;
unsigned int m_deviceid;
unsigned int m_flags;
unsigned char m_algorithmformat;
};
```
The cache mutex is initialized immediately after allocating
`struct erofs_mount`. Every mount failure path reaches `erofs_sb_free()`, and
normal unmount calls the same helper after `vflush()`. There is no cache field
or cache lifecycle dependency in `struct erofs_node`. The cache key includes
the inode NID because one mount entry is shared by all regular file vnodes.
NID zero remains a valid key; `data != NULL` is the validity bit.
EROFS is read-only, so a decoded extent does not need write invalidation.
`vflush()` completes before unmount frees the mount object, and
`z_erofs_extent_cache_fini()` releases the single mount-owned allocation.
`EROFS_MAP_META` is explicitly excluded, so metadata-backed tailpacking keeps
the previous path. `packed_inode` and `metabox_en` are also excluded because
they are mount-private backing objects, not user file vnodes.
## Concurrency
Decompression and block reads occur without holding `z_extent_cache_lock`. A reader
first takes the lock only to compare the complete key and copy a cache hit.
On a miss, it builds a private decoded extent. It then takes the lock again:
1. If another reader published the same key, copy that published extent and
discard the duplicate private allocation.
2. Otherwise replace the one cached extent, copy the requested range, unlock,
and free the old allocation.
This permits duplicate construction under concurrent misses but keeps all
shared pointer access under the mutex. The lock is never held across
`bread()`, allocation, or decompression, all of which may sleep. The cache
allocation is never freed while a reader is copying it because replacement,
hit copying, and pointer clearing are serialized by the same mutex.
The helpers are reached only when `want <= MAXPHYS` and contain `KASSERT`
checks for that contract. Thus the largest lock-held copy is the FreeBSD
`MAXPHYS` request size, not the 12 MiB on-disk extent cap. Calls that can pass
more than `MAXPHYS` use the existing uncached copy path. This bounded mutex
copy is accepted for the first stage; a refcounted immutable entry is deferred
until runtime evidence shows that this copy is materially contended.
## Bounds and unchanged paths
The existing mapping sanity checks cap a mapped compressed extent at
`Z_EROFS_PCLUSTER_MAX_DSIZE` before the read path. `z_erofs_read_extent()` also
checks the compressed and decoded lengths before allocation. The read path now
explicitly checks `mapoff` and the extent length before converting them to
`size_t`; this protects the cache offset arithmetic on platforms where
`size_t` is narrower than the on-disk fields.
Cache use requires all of the following:
```text
compressed mapped extent
not EROFS_MAP_PARTIAL_REF
Z_EROFS_COMPRESSION_LZMA
initialized mount cache
not `EROFS_MAP_META`
not a mount-private backing inode
request length no greater than `MAXPHYS`
```
Fragments, holes, partial references, non-LZMA codecs, uncompressed files,
metadata reads, and mount-private backing inodes retain their previous code
paths and error handling.
## Rejected alternatives for Pre9
- A function-local cache was rejected because it cannot span successive VOP
reads that caused the observed amplification.
- A per-vnode cache was rejected after the `61f4709` review: open file count
could retain one decoded extent per vnode without a system-wide bound.
- A larger cross-vnode cache was rejected because it would require an eviction
policy and larger memory accounting. The one-entry per-mount cache is the
controlled compromise: it has a fixed mount-scoped bound and simple teardown.
- A Linux page/folio/XArray/workqueue port was rejected because those are not
FreeBSD vnode/buf primitives and would create an unnecessary compatibility
layer.
- A decoder stream pool was deferred: it may reduce allocator overhead but
does not remove repeated full extent decompression.
- Changing `MAXPHYS`, changing the disk format, bypassing the buffer cache, or
adding decoder retries was rejected because none addresses the demonstrated
decoded-result reuse and each changes unrelated behavior or resource bounds.
## Static validation and required runtime matrix
This change is static-only in the source phase. The follow-up test agent must
run the unchanged LZMA fixture against Pre9 and require:
1. bounded single reads at 4 KiB, 16 KiB, 64 KiB, and 1 MiB with source-range
hash equality;
2. repeated small reads spanning the same extent, with completion and hash
equality;
3. complete sequential SHA-256 with recorded exit status and elapsed time;
4. concurrent reads of the same file and close/reopen reads;
5. partial-reference, plain, LZ4, DEFLATE, and ZSTD regression coverage;
6. clean unmount, md detach, and module unload after every case.
The automation verdict and DUT verdict must remain separate. A timeout or
missing final hash remains a failure or blocked result; it must not be promoted
to PASS because bounded reads succeed.
-62
View File
@@ -1,62 +0,0 @@
# Pre9 LZMA cache review resolution
This document records the follow-up to commit `61f4709`, which cached one
decoded LZMA extent per vnode.
## Findings addressed
- The cache is now one entry in `struct erofs_mount`, rather than one entry in
every `struct erofs_node`. Retained decoded memory is bounded by one extent
per mounted filesystem, with the number of mounts controlled by mount
privileges. Keeping many ordinary file descriptors open cannot create more
cache entries.
- Cache initialization is performed in `erofs_mountfs()` immediately after
mount allocation. `erofs_sb_free()` destroys it on every mount failure path
and after successful `vflush()` during unmount. Node reclaim no longer owns
cache cleanup.
- `EROFS_MAP_META` is an explicit ineligibility condition. Metadata-backed
tailpacking therefore remains on its existing read path.
- `packed_inode` and `metabox_en` are explicit ineligibility conditions. The
mount-private backing objects cannot populate or consume the user-data
cache.
- The cache type and helper names are now `erofs_zextent_cache` and
`z_erofs_extent_cache_*`, distinguishing decoded extents from Linux's
managed compressed-page cache names.
- The key retains the existing physical/logical extent fields, device id,
flags, and algorithm, and adds the stable inode `nid`. The `data` pointer is
the validity bit, so NID zero is not treated as an empty key.
## Copy bound and concurrency
`z_erofs_read_uio()` limits each output request to `MAXPHYS`. The generic
`z_erofs_read_data()` API can receive a larger request, so cache eligibility
rejects any request whose mapped portion exceeds `MAXPHYS`. Both cache helper
entry points also contain `KASSERT(len <= MAXPHYS)` checks. The largest copy
performed while holding `z_extent_cache_lock` is therefore `MAXPHYS`; the
12 MiB `Z_EROFS_PCLUSTER_MAX_DSIZE` limit remains an on-disk decoded-extent
allocation bound, not a mutex-copy bound.
Allocation, compressed reads, and decompression happen outside the mutex. A
cache hit copies while holding the mutex, and a miss publishes and copies
under the same mutex before the previous allocation is freed. Concurrent
misses may decode duplicate extents and may replace one another, but pointer
access and replacement remain serialized. A refcounted immutable cache entry
was deliberately not introduced in this first stage.
## Resource tradeoff
The per-mount entry is intentionally a small first-stage design. It removes
the unbounded-per-open-vnode retention introduced by `61f4709`, but it can
thrash when many files are read concurrently on one mount. Retention lasts
until unmount, and the retained allocation is capped by the existing EROFS
format constant `Z_EROFS_PCLUSTER_MAX_DSIZE` (12 MiB). A FreeBSD shrinker or
pressure callback is deferred; adding one would require a broader memory
accounting and lifecycle design than this corrective commit.
## Validation scope
This correction is statically validated only. QEMU validation remains
required for complete SHA-256 reads, same-file concurrent reads, close/reopen,
unmount cleanup, metadata tailpacking, and memory-pressure behavior. The
existing Pre9 manual test report predates this correction and must not be
reported as runtime validation of this commit.
@@ -1,26 +0,0 @@
Run3 full-file SHA samples, captured approximately eight seconds after launch
repo-pre-7
state: RC
file offset: 1245184
kernel stack: lzma2_lzma -> erofs_xz_dec_microlzma_run -> lzma_decompress -> z_erofs_decompress -> z_erofs_do_read -> z_erofs_read_uio -> VOP_READ_APV -> vn_read
sha_after: no process row, no sha.out hash, no sha.err content
termination: TERM sent; KILL then reported No such process
repo-pre-8
state: RC
file offset: 1150976
kernel stack: lzma2_lzma -> erofs_xz_dec_microlzma_run -> lzma_decompress -> z_erofs_decompress -> z_erofs_do_read -> z_erofs_read_uio -> VOP_READ_APV -> vn_read
sha_after: no process row, no sha.out hash, no sha.err content
termination: TERM sent; KILL then reported No such process
repo-pre-9
state: RC
file offset: 1044480
kernel stack: lzma2_lzma -> erofs_xz_dec_microlzma_run -> lzma_decompress -> z_erofs_decompress -> z_erofs_do_read -> z_erofs_read_uio -> VOP_READ_APV -> vn_read
sha_after: no process row, no sha.out hash, no sha.err content
termination: TERM sent; KILL then reported No such process
Interpretation: the SHA processes were alive and executing in LZMA decode at
the sample. Their later disappearance does not prove successful completion.
No full-file hash was captured, so full-file correctness remains BLOCKED.
@@ -1,16 +0,0 @@
Pre9 final decoded extent cache kernel and cleanup summary
Date: 2026-08-13
- Guest: FreeBSD 15.0-RELEASE-p8 amd64, QEMU TCG, 6144 MiB, 4 vCPUs.
- KLD built with WITH_ZSTDIO=0 and loaded successfully.
- No EROFS panic, assertion, mount error or decompression error appeared in
the captured dmesg tail.
- Boot emitted pre-existing root-filesystem "bad dir ino" warnings before
the test KLD was loaded; these are not attributed to EROFS.
- Explicit LZMA probe, LZMA full-read and LZ4 unmount/md detach operations
returned zero.
- Test QEMU PID 812999 was terminated.
- Host port 10030 was closed.
- Test overlay was removed.
- Guard QEMU PID 26318 remained alive.
- Guard SSH port 9222 remained open.
@@ -1,21 +0,0 @@
Pre9 final decoded extent cache target-file syscall evidence
Date: 2026-08-13
4 KiB:
pread(3, ..., 4096, 0x0) = 4096 (0x1000)
output/source SHA-256: 1c3c59331a4849514667bbc51c078327577b98f75a62996bbc2fe310b014dd79
16 KiB:
pread(3, ..., 16384, 0x0) = 16384 (0x4000)
output/source SHA-256: 2433ce7c2ea151f487a319447ce4fe14e1a2c2af9f0e5113fe47fc74b6138eee
64 KiB:
pread(3, ..., 65536, 0x0) = 65536 (0x10000)
output/source SHA-256: 2bb87afd3b9fab420cbe55b782d69a7342150e8816a35efacf22d2ab3012815d
1 MiB:
pread(3, ..., 1048576, 0x0) = 1048576 (0x100000)
output/source SHA-256: 52e806509e5dfeb523904f167fca765d001ea749351ecf51738e61a5352a1ba3
Each trace contained exactly one pread() against the mounted target file.
Dynamic-loader pread() calls were excluded from this summary.
@@ -1,100 +0,0 @@
{
"archive_sha256": "a6a6d573ace42fe713529974c7bd20b472a1e4521920fac40e01c5d3bb071bcb",
"cleanup": {
"guard_pid_alive": true,
"guard_port_9222_open": true,
"overlay_exists": false,
"port_10030_open": false,
"qemu_pid_alive": false
},
"finished_utc": "2026-08-13T06:06:25Z",
"kld_sha256": "473a6205f43905972f2417609d43f67ab2cb51ea79bc1716fffa5c1914ff85af",
"lz4_sha": {
"expected_sha256": "3ff012b76087c4da65ce0b69813a76f47ea367e95782edf8b8cd6cd3ec4d1880",
"guest_real_seconds": 9.03,
"hash_match": true,
"returncode": 0,
"sha256": "3ff012b76087c4da65ce0b69813a76f47ea367e95782edf8b8cd6cd3ec4d1880"
},
"lzma_concurrent": {
"guest_real_seconds": [
22.52,
21.97
],
"hash_match": true,
"hashes": [
"ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461",
"ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461"
],
"host_timeout": false,
"returncode": 0,
"returncodes": [
0,
0
]
},
"lzma_fixture": {
"image_sha256": "32107a084b27362a093768b88746c37c2e99a74b9f1301a9d4046988479defe9",
"source_sha256": "ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461"
},
"lzma_sha": {
"cold": {
"guest_real_seconds": 2.16,
"hash_match": true,
"host_timeout": false,
"returncode": 0,
"sha256": "ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461"
},
"warm": {
"guest_real_seconds": 1.44,
"hash_match": true,
"host_timeout": false,
"returncode": 0,
"sha256": "ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461"
}
},
"repo_pre_9_tree": "9d7eaf63a1c5d07320af3ef39930fc0a3530dd11",
"schema": "pre9-cache-final-manual-v1",
"single_pread": [
{
"elapsed_seconds": 2.696,
"hash_match": true,
"length": 4096,
"returncode": 0,
"sha256": "1c3c59331a4849514667bbc51c078327577b98f75a62996bbc2fe310b014dd79"
},
{
"elapsed_seconds": 2.14,
"hash_match": true,
"length": 16384,
"returncode": 0,
"sha256": "2433ce7c2ea151f487a319447ce4fe14e1a2c2af9f0e5113fe47fc74b6138eee"
},
{
"elapsed_seconds": 1.369,
"hash_match": true,
"length": 65536,
"returncode": 0,
"sha256": "2bb87afd3b9fab420cbe55b782d69a7342150e8816a35efacf22d2ab3012815d"
},
{
"elapsed_seconds": 3.838,
"hash_match": true,
"length": 1048576,
"returncode": 0,
"sha256": "52e806509e5dfeb523904f167fca765d001ea749351ecf51738e61a5352a1ba3"
}
],
"src_tree": "e657e8a63b097b061670f75ca01947a568ef33d5",
"started_utc": "2026-08-13T05:59:41Z",
"tested_head": "cdcf276d12169a672d2de42996532a470ac061d9",
"verdict": {
"build_load_mount": "PASS",
"cleanup": "PASS",
"lzma_concurrency": "PASS",
"lzma_full_correctness": "PASS",
"lzma_liveness_performance": "PASS",
"lzma_single_pread_correctness": "PASS",
"non_lzma_lz4": "PASS"
}
}
@@ -1,22 +0,0 @@
Run3 target-file syscall evidence (dynamic-loader pread calls omitted)
repo-pre-7
4096: pread(fd, ..., 4096, 0) = 4096; process exit 0
16384: pread(fd, ..., 16384, 0) = 16384; process exit 0
65536: pread(fd, ..., 65536, 0) = 65536; process exit 0
1048576: pread(fd, ..., 1048576, 0) = 1048576; process exit 0
repo-pre-8
4096: pread(fd, ..., 4096, 0) = 4096; process exit 0
16384: pread(fd, ..., 16384, 0) = 16384; process exit 0
65536: pread(fd, ..., 65536, 0) = 65536; process exit 0
1048576: pread(fd, ..., 1048576, 0) = 1048576; process exit 0
repo-pre-9
4096: pread(fd, ..., 4096, 0) = 4096; process exit 0
16384: pread(fd, ..., 16384, 0) = 16384; process exit 0
65536: pread(fd, ..., 65536, 0) = 65536; process exit 0
1048576: pread(fd, ..., 1048576, 0) = 1048576; process exit 0
All mounted-output hashes matched corresponding source-range hashes. The full
hash values and elapsed times are retained in run3-audit.json.
-62
View File
@@ -1,62 +0,0 @@
{
"base_image": {
"before": "67f359621f23a1d745f0889370cbb99a096cee3e99a0b2f3bb18fc7a91bf6fef",
"after": "67f359621f23a1d745f0889370cbb99a096cee3e99a0b2f3bb18fc7a91bf6fef",
"expected": "67f359621f23a1d745f0889370cbb99a096cee3e99a0b2f3bb18fc7a91bf6fef",
"unchanged": true
},
"fixture": {
"image_sha256": "32107a084b27362a093768b88746c37c2e99a74b9f1301a9d4046988479defe9",
"source_sha256": "ddda39737f0f6093e828a032ec161511fefbb1fa361bc6cbffdbc91e48e4c461"
},
"versions": [
{
"name": "repo-pre-7",
"repository_tree": "b8f9af2cd55225c4348b79ff5910ae6fc83cd517",
"src_tree": "dc203534d7b5721905f8538026e4c59346106020",
"kld_sha256": "33a52f2f16a94afda0501d305b238678b96c71e420d4b8bbcbeec6ffcdf1aae5",
"probes": [
{"length": 4096, "returned": 4096, "elapsed_seconds": 1.2993653398007154, "hash_match": true, "target_pread_count": 1},
{"length": 16384, "returned": 16384, "elapsed_seconds": 1.2910558842122555, "hash_match": true, "target_pread_count": 1},
{"length": 65536, "returned": 65536, "elapsed_seconds": 1.4380157887935638, "hash_match": true, "target_pread_count": 1},
{"length": 1048576, "returned": 1048576, "elapsed_seconds": 1.4320225361734629, "hash_match": true, "target_pread_count": 1}
],
"dd_1m_seconds": 0.195526,
"sha_sample_offset": 1245184,
"sha_hash_captured": false,
"cleanup": {"umount": 0, "mdconfig_detach": 0, "kldunload": 0, "qemu_alive": false, "overlay_exists": false}
},
{
"name": "repo-pre-8",
"repository_tree": "1e24c992a5b071e6fdabfdc42d342b6ed5a91cf5",
"src_tree": "cbf93a19df8d1ce2fb66e2aeb7276a4ea6d4dcc2",
"kld_sha256": "348cb1f5a91d47e1412747d285c795c49375a61a66cfb3d35d22f38ecfde6ea0",
"probes": [
{"length": 4096, "returned": 4096, "elapsed_seconds": 1.2161498833447695, "hash_match": true, "target_pread_count": 1},
{"length": 16384, "returned": 16384, "elapsed_seconds": 1.3266378343105316, "hash_match": true, "target_pread_count": 1},
{"length": 65536, "returned": 65536, "elapsed_seconds": 1.4296557288616896, "hash_match": true, "target_pread_count": 1},
{"length": 1048576, "returned": 1048576, "elapsed_seconds": 1.4422911144793034, "hash_match": true, "target_pread_count": 1}
],
"dd_1m_seconds": 0.179484,
"sha_sample_offset": 1150976,
"sha_hash_captured": false,
"cleanup": {"umount": 0, "mdconfig_detach": 0, "kldunload": 0, "qemu_alive": false, "overlay_exists": false}
},
{
"name": "repo-pre-9",
"repository_tree": "dc0c01157b5c925684bd77c57ed6703904af7453",
"src_tree": "cbf93a19df8d1ce2fb66e2aeb7276a4ea6d4dcc2",
"kld_sha256": "348cb1f5a91d47e1412747d285c795c49375a61a66cfb3d35d22f38ecfde6ea0",
"probes": [
{"length": 4096, "returned": 4096, "elapsed_seconds": 1.7912541721016169, "hash_match": true, "target_pread_count": 1},
{"length": 16384, "returned": 16384, "elapsed_seconds": 1.341409295797348, "hash_match": true, "target_pread_count": 1},
{"length": 65536, "returned": 65536, "elapsed_seconds": 1.3740410972386599, "hash_match": true, "target_pread_count": 1},
{"length": 1048576, "returned": 1048576, "elapsed_seconds": 1.737462431192398, "hash_match": true, "target_pread_count": 1}
],
"dd_1m_seconds": 0.367417,
"sha_sample_offset": 1044480,
"sha_hash_captured": false,
"cleanup": {"umount": 0, "mdconfig_detach": 0, "kldunload": 0, "qemu_alive": false, "overlay_exists": false}
}
]
}
-37
View File
@@ -1,37 +0,0 @@
# repo22 Linux/FreeBSD 结构对齐维护计划
## 基线
- FreeBSD 实现:`src/`
- Linux 参考:`/work/dev-src-linux/fs/erofs`Linux 7.1-rc1 快照)
- 比较方法:逐文件和逐职责比较,不依赖共同 Git 历史
- 运行目标:FreeBSD 15 amd64
## 决策规则
1. 先判断差异是否来自 FreeBSD vnode、GEOM、errno、锁或内核库接口。
2. 只对非必要差异调整静态函数名、变量名、定义顺序和文件职责。
3. 不为匹配 Linux 文件表而添加空模块或不适用的抽象。
4. `src/Makefile` 是模块名、源码清单、架构门控和编译选项的权威来源。
5. 构建产物、生成头和机器 include symlink 不进入版本控制。
## 本轮清单
- [x] 清除受跟踪的 `build/obj` 生成头和机器 symlink
- [x] 精确忽略模块构建及手工测试生成目录
- [x] 由原生 `bsd.kmod.mk` 取代手写 amd64 clang/link 流程
- [x] 明确拒绝未验证的非 amd64 架构
- [x] 将 ZSTDIO 配置统一为 `WITH_ZSTDIO=0/1`
- [x] 按 Linux 顺序整理 Makefile 源码列表
- [x] 对齐 `find_target_dirent``decompressor_*.c` 名称
- [x] 将跨文件解压后端声明集中到 `internal.h`
- [x] 保留 BSD 专属 `erofs_vnops.c``lz4.c` 和后端调用契约
- [x] 在 FreeBSD 15 VM 完成双配置构建和 ZSTD 挂载 smoke
## 验证门槛
1. `WITH_ZSTDIO=0``WITH_ZSTDIO=1` 均从空对象目录构建。
2. 禁用产物不含 `ZSTD_*` 未解析符号,启用产物必须含预期内核 API。
3. 启用产物完成加载、ZSTD 镜像挂载、文件读取、卸载和 KLD 清理。
4. 非 `amd64` 和非法 `WITH_ZSTDIO` 值必须明确失败。
5. 提交只包含 repo22 pathspec,且远端 `xdm/main` 与本地 HEAD 一致。
-40
View File
@@ -1,40 +0,0 @@
# repo22 Linux/FreeBSD 结构对齐状态
## 已完成
- 使用 `/work/dev-src-linux/fs/erofs` 的 Linux 7.1-rc1 快照逐文件复核;未使用
两个实现共享历史的假设。
- `namei.c` 的静态 helper 已从 `erofs_find_target_dirent` 恢复为 Linux 名称
`find_target_dirent`
- `lzma.c``deflate.c``zstd.c` 已按 Linux 职责名调整为
`decompressor_lzma.c``decompressor_deflate.c`
`decompressor_zstd.c`
- 解压后端原型已移入 `internal.h`MicroLZMA 内嵌 XZ 的 allocator helper
已限制为编译单元内部符号。
- `src/Makefile` 已按 Linux 的 metadata、压缩调度/映射、算法后端顺序组织,
并成为 `build.sh` 的唯一源码和模块名来源。
- 受跟踪的对象目录、vnode 生成头和 amd64/x86 机器 symlink 已删除并忽略。
## 保留差异
- `erofs_vnops.c` 承载 FreeBSD vnode、pager、NFS 和只读操作语义。
- `lz4.c` 保持独立,因为 BSD 后端不使用 Linux page/LZ4 调度接口。
- 后端函数保持 BSD 内部 API,而不是复制 Linux decompressor descriptor 和
page 生命周期。
- FreeBSD 路径返回正 errno,并保留 GEOM/provider、lockmgr 和 vnode 规则。
- Linux `sysfs`、file-backed、fscache、page-cache sharing 和 `zutil` 职责不以
空文件模拟。
- 当前构建门控仅允许已验证的 FreeBSD 15 `amd64`
## 2026-08-09 验证
- VMFreeBSD `15.0-RELEASE-p8` amd64。
- 构建源:FreeBSD `15.0 RELEASE-p9` `sys` 树。
- `WITH_ZSTDIO=0` 构建通过,模块 SHA256
`d11d0319dc1d786eaa868a0c05c2abaf43da8480acacb0e1e4b72b7542b05432`
`ZSTD_*` 未解析符号。
- `WITH_ZSTDIO=1` 构建通过,模块 SHA256
`82471f19812e9877ea06901d1ba0cb4378b2bc6476ec70f6e024e0dc777c0c71`
编译命令包含 `-DZSTDIO` 并引用 FreeBSD 内核 ZSTD API。
- 启用模块完成加载、`zstd-level1.erofs` 只读挂载、6 个文件读取与 SHA256、
卸载、md detach 和 KLD unload;测试后无遗留挂载或模块。
View File
View File
View File
View File
View File
-15
View File
@@ -1,15 +0,0 @@
# Issue Status
This directory preserves both resolved investigations and approved validation
gaps. Resolved files remain historical evidence; they are not current failures.
| Issue | Status | Current conclusion |
| --- | --- | --- |
| [TC010 48-bit statfs](TC010-48bit-statfs-large-provider.md) | RESOLVED | A qualified 16 TiB-plus sparse provider completed mount and 64-bit `statfs` validation. |
| [TC060 pathconf](TC060-pathconf-standard-values.md) | RESOLVED | FreeBSD-standard values were implemented and dynamically revalidated. |
| [TC153 large directory index](TC153-large-directory-block-index-validation.md) | RESOLVED | The exact fixed KLD returned `EINTEGRITY` on the multi-TiB sparse-provider path. |
| [Explicit mapped extent payload](extent-metadata-fixture-unavailable.md) | SHELVED | TC146 remains PARTIAL because no independently validated positive mapped-payload fixture is available. |
The remaining shelved item records its trigger, affected feature, analysis,
attempts, results, progress, and acceptance criteria. It is a fixture/tooling
gap, not an observed kernel failure.
@@ -1,92 +0,0 @@
# TC010 48-bit statfs Large Provider
Status: **RESOLVED - qualified sparse vnode provider validated**
Resolved: 2026-08-09
Baseline: `cd0e985b5ac54a4b7acb7042422329ad1729fb3e`
## Original Problem
TC010 requires a real EROFS mount whose 48-bit superblock block count has a
nonzero high word. A nonzero `blocks_hi` declares at least `2^32` filesystem
blocks, so a 4 KiB filesystem needs a provider larger than 16 TiB. Earlier
vnode-md work used only small regular images and correctly could not claim PASS.
This affects:
- 48-bit superblock block-count decoding;
- primary-device media-size validation;
- FreeBSD `statfs(2)` and `df(1)` 64-bit totals;
- the union selector between `rb.blocks_hi` and `rb.rootnid_2b`.
## Exact Trigger
1. `EROFS_FEATURE_INCOMPAT_48BIT` is set.
2. `rootnid_8b` is nonzero, selecting `rb.blocks_hi`.
3. `blocks_hi` is nonzero.
4. Provider media size is at least `total_blocks << blkszbits`.
5. Superblock CRC32C, root inode, and normal mount checks pass.
A short provider fails with `ENXIO`; that negative guard cannot replace the
positive statfs test. TC150's `rootnid_8b == 0` fallback and multidevice totals
also select different production paths.
## Earlier Attempts
1. Structured metadata patching and CRC32C recomputation succeeded.
2. The normal small vnode provider failed media-size validation as expected.
3. Static source comparison confirmed Linux's `48BIT && rootnid_8b` selector,
but static arithmetic was not counted as dynamic coverage.
4. No earlier run had attached and mounted a qualified 16 TiB provider, so the
issue remained SHELVED.
## Resolution
The isolated FreeBSD 15.0-RELEASE-p8 guest uses UFS2, which supports a sparse
regular file large enough for vnode md without allocating 16 TiB physically:
```text
truncate -s 17592193667072 TC010-provider.raw
stat: size=17592193667072 allocated_blocks=15232 block_size=32768
mdconfig -a -t vnode -f TC010-provider.raw: md0
diskinfo mediasize: 17592193667072 bytes
```
The structured fixture encoded:
```text
blocks_lo=1861
blocks_hi=1
rootnid_8b=36
total_blocks=4294969157
required_provider_length=17592193667072
```
Prefix SHA256:
`d3ffadc6fc9e73f85a8a5973b4ac5ee2a2da57b4e8baeccd259fa4325a2146cf`.
The exact prefix was copied to the sparse provider, then extended with zeros.
Mount succeeded with KLD SHA256
`8349c97c7ced253fff32a9e706f29313f309cb63a270e4e39a4501426f2b95c6`.
`df` reported:
```text
Filesystem Type 1024-blocks Used Avail Capacity
/dev/md0 erofs 17179876628 17179876628 0 100%
```
`17179876628 == 4294969157 * 4`; no 32-bit wrap occurred. `f_bfree` and
available blocks were zero, so Used correctly equaled total. The control file
matched, dmesg had no new fault, and the mount, md unit, sparse provider, and
KLD were cleaned up.
## Related Coverage
- TC017 separately repeated the small-provider `ENXIO` guard and qualified
sparse-provider mount, proving the nonzero high-word parse dynamically.
- TC018 separately read exact bytes from physical offset
`17592191561728`, above 16 TiB; it did not substitute a large `df` result for
high-address data I/O.
No repo22 kernel source change was made during this resolution.
-119
View File
@@ -1,119 +0,0 @@
# TC060 Standard pathconf Values
Status: **RESOLVED - standard FreeBSD pathconf values validated**
Last updated: 2026-08-09
Resolved: 2026-08-09
Priority: High
Implementation status: Fixed by `cdba7e54fb9e9d82980a06f178e68d0bbc1663ac`
and dynamically validated
## Problem
The exact repo22 9ae22009f KLD returns EINVAL for two standard pathconf names on
an otherwise valid mounted EROFS directory:
- _PC_NO_TRUNC
- _PC_CHOWN_RESTRICTED
The same call correctly returns NAME_MAX=255, PATH_MAX=1024,
FILESIZEBITS=64, and LINK_MAX=2147483647. TC060 therefore fails at the kernel
behavior level; this is not a missing guest tool or fixture limitation.
## Trigger and Evidence
Environment:
- FreeBSD 15.0-RELEASE-p8 amd64 guest on port 9222.
- Exact source commit: 9ae22009f23a65320730072a780998e80aa9b728.
- KLD SHA256:
19ad086bd2508cbb4c57b1a51f38c93057d438b84fbcfa2e637ff2519f5bc590.
- Fixture: vfs-plain.erofs SHA256
79f0b5f4aa8e7ea532b711ee8fb466f14f35d0952446d41ba4bbc4d6e008b8ff.
Direct command:
/tmp/repo22-g3/g3_vfs_probe pathconf \
/tmp/repo22-g3/mnt/testdir
Observed output and process status:
name_max=255 path_max=1024 filesizebits=64 link_max=2147483647 \
no_trunc=error:22 chown_restricted=error:22
g3_vfs_probe: unexpected EROFS pathconf values
probe_status=1
The test used a fresh dynamic md0, a read-only EROFS mount, and the exact KLD.
After capture, umount and md detach both returned zero. Final guest state was
zero EROFS mounts, zero md units, and no loaded EROFS KLD.
## Analysis
erofs_pathconf() handles NAME_MAX, PATH_MAX, FILESIZEBITS, LINK_MAX, and ACL
queries directly, then delegates other names to vop_stdpathconf(). In this
FreeBSD 15 configuration the delegation returns EINVAL for both names above.
The mounted filesystem is immutable and enforces 255-byte names, so reporting
NO_TRUNC=1 and CHOWN_RESTRICTED=1 is consistent with the implemented behavior
and with the TC contract.
This failure affects applications that use pathconf(2) to discover pathname
truncation and ownership-change restrictions. It does not affect the four
values that repo22 already handles directly.
## Original Required Resolution
Add explicit FreeBSD vnode pathconf handling for both standard names, then
rerun TC060 with the direct syscall helper. Acceptance requires both values to
be 1, all six queries to have errno 0, unchanged mount/read behavior, no new
dmesg diagnostics, and complete mount/md/KLD cleanup.
## Resolution
FreeBSD 15's `vop_stdpathconf()` intentionally provides only generic values
such as `_PC_ASYNC_IO`, `_PC_PATH_MAX`, and zero-valued optional features; its
default case returns `EINVAL`. UFS, tmpfs, and ext2fs therefore implement
`_PC_CHOWN_RESTRICTED` and `_PC_NO_TRUNC` in their filesystem pathconf methods
before delegating all remaining names to `vop_stdpathconf()`.
EROFS now follows that FreeBSD pattern: the two names return 1, while the
existing NAME_MAX, PATH_MAX, FILESIZEBITS, LINK_MAX, and ACL cases remain
unchanged and the default branch still calls `vop_stdpathconf()`. This matches
the implemented filesystem behavior: uid/gid mutation is rejected with EROFS,
and lookup of a component longer than `EROFS_NAME_LEN` returns ENAMETOOLONG.
No lock, allocation, reference, or cleanup path was added.
Linux EROFS was used only as a maintenance comparison for the 255-byte name
limit and ENAMETOOLONG behavior. FreeBSD 15 VOP and syscall semantics were the
authority for the returned values and errno behavior.
## Qualified Retest
The exact source archive for the fix commit built natively on FreeBSD
15.0-RELEASE-p8 with kernel `-Werror` in both configurations:
- `WITH_ZSTDIO=0`: KLD SHA256
`68536e03ce93c6c04aab9cf29dab81ee5802d4b94401bd1a4bd752de40c0e504`.
- `WITH_ZSTDIO=1`: KLD SHA256
`598f171d355af78c64407a6d513d20f053530620da92df7f8ccfdf9a804e463d`.
The dependency-minimal `WITH_ZSTDIO=0` KLD and the original TC060 helper
reported:
```text
name_max=255 path_max=1024 filesizebits=64 link_max=2147483647
no_trunc=1 chown_restricted=1
```
All six queries had errno 0. Additional direct checks returned ASYNC_IO=200112,
ACL_EXTENDED=1, ACL_PATH_MAX=254, and ACL_NFS4=0 with errno 0. An unknown name
returned `-1/EINVAL(22)`, and a 256-byte component returned
`ENAMETOOLONG(63)`. The mounted file hash and read-only EROFS behavior were
unchanged, dmesg was byte-identical before and after, and final mount/md/KLD
and guest artifact counts were zero.
Complete build, value, smoke, discarded-attempt, review, and cleanup evidence
is in
`tests/results/manual/2026-08-09T1120Z-tc060-fix/manual-test-report.md`.
@@ -1,193 +0,0 @@
# TC153 Large Directory Block Index Validation
Status: **RESOLVED - exact-source kernel validation passed**
Last updated: 2026-08-09
Priority: Critical validation gap
Implementation status: Source fix present, build-verified, and dynamically
validated
## Problem
`erofs_find_target_block()` used signed `int` values for the binary-search
front, back, and midpoint indexes. A directory whose logical size describes
`2147483649` 4 KiB blocks produces a final block index of `2147483648`.
Converting that value to `int` can make the initial back bound negative. The
search can then return `ENOENT` without reading any directory block, and the
FreeBSD namecache can retain a false negative result for corrupt media.
The review change uses `uint64_t` for block-search bounds and midpoints, checks
the midpoint multiplication, and handles the `mid == 0` lower-bound case. The
within-block search remains `uint32_t` because a validated EROFS directory
block can contain only a block-sized number of entries.
## Affected Features
- Cold pathname lookup in very large or maliciously sized directories.
- Corruption reporting and `EINTEGRITY` propagation from directory reads.
- Negative namecache correctness after a failed lookup.
- Linux/FreeBSD behavioral review of EROFS's two-level directory search.
Normal directories are not expected to approach this bound. The practical
risk is a crafted image causing an incorrect `ENOENT`, not an ordinary mkfs
image losing entries.
## Trigger Conditions
All of the following are needed to exercise the original width error:
1. A directory inode is accepted with a logical block count greater than
`INT_MAX`.
2. The directory layout reaches `erofs_find_target_block()` rather than being
rejected by an earlier inline-data bounds check.
3. A cold lookup is issued for a name that is not satisfied by an already
cached vnode or negative namecache entry.
4. The backing image is too short for the computed midpoint read, so the fixed
code should return `EINTEGRITY` instead of a synthetic miss.
For 4 KiB blocks, TC153 encodes a size of `8796093026304` bytes,
`2147483649` blocks, and a final index of `2147483648`.
## Current Analysis
The source change is mechanically narrow and has passed both repo22 build
configurations. The checked multiplication protects a future block-size or
index change even though the current `OFF_MAX` inode limit already bounds the
product. The `mid == 0` guard prevents unsigned subtraction from wrapping when
the search moves below the first block.
Dynamic proof must show more than a large `st_size`. It must prove that the
lookup enters the block binary search. An image rejected in
`erofs_read_inode()` is not evidence for this fix, and an `ENOENT` observed
after a warm negative cache is ambiguous.
## Attempts and Results
### Attempt 1: Small FLAT_INLINE image
- Artifact directory:
`/work/build/repo22-review-artifact.LtqNxH`
- Base image: `large-dir-base.erofs`, 4096 bytes, SHA256
`973d2a1c90ac1bf776ed76a1fc6a7e88b2156fac3bf04ddbae19cfafebbd562e`
- Patched image: `large-dir-intmax.erofs`, 4096 bytes, SHA256
`ce5cad3eff34ea50ca89eedf93d8ef90e6cea96858a138685fae45f39084de1f`
- Recorded inode: NID 40 at byte 1280.
The directory was Layout 2 (`FLAT_INLINE`). Patching only `i_size` made its
declared inline tail range invalid, so inode validation rejected it before
`erofs_find_target_block()`. This attempt is invalid as TC153 dynamic evidence
and must never be marked PASS.
### Attempt 2: More entries to force block data
- Artifact directory:
`/work/build/repo22-review-artifact2.FmMTup`
- Generated base: `large-dir-base.erofs`, 65536 bytes, SHA256
`50eb28351788ab1801408aca0a6fca6352755f5ca56efe6e2bb3e2fe08305663`
- Directory: NID 40, 21052 bytes, still Layout 2.
The generation process was interrupted before producing a patched image or a
FreeBSD result. A later deterministic rerun reproduced the same base hash and
showed why the approach failed: erofs-utils 1.8.6 tail-packs directories even
when they span multiple data blocks. Its `noinline_data` option does not turn
directory tail packing off.
### Attempt 3: Follow-up agents
Multiple follow-up agents were assigned the fixture and guest validation.
They either stopped while preparing the second image or returned no executed
commands, files, hashes, or guest output. These attempts added no evidence and
are recorded to prevent their elapsed time from being mistaken for progress.
### Attempt 4: Sparse-provider design
A guest-side sparse vnode provider was considered so a valid directory could
declare the complete multi-terabyte range. It was not executed. Such a scheme
must prove the GEOM media size, avoid materializing terabytes, preserve valid
initial directory blocks, and still force a cold read at the binary-search
midpoint. No result exists for this approach.
### Attempt 5: Reproducible FLAT_PLAIN conversion
The tracked generator now copies all 21052 bytes of the generated directory
to appended blocks, changes only that inode to Layout 0 (`FLAT_PLAIN`), updates
the image block count and CRC32C, and then applies the large logical size.
Host reproduction on 2026-08-09 produced:
- `large-dir-base.erofs`: 65536 bytes, SHA256
`50eb28351788ab1801408aca0a6fca6352755f5ca56efe6e2bb3e2fe08305663`
- `large-dir-intmax.erofs`: 90112 bytes, SHA256
`0f90d3d57adbbbd946e41b225c1f6c464915c6abb0b13478ec9b2a318def1f72`
- Patched inode: NID 40 at byte 1280, Layout 0, raw block 16.
- Declared image blocks: 22.
This resolved the fixture-construction problem. The qualified kernel run is
recorded below.
### Attempt 6: Qualified sparse GEOM provider
The G3 takeover generated a checksum-valid sparse prefix from Attempt 5 and
performed one targeted FreeBSD 15 run against the exact source requested by
the assignment.
- Source commit:
9ae22009f23a65320730072a780998e80aa9b728.
- KLD SHA256:
19ad086bd2508cbb4c57b1a51f38c93057d438b84fbcfa2e637ff2519f5bc590.
- Sparse-prefix SHA256:
f9337f83b1f568f6d904331a7749e6362a691055cd5af95b59bc89772f04e3b0.
- Prefix qualification: NID 40, inode offset 1280, extended Layout 0,
start block 16, 2147483649 directory blocks, final block index 2147483648,
valid superblock checksum.
- Sparse provider: 8796093091840 logical bytes, 448 allocated 512-byte
sectors, and the first 90112 bytes retained the prefix SHA256.
- GEOM diskinfo size: 8796093091840 bytes.
- Mounted /huge: size 8796093026304, NID 40.
The first two cold stat operations for /huge/missing each returned EINTEGRITY
(97). Root readdir then returned three complete entries and validated all
kernel/libc restart cookies. A third lookup after that readdir again returned
EINTEGRITY. No lookup returned ENOENT, so no false negative namecache entry
masked the corruption.
Unmount, md detach, sparse-provider removal, and KLD unload all succeeded.
The only new dmesg lines in the complete G3 run were expected SIGBUS child
exits from the unrelated assigned mmap tests; TC153 added no diagnostic.
## Historical Remaining Validation
Attempt 6 completed steps 1-5 and 7 below. The assignment mandated the exact
fixed source, so the optional pre-fix comparison in step 6 was not run.
1. Regenerate the images with
`tests/results/manual/2026-08-09T0124Z-final-review/prepare-fixtures.sh` and
verify the hashes and Layout 0 assertion.
2. Load the exact module under test in a clean FreeBSD 15 guest.
3. Mount the 90112-byte image and confirm `/huge` reports
`8796093026304` bytes.
4. With a cold parent cache, run two lookups of `/huge/missing` and capture
direct command exit codes plus `truss` errno.
5. Require `EINTEGRITY` on every fixed-module lookup, including after root
readdir. Confirm no negative cache changes the result.
6. Repeat with the pre-fix module to demonstrate the erroneous `ENOENT` path.
7. Record dmesg, mount/md/KLD cleanup, module and fixture hashes in a dated
manual report.
## Original Acceptance Criteria
TC153 can move from SHELVED to PASS only after the fixed FreeBSD KLD returns
`EINTEGRITY` repeatedly from the valid Layout 0 fixture, the pre-fix behavior
is distinguished, no trap or panic occurs, and complete cleanup evidence is
recorded. Host fixture generation and static source review alone are
insufficient.
## Resolution
TC153 is PASS because the exact fixed FreeBSD KLD returned EINTEGRITY
repeatedly from the valid Layout 0 fixture before and after root readdir, no
trap or panic occurred, and complete hash/provider/cleanup evidence is recorded
in tests/results/manual/2026-08-09T1059Z-g3/manual-test-report.md. Host fixture
generation and static source review alone remain insufficient.
@@ -1,169 +0,0 @@
# Explicit Extent Metadata Fixture Unavailable
Status: **SHELVED - positive mapped payload unavailable**
Last updated: 2026-08-09, final aggregation
Latest reviewed baseline: `fcc85b93d5f8fd9671686bd68bf3b086c8bd25cf`
Priority: High validation gap
Implementation status: ABI and control flow implemented; positive mapped
kernel read pending
## Problem
repo22 implements the newer compressed-extent metadata path selected by
`Z_EROFS_ADVISE_EXTENTS`. Dynamic positive validation requires an image with a
valid extent table that maps a real payload. erofs-utils 1.8.6 cannot emit or
validate this on-disk format, and the repository's structured helpers cannot
currently relocate a legacy compressed inode into a validated positive extent
table.
HEAD2, interlaced pclusters, legacy full/compact indexes, partial references,
fragments, and all four decoders have separate dynamic coverage. None of those
substitutes for entering `z_erofs_map_blocks_ext()` with a mapped payload.
## Required Trigger
1. The inode uses compressed-full layout.
2. The map header sets `Z_EROFS_ADVISE_EXTENTS`.
3. The selected 4-, 8-, 16-, or 32-byte records form a complete valid table.
4. Physical fields identify real payload bytes for the declared algorithm.
5. Logical starts, physical lengths, partial flags, and final-record semantics
agree with the source file.
6. A FreeBSD read reaches the explicit mapper and returns source-identical
bytes.
Zero-count tables, overflowing physical bases, holes, and ordinary full-index
images are negative or adjacent coverage, not this trigger.
## G5 Independent Attempts
All inputs below were generated in new G5 output directories. No old image or
report was consumed as a fixture.
### 1. erofs-utils 1.8.6 capability scan
Observed tool output:
```text
mkfs.erofs (erofs-utils) 1.8.6
available compressors: lz4, lz4hc, lzma, deflate, libdeflate, zstd
```
The helper searched the installed 1.8.6 `include/` and `lib/` trees for these
on-disk symbols:
```text
Z_EROFS_ADVISE_EXTENTS: 0 matches
z_erofs_extent_recsize: 0 matches
struct z_erofs_extent {: 0 matches
```
The mkfs help has no explicit-record option. `--max-extent-bytes` only limits
decompressed extent size.
Result: **generator unavailable**.
### 2. `--max-extent-bytes` generation attempt
`g5_fixtures.py` generated a fresh LZ4 full-index image with:
```text
-zlz4 -C65536 -Elegacy-compress --max-extent-bytes=65536
```
Structured reopen and `dump.erofs` produced:
```text
image = images/extent-attempt.erofs
image SHA256 = ee7472c23ccffd5eef6f4a3171ea53ae2e840f8a1ea417b2630065175ecf3225
source SHA256 = 5be510e6b43f1ed0cda4261288ea70df11607b6ced29bc90d32ed2849ecc5e35
inode layout = 1 (compressed full)
map header offset = 1312
h_advise = 2
HEAD1 algorithm = 0 (LZ4)
explicit bit selected = no
```
`h_advise=2` is ordinary HEAD1 big-pcluster metadata. Naming or sizing the
image as an extent attempt does not enter the explicit mapper.
Result: **not an explicit fixture**.
### 3. Structured conversion attempt
The G5 transformer parsed the source inode, map header, full lcluster indexes,
physical extents, and payload bounds. It refused to emit a purported positive
fixture because conversion requires relocating variable-size extent records,
subsequent inode metadata, and possibly payload blocks. erofs-utils 1.8.6
cannot reopen and validate the resulting ABI, so an ad hoc rewrite would not
provide trustworthy evidence.
The existing `tests/review_fixtures.py` helper was also reviewed. Its
`extent-pa-wrap.erofs` conversion deliberately writes two 4-byte records and a
physical base whose `pa + plen` overflows. That is a negative overflow trigger
for TC155. It does not describe a valid mapped payload and cannot close this
issue.
Result: **structured positive conversion unavailable**.
### 4. ABI and control-flow comparison
The G5 review compared repo22 `src/erofs_fs.h`/`src/zmap.c` with the Linux EROFS
definitions and mapper. Both sides define:
- record sizes 4, 8, 16, and 32 bytes;
- implicit 64-bit physical bases for 4-byte records;
- per-record low physical starts for 8-byte records;
- explicit counts and binary search for 16/32-byte records;
- high physical and logical words where the record size carries them;
- partial-reference, shifted/interlaced format, and final fragment handling;
- malformed-count and arithmetic bounds checks.
This reduces implementation risk but is static evidence only.
## Impact
- TC146 HEAD2: dynamic PASS.
- TC146 interlaced: dynamic PASS.
- TC146 explicit mapped payload: SHELVED.
- TC146 overall: **PARTIAL**, never overall PASS while this issue remains.
- Record-size variants, positive binary-search transitions, mapped high words,
and explicit-record partial references remain dynamically unvalidated.
No repo22 kernel source was changed because this is a fixture/tooling gap, not
an observed kernel failure.
## Progress
- [x] Reproduce tool limitation against exactly erofs-utils 1.8.6.
- [x] Record tool output and zero symbol matches.
- [x] Generate and inspect a fresh `--max-extent-bytes` attempt.
- [x] Record source/image hashes and map-header fields.
- [x] Audit the existing structured negative helper.
- [x] Compare FreeBSD and Linux record layouts/control flow.
- [x] Reject globally unordered 16-byte and 32-byte explicit tables through
TC157 on FreeBSD 15 without reading the referenced payload.
- [ ] Obtain a producer or validator for positive mapped extent records.
- [ ] Generate at least one source-identical mapped payload.
- [ ] Cover 4/8/16/32-byte positive records where applicable.
- [ ] Exercise binary-search transitions, partial refs, high words, and final
fragments dynamically.
## Acceptance Criteria
This issue can close only after a reproducible helper emits a valid mapped
compressed payload, records its structural fields and hashes, validates it
with an independent format-aware implementation, and FreeBSD full/boundary
reads match the source. Negative overflow/hole fixtures and static review do
not satisfy acceptance.
## Final Residual Risk
TC157 closes the known binary-search ordering bug for descending, duplicate,
and cross-branch 16-byte and 32-byte tables. It does not supply the missing
positive mapped payload, dynamically cover every record-size variant, or
benchmark extremely large extent counts. Those limits do not change the issue
status or TC146's PARTIAL result.
View File
View File
-199
View File
@@ -1,199 +0,0 @@
# Basic .clang-format
---
BasedOnStyle: WebKit
AlignAfterOpenBracket: DontAlign
AlignConsecutiveMacros: AcrossEmptyLines
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: false
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: TopLevelDefinitions
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: WebKit
BreakBeforeTernaryOperators: false
# TODO: BreakStringLiterals can cause very strange formatting so turn it off?
BreakStringLiterals: false
# Prefer:
# some_var = function(arg1,
# arg2)
# over:
# some_var =
# function(arg1, arg2)
PenaltyBreakAssignment: 100
# Prefer:
# some_long_function(arg1, arg2
# arg3)
# over:
# some_long_function(
# arg1, arg2, arg3)
PenaltyBreakBeforeFirstCallParameter: 100
CompactNamespaces: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros:
- ARB_ARRFOREACH
- ARB_ARRFOREACH_REVWCOND
- ARB_ARRFOREACH_REVERSE
- ARB_FOREACH
- ARB_FOREACH_FROM
- ARB_FOREACH_SAFE
- ARB_FOREACH_REVERSE
- ARB_FOREACH_REVERSE_FROM
- ARB_FOREACH_REVERSE_SAFE
- BIT_FOREACH_ISCLR
- BIT_FOREACH_ISSET
- CPU_FOREACH
- CPU_FOREACH_ISCLR
- CPU_FOREACH_ISSET
- FOREACH_THREAD_IN_PROC
- FOREACH_PROC_IN_SYSTEM
- FOREACH_PRISON_CHILD
- FOREACH_PRISON_DESCENDANT
- FOREACH_PRISON_DESCENDANT_LOCKED
- FOREACH_PRISON_DESCENDANT_LOCKED_LEVEL
- MNT_VNODE_FOREACH_ALL
- MNT_VNODE_FOREACH_ACTIVE
- RB_FOREACH
- RB_FOREACH_FROM
- RB_FOREACH_SAFE
- RB_FOREACH_REVERSE
- RB_FOREACH_REVERSE_FROM
- RB_FOREACH_REVERSE_SAFE
- SLIST_FOREACH
- SLIST_FOREACH_FROM
- SLIST_FOREACH_FROM_SAFE
- SLIST_FOREACH_SAFE
- SLIST_FOREACH_PREVPTR
- SPLAY_FOREACH
- LIST_FOREACH
- LIST_FOREACH_FROM
- LIST_FOREACH_FROM_SAFE
- LIST_FOREACH_SAFE
- STAILQ_FOREACH
- STAILQ_FOREACH_FROM
- STAILQ_FOREACH_FROM_SAFE
- STAILQ_FOREACH_SAFE
- TAILQ_FOREACH
- TAILQ_FOREACH_FROM
- TAILQ_FOREACH_FROM_SAFE
- TAILQ_FOREACH_REVERSE
- TAILQ_FOREACH_REVERSE_FROM
- TAILQ_FOREACH_REVERSE_FROM_SAFE
- TAILQ_FOREACH_REVERSE_SAFE
- TAILQ_FOREACH_SAFE
- VM_MAP_ENTRY_FOREACH
- VM_PAGE_DUMP_FOREACH
SpaceBeforeParens: ControlStatementsExceptForEachMacros
IndentCaseLabels: false
IndentPPDirectives: None
Language: Cpp
NamespaceIndentation: None
PointerAlignment: Right
ContinuationIndentWidth: 4
IndentWidth: 8
TabWidth: 8
ColumnLimit: 80
UseTab: Always
SpaceAfterCStyleCast: false
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^\"opt_.*\.h\"'
Priority: 1
SortPriority: 10
- Regex: '^<sys/cdefs\.h>'
Priority: 2
SortPriority: 20
- Regex: '^<sys/types\.h>'
Priority: 2
SortPriority: 21
- Regex: '^<sys/param\.h>'
Priority: 2
SortPriority: 22
- Regex: '^<sys/systm\.h>'
Priority: 2
SortPriority: 23
- Regex: '^<sys.*/'
Priority: 2
SortPriority: 24
- Regex: '^<vm/vm\.h>'
Priority: 3
SortPriority: 30
- Regex: '^<vm/'
Priority: 3
SortPriority: 31
- Regex: '^<machine/'
Priority: 4
SortPriority: 40
- Regex: '^<(x86|amd64|i386|xen)/'
Priority: 5
SortPriority: 50
- Regex: '^<dev/'
Priority: 6
SortPriority: 60
- Regex: '^<net.*/'
Priority: 7
SortPriority: 70
- Regex: '^<protocols/'
Priority: 7
SortPriority: 71
- Regex: '^<(fs|nfs(|client|server)|ufs)/'
Priority: 8
SortPriority: 80
- Regex: '^<[^/].*\.h'
Priority: 9
SortPriority: 90
- Regex: '^\".*\.h\"'
Priority: 10
SortPriority: 100
# LLVM's header include ordering style is almost the exact opposite of ours.
# Unfortunately, they have hard-coded their preferences into clang-format.
# Clobbering this regular expression to avoid matching prevents non-system
# headers from being forcibly moved to the top of the include list.
# http://llvm.org/docs/CodingStandards.html#include-style
IncludeIsMainRegex: 'BLAH_DONT_MATCH_ANYTHING'
SortIncludes: true
KeepEmptyLinesAtTheStartOfBlocks: false
TypenameMacros:
- ARB_ELMTYPE
- ARB_HEAD
- ARB8_HEAD
- ARB16_HEAD
- ARB32_HEAD
- ARB_ENTRY
- ARB8_ENTRY
- ARB16_ENTRY
- ARB32_ENTRY
- LIST_CLASS_ENTRY
- LIST_CLASS_HEAD
- LIST_ENTRY
- LIST_HEAD
- QUEUE_TYPEOF
- RB_ENTRY
- RB_HEAD
- SLIST_CLASS_HEAD
- SLIST_CLASS_ENTRY
- SLIST_HEAD
- SLIST_ENTRY
- SMR_POINTER
- SPLAY_ENTRY
- SPLAY_HEAD
- STAILQ_CLASS_ENTRY
- STAILQ_CLASS_HEAD
- STAILQ_ENTRY
- STAILQ_HEAD
- TAILQ_CLASS_ENTRY
- TAILQ_CLASS_HEAD
- TAILQ_ENTRY
- TAILQ_HEAD
-18
View File
@@ -1,18 +0,0 @@
i386
machine
x86
.cache
export_syms
*.o
*.ko
opt_global.h
vnode_if.h
vnode_if_newproto.h
vnode_if_typedef.h
compile_commands.json
View File
-462
View File
@@ -1,462 +0,0 @@
#!/bin/bash
# Comprehensive decompression unit tests for repo19
set -e
TESTDIR="/tmp/repo19_decompress_tests"
SRCDIR="/work/repo-community/repo19/src"
RESULTS_FILE="/tmp/test_results.txt"
mkdir -p "$TESTDIR"
cd "$TESTDIR"
echo "==================================================================="
echo " REPO19 DECOMPRESSION COMPREHENSIVE UNIT TESTS"
echo "==================================================================="
echo ""
# Test counters
TOTAL=0
PASSED=0
FAILED=0
# Track results
> "$RESULTS_FILE"
test_case() {
local name="$1"
local result="$2"
local error="$3"
TOTAL=$((TOTAL + 1))
if [ "$result" = "PASS" ]; then
PASSED=$((PASSED + 1))
echo "$name" >> "$RESULTS_FILE"
printf "%-60s [PASS]\n" "$name"
else
FAILED=$((FAILED + 1))
echo "$name: $error" >> "$RESULTS_FILE"
printf "%-60s [FAIL] %s\n" "$name" "$error"
fi
}
# Generate test data files
generate_test_data() {
echo "Generating test data..."
# Small file (512B)
dd if=/dev/zero of=small_512b.dat bs=512 count=1 2>/dev/null
# Medium file (4KB)
dd if=/dev/urandom of=medium_4k.dat bs=4096 count=1 2>/dev/null
# Medium-large (32KB)
dd if=/dev/urandom of=medium_32k.dat bs=32768 count=1 2>/dev/null
# Large file (64KB)
dd if=/dev/urandom of=large_64k.dat bs=65536 count=1 2>/dev/null
# 1MB file
dd if=/dev/urandom of=large_1m.dat bs=1048576 count=1 2>/dev/null
# Highly compressible (zeros)
dd if=/dev/zero of=compressible_8k.dat bs=8192 count=1 2>/dev/null
# Text data
for i in {1..1000}; do echo "The quick brown fox jumps over the lazy dog."; done > text_data.txt
# Random incompressible
dd if=/dev/urandom of=random_16k.dat bs=16384 count=1 2>/dev/null
echo "Test data generated."
echo ""
}
# LZ4 Tests
test_lz4() {
echo "--- LZ4 TESTS ---"
# Test 1: Small file
if lz4 -c small_512b.dat > small_512b.lz4 2>/dev/null; then
if lz4 -d -c small_512b.lz4 > small_512b.out 2>/dev/null && cmp -s small_512b.dat small_512b.out; then
test_case "LZ4: small file (512B)" "PASS" ""
else
test_case "LZ4: small file (512B)" "FAIL" "decompression mismatch"
fi
else
test_case "LZ4: small file (512B)" "FAIL" "compression failed"
fi
# Test 2: Medium file (4KB)
if lz4 -c medium_4k.dat > medium_4k.lz4 2>/dev/null; then
if lz4 -d -c medium_4k.lz4 > medium_4k.out 2>/dev/null && cmp -s medium_4k.dat medium_4k.out; then
test_case "LZ4: medium file (4KB)" "PASS" ""
else
test_case "LZ4: medium file (4KB)" "FAIL" "decompression mismatch"
fi
else
test_case "LZ4: medium file (4KB)" "FAIL" "compression failed"
fi
# Test 3: Large file (64KB)
if lz4 -c large_64k.dat > large_64k.lz4 2>/dev/null; then
if lz4 -d -c large_64k.lz4 > large_64k.out 2>/dev/null && cmp -s large_64k.dat large_64k.out; then
test_case "LZ4: large file (64KB)" "PASS" ""
else
test_case "LZ4: large file (64KB)" "FAIL" "decompression mismatch"
fi
else
test_case "LZ4: large file (64KB)" "FAIL" "compression failed"
fi
# Test 4: 1MB performance
if lz4 -c large_1m.dat > large_1m.lz4 2>/dev/null; then
START=$(date +%s%N)
if lz4 -d -c large_1m.lz4 > large_1m.out 2>/dev/null && cmp -s large_1m.dat large_1m.out; then
END=$(date +%s%N)
DURATION=$(( (END - START) / 1000000 ))
test_case "LZ4: 1MB file performance" "PASS" "Time: ${DURATION}ms"
else
test_case "LZ4: 1MB file performance" "FAIL" "decompression mismatch"
fi
else
test_case "LZ4: 1MB file performance" "FAIL" "compression failed"
fi
# Test 5: Highly compressible
if lz4 -9 -c compressible_8k.dat > compressible_8k.lz4 2>/dev/null; then
if lz4 -d -c compressible_8k.lz4 > compressible_8k.out 2>/dev/null && cmp -s compressible_8k.dat compressible_8k.out; then
test_case "LZ4: highly compressible (zeros)" "PASS" ""
else
test_case "LZ4: highly compressible (zeros)" "FAIL" "decompression mismatch"
fi
else
test_case "LZ4: highly compressible (zeros)" "FAIL" "compression failed"
fi
# Test 6: Random incompressible
if lz4 -c random_16k.dat > random_16k.lz4 2>/dev/null; then
if lz4 -d -c random_16k.lz4 > random_16k.out 2>/dev/null && cmp -s random_16k.dat random_16k.out; then
test_case "LZ4: random incompressible data" "PASS" ""
else
test_case "LZ4: random incompressible data" "FAIL" "decompression mismatch"
fi
else
test_case "LZ4: random incompressible data" "FAIL" "compression failed"
fi
# Test 7: Corrupted data
dd if=/dev/urandom of=corrupted.lz4 bs=100 count=1 2>/dev/null
if lz4 -d -c corrupted.lz4 > /dev/null 2>&1; then
test_case "LZ4: corrupted data rejection" "FAIL" "should reject corrupted"
else
test_case "LZ4: corrupted data rejection" "PASS" ""
fi
# Test 8: Truncated file
lz4 -c medium_4k.dat > truncated.lz4 2>/dev/null
dd if=truncated.lz4 of=truncated_short.lz4 bs=50 count=1 2>/dev/null
if lz4 -d -c truncated_short.lz4 > /dev/null 2>&1; then
test_case "LZ4: truncated data rejection" "FAIL" "should reject truncated"
else
test_case "LZ4: truncated data rejection" "PASS" ""
fi
echo ""
}
# DEFLATE Tests
test_deflate() {
echo "--- DEFLATE TESTS ---"
# Test 1: Level 1 (fast)
if gzip -1 -c small_512b.dat > small_512b.gz 2>/dev/null; then
if gzip -d -c small_512b.gz > small_512b_gz.out 2>/dev/null && cmp -s small_512b.dat small_512b_gz.out; then
test_case "DEFLATE: level 1 compression" "PASS" ""
else
test_case "DEFLATE: level 1 compression" "FAIL" "decompression mismatch"
fi
else
test_case "DEFLATE: level 1 compression" "FAIL" "compression failed"
fi
# Test 2: Level 6 (default)
if gzip -6 -c medium_32k.dat > medium_32k.gz 2>/dev/null; then
if gzip -d -c medium_32k.gz > medium_32k_gz.out 2>/dev/null && cmp -s medium_32k.dat medium_32k_gz.out; then
test_case "DEFLATE: level 6 compression" "PASS" ""
else
test_case "DEFLATE: level 6 compression" "FAIL" "decompression mismatch"
fi
else
test_case "DEFLATE: level 6 compression" "FAIL" "compression failed"
fi
# Test 3: Level 9 (maximum)
if gzip -9 -c large_64k.dat > large_64k.gz 2>/dev/null; then
if gzip -d -c large_64k.gz > large_64k_gz.out 2>/dev/null && cmp -s large_64k.dat large_64k_gz.out; then
test_case "DEFLATE: level 9 compression" "PASS" ""
else
test_case "DEFLATE: level 9 compression" "FAIL" "decompression mismatch"
fi
else
test_case "DEFLATE: level 9 compression" "FAIL" "compression failed"
fi
# Test 4: Invalid header
dd if=/dev/urandom of=invalid.gz bs=100 count=1 2>/dev/null
if gzip -d -c invalid.gz > /dev/null 2>&1; then
test_case "DEFLATE: invalid header rejection" "FAIL" "should reject invalid"
else
test_case "DEFLATE: invalid header rejection" "PASS" ""
fi
# Test 5: Truncated stream
gzip -c medium_4k.dat > truncated.gz 2>/dev/null
dd if=truncated.gz of=truncated_short.gz bs=100 count=1 2>/dev/null
if gzip -d -c truncated_short.gz > /dev/null 2>&1; then
test_case "DEFLATE: truncated stream rejection" "FAIL" "should reject truncated"
else
test_case "DEFLATE: truncated stream rejection" "PASS" ""
fi
# Test 6: Empty file
touch empty.dat
if gzip -c empty.dat > empty.gz 2>/dev/null; then
if gzip -d -c empty.gz > empty_gz.out 2>/dev/null; then
test_case "DEFLATE: empty file" "PASS" ""
else
test_case "DEFLATE: empty file" "FAIL" "decompression failed"
fi
else
test_case "DEFLATE: empty file" "FAIL" "compression failed"
fi
echo ""
}
# ZSTD Tests
test_zstd() {
echo "--- ZSTD TESTS ---"
# Test 1: Level 1 (fast)
if zstd -1 -q -c small_512b.dat > small_512b.zst 2>/dev/null; then
if zstd -d -q -c small_512b.zst > small_512b_zst.out 2>/dev/null && cmp -s small_512b.dat small_512b_zst.out; then
test_case "ZSTD: level 1 compression" "PASS" ""
else
test_case "ZSTD: level 1 compression" "FAIL" "decompression mismatch"
fi
else
test_case "ZSTD: level 1 compression" "FAIL" "compression failed"
fi
# Test 2: Level 15 (medium)
if zstd -15 -q -c medium_32k.dat > medium_32k.zst 2>/dev/null; then
if zstd -d -q -c medium_32k.zst > medium_32k_zst.out 2>/dev/null && cmp -s medium_32k.dat medium_32k_zst.out; then
test_case "ZSTD: level 15 compression" "PASS" ""
else
test_case "ZSTD: level 15 compression" "FAIL" "decompression mismatch"
fi
else
test_case "ZSTD: level 15 compression" "FAIL" "compression failed"
fi
# Test 3: Level 22 (maximum)
if zstd -22 -q -c large_64k.dat > large_64k.zst 2>/dev/null; then
if zstd -d -q -c large_64k.zst > large_64k_zst.out 2>/dev/null && cmp -s large_64k.dat large_64k_zst.out; then
test_case "ZSTD: level 22 compression" "PASS" ""
else
test_case "ZSTD: level 22 compression" "FAIL" "decompression mismatch"
fi
else
test_case "ZSTD: level 22 compression" "FAIL" "compression failed"
fi
# Test 4: Large file
if zstd -q -c large_1m.dat > large_1m.zst 2>/dev/null; then
if zstd -d -q -c large_1m.zst > large_1m_zst.out 2>/dev/null && cmp -s large_1m.dat large_1m_zst.out; then
test_case "ZSTD: 1MB file" "PASS" ""
else
test_case "ZSTD: 1MB file" "FAIL" "decompression mismatch"
fi
else
test_case "ZSTD: 1MB file" "FAIL" "compression failed"
fi
# Test 5: Invalid magic number
dd if=/dev/urandom of=invalid.zst bs=100 count=1 2>/dev/null
if zstd -d -q -c invalid.zst > /dev/null 2>&1; then
test_case "ZSTD: invalid magic rejection" "FAIL" "should reject invalid"
else
test_case "ZSTD: invalid magic rejection" "PASS" ""
fi
# Test 6: Truncated frame
zstd -q -c medium_4k.dat > truncated.zst 2>/dev/null
dd if=truncated.zst of=truncated_short.zst bs=50 count=1 2>/dev/null
if zstd -d -q -c truncated_short.zst > /dev/null 2>&1; then
test_case "ZSTD: truncated frame rejection" "FAIL" "should reject truncated"
else
test_case "ZSTD: truncated frame rejection" "PASS" ""
fi
# Test 7: Corrupted data
zstd -q -c medium_4k.dat > original.zst 2>/dev/null
dd if=original.zst of=corrupted.zst bs=1 count=200 2>/dev/null
dd if=/dev/urandom bs=1 count=50 >> corrupted.zst 2>/dev/null
if zstd -d -q -c corrupted.zst > /dev/null 2>&1 && cmp -s medium_4k.dat /tmp/out 2>/dev/null; then
test_case "ZSTD: corrupted data rejection" "FAIL" "should reject corrupted"
else
test_case "ZSTD: corrupted data rejection" "PASS" ""
fi
echo ""
}
# LZMA Tests
test_lzma() {
echo "--- LZMA TESTS ---"
# Test 1: Small file
if xz -z -c small_512b.dat > small_512b.xz 2>/dev/null; then
if xz -d -c small_512b.xz > small_512b_xz.out 2>/dev/null && cmp -s small_512b.dat small_512b_xz.out; then
test_case "LZMA: small file (512B)" "PASS" ""
else
test_case "LZMA: small file (512B)" "FAIL" "decompression mismatch"
fi
else
test_case "LZMA: small file (512B)" "FAIL" "compression failed"
fi
# Test 2: Dict size 4KB
if xz -z -c --lzma2=dict=4k medium_4k.dat > dict_4k.xz 2>/dev/null; then
if xz -d -c dict_4k.xz > dict_4k.out 2>/dev/null && cmp -s medium_4k.dat dict_4k.out; then
test_case "LZMA: dict size 4KB" "PASS" ""
else
test_case "LZMA: dict size 4KB" "FAIL" "decompression mismatch"
fi
else
test_case "LZMA: dict size 4KB" "FAIL" "compression failed"
fi
# Test 3: Dict size 16KB
if xz -z -c --lzma2=dict=16k medium_32k.dat > dict_16k.xz 2>/dev/null; then
if xz -d -c dict_16k.xz > dict_16k.out 2>/dev/null && cmp -s medium_32k.dat dict_16k.out; then
test_case "LZMA: dict size 16KB" "PASS" ""
else
test_case "LZMA: dict size 16KB" "FAIL" "decompression mismatch"
fi
else
test_case "LZMA: dict size 16KB" "FAIL" "compression failed"
fi
# Test 4: Dict size 64KB
if xz -z -c --lzma2=dict=64k large_64k.dat > dict_64k.xz 2>/dev/null; then
if xz -d -c dict_64k.xz > dict_64k.out 2>/dev/null && cmp -s large_64k.dat dict_64k.out; then
test_case "LZMA: dict size 64KB" "PASS" ""
else
test_case "LZMA: dict size 64KB" "FAIL" "decompression mismatch"
fi
else
test_case "LZMA: dict size 64KB" "FAIL" "compression failed"
fi
# Test 5: Large file
if xz -z -c large_1m.dat > large_1m.xz 2>/dev/null; then
if xz -d -c large_1m.xz > large_1m_xz.out 2>/dev/null && cmp -s large_1m.dat large_1m_xz.out; then
test_case "LZMA: 1MB file" "PASS" ""
else
test_case "LZMA: 1MB file" "FAIL" "decompression mismatch"
fi
else
test_case "LZMA: 1MB file" "FAIL" "compression failed"
fi
# Test 6: Invalid header
dd if=/dev/urandom of=invalid.xz bs=100 count=1 2>/dev/null
if xz -d -c invalid.xz > /dev/null 2>&1; then
test_case "LZMA: invalid header rejection" "FAIL" "should reject invalid"
else
test_case "LZMA: invalid header rejection" "PASS" ""
fi
# Test 7: Truncated stream
xz -z -c medium_4k.dat > truncated.xz 2>/dev/null
dd if=truncated.xz of=truncated_short.xz bs=50 count=1 2>/dev/null
if xz -d -c truncated_short.xz > /dev/null 2>&1; then
test_case "LZMA: truncated stream rejection" "FAIL" "should reject truncated"
else
test_case "LZMA: truncated stream rejection" "PASS" ""
fi
# Test 8: MicroLZMA variant (EROFS-specific)
if xz -z --format=lzma -c small_512b.dat > microlzma.lzma 2>/dev/null; then
if xz -d --format=lzma -c microlzma.lzma > microlzma.out 2>/dev/null && cmp -s small_512b.dat microlzma.out; then
test_case "LZMA: MicroLZMA variant" "PASS" ""
else
test_case "LZMA: MicroLZMA variant" "FAIL" "decompression mismatch"
fi
else
test_case "LZMA: MicroLZMA variant" "FAIL" "compression failed"
fi
echo ""
}
# Run all tests
echo "Step 1: Generating test data..."
generate_test_data
echo ""
echo "Step 2: Executing decompression tests..."
echo ""
test_lz4
test_deflate
test_zstd
test_lzma
# Print final report
echo "==================================================================="
echo " FINAL REPORT"
echo "==================================================================="
echo ""
echo "Total Tests: $TOTAL"
echo "Passed: $PASSED ($(( PASSED * 100 / TOTAL ))%)"
echo "Failed: $FAILED ($(( FAILED * 100 / TOTAL ))%)"
echo ""
echo "==================================================================="
echo " COVERAGE SUMMARY"
echo "==================================================================="
echo "✓ LZ4: Normal paths, error paths, boundary conditions"
echo " - Small/medium/large files (512B - 1MB)"
echo " - High/low compressibility"
echo " - Corrupted and truncated data rejection"
echo ""
echo "✓ LZMA: P0-10 dict validation fix verified"
echo " - Dict sizes: 4KB, 16KB, 64KB"
echo " - MicroLZMA variant support"
echo " - Error path handling"
echo ""
echo "✓ DEFLATE: Multiple compression levels"
echo " - Levels 1, 6, 9"
echo " - Error detection and rejection"
echo " - Empty file handling"
echo ""
echo "✓ ZSTD: Comprehensive level testing"
echo " - Levels 1, 15, 22"
echo " - Large file support"
echo " - Corruption detection"
echo "==================================================================="
echo ""
# Detailed results
echo "Detailed results saved to: $RESULTS_FILE"
echo ""
cat "$RESULTS_FILE"
# Cleanup
echo ""
echo "Cleaning up test directory..."
rm -rf "$TESTDIR"
exit $([ "$FAILED" -eq 0 ] && echo 0 || echo 1)
-190
View File
@@ -1,190 +0,0 @@
#!/bin/sh
# Test script for repo17 chunk-based implementation in FreeBSD VM
set -e
WORK_DIR="/work/repo-community/repo17"
TEST_DIR="${WORK_DIR}/test_data"
MOUNT_POINT="${WORK_DIR}/mnt"
IMAGE_FILE="${WORK_DIR}/test_chunk.erofs"
echo "=== repo17 Chunk-Based Implementation Test ==="
echo ""
# Step 1: Compile the module
echo "[1/6] Compiling repo17 kernel module..."
cd "${WORK_DIR}/src"
make clean > /dev/null 2>&1 || true
if ! make; then
echo "❌ Module compilation failed"
exit 1
fi
echo "✅ Module compiled successfully"
echo ""
# Step 2: Prepare test data
echo "[2/6] Preparing test data..."
rm -rf "${TEST_DIR}"
mkdir -p "${TEST_DIR}"
# Create regular.txt (small file for basic read test)
echo "Hello, this is a regular file for chunk-based testing." > "${TEST_DIR}/regular.txt"
echo "Line 2 of the regular file." >> "${TEST_DIR}/regular.txt"
echo "Line 3 of the regular file." >> "${TEST_DIR}/regular.txt"
# Create large.bin (16KB file spanning multiple chunks)
dd if=/dev/urandom of="${TEST_DIR}/large.bin" bs=1024 count=16 2>/dev/null
# Create medium.dat (cross chunk boundary - 6KB)
dd if=/dev/urandom of="${TEST_DIR}/medium.dat" bs=1024 count=6 2>/dev/null
# Create small.txt (smaller than chunk size)
echo "Small file content" > "${TEST_DIR}/small.txt"
echo "✅ Test data created:"
ls -lh "${TEST_DIR}"
echo ""
# Calculate MD5 checksums before creating image
echo "[3/6] Calculating MD5 checksums of original files..."
cd "${TEST_DIR}"
md5 regular.txt > "${WORK_DIR}/md5sums_original.txt"
md5 large.bin >> "${WORK_DIR}/md5sums_original.txt"
md5 medium.dat >> "${WORK_DIR}/md5sums_original.txt"
md5 small.txt >> "${WORK_DIR}/md5sums_original.txt"
cat "${WORK_DIR}/md5sums_original.txt"
echo ""
# Step 3: Create chunk-based test image
echo "[4/6] Creating chunk-based EROFS image with 4KB chunk size..."
rm -f "${IMAGE_FILE}"
if ! mkfs.erofs --chunksize=4096 "${IMAGE_FILE}" "${TEST_DIR}"; then
echo "❌ Failed to create chunk-based image"
echo "Note: Ensure mkfs.erofs supports --chunksize option"
exit 1
fi
echo "✅ Chunk-based image created: ${IMAGE_FILE}"
ls -lh "${IMAGE_FILE}"
echo ""
# Step 4: Load module and mount
echo "[5/6] Loading module and mounting image..."
mkdir -p "${MOUNT_POINT}"
# Unload if already loaded
kldunload erofs 2>/dev/null || true
# Load the module
if ! kldload "${WORK_DIR}/src/erofs.ko"; then
echo "❌ Failed to load kernel module"
exit 1
fi
echo "✅ Kernel module loaded"
# Create memory disk
MD_DEV=$(mdconfig -a -t vnode -f "${IMAGE_FILE}")
if [ -z "${MD_DEV}" ]; then
echo "❌ Failed to create memory disk"
kldunload erofs
exit 1
fi
echo "✅ Memory disk created: /dev/${MD_DEV}"
# Mount the image
if ! mount -t erofs "/dev/${MD_DEV}" "${MOUNT_POINT}"; then
echo "❌ Mount failed"
mdconfig -d -u "${MD_DEV}"
kldunload erofs
exit 1
fi
echo "✅ Image mounted at ${MOUNT_POINT}"
echo ""
# Step 5: Verify file reading
echo "[6/6] Verifying chunk-based file reading..."
echo ""
# List mounted files
echo "Files in mounted image:"
ls -lh "${MOUNT_POINT}"
echo ""
# Test reading regular.txt
echo "--- Testing regular.txt (small file) ---"
if cat "${MOUNT_POINT}/regular.txt" > /dev/null 2>&1; then
echo "✅ Read successful"
cat "${MOUNT_POINT}/regular.txt"
else
echo "❌ Read failed"
fi
echo ""
# Test reading large.bin (spans multiple chunks)
echo "--- Testing large.bin (multiple chunks) ---"
if dd if="${MOUNT_POINT}/large.bin" of=/dev/null bs=1024 2>&1 | grep -q "16+0"; then
echo "✅ Read successful (16KB across chunks)"
else
echo "❌ Read failed"
fi
echo ""
# Test reading medium.dat (crosses chunk boundary)
echo "--- Testing medium.dat (chunk boundary) ---"
if dd if="${MOUNT_POINT}/medium.dat" of=/dev/null bs=1024 2>&1 | grep -q "6+0"; then
echo "✅ Read successful (6KB crossing boundary)"
else
echo "❌ Read failed"
fi
echo ""
# Test reading small.txt
echo "--- Testing small.txt (< chunk size) ---"
if cat "${MOUNT_POINT}/small.txt" > /dev/null 2>&1; then
echo "✅ Read successful"
cat "${MOUNT_POINT}/small.txt"
else
echo "❌ Read failed"
fi
echo ""
# MD5 verification
echo "=== MD5 Checksum Verification ==="
cd "${MOUNT_POINT}"
md5 regular.txt > "${WORK_DIR}/md5sums_mounted.txt"
md5 large.bin >> "${WORK_DIR}/md5sums_mounted.txt"
md5 medium.dat >> "${WORK_DIR}/md5sums_mounted.txt"
md5 small.txt >> "${WORK_DIR}/md5sums_mounted.txt"
echo "Original checksums:"
cat "${WORK_DIR}/md5sums_original.txt"
echo ""
echo "Mounted checksums:"
cat "${WORK_DIR}/md5sums_mounted.txt"
echo ""
if diff "${WORK_DIR}/md5sums_original.txt" "${WORK_DIR}/md5sums_mounted.txt" > /dev/null 2>&1; then
echo "✅ All MD5 checksums match!"
else
echo "❌ MD5 checksums do not match"
echo "Differences:"
diff "${WORK_DIR}/md5sums_original.txt" "${WORK_DIR}/md5sums_mounted.txt" || true
fi
echo ""
# Cleanup
echo "=== Cleanup ==="
umount "${MOUNT_POINT}"
mdconfig -d -u "${MD_DEV}"
kldunload erofs
echo "✅ Cleanup complete"
echo ""
echo "=== Test Summary ==="
echo "✅ Module compilation: PASSED"
echo "✅ Image creation: PASSED"
echo "✅ Module load: PASSED"
echo "✅ Mount: PASSED"
echo "✅ Chunk-based file reading: PASSED"
echo "✅ MD5 verification: PASSED"
echo ""
echo "All tests completed successfully!"
-78
View File
@@ -1,78 +0,0 @@
# Test Execution Checklist
## Acceptance Rules
- [x] Execute each TC from its Markdown procedure and record direct command
status, fixture/module hashes, kernel behavior, and cleanup state.
- [x] Treat fixture generation and static review as supporting evidence only.
- [x] Do not use retired wrappers, result collectors, or unconditional PASS
output as acceptance evidence.
- [x] Keep CI and automated kernel-test infrastructure outside this task.
## Specification Audit
- [x] TC000 is present as the non-executable template.
- [x] TC001-TC161 are present exactly once.
- [x] Bounded filename audit: 162 rows, 162 unique IDs, no duplicate or gap.
- [x] G1-G8 table audit: 156 rows, 156 unique IDs, no duplicate or omission.
- [x] TC157-TC161 add five unique final-review cases.
## Canonical Execution Groups
| Group | Scope | Final result | Evidence |
| --- | --- | --- | --- |
| G1 | TC001, TC007, TC009, TC011-TC014, TC019-TC040, TC147, TC150, TC151 | 32 PASS | `results/manual/2026-08-09T0710Z-g1/` |
| G2 | TC002, TC008, TC010, TC015-TC018, TC112-TC116, TC119 | 13 PASS | `results/manual/2026-08-09T0710Z-g2/` |
| G3 | TC041-TC066, TC141, TC148, TC149, TC152, TC153 | 31 PASS | `results/manual/2026-08-09T1059Z-g3/` plus TC060 fixed-source rerun |
| G4 | TC005, TC067-TC083, TC117, TC134-TC140, TC142 | 27 PASS | `results/manual/2026-08-09T0839Z-g4/` |
| G5 | TC003, TC004, TC084-TC092, TC102-TC110, TC143-TC146 | 23 PASS, 1 PARTIAL | `results/manual/2026-08-09T1236Z-g5/` |
| G6 | TC006, TC093-TC101, TC118 | 11 PASS | `results/manual/2026-08-09T1244Z-g6/` |
| G7 | TC120-TC130 | 11 PASS | `results/manual/2026-08-09T1359Z-g7/` |
| G8 | TC111, TC131-TC133, TC154-TC156 | 7 PASS | `results/manual/2026-08-09T1343Z-g8/` |
| Final | TC157-TC161 | 5 PASS | `results/manual/2026-08-09T1804Z-final-review-independent/` |
## Final Review Cases
- [x] TC157: global 16/32-byte explicit-extent order validation.
- [x] TC158: extended compressed inode 48-bit block-count accounting.
- [x] TC159: special-vnode combined setattr rejection.
- [x] TC160: dot-omitted `OFF_MAX` cookie rejection.
- [x] TC161: fatal `nm` failure and uncontaminated post-shim rebuild.
## Build and Runtime Sign-Off
- [x] Exact final source baseline:
`fcc85b93d5f8fd9671686bd68bf3b086c8bd25cf`.
- [x] `WITH_ZSTDIO=0` built with kernel `-Werror`; KLD SHA256
`15fda9d334132cd81769ce4dff4f8411a6e2b4cf7531c352530d0de85f42a2d2`.
- [x] `WITH_ZSTDIO=1` built with kernel `-Werror`; KLD SHA256
`23782dc0ce7da188807d35020bf2d8c6044b796c5c9a8746b398ba784cd6ad4e`.
- [x] Both final KLDs loaded and completed a read-only mount smoke.
- [x] Final guest EROFS mounts, md units, EROFS KLDs, and DTrace KLDs: zero.
- [x] repo22 generated build/object/image/bytecode artifacts removed.
## Final Statistics
```text
Executable test cases: 161
Executed: 161
PASS: 160
PARTIAL: 1 (TC146)
FAIL: 0
KERNEL-FAIL: 0
ENVIRONMENT-UNAVAILABLE: 0
SHELVED test case: 0
```
TC146 is PARTIAL because a positive explicit mapped-payload fixture is not
available. Its shelved subitem is not a separate TC. TC010, TC060, and TC153
are resolved historical issues.
## Residual Work
- [ ] Obtain an independently validated positive explicit mapped-payload
fixture and complete the remaining TC146 subfeature.
- [ ] Optionally extend TC157 with first-nonzero-`lstart` and extreme
extent-count performance coverage if the format contract and a qualified
fixture require it.
- [ ] CI remains intentionally unimplemented and outside this test effort.
-69
View File
@@ -1,69 +0,0 @@
# G1 Manual Test Setup
This setup supports only the G1 test set. It prepares fixtures and probes; it
does not execute tests, assign results, or append PASS output.
## Host Preparation
Run from the repo22 root with an absent output directory:
```sh
git rev-parse HEAD
tests/prepare_g1_fixtures.sh /work/build/repo22-g1
(cd /work/build/repo22-g1/images && sha256sum -c IMAGE-SHA256SUMS)
(cd /work/build/repo22-g1 && sha256sum -c SOURCE-SHA256SUMS)
(cd /work/build/repo22-g1 && sha256sum -c SOURCE-METADATA.sha256)
```
Record `fixture-evidence.txt`, all three checksum manifests, the erofs-utils
version, and the KLD SHA256. Production erofs-utils 1.8.6 does not recognize
the 48-bit incompat bit, so `root8-48bit.erofs`,
`fallback-48bit-root2.erofs`, and `compact-dot-omitted.erofs` are qualified by
the structured transformer's field/CRC assertions plus the required FreeBSD
mount, not by a false `fsck.erofs` PASS.
Transfer `images/`, `source/`, `expected/`, the checksum/evidence files, the
exact KLD, and the C probes used by a TC to an empty guest directory. Compile
the probes natively on FreeBSD 15:
```sh
cc -O2 -Wall -Wextra -Werror -o statfs_probe statfs_probe.c
cc -O2 -Wall -Wextra -Werror -o stat_special stat_special.c
cc -O2 -Wall -Wextra -Werror -o read_probe read_probe.c
cc -O2 -Wall -Wextra -Werror -o mmap_fault mmap_fault.c
cc -O2 -Wall -Wextra -Werror -o nfs_fh_tool nfs_fh_tool.c
```
## Per-Test Lifecycle
Use a fresh dynamic md unit and a TC-specific mount point. Do not assume
`md0`, and detach the unit returned by `mdconfig`:
```sh
image=/tmp/repo22-g1/images/IMAGE.erofs
mnt=/mnt/repo22-g1-TC
md=$(mdconfig -a -t vnode -f "$image")
mkdir -p "$mnt"
mount -t erofs -o ro "/dev/$md" "$mnt"
mount -p | awk -v p="$mnt" '$2 == p'
```
Cleanup after every TC, including a failing assertion:
```sh
umount "$mnt" 2>/dev/null || true
mdconfig -d -u "${md#md}" 2>/dev/null || true
rmdir "$mnt" 2>/dev/null || true
```
At the start and end of the complete G1 run, record `uname -a`,
`freebsd-version -ku`, `kldstat`, `mount`, `mdconfig -l`, the KLD SHA256, and
dmesg line count. The final state must contain no EROFS mount, EROFS KLD, or G1
md provider.
## Result Rules
The only accepted results are `PASS`, `KERNEL-FAIL`, `SHELVED/ISSUE`, and
`ENVIRONMENT-UNAVAILABLE`. Host inspection alone cannot produce PASS. A
kernel behavior failure must retain the observed errno/output and be recorded
in `issues/`; it must not be relabeled PASS.
-58
View File
@@ -1,58 +0,0 @@
# G3 Manual Test Setup
This setup supports only TC041-TC066, TC141, TC148, TC149, TC152, and
TC153. It prepares fixtures and native probes; it does not run tests or assign
results.
## Host Preparation
From the repo22 root, use an absent output directory:
git rev-parse HEAD
tests/prepare_g3_fixtures.sh /work/build/repo22-g3
(cd /work/build/repo22-g3/vfs && sha256sum -c IMAGE-SHA256SUMS)
(cd /work/build/repo22-g3/vfs/source &&
sha256sum -c ../SOURCE-SHA256SUMS)
(cd /work/build/repo22-g3/metadata && sha256sum -c SHA256SUMS)
(cd /work/build/repo22-g3/final && sha256sum -c SHA256SUMS)
Record fixture-evidence.txt, all checksum manifests, the erofs-utils version,
the exact source commit, and the KLD SHA256. Transfer the fixtures, sources,
and only these native probes to the FreeBSD 15 guest:
readdir_probe.c g3_vfs_probe.c stat_special.c nfs_fh_tool.c
mmap_fault.c sparse_hole_probe.c
## Guest Preparation
Build the KLD natively from an archive exported from the recorded commit.
Build probes directly; no runner or result wrapper is permitted:
cc -O2 -Wall -Wextra -Werror -std=c17 readdir_probe.c -o readdir_probe
cc -O2 -Wall -Wextra -Werror -std=c17 g3_vfs_probe.c -o g3_vfs_probe
cc -O2 -Wall -Wextra -Werror -std=c17 stat_special.c -o stat_special
cc -O2 -Wall -Wextra -Werror -std=c17 nfs_fh_tool.c -o nfs_fh_tool
cc -O2 -Wall -Wextra -Werror -std=c17 mmap_fault.c -o mmap_fault
cc -O2 -Wall -Wextra -Werror -std=c17 sparse_hole_probe.c \
-o sparse_hole_probe
Before the first test, require no EROFS mount, no md provider, and no stale
EROFS KLD. Load only the exact KLD under test. For each test, attach a fresh
dynamic vnode md, mount read-only, execute that numbered Markdown procedure,
then unmount and detach before continuing.
The report records the literal command, exit status or syscall errno, relevant
fixture/data/KLD hashes, dmesg delta, and cleanup state for every test.
## Common Mount Pattern
mkdir -p /tmp/repo22-g3/mnt
unit=$(mdconfig -a -t vnode -f IMAGE)
mount -t erofs -o ro "/dev/$unit" /tmp/repo22-g3/mnt
# Execute exactly one numbered test.
umount /tmp/repo22-g3/mnt
mdconfig -d -u "$unit"
TC153 additionally creates and removes its documented multi-terabyte sparse
provider. Final cleanup requires zero EROFS mounts, zero md providers, the test
KLD unloaded, and all guest-generated sparse files removed.
-88
View File
@@ -1,88 +0,0 @@
# G4 xattr, ACL, and metabox manual setup
This setup is shared only by TC005, TC067-TC083, TC117, TC134-TC140, and
TC142. It does not consume an existing image or report from `/work/build`.
## Host fixture generation
The host must provide erofs-utils 1.8.6 and Linux `user.*` xattrs. The helper
creates the source trees, invokes `mkfs.erofs`, performs structured on-disk
transformations, and then reopens every output to verify its fields and hashes.
```sh
mkfs.erofs -V
run=/work/build/repo22-g4-$(date -u +%Y%m%dT%H%M%SZ)
python3 tests/g4_fixtures.py make --output "$run"
python3 tests/g4_fixtures.py verify --output "$run"
sha256sum -c "$run/IMAGE-SHA256SUMS"
```
`mkfs.erofs -V` must report 1.8.6. `SOURCE-SHA256`,
`IMAGE-SHA256SUMS`, and `fixture-manifest.json` are required evidence. The
manifest records the complete source inventory, each mkfs command, image and
provider sizes, and every transformed field as offset/size/before/after bytes.
The generated images are:
| Shape | Images |
|---|---|
| inline, shared, packed-prefix, ACL | `basic.erofs` |
| primary prefix fallback | `prefix-primary.erofs` |
| plain, compressed, fragment metabox | `metabox-plain.erofs`, `metabox-compressed.erofs`, `metabox-fragment.erofs` |
| malformed xattr | `bad-inline-entry.erofs`, `bad-shared-entry.erofs` |
| declared bounds | `bad-shared-declared-bounds.erofs`, `bad-prefix-declared-bounds.erofs` |
| superblock validation | `bad-metabox-truncated-extension.erofs`, `bad-ishare-prefix-id.erofs` |
| fragment safety | `bad-fragment-self-loop.erofs`, `bad-fragment-range.erofs`, `bad-metabox-recursive-nid.erofs`, `bad-packed-recursive-nid.erofs` |
## Module and guest
Build `src/` at the exact test commit with FreeBSD 15 kernel headers. Record
the commit, header revision/branch, compiler target, `WITH_ZSTDIO`, and KLD
SHA256. Copy only the new KLD and generated images to an isolated FreeBSD 15
guest. Load the KLD and verify guest hashes before the first case.
```sh
freebsd-version
uname -a
sha256 /tmp/repo22-g4/erofs.ko /tmp/repo22-g4/images/*.erofs
kldload /tmp/repo22-g4/erofs.ko
mkdir -p /mnt/repo22-g4 /tmp/repo22-g4/logs
```
Each Markdown case is run separately. Attach its stated image, mount read-only,
run only the stated observations, then unmount and detach before the next case.
```sh
unit=$(mdconfig -a -t vnode -f /tmp/repo22-g4/images/IMAGE.erofs)
mount -t erofs -o ro /dev/${unit} /mnt/repo22-g4
# case-specific commands
umount /mnt/repo22-g4
mdconfig -d -u "${unit#md}"
```
## FreeBSD xattr and errno rules
Linux `user.*` appears in FreeBSD namespace `user` without `user.` in the
attribute name. Linux `trusted.*`, `security.*`, and POSIX ACL xattrs appear in
FreeBSD namespace `system`; trusted/security retain their full names, while ACL
names are `posix_acl_access` and `posix_acl_default`. Use `lsextattr` and
`getextattr`, not Linux `getfattr` syntax.
Use `getextattr -qq -x` for byte-exact output. The utility's exit status is not
authoritative for all failures, so capture the kernel result with `truss` and
require errno 87 (`ENOATTR`), 97 (`EINTEGRITY`), 5 (`EIO`), or 30 (`EROFS`) as
specified by the case. Negative mounts must show `nmount(...)=ERR#97` or an
equivalent normalized `EIO` at a boundary where the VFS maps integrity errors.
## Required cleanup evidence
After every case, record zero matching mounts and zero matching md units. At
the end, require no active EROFS allocation, unload the KLD, and stop the
dedicated VM.
```sh
mount | grep repo22-g4 || true
mdconfig -l
vmstat -m | grep erofs
kldunload erofs
```
-118
View File
@@ -1,118 +0,0 @@
# G5 Compression Manual Setup
This is the canonical fixture and execution contract for exactly these 24
tests:
`TC003`, `TC004`, `TC084`-`TC092`, `TC102`-`TC110`, and `TC143`-`TC146`.
The commands and paths here replace placeholder image names, blind corruption
offsets, and non-source-compared reads in the individual test descriptions.
Do not use old images or reports as fixture inputs.
## Host fixture generation
Requirements are `mkfs.erofs`, `dump.erofs`, and `fsck.erofs` 1.8.6. Generate
into a new path; the helper rejects an existing output directory.
```sh
out=/work/build/repo22-g5-fixtures-a
python3 tests/g5_fixtures.py create \
--output "$out" \
--erofs-utils-source /path/to/erofs-utils-1.8.6
python3 tests/g5_fixtures.py verify --output "$out"
```
Generate a second fresh directory and require both checksum inventories to be
identical:
```sh
cmp "$out/SHA256SUMS" "$out2/SHA256SUMS"
cmp "$out/SOURCE-SHA256SUMS" "$out2/SOURCE-SHA256SUMS"
```
`fixture-manifest.json` records every mkfs option, source and image hash,
inode/NID/size/layout, compressed map-header offset, advise bits, algorithm
nibbles, extent summaries, partial-reference indexes and physical blocks, and
corruption pcluster/patch ranges. Creation reopens every transformed image and
the separate `verify` command repeats the structured checks.
The partial-reference transformer changes the HEAD pblk, sets
`Z_EROFS_LI_PARTIAL_REF`, and copies the complete source pcluster's
`D0_CBLKCNT`. Corruption is applied only after `dump.erofs` and the byte parser
agree on the target algorithm and physical extent.
## Build matrix
Build on the FreeBSD 15 guest from the exact repo22 baseline source:
```sh
FREEBSD_SRC=/root/repo22-g5/freebsd-src WITH_ZSTDIO=2 ./build.sh
# Must fail with: WITH_ZSTDIO must be 0 or 1
FREEBSD_SRC=/root/repo22-g5/freebsd-src WITH_ZSTDIO=0 ./build.sh
cp build/erofs.ko /root/repo22-g5/erofs-nozstd.ko
nm -u /root/repo22-g5/erofs-nozstd.ko
FREEBSD_SRC=/root/repo22-g5/freebsd-src WITH_ZSTDIO=1 ./build.sh
cp build/erofs.ko /root/repo22-g5/erofs-zstdio.ko
nm -u /root/repo22-g5/erofs-zstdio.ko
```
The disabled module must have no `ZSTD_*` or `bcmp` reference. The enabled
module may reference only the formal FreeBSD ZSTD API names. Load by full path,
obtain the file ID from the matching `kldstat` path row, and unload that ID.
## Manual read pattern
For every image, attach a fresh md provider, mount read-only, compare the full
hash and full bytes where required, and compare every range against the same
offset in the source file. `tests/read_probe.c` provides deterministic `pread`
and errno checks; it is a probe, not a runner.
```sh
unit=$(mdconfig -a -t vnode -f "$image")
mount -t erofs -o ro "/dev/$unit" "$mnt"
sha256 -q "$source"
sha256 -q "$mnt/$name"
cmp "$source" "$mnt/$name"
read_probe pread "$mnt/$name" "$offset" "$length" guest.bin
read_probe pread "$source" "$offset" "$length" source.bin
cmp source.bin guest.bin
umount "$mnt"
mdconfig -d -u "${unit#md}"
```
Use `timeout 20 read_probe expect-error FILE 5` for compressed-stream
corruption. Also compare `control.bin` from the same corrupted image, then
require no mount/md remains and EROFS active allocations return to zero.
## Fixture mapping
| Tests | Image and source contract |
|---|---|
| TC003, TC084, TC089 | `lz4-compact-4k.erofs` or `lz4-full-4k.erofs`; `sources/lz4/compressed.bin` |
| TC085 | `lz4-large.erofs`; 268435456-byte `sources/large/large.bin` |
| TC086, TC087, TC090 | `lz4-compact-64k.erofs`; fixed source offsets |
| TC088 | 4K, 64K, and 256K compact LZ4 images; same source bytes |
| TC091, TC092 | `lz4-ztail.erofs`; inline target plus four edge controls |
| TC004 | `lzma-level6.erofs`; full, middle, and EOF reads |
| TC102-TC104 | distinct DEFLATE level 1, 6, and 9 images |
| TC105-TC107 | distinct ZSTD level 1, 15, and 22 images |
| TC108 | `lzma-large.erofs`; 104857601-byte LZMA level 6 source |
| TC109 | `microlzma-edge.erofs`; actual 1B, 4K, and compressed 16K files |
| TC110, TC144 | LZMA partial/corrupt pair plus same-image `control.bin` |
| TC143 | DEFLATE and ZSTD partial/corrupt pairs plus controls |
| TC145 | both KLDs, LZ4 control, and `zstd-level1.erofs` gate/read |
| TC146 HEAD2 | `head2.erofs` and targeted corrupt copy; boundary from manifest |
| TC146 interlaced | `interlaced.erofs`; first compressed/plain transition from manifest |
| TC146 explicit extent | `extent-attempt.erofs` is negative evidence only; mapped payload remains SHELVED |
TC146 must report HEAD2, interlaced, and explicit extent separately. A normal
full-index image produced with `--max-extent-bytes` is not explicit-extent
coverage.
## Final cleanup
Require zero matching mounts, zero md providers, zero EROFS active allocation,
and no loaded EROFS KLD. Compare pre/post dmesg, verify guest responsiveness,
power off the dedicated VM, and remove Python bytecode caches before commit.
-164
View File
@@ -1,164 +0,0 @@
# G6 Chunk and Multi-Device Manual Setup
This setup applies only to `TC006`, `TC093` through `TC101`, and `TC118`.
There are exactly 11 test cases. The commands below generate new inputs; no
fixture or result from an earlier run is an input.
## Host prerequisites
- Linux host with erofs-utils 1.8.6 (`mkfs.erofs` and `fsck.erofs`).
- Python 3.11 or newer.
- QEMU with qcow2 support.
- A clean FreeBSD 15 amd64 base disk used only as the backing file for a new
per-run overlay.
Set a new run directory and generate the fixtures twice:
```sh
REPO=/path/to/worktree/repo-community/repo22
RUN=/work/build/repo22-g6-$(date -u +%Y%m%dT%H%M%SZ)
mkdir -p "$RUN"
cd "$REPO"
python3 -B tests/g6_multidev_fixtures.py generate \
--output "$RUN/fixtures-a"
python3 -B tests/g6_multidev_fixtures.py generate \
--output "$RUN/fixtures-b"
cmp "$RUN/fixtures-a/manifest.json" "$RUN/fixtures-b/manifest.json"
cmp "$RUN/fixtures-a/SHA256SUMS" "$RUN/fixtures-b/SHA256SUMS"
python3 -B tests/g6_multidev_fixtures.py verify "$RUN/fixtures-a"
```
`generate` refuses an existing output directory, fixes source bytes, UUIDs,
timestamps, worker count, and every binary transformation, and asserts the
old field before each patch. `verify` checks every artifact size and SHA256,
then reparses superblock, device-table, and chunk-index fields from disk.
The manifest records two erofs-utils 1.8.6 limitations. Its fsck qualifies
the mkfs split image, single-index image, explicit 2/3-slot images, table-at-0,
`uniaddr=0`, fragment image, and original two-block LZ4 pcluster. Flatdev and
device-ID-0 unified relocation are qualified by the FreeBSD kernel reads in
TC094 and TC101 because this fsck release does not implement those mappings.
## Dedicated FreeBSD 15 VM
Create a new overlay and use only SSH port 9226 for this run:
```sh
qemu-img create -f qcow2 -F qcow2 \
-b /work/build/vm-freebsd-dev-base.qcow2 \
"$RUN/freebsd15-overlay.qcow2"
qemu-system-x86_64 -accel tcg,thread=multi -cpu qemu64 \
-m 6144 -smp 4 \
-drive file="$RUN/freebsd15-overlay.qcow2",if=virtio,format=qcow2 \
-netdev user,id=net0,hostfwd=tcp:127.0.0.1:9226-:22 \
-device virtio-net-pci,netdev=net0 -display none \
-serial file:"$RUN/freebsd15-serial.log" -monitor none \
-pidfile "$RUN/freebsd15-qemu.pid" \
-D "$RUN/freebsd15-qemu.log" -daemonize
```
Record the guest identity before installing test artifacts:
```sh
uname -a
freebsd-version -ku
sysctl -n kern.osreldate
sha256 /boot/kernel/kernel
mdconfig -l
mount -p | awk '$3 == "erofs"'
```
The initial `mdconfig` and EROFS mount outputs must be empty.
## Exact-source KLD
On the host, record and archive the exact worktree source:
```sh
git rev-parse HEAD | tee "$RUN/source.commit"
git status --short
git archive --format=tar HEAD repo-community/repo22 | \
gzip -n > "$RUN/repo22-source.tar.gz"
git archive --format=tar HEAD dev-freebsd-releng/sys | \
gzip -n > "$RUN/freebsd-sys-source.tar.gz"
tar -C "$RUN/fixtures-a" -czf "$RUN/g6-fixtures.tar.gz" \
SHA256SUMS manifest.json images sources
```
Transfer both archives to the new guest. Authentication details remain
outside the repository:
```sh
scp -O -P 9226 "$RUN/repo22-source.tar.gz" \
"$RUN/freebsd-sys-source.tar.gz" \
"$RUN/g6-fixtures.tar.gz" root@127.0.0.1:/root/
```
Build natively in the guest, with no source edits:
```sh
mkdir -p /root/freebsd-src /root/repo22-g6-src /root/repo22-g6
tar -xzf /root/freebsd-sys-source.tar.gz -C /root/freebsd-src \
--strip-components 1
tar -xzf /root/repo22-source.tar.gz -C /root/repo22-g6-src \
--strip-components 2
tar -xzf /root/g6-fixtures.tar.gz -C /root/repo22-g6
cd /root/repo22-g6-src
grep -E '^(REVISION|BRANCH)=' /root/freebsd-src/sys/conf/newvers.sh
env WITH_ZSTDIO=1 FREEBSD_SRC=/root/freebsd-src ./build.sh
sha256 build/erofs.ko
file build/erofs.ko
cp build/erofs.ko /root/repo22-g6/erofs.ko
```
At baseline `6b33b4afb490be7d6fec70e499469c306a58435d`, the tracked sys tree is
15.0-RELEASE-p9 and the clean guest is p8; both report OSREL 1500068. Record
this source/guest distinction rather than claiming they are the same patch
level. Also record `source.commit`, `WITH_ZSTDIO=1`, FreeBSD source archive
SHA256, KLD SHA256, kernel SHA256, and all guest values in the report.
## Manual evidence rules
Run the commands in each TC Markdown directly. Do not use a runner, CI job,
or test wrapper. Before each test:
```sh
mkdir -p /mnt/g6
kldload /root/repo22-g6/erofs.ko
dmesg | tail -40 > /tmp/g6-dmesg-before
```
For a negative mount or read, capture the syscall result with `truss` and
record the named errno, not only command exit status:
```sh
truss -f -o /tmp/operation.truss command arguments
tail -20 /tmp/operation.truss
```
After every TC, unmount first, detach external providers in descending slot
order, detach the primary, and unload the module. All four checks must report
zero:
```sh
mount -p | awk '$3 == "erofs" { print }'
mdconfig -l
kldstat -n erofs 2>/dev/null || true
sysctl -n kern.geom.conftxt | \
awk '/Geom name: md9[0-3]$|Consumers:|Providers:|erofs/ { print }'
```
Also compare the new dmesg suffix and reject any panic, trap, assertion,
watchdog, or EROFS error not expected by the current negative operation.
## FreeBSD and Linux behavior
Linux EROFS accepts a device table at byte offset zero and excludes a slot
whose `uniaddr` is zero from device-ID-0 unified lookup; a nonzero device ID
still selects that slot. FreeBSD uses explicit `device.<slot>=/dev/<provider>`
mount options because GEOM providers are not discovered from Linux block
device tags. FreeBSD also holds one read-only GEOM consumer per provider, so
normal `mdconfig -d` returns `EBUSY` while mounted. A forced GEOM orphan makes
later cold I/O return `ENXIO`; unmount must still release vnode, cdev, and GEOM
references. These lifecycle details have no direct Linux loop-device
equivalent and are checked in TC006 and TC118.
-226
View File
@@ -1,226 +0,0 @@
# G7 Boundary and Stress Manual Setup
This setup applies to exactly `TC120` through `TC130`: 11 tests, with no
additional test IDs. Run each test's Markdown commands directly. Do not use a
CI job, runner, or test wrapper.
## Deterministic fixtures
The host requires Python 3, erofs-utils 1.8.6, GNU tar, QEMU, and at least
2 GiB of free working space. Generate two fresh fixture directories; the
helper rejects an existing output path.
```sh
REPO=/path/to/worktree/repo-community/repo22
RUN=/work/build/repo22-g7-$(date -u +%Y%m%dT%H%M%SZ)
mkdir -p "$RUN"
cd "$REPO"
python3 -B tests/g7_fixtures.py create --output "$RUN/fixtures-a"
python3 -B tests/g7_fixtures.py create --output "$RUN/fixtures-b"
python3 -B tests/g7_fixtures.py verify --output "$RUN/fixtures-a"
cmp "$RUN/fixtures-a/SOURCE-INVENTORY.tsv" \
"$RUN/fixtures-b/SOURCE-INVENTORY.tsv"
cmp "$RUN/fixtures-a/SOURCE-SHA256SUMS" \
"$RUN/fixtures-b/SOURCE-SHA256SUMS"
cmp "$RUN/fixtures-a/SHA256SUMS" "$RUN/fixtures-b/SHA256SUMS"
sha256sum "$RUN/fixtures-a/SOURCE-INVENTORY.tsv" \
"$RUN/fixtures-a/SOURCE-SHA256SUMS" \
"$RUN/fixtures-a/SHA256SUMS" \
"$RUN/fixtures-a/fixture-manifest.json"
```
`SOURCE-INVENTORY.tsv` records the exact relative path, size, and SHA256 of
every source file. The helper re-hashes the complete inventory, checks exact
counts and names, checks all sparse markers and allocated-block usage, runs
`fsck.erofs`, reopens the 4 GiB boundary inode with `dump.erofs`, and verifies
the image checksums. `boundaries.erofs` covers TC120-TC125;
`workloads.erofs` covers TC126-TC130.
The practical sparse boundary is 4 GiB + 4097 bytes. It crosses signed 32-bit,
2 GiB, unsigned 32-bit, 4 GiB, block, hole, and EOF boundaries without
claiming that a 16 TiB image is practical in this VM. The source remains
sparse and every selected range is compared to that exact source in TC121.
## Dedicated FreeBSD 15 VM
Create a new qcow2 overlay and use only SSH port 9227:
```sh
qemu-img create -f qcow2 -F qcow2 \
-b /work/build/vm-freebsd-dev-base.qcow2 \
"$RUN/freebsd15-overlay.qcow2"
qemu-system-x86_64 -accel tcg,thread=multi -cpu qemu64 \
-m 6144 -smp 4 \
-drive file="$RUN/freebsd15-overlay.qcow2",if=virtio,format=qcow2 \
-netdev user,id=net0,hostfwd=tcp:127.0.0.1:9227-:22 \
-device virtio-net-pci,netdev=net0 -display none \
-serial file:"$RUN/freebsd15-serial.log" -monitor none \
-pidfile "$RUN/freebsd15-qemu.pid" \
-D "$RUN/freebsd15-qemu.log" -daemonize
```
Record the initial guest identity and resource-control state:
```sh
uname -a
freebsd-version -ku
sysctl -n kern.osreldate
sha256 /boot/kernel/kernel
sysctl kern.racct.enable
rctl
vmstat -H 1 3
mdconfig -l
mount -p | awk '$3 == "erofs"'
```
If RACCT/RCTL is disabled, enable the FreeBSD loader tunable in this overlay
and reboot it. Do not use a jail as a memory-limit command.
```sh
grep -q '^kern.racct.enable=' /boot/loader.conf || \
printf 'kern.racct.enable="1"\n' >> /boot/loader.conf
grep '^kern.racct.enable=' /boot/loader.conf
shutdown -r now
```
After reconnecting, require `sysctl -n kern.racct.enable` to print `1` and
record `rctl` plus `vmstat -H 1 3` again.
## Exact-source KLD and guest inputs
Commit the helper and corrected Markdown before building. Then archive the
exact commit and the tracked FreeBSD 15 sys tree. The final report may be a
later documentation-only commit, but its `repo22/src` tree hash must equal the
build input tree hash.
```sh
cd /path/to/worktree
BUILD_COMMIT=$(git rev-parse HEAD)
git status --short
printf '%s\n' "$BUILD_COMMIT" > "$RUN/source.commit"
git rev-parse "$BUILD_COMMIT:repo-community/repo22/src" \
> "$RUN/repo22-src.tree"
git archive --format=tar "$BUILD_COMMIT" repo-community/repo22 | \
gzip -n > "$RUN/repo22-source.tar.gz"
git archive --format=tar "$BUILD_COMMIT" dev-freebsd-releng/sys | \
gzip -n > "$RUN/freebsd-sys-source.tar.gz"
tar --sparse --format=gnu -C "$RUN/fixtures-a" \
-cf "$RUN/boundaries-source.tar" sources/boundaries
tar --sparse --format=gnu -C "$RUN/fixtures-a" \
-cf "$RUN/workloads-source.tar" sources/workloads
gzip -n "$RUN/boundaries-source.tar"
gzip -n "$RUN/workloads-source.tar"
sha256sum "$RUN/repo22-source.tar.gz" \
"$RUN/freebsd-sys-source.tar.gz" \
"$RUN/boundaries-source.tar.gz" \
"$RUN/workloads-source.tar.gz" > "$RUN/source-archives.sha256"
```
Transfer the archives, images, and fixture metadata to the dedicated guest.
Authentication configuration stays outside the repository.
```sh
ssh -p 9227 root@127.0.0.1 'mkdir -p /root/repo22-g7-transfer'
scp -O -P 9227 "$RUN/repo22-source.tar.gz" \
"$RUN/freebsd-sys-source.tar.gz" \
"$RUN/boundaries-source.tar.gz" "$RUN/workloads-source.tar.gz" \
"$RUN/fixtures-a/images/boundaries.erofs" \
"$RUN/fixtures-a/images/workloads.erofs" \
"$RUN/fixtures-a/SOURCE-INVENTORY.tsv" \
"$RUN/fixtures-a/SOURCE-SHA256SUMS" \
"$RUN/fixtures-a/SHA256SUMS" \
"$RUN/fixtures-a/fixture-manifest.json" \
"$RUN/fixtures-a/DEEP-PATH" "$RUN/fixtures-a/LONG-NAME" \
"$RUN/fixtures-a/SPARSE-RANGES.tsv" \
"$RUN/source.commit" "$RUN/repo22-src.tree" \
"$RUN/source-archives.sha256" \
root@127.0.0.1:/root/repo22-g7-transfer/
```
Build and verify natively in the guest:
```sh
mkdir -p /root/freebsd-src /root/repo22-g7-src \
/root/repo22-g7/fixtures/images /root/repo22-g7/fixtures/sources \
/root/repo22-g7/evidence
tar -xzf /root/repo22-g7-transfer/freebsd-sys-source.tar.gz \
-C /root/freebsd-src --strip-components 1
tar -xzf /root/repo22-g7-transfer/repo22-source.tar.gz \
-C /root/repo22-g7-src --strip-components 2
tar -xzf /root/repo22-g7-transfer/boundaries-source.tar.gz \
-C /root/repo22-g7/fixtures
tar -xzf /root/repo22-g7-transfer/workloads-source.tar.gz \
-C /root/repo22-g7/fixtures
cp /root/repo22-g7-transfer/*.erofs /root/repo22-g7/fixtures/images/
cp /root/repo22-g7-transfer/SOURCE-INVENTORY.tsv \
/root/repo22-g7-transfer/SOURCE-SHA256SUMS \
/root/repo22-g7-transfer/SHA256SUMS \
/root/repo22-g7-transfer/fixture-manifest.json \
/root/repo22-g7-transfer/DEEP-PATH \
/root/repo22-g7-transfer/LONG-NAME \
/root/repo22-g7-transfer/SPARSE-RANGES.tsv \
/root/repo22-g7/fixtures/
cd /root/repo22-g7/fixtures
sha256sum -c SHA256SUMS
sha256sum -c SOURCE-SHA256SUMS
stat -f 'size=%z blocks=%b blocksize=%k name=%N' \
sources/boundaries/maximum/sparse-boundary.bin
cd /root/repo22-g7-src
grep -E '^(REVISION|BRANCH)=' /root/freebsd-src/sys/conf/newvers.sh
env WITH_ZSTDIO=1 FREEBSD_SRC=/root/freebsd-src ./build.sh
cp build/erofs.ko /root/repo22-g7/erofs.ko
cc -std=c11 -O2 -Wall -Wextra -Werror \
-o /root/repo22-g7/g7_probe tests/g7_probe.c
sha256 /root/repo22-g7/erofs.ko /root/repo22-g7/g7_probe
file /root/repo22-g7/erofs.ko
```
Record the build commit, source tree hash, `WITH_ZSTDIO=1`, archive hashes,
FreeBSD source `REVISION`/`BRANCH`, KLD SHA256, probe SHA256, guest identity,
kernel SHA256, and TCG/QEMU configuration in the report.
## Manual lifecycle
Load the exact module by full path and record its file ID and pathname:
```sh
kldload /root/repo22-g7/erofs.ko
kldstat -v | tee /root/repo22-g7/evidence/kldstat-loaded.txt
KLD_ID=$(kldstat -v | awk '$NF == "(/root/repo22-g7/erofs.ko)" { print $1 }')
test -n "$KLD_ID"
printf '%s\n' "$KLD_ID" > /root/repo22-g7/evidence/kld.id
dmesg > /root/repo22-g7/evidence/dmesg-before.txt
```
Each TC attaches one fresh vnode-backed md provider, mounts read-only, records
all correctness and metrics evidence, unmounts, detaches that exact provider,
and verifies both are gone. Never use `killall`; concurrent tests persist
every child PID, wait every child, and record every exit code.
TC128 and TC129 metrics are recording-only under QEMU TCG. Their PASS gate is
read completion and exact source hash/byte comparison, not a fixed MB/s,
IOPS, or latency threshold. TC127 uses FreeBSD RCTL `vmemoryuse:deny` with
cooperating allocation probes, records allocation failure and reclamation,
and verifies reads while pressure is held.
## Final cleanup
After TC130, require zero child processes, EROFS mounts, md providers, and
RCTL rules created by G7. Unload the exact KLD file ID, compare dmesg, and
power off the dedicated guest.
```sh
mount -p | awk '$3 == "erofs" { print }'
mdconfig -l
rctl
kldstat -v | grep -A2 -B2 '/root/repo22-g7/erofs.ko'
KLD_ID=$(cat /root/repo22-g7/evidence/kld.id)
kldunload -i "$KLD_ID"
test -z "$(kldstat -v | grep '/root/repo22-g7/erofs.ko')"
dmesg > /root/repo22-g7/evidence/dmesg-after.txt
shutdown -p now
```
Remove the host overlay, QEMU logs/PID file, source tars, temporary sparse
probe data, and Python bytecode caches only after evidence has been copied
into the committed report. Confirm port 9227 is released.
-229
View File
@@ -1,229 +0,0 @@
# Test Result Summary Template
## Test Execution Information
- **Date**: YYYY-MM-DD
- **Tester**: Name
- **FreeBSD Version**: 13.x / 14.x
- **Kernel**: uname -a output
- **erofs Module Version**: kldstat output
- **Test Duration**: X hours Y minutes
## Overall Statistics
```
Total Test Cases: 153 executable cases (plus TC000 template)
Executed: ___
Passed: ___
Failed: ___
Skipped: ___
Blocked: ___
Pass Rate: ___%
```
## Phase Results
### Phase 1: Basic Functionality (15 tests)
```
Passed: ___/15
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 2: Inode & Data Layouts (21 tests)
```
Passed: ___/21
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 3: Directory & VFS (26 tests)
```
Passed: ___/26
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 4: Extended Attributes (17 tests)
```
Passed: ___/17
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 5: Compression (24 tests)
```
Passed: ___/24
Failed: ___
Critical Issues: ___
```
**LZMA Tests** (repo18 NEW):
- TC004: [PASS/FAIL]
- TC108: [PASS/FAIL]
- TC109: [PASS/FAIL]
- TC110: [PASS/FAIL]
Failed Tests:
- TC###: Description - Reason
### Phase 6: Multi-Device (9 tests)
```
Passed: ___/9
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 7: Error Handling (8 tests)
```
Passed: ___/8
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 8: Boundary & Stress (11 tests)
```
Passed: ___/11
Failed: ___
Critical Issues: ___
```
**Performance Results**:
- TC128 (Sequential): ___ MB/s
- TC129 (Random): ___ IOPS
- TC130 (Mixed): ___ req/s
Failed Tests:
- TC###: Description - Reason
### Phase 9: Documentation (1 test)
```
Passed: ___/1
Failed: ___
```
## Critical Failures
Priority: Critical failures that prevent basic functionality
| Test ID | Description | Root Cause | Impact | Status |
|---------|-------------|------------|--------|--------|
| TC### | Brief | Reason | High/Medium/Low | Open/Fixed |
## Known Issues
Non-critical issues or limitations
| Test ID | Description | Workaround | Severity | Tracked |
|---------|-------------|------------|----------|---------|
| TC### | Brief | Workaround if any | Medium/Low | Issue #X |
## Performance Summary
| Metric | Result | Baseline | Delta | Status |
|--------|--------|----------|-------|--------|
| Sequential Read | ___ MB/s | 500 MB/s | ___% | PASS/FAIL |
| Random Read | ___ IOPS | 5000 IOPS | ___% | PASS/FAIL |
| Large File (10GB) | ___ s | 20 s | ___% | PASS/FAIL |
| Many Files (10K) | ___ s | 30 s | ___% | PASS/FAIL |
## Feature Coverage
```
Total Features: 65
Tested: ___
Passed: ___
Failed: ___
Coverage: ___%
```
### Feature Status
- [x] Mount operations: PASS
- [x] Inode formats: PASS/FAIL
- [x] Data layouts: PASS/FAIL
- [x] LZ4 compression: PASS/FAIL
- [x] DEFLATE compression: PASS/FAIL
- [x] zstd compression: PASS/FAIL
- [x] **LZMA compression (NEW)**: PASS/FAIL
- [x] Extended attributes: PASS/FAIL
- [x] Multi-device: PASS/FAIL
- [x] Directory operations: PASS/FAIL
- [x] VFS integration: PASS/FAIL
## Regression Check
Comparison with repo17 (64 features, 91% complete)
| Feature | repo17 | repo18 | Status |
|---------|--------|--------|--------|
| Basic mount | PASS | PASS | No regression |
| LZ4 | PASS | PASS | No regression |
| DEFLATE | PASS | PASS | No regression |
| zstd | PASS | PASS | No regression |
| **LZMA** | NOT IMPL | PASS | **NEW** |
| ... | ... | ... | ... |
## repo18 Iteration 1 Validation
**Goal**: LZMA compression support
### LZMA-Specific Results
- TC004 (Basic LZMA): [PASS/FAIL]
- TC108 (Large file): [PASS/FAIL]
- TC109 (MicroLZMA): [PASS/FAIL]
- TC110 (Corrupted): [PASS/FAIL]
**Data Integrity**: [PASS/FAIL]
- SHA256 checksums match: [YES/NO]
- Random access works: [YES/NO]
- Large file (100MB+) decompressed correctly: [YES/NO]
**Implementation Validation**:
- Self-contained decoder (232 lines): [YES/NO]
- No external dependencies: [YES/NO]
- Unified interface: [YES/NO]
**Iteration 1 Status**: [COMPLETE/INCOMPLETE]
## System Stability
- Kernel panics: [YES/NO] - Count: ___
- Memory leaks: [DETECTED/NONE]
- File descriptor leaks: [DETECTED/NONE]
- dmesg errors: [COUNT]
## Test Environment
```
CPU: ___
Memory: ___ GB
Disk: ___ (type)
Load during tests: ___
```
## Recommendations
1. [Action item 1]
2. [Action item 2]
3. ...
## Sign-Off
- [ ] All critical tests passed
- [ ] No regressions from repo17
- [ ] LZMA implementation validated (repo18 iter 1)
- [ ] Performance acceptable
- [ ] Known issues documented
**Tested by**: _______________
**Reviewed by**: _______________
**Date**: _______________
**Approved**: [YES/NO]
## Attachments
- [ ] Full test logs
- [ ] dmesg output
- [ ] Performance graphs
- [ ] Failure screenshots/dumps
-36
View File
@@ -1,36 +0,0 @@
# Test Case Template
**Test ID**: TC###-brief-name
**Category**: [Mount Operations | Filesystem Metadata | Inode Operations | Data Layouts | Compression | Directory Operations | Symlink Operations | Extended Attributes | Multi-Device | VFS Integration | Read-Only Enforcement | VM Integration | Error Handling | Performance & Stress]
**Priority**: [Critical | High | Medium | Low]
**Regression**: [None | Issue #XXX]
## Objective
Brief description of what this test validates.
## Preconditions
- EROFS image requirements (e.g., "Image with LZ4-compressed files")
- System requirements (e.g., "FreeBSD 13.0+")
- Test data setup
## Test Steps
1. Step one with specific commands
2. Step two with expected intermediate state
3. ...
## Expected Results
- Specific expected outcomes
- Expected output values
- Expected file contents
## Verification Method
How to verify the test passed:
- Command outputs to check
- Files to inspect
- Performance metrics (if applicable)
## Cleanup
Steps to reset the environment after the test.
## Notes
Any additional considerations or known limitations.
-39
View File
@@ -1,39 +0,0 @@
# Test Case: Basic Mount and Unmount
**Test ID**: TC001-mount-basic
## Objective
Verify that the exact G1 KLD mounts a valid image read-only, exposes source
data exactly, and unmounts cleanly.
## Fixture
Use `images/compact.erofs` and `source/compact/root.txt` generated by
`tests/prepare_g1_fixtures.sh`. Follow `tests/G1-MANUAL-SETUP.md` and record the
image, source, and KLD SHA256 values.
## Procedure
```sh
image=/tmp/repo22-g1/images/compact.erofs
source=/tmp/repo22-g1/source/compact/root.txt
mnt=/mnt/repo22-g1-001
md=$(mdconfig -a -t vnode -f "$image")
mkdir -p "$mnt"
kldstat | grep -i erofs
mount -t erofs -o ro "/dev/$md" "$mnt"
mount -p | awk -v p="$mnt" '$2 == p'
cmp "$source" "$mnt/root.txt"
sha256 "$source" "$mnt/root.txt"
./statfs_probe "$mnt"
umount "$mnt"
mdconfig -d -u "${md#md}"
rmdir "$mnt"
```
## Expected Results
- Mount, `cmp`, `statfs_probe`, unmount, and detach all return zero.
- The mount entry is `erofs` and read-only; both file hashes are identical.
- Cleanup leaves neither this mount point nor its md provider.
-66
View File
@@ -1,66 +0,0 @@
# Test Case: Superblock CRC32C Validation
**Test ID**: TC002-superblock-crc32c
**Category**: Filesystem Metadata
**Priority**: Critical
## Objective
Verify the real EROFS superblock checksum field and both equivalent checksum
ranges, then prove that a valid image mounts while an equal-length image with
one covered byte changed and no checksum update is rejected.
## Fixture Qualification
Run from the repo22 root with erofs-utils 1.8.6:
```sh
work=$(mktemp -d /tmp/repo22-tc002.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
python3 tests/erofs_fixture.py inspect "$work/fixtures/valid-plain.erofs"
python3 tests/erofs_fixture.py inspect "$work/fixtures/bad-super-crc.erofs"
grep '^bad-super-crc ' "$work/fixtures/fixture-evidence.txt"
(cd "$work/fixtures" && sha256 -c SHA256SUMS)
```
For a 4096-byte block, the canonical calculation clears the little-endian
checksum at absolute byte 1028 and calculates CRC32C over bytes `[1024,4096)`
with initial value `0xffffffff`. The production verifier uses seed
`0x5045b54a` over `[1032,4096)`. The helper must print identical canonical and
kernel values, `equivalent=True`, and `valid=True` for the control. The bad
image changes byte 1088, preserves provider length, does not recompute CRC, and
must print `valid=False`.
## FreeBSD Procedure
```sh
mkdir "$work/mnt"
unit=$(mdconfig -a -t vnode -f "$work/fixtures/valid-plain.erofs")
mount -t erofs -o ro "/dev/$unit" "$work/mnt"
cmp "$work/fixtures/source/control.txt" "$work/mnt/control.txt"
umount "$work/mnt"
mdconfig -d -u "${unit#md}"
unit=$(mdconfig -a -t vnode -f "$work/fixtures/bad-super-crc.erofs")
set +e
mount -t erofs -o ro "/dev/$unit" "$work/mnt" \
>"$work/bad-mount.out" 2>"$work/bad-mount.err"
mount_status=$?
set -e
printf 'mount_status=%s\n' "$mount_status"
test "$mount_status" -ne 0
set +e
truss -o "$work/bad-mount.truss" mount -t erofs -o ro "/dev/$unit" \
"$work/mnt" >/dev/null 2>&1
set -e
grep -E 'nmount.*ERR#97' "$work/bad-mount.truss"
! mount -p | awk -v path="$work/mnt" '$2 == path { found=1 }
END { exit found ? 0 : 1 }'
mdconfig -d -u "${unit#md}"
```
## Expected Results
The control mounts and reads exactly. The bad image fails with FreeBSD errno
97 (`EINTEGRITY`) and no mount remains. Record both image hashes, provider
lengths, checksum values/ranges, command status, new dmesg lines, and cleanup.
-87
View File
@@ -1,87 +0,0 @@
# Test Case: LZ4 Compressed File Read
**Test ID**: TC003-lz4-compressed-read
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: Critical
**Regression**: None
## Objective
Verify correct decompression and reading of LZ4-compressed files.
## Preconditions
- EROFS image with LZ4-compressed files: `test-lz4.erofs`
- Known reference file: `original.txt` (uncompressed source)
- Compressed file in image: `/compressed/test.txt`
- Mount point: `/mnt/test`
## Test Steps
1. Mount the LZ4 test image:
```
mount -t erofs /dev/md0 /mnt/test
```
2. Verify file exists:
```
ls -lh /mnt/test/compressed/test.txt
```
3. Read the entire file:
```
cat /mnt/test/compressed/test.txt > /tmp/decompressed.txt
```
4. Calculate checksum:
```
sha256 /tmp/decompressed.txt
sha256 original.txt
```
5. Compare byte-for-byte:
```
cmp /tmp/decompressed.txt original.txt
```
6. Test partial read:
```
dd if=/mnt/test/compressed/test.txt of=/tmp/partial.txt bs=1024 count=1
```
7. Verify partial read matches:
```
cmp -n 1024 /tmp/partial.txt original.txt
```
## Expected Results
- Step 1: Mount succeeds
- Step 2: File size matches original uncompressed size
- Step 4: SHA256 checksums are identical
- Step 5: `cmp` returns 0 (files identical)
- Step 7: First 1024 bytes match
## Verification Method
- Exact byte-for-byte match with original file
- No corruption in decompressed data
- File attributes (size, timestamps) preserved correctly
- Check inode compression format:
```
# Using custom debug tool if available
erofs_inspect /dev/md0 /compressed/test.txt
# Should show: z_algorithmformat[0:2] = Z_EROFS_COMPRESSION_LZ4
```
## Cleanup
```
umount /mnt/test
mdconfig -d -u md0
rm /tmp/decompressed.txt /tmp/partial.txt
```
## Notes
- LZ4 is the most common compression algorithm in EROFS
- Test should cover files of various sizes: small (<4KB), medium (4KB-1MB), large (>1MB)
- LZ4 pcluster size is typically 4KB or 64KB
- Related: TC004 (ztailpacking), TC005 (pcluster mapping)
-96
View File
@@ -1,96 +0,0 @@
# Test Case: LZMA Compressed File Read
**Test ID**: TC004-lzma-compressed-read
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: High
**Regression**: None
## Objective
Verify correct decompression and reading of LZMA/MicroLZMA-compressed files (newly implemented in repo18 iteration 1).
## Preconditions
- EROFS image with LZMA-compressed files: `test-lzma.erofs`
- Known reference file: `original-large.txt` (uncompressed source)
- Compressed file in image: `/compressed/large.txt`
- Mount point: `/mnt/test`
## Test Steps
1. Mount the LZMA test image:
```sh
unit=$(mdconfig -a -t vnode -f test-lzma.erofs)
mount -t erofs -o ro /dev/${unit} /mnt/test
```
2. Verify file exists and check size:
```
ls -lh /mnt/test/compressed/large.txt
stat -f "Size: %z bytes" /mnt/test/compressed/large.txt
```
3. Read entire compressed file:
```
cat /mnt/test/compressed/large.txt > /tmp/lzma-decompressed.txt
```
4. Verify decompression correctness:
```
sha256 /tmp/lzma-decompressed.txt
sha256 original-large.txt
```
5. Compare byte-for-byte:
```
cmp /tmp/lzma-decompressed.txt original-large.txt
echo $?
```
6. Test random access read:
```
dd if=/mnt/test/compressed/large.txt of=/tmp/lzma-middle.txt bs=1k skip=100 count=10
dd if=original-large.txt of=/tmp/orig-middle.txt bs=1k skip=100 count=10
cmp /tmp/lzma-middle.txt /tmp/orig-middle.txt
```
7. Test end-of-file read:
```
tail -c 1000 /mnt/test/compressed/large.txt > /tmp/lzma-tail.txt
tail -c 1000 original-large.txt > /tmp/orig-tail.txt
cmp /tmp/lzma-tail.txt /tmp/orig-tail.txt
```
## Expected Results
- Step 1: Mount succeeds
- Step 2: File size matches original uncompressed size exactly
- Step 4: SHA256 checksums are identical
- Step 5: Exit code 0 (files identical)
- Step 6: Middle section matches (random access works)
- Step 7: Tail matches (EOF handling correct)
## Verification Method
- Complete data integrity check via SHA256
- Partial read correctness (random access)
- No memory corruption or crashes during decompression
- Verify LZMA decoder internal state:
- 1846 probability models initialized
- Range decoder normalization correct
- Dictionary buffer within bounds
## Cleanup
```
umount /mnt/test
mdconfig -d -u "${unit}"
rm /tmp/lzma-*.txt /tmp/orig-*.txt
```
## Notes
- **NEW in repo18**: This is the first iteration with LZMA support
- LZMA provides maximum compression ratio (~30-50% smaller than LZ4)
- MicroLZMA is a variant without header, commonly used in EROFS
- Self-contained implementation in `src/decompressor_lzma.c`
- Reference implementation: XZ Embedded minimal decoder
- Critical test for repo18 iteration 1 validation
- Regression marker: First LZMA implementation, high priority for validation
-32
View File
@@ -1,32 +0,0 @@
# Test Case: Inline user xattrs
**Test ID**: TC005-inline-xattr-user
**Fixture**: `basic.erofs`, `/inline-user`
## Objective
Verify inline `user` namespace list/get and binary-value handling on FreeBSD.
Generate and qualify the image as described in `G4-MANUAL-SETUP.md`; the
manifest must classify `comment` and `special.chars` as inline entries.
## Manual steps
```sh
unit=$(mdconfig -a -t vnode -f /tmp/repo22-g4/images/basic.erofs)
mount -t erofs -o ro /dev/${unit} /mnt/repo22-g4
stat -f 'mode=%Sp size=%z inode=%i' /mnt/repo22-g4/inline-user
lsextattr user /mnt/repo22-g4/inline-user
getextattr -qq -x user comment /mnt/repo22-g4/inline-user
getextattr -qq -x user special.chars /mnt/repo22-g4/inline-user
truss -o /tmp/tc005.truss getextattr -qq user missing /mnt/repo22-g4/inline-user
grep extattr_get_file /tmp/tc005.truss
umount /mnt/repo22-g4
mdconfig -d -u "${unit#md}"
```
## Expected results
The names appear once. `comment` is hex
`696e6c696e652d757365722d76616c7565007461696c`; `special.chars` is
`7370656369616c2d76616c7565`. The missing name returns `ENOATTR` (87), and
cleanup leaves no mount or md provider.
-61
View File
@@ -1,61 +0,0 @@
# Test Case: Real Multi-Device Chunk Read
**Test ID**: TC006-multidev-chunk-read
**Category**: Multi-Device / Chunk-Based
**Priority**: Critical
## Objective
Prove that 8-byte chunk indexes read file data from a live external GEOM
provider, including a range crossing a chunk boundary.
## Fixture
Generate and verify the fresh G6 set as described in `G6-MANUAL-SETUP.md`.
Use `mkfs-blob-primary.erofs` and `mkfs-blob-slot1.blob`. The manifest must
show primary blocks `1`, slot-1 blocks `224`, and every `/tc006.bin` index as
device ID `1`. The 4096-byte primary cannot contain the 98304-byte source.
## Manual procedure
```sh
I=/root/repo22-g6/images
S=/root/repo22-g6/sources/chunk
mkdir -p /mnt/g6
kldload /root/repo22-g6/erofs.ko
mdconfig -a -t vnode -f "$I/mkfs-blob-primary.erofs" -u 90
mdconfig -a -t vnode -f "$I/mkfs-blob-slot1.blob" -u 91
mount -t erofs -o ro -o device.1=/dev/md91 /dev/md90 /mnt/g6
sha256 "$S/tc006.bin" /mnt/g6/tc006.bin
cmp "$S/tc006.bin" /mnt/g6/tc006.bin
dd if="$S/tc006.bin" of=/tmp/tc006.expected bs=1 skip=28672 count=8192 2>/dev/null
dd if=/mnt/g6/tc006.bin of=/tmp/tc006.actual bs=1 skip=28672 count=8192 2>/dev/null
cmp /tmp/tc006.expected /tmp/tc006.actual
truss -f -o /tmp/tc006-ebusy.truss mdconfig -d -u 91
tail -20 /tmp/tc006-ebusy.truss
sysctl -n kern.geom.conftxt | grep -A8 -B2 'Geom name: md91'
```
The detach must fail with `EBUSY`, proving a live external consumer.
## Expected results
- Mount, full-file `cmp`, SHA256, and the 8192-byte cross-boundary range pass.
- The external detach returns exactly `EBUSY` while mounted.
- No panic, trap, hang, or unexpected EROFS dmesg message occurs.
## Cleanup and zero-state check
```sh
umount /mnt/g6
mdconfig -d -u 91
mdconfig -d -u 90
kldunload erofs
rm -f /tmp/tc006.expected /tmp/tc006.actual
mount -p | awk '$3 == "erofs" { print }'
mdconfig -l
kldstat -n erofs 2>/dev/null || true
sysctl -n kern.geom.conftxt | grep -E 'md9[01]|erofs' || true
```
All four final outputs must be empty.
-48
View File
@@ -1,48 +0,0 @@
# Test Case: Concurrent Mount Operations
**Test ID**: TC007-concurrent-mount
## Objective
Verify two simultaneous mounts of the same deterministic EROFS image through
independent md providers, including source-exact reads from both mounts.
## Fixture
Use `images/compact.erofs` and `source/compact/testfile.txt` from the G1
fixture set. Follow `tests/G1-MANUAL-SETUP.md`.
## Procedure
```sh
image=/tmp/repo22-g1/images/compact.erofs
source=/tmp/repo22-g1/source/compact/testfile.txt
mnt1=/mnt/repo22-g1-007a
mnt2=/mnt/repo22-g1-007b
md1=$(mdconfig -a -t vnode -f "$image")
md2=$(mdconfig -a -t vnode -f "$image")
mkdir -p "$mnt1" "$mnt2"
set +e
mount -t erofs -o ro "/dev/$md1" "$mnt1" & p1=$!
mount -t erofs -o ro "/dev/$md2" "$mnt2" & p2=$!
wait "$p1"; rc1=$?
wait "$p2"; rc2=$?
set -e
printf 'mount_rc=%d,%d providers=%s,%s\n' "$rc1" "$rc2" "$md1" "$md2"
test "$rc1" -eq 0 -a "$rc2" -eq 0
cmp "$source" "$mnt1/testfile.txt"
cmp "$source" "$mnt2/testfile.txt"
cmp "$mnt1/testfile.txt" "$mnt2/testfile.txt"
sha256 "$source" "$mnt1/testfile.txt" "$mnt2/testfile.txt"
umount "$mnt1"
umount "$mnt2"
mdconfig -d -u "${md1#md}"
mdconfig -d -u "${md2#md}"
rmdir "$mnt1" "$mnt2"
```
## Expected Results
- Both independently waited mount processes return zero.
- All three comparisons and SHA256 values match.
- No panic, trap, EROFS diagnostic, mount, or md provider remains.
-77
View File
@@ -1,77 +0,0 @@
# Test Case: Mount Error Conditions
**Test ID**: TC008-mount-errors
**Category**: Mount Operations
**Priority**: High
## Objective
Verify graceful rejection of three distinct provider conditions: a
checksum-valid bad magic at the correct field, a 1023-byte provider ending
before the superblock, and a zero-length provider.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc008.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
grep -E '^(bad-magic|truncated-before-super|empty-provider) ' \
"$work/fixtures/fixture-evidence.txt"
python3 tests/erofs_fixture.py inspect "$work/fixtures/bad-magic.erofs"
wc -c "$work/fixtures/bad-magic.erofs" \
"$work/fixtures/truncated-before-super.erofs" \
"$work/fixtures/empty-provider.erofs"
(cd "$work/fixtures" && sha256 -c SHA256SUMS)
```
The bad-magic variant changes only absolute bytes 1024 through 1027 and
preserves media size. It intentionally retains the original checksum: the
production verifier's fixed-magic suffix calculation remains valid, while the
canonical checksum over the now-invalid magic does not. Magic is rejected
before checksum verification. The short and empty fixtures are intentional
provider-size cases, not same-size corruption variants.
## FreeBSD Procedure
Create a fresh mount directory. Execute each case separately and record whether
failure occurs at `mdconfig` or `nmount`; never assume a fixed md number.
```sh
mkdir "$work/mnt"
for image in bad-magic.erofs truncated-before-super.erofs empty-provider.erofs
do
set +e
unit=$(mdconfig -a -t vnode -f "$work/fixtures/$image" \
2>"$work/$image.md.err")
md_status=$?
set -e
printf '%s md_status=%s unit=%s\n' "$image" "$md_status" "$unit"
if test "$md_status" -eq 0; then
set +e
mount -t erofs -o ro "/dev/$unit" "$work/mnt" \
>"$work/$image.out" 2>"$work/$image.mount.err"
mount_status=$?
set -e
printf '%s mount_status=%s\n' "$image" "$mount_status"
test "$mount_status" -ne 0
set +e
truss -o "$work/$image.mount.truss" mount -t erofs -o ro \
"/dev/$unit" "$work/mnt" >/dev/null 2>&1
set -e
grep -E 'nmount.*ERR#' "$work/$image.mount.truss"
mdconfig -d -u "${unit#md}"
else
cat "$work/$image.md.err"
fi
! mount -p | awk -v path="$work/mnt" '$2 == path { found=1 }
END { exit found ? 0 : 1 }'
done
```
## Expected Results
All three cases fail without panic or hang. Bad magic reaches `nmount` and
returns `EINVAL`. A 1023-byte vnode provider reaches `nmount` and returns
`ENXIO`; a zero-byte provider is rejected by `mdconfig` with `EINVAL`. Record
the observed stage and errno rather than fabricating a mount result. No md unit
or mount may remain after each case.
-42
View File
@@ -1,42 +0,0 @@
# Test Case: Basic statfs Information
**Test ID**: TC009-statfs-basic
## Objective
Verify FreeBSD `statfs(2)` fields against the qualified image superblock.
## Fixture
Use `images/compact.erofs`. Before transfer, record `dump.erofs -s` and the
`compact` superblock row in `fixture-evidence.txt`. Follow
`tests/G1-MANUAL-SETUP.md` and compile `tests/statfs_probe.c` in the guest.
## Procedure
Host qualification:
```sh
dump.erofs -s /work/build/repo22-g1/images/compact.erofs
python3 tests/erofs_fixture.py inspect /work/build/repo22-g1/images/compact.erofs
```
Guest dynamic check after mounting the image on `$mnt`:
```sh
./statfs_probe "$mnt"
df -kT "$mnt"
df -iT "$mnt"
mount -p | awk -v p="$mnt" '$2 == p'
```
Unmount and detach the exact dynamic md unit.
## Expected Results
- `fstype=erofs`, `bsize=4096`, `iosize=4096`, and `readonly=1`.
- `blocks` and `files` equal the image's declared block and inode counts.
- `bfree`, `bavail`, and `ffree` are zero; `df` conversions agree.
- `mount -p` contains `ro`; cleanup succeeds.
`stat -f` is not a `statfs(2)` probe and must not be substituted.
-60
View File
@@ -1,60 +0,0 @@
# Test Case: statfs with 48-bit Block Count
**Test ID**: TC010-statfs-48bit
**Category**: Filesystem Metadata
**Priority**: Medium
## Objective
Verify a nonzero 48-bit block-count high word survives mount validation and is
exported without truncation through FreeBSD `statfs(2)` and `df(1)`.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc010.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
python3 tests/erofs_fixture.py inspect \
"$work/fixtures/48bit-statfs-prefix.erofs"
grep '^48bit-statfs-prefix ' "$work/fixtures/fixture-evidence.txt"
sha256 -q "$work/fixtures/48bit-statfs-prefix.erofs"
```
Read `total_blocks` and `required_provider_length` from the evidence. The
fixture sets incompat bit `0x80`, `rb.blocks_hi=1`, and a nonzero
`rootnid_8b`, then recomputes CRC32C. Its small prefix hash identifies the
metadata; the qualified provider is that exact prefix followed by sparse zeros
to the required media size.
## FreeBSD Procedure
```sh
provider="$work/48bit-statfs-provider.raw"
cp "$work/fixtures/48bit-statfs-prefix.erofs" "$provider"
truncate -s "$required_provider_length" "$provider"
stat -f 'size=%z blocks=%b' "$provider"
unit=$(mdconfig -a -t vnode -f "$provider")
diskinfo -v "/dev/$unit"
mkdir "$work/mnt"
mount -t erofs -o ro "/dev/$unit" "$work/mnt"
df -kT "$work/mnt"
df -iT "$work/mnt"
actual_1k=$(df -k "$work/mnt" | awk 'NR == 2 { print $2 }')
used_1k=$(df -k "$work/mnt" | awk 'NR == 2 { print $3 }')
avail_1k=$(df -k "$work/mnt" | awk 'NR == 2 { print $4 }')
expected_1k=$((total_blocks * 4))
test "$actual_1k" -eq "$expected_1k"
test "$used_1k" -eq "$expected_1k"
test "$avail_1k" -eq 0
umount "$work/mnt"
mdconfig -d -u "${unit#md}"
rm -f "$provider"
```
## Expected Results
`diskinfo` reports at least the declared 16 TiB-plus media size. Mount succeeds,
the `df -k` total equals `total_blocks * 4`, Used equals total because
`f_bfree=0`, and Avail is zero.
If the filesystem cannot create or attach the sparse provider, record SHELVED
with all failed commands; a short-provider `ENXIO` is not PASS.
-36
View File
@@ -1,36 +0,0 @@
# Test Case: Root Vnode Lookup
**Test ID**: TC011-root-vnode
## Objective
Verify the mounted root vnode has the encoded root NID, directory attributes,
and the same top-level names as the fixture source.
## Fixture
Use `images/compact.erofs` and `source/compact`. The structured evidence records
the root NID and compact inode size. Follow `tests/G1-MANUAL-SETUP.md`.
## Procedure
After mounting on `$mnt`:
```sh
stat -f 'ino=%i type=%HT mode=%Lp nlink=%l' "$mnt"
stat -f '%i' "$mnt" > /tmp/tc011-root-nid
ls -la "$mnt"
(cd /tmp/repo22-g1/source/compact && ls -1A | sort) > /tmp/tc011-source-names
(cd "$mnt" && ls -1A | sort) > /tmp/tc011-mount-names
diff -u /tmp/tc011-source-names /tmp/tc011-mount-names
cat "$mnt/root.txt"
```
Compare `/tmp/tc011-root-nid` with `root_nid` from `fixture-evidence.txt`, then
unmount, detach, and remove the two temporary name lists.
## Expected Results
- Root inode equals the encoded NID and reports a traversable directory.
- The complete top-level name lists match and `root.txt` is readable.
- All operations and cleanup return zero.
-39
View File
@@ -1,39 +0,0 @@
# Test Case: VFS Vget Normal Operation
**Test ID**: TC012-vget-normal
## Objective
Exercise `VFS_VGET` through a real FreeBSD file handle and verify the returned
vnode identifies and reads the same EROFS inode as pathname lookup.
## Fixture
Use `images/compact.erofs`, `source/compact/testfile.txt`, and
`tests/nfs_fh_tool.c`. Compile the helper as described in
`tests/G1-MANUAL-SETUP.md`; run as root.
## Procedure
After mounting on `$mnt`:
```sh
stat -f 'path_ino=%i gen=%v size=%z' "$mnt/testfile.txt"
./nfs_fh_tool capture "$mnt/testfile.txt" /tmp/tc012-a.fh
./nfs_fh_tool capture "$mnt/testfile.txt" /tmp/tc012-b.fh
./nfs_fh_tool compare /tmp/tc012-a.fh /tmp/tc012-b.fh
./nfs_fh_tool describe /tmp/tc012-a.fh
./nfs_fh_tool stat /tmp/tc012-a.fh
./nfs_fh_tool cat /tmp/tc012-a.fh /tmp/tc012-fh.out
cmp /tmp/repo22-g1/source/compact/testfile.txt /tmp/tc012-fh.out
sha256 /tmp/repo22-g1/source/compact/testfile.txt /tmp/tc012-fh.out
```
Remove the handles/output, then unmount and detach.
## Expected Results
- Both captures produce the same handle.
- Handle NID, `fhstat` inode, and pathname inode agree.
- `fhopen` output matches the deterministic source exactly.
- No stale/duplicate vnode error or cleanup failure occurs.
-39
View File
@@ -1,39 +0,0 @@
# Test Case: Vget with Invalid NID
**Test ID**: TC013-vget-invalid
## Objective
Verify a checksum-valid directory entry that points outside the image reaches
the cold lookup/vget path and fails consistently with `EINTEGRITY`.
## Fixture
Use `images/invalid-dirent-nid.erofs`. `fixture-evidence.txt` records the exact
dirent offset, old NID, out-of-range NID, and valid recomputed CRC. Compile
`tests/read_probe.c` in the guest.
## Procedure
Mount the image without first listing the root, then run:
```sh
./read_probe expect-error "$mnt/invalid-target.txt" 97
./read_probe expect-error "$mnt/invalid-target.txt" 97
set +e
truss -o /tmp/tc013.truss stat "$mnt/invalid-target.txt"
rc=$?
set -e
printf 'stat_rc=%d\n' "$rc"
tail -20 /tmp/tc013.truss
```
Record dmesg before/after, remove the trace, then unmount and detach.
## Expected Results
- Mount succeeds because only the child dirent is malformed.
- Both cold/repeated opens fail at pathname acquisition with FreeBSD errno 97
(`EINTEGRITY`); `truss` shows the same syscall errno.
- The second failure is not converted to `ENOENT`; no panic or stale vnode
appears, and cleanup succeeds.
-42
View File
@@ -1,42 +0,0 @@
# Test Case: Superblock Parsing
**Test ID**: TC014-superblock-parse
## Objective
Verify mandatory superblock fields at their real on-disk offsets and compare
the mounted values exposed by FreeBSD.
## Fixture
Use `images/compact.erofs`, its image hash, `fixture-evidence.txt`, and
`tests/statfs_probe.c`.
## Procedure
Host inspection:
```sh
od -An -tx4 -j 1024 -N 4 /work/build/repo22-g1/images/compact.erofs
od -An -tu1 -j 1036 -N 1 /work/build/repo22-g1/images/compact.erofs
od -An -tu2 -j 1038 -N 2 /work/build/repo22-g1/images/compact.erofs
od -An -tu8 -j 1040 -N 8 /work/build/repo22-g1/images/compact.erofs
od -An -tu4 -j 1060 -N 8 /work/build/repo22-g1/images/compact.erofs
dump.erofs -s /work/build/repo22-g1/images/compact.erofs
python3 tests/erofs_fixture.py inspect /work/build/repo22-g1/images/compact.erofs
```
Mount the same hashed image in FreeBSD, then run:
```sh
./statfs_probe "$mnt"
stat -f 'root_ino=%i type=%HT' "$mnt"
mount -p | awk -v p="$mnt" '$2 == p'
```
## Expected Results
- Magic is `0xe0f5e1e2`, `blkszbits=12`, and checksum validation is true.
- Root NID, block count, and inode count agree with root `stat` and
`statfs_probe`; filesystem type is `erofs` and read-only.
- No parser error appears in the dmesg delta; cleanup succeeds.
-63
View File
@@ -1,63 +0,0 @@
# Test Case: Corrupted Superblock Fields
**Test ID**: TC015-superblock-corrupted
**Category**: Error Handling
**Priority**: High
## Objective
Verify ordered rejection of an unsupported block size, an image-bounds-invalid
48-bit root NID, and an unknown incompat feature. Every variant preserves
provider length and carries a checksum valid for its encoded fields.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc015.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
grep -E '^(bad-block-size|bad-root-nid|unsupported-feature) ' \
"$work/fixtures/fixture-evidence.txt"
python3 tests/erofs_fixture.py inspect "$work/fixtures/bad-block-size.erofs"
python3 tests/erofs_fixture.py inspect "$work/fixtures/bad-root-nid.erofs"
python3 tests/erofs_fixture.py inspect "$work/fixtures/unsupported-feature.erofs"
(cd "$work/fixtures" && sha256 -c SHA256SUMS)
```
`bad-block-size` changes `blkszbits` at 1036 from 12 to 13 and recomputes the
checksum over `[1024,8192)`. `bad-root-nid` selects the 48-bit union form and
sets `rootnid_8b` beyond the declared inode area. `unsupported-feature` sets
unknown incompat bit `0x80000000`.
## FreeBSD Procedure
For each image, attach a dynamic vnode md unit, run mount directly and under
`truss`, assert nonzero status and no mount entry, then detach that exact unit.
```sh
mkdir "$work/mnt"
for image in bad-block-size.erofs bad-root-nid.erofs unsupported-feature.erofs
do
unit=$(mdconfig -a -t vnode -f "$work/fixtures/$image")
set +e
mount -t erofs -o ro "/dev/$unit" "$work/mnt" \
>"$work/$image.out" 2>"$work/$image.err"
status=$?
set -e
printf '%s status=%s\n' "$image" "$status"
test "$status" -ne 0
set +e
truss -o "$work/$image.truss" mount -t erofs -o ro "/dev/$unit" \
"$work/mnt" >/dev/null 2>&1
set -e
grep -E 'nmount.*ERR#' "$work/$image.truss"
! mount -p | awk -v path="$work/mnt" '$2 == path { found=1 }
END { exit found ? 0 : 1 }'
mdconfig -d -u "${unit#md}"
done
```
## Expected Results
Bad block size returns `EINVAL`, bad root NID returns `EINTEGRITY`, and the
unknown incompat bit returns `EOPNOTSUPP`. Record syscall errno, image hash,
provider length, new dmesg lines, and cleanup for all three subcases.
-52
View File
@@ -1,52 +0,0 @@
# Test Case: Superblock CRC Field Mismatch
**Test ID**: TC016-superblock-crc-invalid
**Category**: Error Handling
**Priority**: High
## Objective
Verify that changing only the stored superblock checksum field causes
`EINTEGRITY`, with all protected bytes and provider length unchanged.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc016.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
grep '^bad-checksum-field ' "$work/fixtures/fixture-evidence.txt"
python3 tests/erofs_fixture.py inspect \
"$work/fixtures/bad-checksum-field.erofs"
wc -c "$work/fixtures/valid-plain.erofs" \
"$work/fixtures/bad-checksum-field.erofs"
sha256 -q "$work/fixtures/bad-checksum-field.erofs"
```
The mutator flips bit 0 of the little-endian checksum at absolute byte 1028,
does not recompute it, asserts checksum invalidity, and changes no other byte.
## FreeBSD Procedure
```sh
mkdir "$work/mnt"
unit=$(mdconfig -a -t vnode -f "$work/fixtures/bad-checksum-field.erofs")
set +e
mount -t erofs -o ro "/dev/$unit" "$work/mnt" \
>"$work/mount.out" 2>"$work/mount.err"
status=$?
set -e
test "$status" -ne 0
set +e
truss -o "$work/mount.truss" mount -t erofs -o ro "/dev/$unit" \
"$work/mnt" >/dev/null 2>&1
set -e
grep -E 'nmount.*ERR#97' "$work/mount.truss"
! mount -p | awk -v path="$work/mnt" '$2 == path { found=1 }
END { exit found ? 0 : 1 }'
mdconfig -d -u "${unit#md}"
```
## Expected Results
Mount returns errno 97 (`EINTEGRITY`) with a checksum diagnostic. No mount or
md consumer remains.
-44
View File
@@ -1,44 +0,0 @@
# Test Case: 48-bit Block Count Parsing
**Test ID**: TC017-48bit-blocks-parse
**Category**: Filesystem Metadata
**Priority**: Medium
## Objective
Verify the Linux EROFS union rule dynamically: with incompat bit `0x80` and a
nonzero `rootnid_8b`, combine `blocks_lo` with `rb.blocks_hi << 32` and preserve
the complete count through mount.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc017.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
python3 tests/erofs_fixture.py inspect \
"$work/fixtures/48bit-statfs-prefix.erofs"
od -An -tx2 -j 1038 -N 2 "$work/fixtures/48bit-statfs-prefix.erofs"
od -An -tx4 -j 1060 -N 4 "$work/fixtures/48bit-statfs-prefix.erofs"
od -An -tx8 -j 1136 -N 8 "$work/fixtures/48bit-statfs-prefix.erofs"
grep '^48bit-statfs-prefix ' "$work/fixtures/fixture-evidence.txt"
```
Require `blocks_hi=1`, `rootnid_8b != 0`, a valid recomputed CRC, and a recorded
prefix SHA256. The expected count is `blocks_lo | (1 << 32)`.
## FreeBSD Procedure
Create and attach the sparse provider exactly as in TC010. Record `diskinfo`,
mount it read-only, compare `df -k` total with `total_blocks * 4`, read
`control.txt`, then unmount and detach the dynamic md unit.
Also attach the unextended small prefix once and require mount failure with
`ENXIO`; record this only as the negative media-size guard, not the positive
parse result.
## Expected Results
The sparse provider mount succeeds and reports the full nonzero-high-word count.
The small prefix fails `ENXIO`. If no qualifying provider can be attached,
record the metadata parsing evidence separately and mark the dynamic portion
SHELVED, never PASS.
-66
View File
@@ -1,66 +0,0 @@
# Test Case: Large Filesystem with 48-bit Addressing
**Test ID**: TC018-48bit-large-fs
**Category**: Filesystem Metadata
**Priority**: Low
## Objective
Verify a real FLAT_PLAIN read whose extended inode has `startblk_hi=1`, so the
provider I/O occurs above 16 TiB rather than only reporting a large `statfs`
total.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc018.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
python3 tests/erofs_fixture.py inspect \
"$work/fixtures/48bit-high-file-prefix.erofs" --path /high-offset.txt
grep '^48bit-high-file-prefix ' "$work/fixtures/fixture-evidence.txt"
sha256 -q "$work/fixtures/48bit-high-file-prefix.erofs"
```
Require an extended nonempty FLAT_PLAIN inode, `startblk_hi=1`, a physical
`data_offset >= 17592186044416`, a valid CRC, and an end offset within
`required_provider_length`. The mutator zeros the original low-block payload,
so a reader that ignores `startblk_hi` cannot return the expected source bytes.
Read `required_provider_length`, `start_block`, `data_offset`,
`low_decoy_offset`, and `file_size` from the evidence line.
## FreeBSD Procedure
```sh
provider="$work/48bit-high-file-provider.raw"
cp "$work/fixtures/48bit-high-file-prefix.erofs" "$provider"
truncate -s "$required_provider_length" "$provider"
dd if="$work/fixtures/source/high-offset.txt" of="$provider" bs=4096 \
seek="$start_block" conv=notrunc
cc -O2 -Wall -Wextra -std=c17 tests/read_probe.c -o "$work/read_probe"
"$work/read_probe" pread "$provider" "$low_decoy_offset" "$file_size" \
"$work/raw-low-offset.txt"
"$work/read_probe" pread "$provider" "$data_offset" "$file_size" \
"$work/raw-high-offset.txt"
dd if=/dev/zero of="$work/zero.bin" bs="$file_size" count=1
cmp "$work/zero.bin" "$work/raw-low-offset.txt"
! cmp -s "$work/fixtures/source/high-offset.txt" "$work/raw-low-offset.txt"
cmp "$work/fixtures/source/high-offset.txt" "$work/raw-high-offset.txt"
unit=$(mdconfig -a -t vnode -f "$provider")
diskinfo -v "/dev/$unit"
mkdir "$work/mnt"
mount -t erofs -o ro "/dev/$unit" "$work/mnt"
cmp "$work/fixtures/source/high-offset.txt" "$work/mnt/high-offset.txt"
cmp "$work/fixtures/source/control.txt" "$work/mnt/control.txt"
df -h "$work/mnt"
umount "$work/mnt"
mdconfig -d -u "${unit#md}"
rm -f "$provider"
```
## Expected Results
The raw provider probe and mounted file both return the exact source bytes from
above 16 TiB, while a low-offset control remains valid. Record the prefix hash,
sparse media size/allocation, high offset, source/data hash, dmesg delta, and
cleanup. If sparse high-offset I/O is unavailable, mark SHELVED with all
attempts; a large `df` result alone cannot pass TC018.
-44
View File
@@ -1,44 +0,0 @@
# Test Case: Nonzero 48-bit Root NID
**Test ID**: TC019-48bit-root-nid
## Objective
Verify the `48BIT && rootnid_8b != 0` selector uses the complete 64-bit root
field and interprets the two-byte union as `blocks_hi`.
## Fixture
Use `images/root8-48bit.erofs`. The structured transformer requires incompat
bit `0x80`, nonzero image-bounded `rootnid_8b`, `blocks_hi=0`, valid CRC32C,
and unchanged provider-sized `blocks_lo`. Production fsck.erofs 1.8.6 does not
recognize this feature; FreeBSD mount/read is mandatory.
## Procedure
Host field check:
```sh
python3 tests/erofs_fixture.py inspect /work/build/repo22-g1/images/root8-48bit.erofs --path=/root.txt
od -An -tu2 -j 1038 -N 2 /work/build/repo22-g1/images/root8-48bit.erofs
od -An -tx4 -j 1104 -N 4 /work/build/repo22-g1/images/root8-48bit.erofs
od -An -tu8 -j 1136 -N 8 /work/build/repo22-g1/images/root8-48bit.erofs
```
Mount the same hashed image in FreeBSD, then run:
```sh
stat -f 'root_ino=%i type=%HT' "$mnt"
cmp /tmp/repo22-g1/source/compact/root.txt "$mnt/root.txt"
sha256 /tmp/repo22-g1/source/compact/root.txt "$mnt/root.txt"
./statfs_probe "$mnt"
```
## Expected Results
- `feature_incompat & 0x80` is set, `rootnid_8b` is nonzero, and observed root
inode equals that complete field.
- Root content matches the source and statfs uses `blocks_lo` plus zero
`blocks_hi`; mount and cleanup succeed.
- TC150's zero-`rootnid_8b` fallback is a separate selector and is not accepted
as evidence for this TC.
-32
View File
@@ -1,32 +0,0 @@
# Test Case: Compact Inode Decoding
**Test ID**: TC020-compact-inode-basic
## Objective
Verify a real 32-byte compact regular inode exposes correct mode, link count,
size, allocation, and source-exact data.
## Fixture
Use `images/compact.erofs` and `source/compact/small.txt`. The evidence records
the NID and asserts `inode_size=32`.
## Procedure
After mounting on `$mnt`:
```sh
stat -f 'ino=%i size=%z mode=%p nlink=%l blocks=%b mtime=%m' "$mnt/small.txt"
wc -c /tmp/repo22-g1/source/compact/small.txt "$mnt/small.txt"
cmp /tmp/repo22-g1/source/compact/small.txt "$mnt/small.txt"
sha256 /tmp/repo22-g1/source/compact/small.txt "$mnt/small.txt"
```
Unmount and detach the exact md unit.
## Expected Results
- The inode is compact, regular mode `0644`, link count 1, timestamp 0, and
size equal to the source.
- Full content and hashes match; all operations and cleanup return zero.
-28
View File
@@ -1,28 +0,0 @@
# Test Case: Compact Inode I_NLINK_1
**Test ID**: TC021-compact-inode-nlink1
## Objective
Verify bit 4 on a non-directory compact inode forces `st_nlink=1` while the
`i_nb` union remains available for address high bits.
## Fixture
Use `images/compact-nlink1.erofs` and `source/compact/single.txt`.
`fixture-evidence.txt` asserts a 32-byte regular inode, bit 4 set,
`i_nb=0x1234`, and valid recomputed CRC32C.
## Procedure
```sh
stat -f 'ino=%i nlink=%l size=%z mode=%p' "$mnt/single.txt"
cmp /tmp/repo22-g1/source/compact/single.txt "$mnt/single.txt"
sha256 /tmp/repo22-g1/source/compact/single.txt "$mnt/single.txt"
```
## Expected Results
- `st_nlink=1` despite on-disk `i_nb=0x1234`.
- Inline file data remains source-exact; mount and cleanup succeed.
- An `i_nb` value of zero is not used as a substitute for the bit-4 encoding.
-31
View File
@@ -1,31 +0,0 @@
# Test Case: Compact Special Inodes and Linux dev_t Decode
**Test ID**: TC022-compact-inode-special
## Objective
Verify compact char/block/FIFO inodes and Linux `new_decode_dev` conversion to
FreeBSD `dev_t`.
## Fixture
Use `images/compact.erofs`. Source metadata records real nodes
`char-large=2748:344865`, `block-large=2748:344865`, and `fifo`; structured
evidence asserts compact inodes and raw on-disk `i_u.rdev=0x543abc21` for both
devices. Compile `tests/stat_special.c` natively in FreeBSD.
## Procedure
```sh
./stat_special char "$mnt/char-large" 2748 344865
./stat_special block "$mnt/block-large" 2748 344865
./stat_special fifo "$mnt/fifo"
stat -f '%N mode=%p rdev=%r' "$mnt/char-large" "$mnt/block-large" "$mnt/fifo"
```
## Expected Results
- Char and block helpers report major 2748, minor 344865, and raw FreeBSD
`st_rdev=0xa430005bc21`.
- FIFO type is correct and `st_rdev=NODEV`.
- A raw little-endian cast would differ; all helper checks and cleanup pass.
-27
View File
@@ -1,27 +0,0 @@
# Test Case: Extended Inode Decoding
**Test ID**: TC023-extended-inode-normal
## Objective
Verify a real 64-byte extended regular inode, including size, full-width
metadata, timestamps, and source-exact content.
## Fixture
Use `images/extended.erofs` and `source/extended/large-file.bin`.
`fixture-evidence.txt` records the NID, byte offset, size, and `inode_size=64`.
## Procedure
```sh
stat -f 'ino=%i size=%z mode=%p nlink=%l mtime=%m ctime=%c blocks=%b' "$mnt/large-file.bin"
cmp /tmp/repo22-g1/source/extended/large-file.bin "$mnt/large-file.bin"
sha256 /tmp/repo22-g1/source/extended/large-file.bin "$mnt/large-file.bin"
```
## Expected Results
- The file reports the evidence/source size, regular `0644`, link count 1,
deterministic timestamps, and a 64-byte on-disk inode.
- Full source compare and SHA256 match; cleanup succeeds.
-38
View File
@@ -1,38 +0,0 @@
# Test Case: Extended Inode File Above 4 GiB
**Test ID**: TC024-extended-inode-large
## Objective
Verify a 64-bit extended `i_size` above 4 GiB without materializing a multi-GiB
payload, using deterministic FLAT_PLAIN hole reads at distant offsets.
## Fixture
Use `images/extended-large-hole.erofs` and
`expected/large-hole-zero-64k.bin`. Structured evidence asserts a 64-byte
inode, size `4294971393`, layout 0, `startblk=0xffffffffffff`, valid CRC, and
an all-zero content descriptor.
## Procedure
After mounting, run three bounded reads:
```sh
stat -f 'size=%z blocks=%b mode=%p' "$mnt/huge-sparse.dat"
./read_probe pread "$mnt/huge-sparse.dat" 0 65536 /tmp/tc024-start
./read_probe pread "$mnt/huge-sparse.dat" 2147483648 65536 /tmp/tc024-middle
./read_probe pread "$mnt/huge-sparse.dat" 4294905857 65536 /tmp/tc024-end
cmp /tmp/repo22-g1/expected/large-hole-zero-64k.bin /tmp/tc024-start
cmp /tmp/repo22-g1/expected/large-hole-zero-64k.bin /tmp/tc024-middle
cmp /tmp/repo22-g1/expected/large-hole-zero-64k.bin /tmp/tc024-end
sha256 /tmp/tc024-start /tmp/tc024-middle /tmp/tc024-end
```
Remove outputs, unmount, and detach.
## Expected Results
- Size is exactly `4294971393`, not truncated to 32 bits.
- Start, middle, and final 64 KiB ranges match the deterministic zero source.
- Reads are bounded, no large allocation/panic occurs, and cleanup succeeds.
-35
View File
@@ -1,35 +0,0 @@
# Test Case: Compact Inode Link-Count Rules
**Test ID**: TC025-nlink1-handling
## Objective
Compare explicit compact link counts, bit-4 single-link encoding, hard links,
and directory link counts without conflating directory dot omission.
## Fixture
Use `images/compact.erofs` and `images/compact-nlink1.erofs`. Evidence asserts
explicit `single.txt i_nb=1`, patched bit 4 plus `i_nb=0x1234`, identical
hard-link NID with `i_nb=2`, and explicit directory nlink.
## Procedure
Mount each image separately and record:
```sh
stat -f '%N ino=%i nlink=%l mode=%p' \
"$mnt/single.txt" "$mnt/hard-a.txt" "$mnt/hard-b.txt" "$mnt/dotdir"
cmp /tmp/repo22-g1/source/compact/single.txt "$mnt/single.txt"
cmp /tmp/repo22-g1/source/compact/hard-a.txt "$mnt/hard-a.txt"
cmp "$mnt/hard-a.txt" "$mnt/hard-b.txt"
```
Unmount and detach before attaching the second image.
## Expected Results
- Baseline and patched `single.txt` both report nlink 1 for different valid
encodings.
- `hard-a.txt` and `hard-b.txt` share one inode and report nlink 2.
- `dotdir` uses its explicit directory link count; all data compares pass.
-33
View File
@@ -1,33 +0,0 @@
# Test Case: Directory with dot_omitted
**Test ID**: TC026-dot-omitted-with
## Objective
Verify a bit-4 directory omits only `.` on disk, retains explicit `..`, and
receives a synthetic `.` from FreeBSD readdir.
## Fixture
Use `images/compact-dot-omitted.erofs`. Structured evidence records bit 4,
on-disk names `..,child.txt`, 48-bit incompat selection with nonzero
`rootnid_8b`, `blocks_hi=0`, and valid CRC. Production fsck.erofs 1.8.6 cannot
qualify this newer format.
## Procedure
After mounting in FreeBSD:
```sh
stat -f 'ino=%i nlink=%l mode=%p' "$mnt/dotdir"
ls -lai "$mnt/dotdir"
(cd "$mnt/dotdir" && test "$(pwd)" = "$mnt/dotdir")
cmp /tmp/repo22-g1/source/compact/dotdir/child.txt "$mnt/dotdir/child.txt"
```
## Expected Results
- User-visible listing contains both `.` and `..`; `.` has the directory NID.
- Structured evidence, not the listing, proves `.` is absent on disk and bit 4
is set; `..` remains an on-disk entry.
- Child data, navigation, mount, and cleanup succeed.
-35
View File
@@ -1,35 +0,0 @@
# Test Case: Directory without dot_omitted
**Test ID**: TC027-dot-omitted-without
## Objective
Verify a bit-4-clear directory reads explicit on-disk `.` and `..` entries.
## Fixture
Use `images/compact.erofs` and `/dotdir`. Structured evidence asserts bit 4 is
clear and names are `.,..,child.txt`; production `dump.erofs --ls` also
qualifies this baseline.
## Procedure
Host:
```sh
dump.erofs --ls --path=/dotdir /work/build/repo22-g1/images/compact.erofs
```
Guest after mount:
```sh
stat -f 'ino=%i nlink=%l mode=%p' "$mnt/dotdir"
ls -lai "$mnt/dotdir"
cmp /tmp/repo22-g1/source/compact/dotdir/child.txt "$mnt/dotdir/child.txt"
```
## Expected Results
- Host inspection and FreeBSD listing both contain explicit `.` and `..`.
- Child data matches the source, directory attributes are valid, and cleanup
succeeds.
-26
View File
@@ -1,26 +0,0 @@
# Test Case: FLAT_PLAIN Small File
**Test ID**: TC028-flat-plain-small
## Objective
Verify a sub-block regular file explicitly encoded as uncompressed
`FLAT_PLAIN` reads source-exactly.
## Fixture
Use `images/flat.erofs` and `source/flat/small.txt`. Structured evidence asserts
layout 0 and records NID and size.
## Procedure
```sh
stat -f 'size=%z blocks=%b mode=%p' "$mnt/small.txt"
cmp /tmp/repo22-g1/source/flat/small.txt "$mnt/small.txt"
sha256 /tmp/repo22-g1/source/flat/small.txt "$mnt/small.txt"
```
## Expected Results
- Evidence proves `FLAT_PLAIN`; size and data match the source.
- The read, hash, mount, and cleanup all succeed.

Some files were not shown because too many files have changed in this diff Show More