test code v1

This commit is contained in:
2026-08-13 10:44:59 +02:00
commit f3b1165f19
301 changed files with 37885 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
# 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
@@ -0,0 +1,283 @@
# 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
@@ -0,0 +1,65 @@
# 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
@@ -0,0 +1,271 @@
.\" 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
@@ -0,0 +1,77 @@
# 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
@@ -0,0 +1,65 @@
# 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
@@ -0,0 +1,83 @@
# 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
@@ -0,0 +1,84 @@
# 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
@@ -0,0 +1,80 @@
# 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
@@ -0,0 +1,268 @@
# 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
@@ -0,0 +1,99 @@
# 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
@@ -0,0 +1,55 @@
# 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
@@ -0,0 +1,86 @@
# 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
@@ -0,0 +1,314 @@
# 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
@@ -0,0 +1,74 @@
# 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
@@ -0,0 +1,191 @@
# 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
@@ -0,0 +1,132 @@
# 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
@@ -0,0 +1,158 @@
# 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
@@ -0,0 +1,142 @@
# 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
@@ -0,0 +1,62 @@
# 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.
@@ -0,0 +1,26 @@
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.
@@ -0,0 +1,16 @@
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.
@@ -0,0 +1,21 @@
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.
@@ -0,0 +1,100 @@
{
"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"
}
}
@@ -0,0 +1,22 @@
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
@@ -0,0 +1,62 @@
{
"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
@@ -0,0 +1,37 @@
# 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
@@ -0,0 +1,40 @@
# 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;测试后无遗留挂载或模块。