This commit is contained in:
2026-08-18 09:20:44 +02:00
commit b826cd721a
522 changed files with 93730 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
# Test Execution Checklist
## Acceptance Rules
- [x] Execute each TC from its Markdown procedure and record direct command
status, fixture/module hashes, kernel behavior, and cleanup state.
- [x] Treat fixture generation and static review as supporting evidence only.
- [x] Do not use retired wrappers, result collectors, or unconditional PASS
output as acceptance evidence.
- [x] Keep CI and automated kernel-test infrastructure outside this task.
## Specification Audit
- [x] TC000 is present as the non-executable template.
- [x] TC001-TC161 are present exactly once.
- [x] Bounded filename audit: 162 rows, 162 unique IDs, no duplicate or gap.
- [x] G1-G8 table audit: 156 rows, 156 unique IDs, no duplicate or omission.
- [x] TC157-TC161 add five unique final-review cases.
## Canonical Execution Groups
| Group | Scope | Final result | Evidence |
| --- | --- | --- | --- |
| G1 | TC001, TC007, TC009, TC011-TC014, TC019-TC040, TC147, TC150, TC151 | 32 PASS | `results/manual/2026-08-09T0710Z-g1/` |
| G2 | TC002, TC008, TC010, TC015-TC018, TC112-TC116, TC119 | 13 PASS | `results/manual/2026-08-09T0710Z-g2/` |
| G3 | TC041-TC066, TC141, TC148, TC149, TC152, TC153 | 31 PASS | `results/manual/2026-08-09T1059Z-g3/` plus TC060 fixed-source rerun |
| G4 | TC005, TC067-TC083, TC117, TC134-TC140, TC142 | 27 PASS | `results/manual/2026-08-09T0839Z-g4/` |
| G5 | TC003, TC004, TC084-TC092, TC102-TC110, TC143-TC146 | 23 PASS, 1 PARTIAL | `results/manual/2026-08-09T1236Z-g5/` |
| G6 | TC006, TC093-TC101, TC118 | 11 PASS | `results/manual/2026-08-09T1244Z-g6/` |
| G7 | TC120-TC130 | 11 PASS | `results/manual/2026-08-09T1359Z-g7/` |
| G8 | TC111, TC131-TC133, TC154-TC156 | 7 PASS | `results/manual/2026-08-09T1343Z-g8/` |
| Final | TC157-TC161 | 5 PASS | `results/manual/2026-08-09T1804Z-final-review-independent/` |
## Final Review Cases
- [x] TC157: global 16/32-byte explicit-extent order validation.
- [x] TC158: extended compressed inode 48-bit block-count accounting.
- [x] TC159: special-vnode combined setattr rejection.
- [x] TC160: dot-omitted `OFF_MAX` cookie rejection.
- [x] TC161: fatal `nm` failure and uncontaminated post-shim rebuild.
## Build and Runtime Sign-Off
- [x] Exact final source baseline:
`fcc85b93d5f8fd9671686bd68bf3b086c8bd25cf`.
- [x] `WITH_ZSTDIO=0` built with kernel `-Werror`; KLD SHA256
`15fda9d334132cd81769ce4dff4f8411a6e2b4cf7531c352530d0de85f42a2d2`.
- [x] `WITH_ZSTDIO=1` built with kernel `-Werror`; KLD SHA256
`23782dc0ce7da188807d35020bf2d8c6044b796c5c9a8746b398ba784cd6ad4e`.
- [x] Both final KLDs loaded and completed a read-only mount smoke.
- [x] Final guest EROFS mounts, md units, EROFS KLDs, and DTrace KLDs: zero.
- [x] repo22 generated build/object/image/bytecode artifacts removed.
## Final Statistics
```text
Executable test cases: 161
Executed: 161
PASS: 160
PARTIAL: 1 (TC146)
FAIL: 0
KERNEL-FAIL: 0
ENVIRONMENT-UNAVAILABLE: 0
SHELVED test case: 0
```
TC146 is PARTIAL because a positive explicit mapped-payload fixture is not
available. Its shelved subitem is not a separate TC. TC010, TC060, and TC153
are resolved historical issues.
## Residual Work
- [ ] Obtain an independently validated positive explicit mapped-payload
fixture and complete the remaining TC146 subfeature.
- [ ] Optionally extend TC157 with first-nonzero-`lstart` and extreme
extent-count performance coverage if the format contract and a qualified
fixture require it.
- [ ] CI remains intentionally unimplemented and outside this test effort.
+69
View File
@@ -0,0 +1,69 @@
# G1 Manual Test Setup
This setup supports only the G1 test set. It prepares fixtures and probes; it
does not execute tests, assign results, or append PASS output.
## Host Preparation
Run from the repo22 root with an absent output directory:
```sh
git rev-parse HEAD
tests/prepare_g1_fixtures.sh /work/build/repo22-g1
(cd /work/build/repo22-g1/images && sha256sum -c IMAGE-SHA256SUMS)
(cd /work/build/repo22-g1 && sha256sum -c SOURCE-SHA256SUMS)
(cd /work/build/repo22-g1 && sha256sum -c SOURCE-METADATA.sha256)
```
Record `fixture-evidence.txt`, all three checksum manifests, the erofs-utils
version, and the KLD SHA256. Production erofs-utils 1.8.6 does not recognize
the 48-bit incompat bit, so `root8-48bit.erofs`,
`fallback-48bit-root2.erofs`, and `compact-dot-omitted.erofs` are qualified by
the structured transformer's field/CRC assertions plus the required FreeBSD
mount, not by a false `fsck.erofs` PASS.
Transfer `images/`, `source/`, `expected/`, the checksum/evidence files, the
exact KLD, and the C probes used by a TC to an empty guest directory. Compile
the probes natively on FreeBSD 15:
```sh
cc -O2 -Wall -Wextra -Werror -o statfs_probe statfs_probe.c
cc -O2 -Wall -Wextra -Werror -o stat_special stat_special.c
cc -O2 -Wall -Wextra -Werror -o read_probe read_probe.c
cc -O2 -Wall -Wextra -Werror -o mmap_fault mmap_fault.c
cc -O2 -Wall -Wextra -Werror -o nfs_fh_tool nfs_fh_tool.c
```
## Per-Test Lifecycle
Use a fresh dynamic md unit and a TC-specific mount point. Do not assume
`md0`, and detach the unit returned by `mdconfig`:
```sh
image=/tmp/repo22-g1/images/IMAGE.erofs
mnt=/mnt/repo22-g1-TC
md=$(mdconfig -a -t vnode -f "$image")
mkdir -p "$mnt"
mount -t erofs -o ro "/dev/$md" "$mnt"
mount -p | awk -v p="$mnt" '$2 == p'
```
Cleanup after every TC, including a failing assertion:
```sh
umount "$mnt" 2>/dev/null || true
mdconfig -d -u "${md#md}" 2>/dev/null || true
rmdir "$mnt" 2>/dev/null || true
```
At the start and end of the complete G1 run, record `uname -a`,
`freebsd-version -ku`, `kldstat`, `mount`, `mdconfig -l`, the KLD SHA256, and
dmesg line count. The final state must contain no EROFS mount, EROFS KLD, or G1
md provider.
## Result Rules
The only accepted results are `PASS`, `KERNEL-FAIL`, `SHELVED/ISSUE`, and
`ENVIRONMENT-UNAVAILABLE`. Host inspection alone cannot produce PASS. A
kernel behavior failure must retain the observed errno/output and be recorded
in `issues/`; it must not be relabeled PASS.
+71
View File
@@ -0,0 +1,71 @@
# G3 Manual Test Setup
This setup supports only TC041-TC066, TC141, TC148, TC149, TC152, and
TC153. It prepares deterministic fixtures and native probes; it does not assign
runtime results.
## Host Preparation
From the `repo-pre-15` root, use an absent output directory:
```sh
git rev-parse HEAD
tests/prepare_g3_fixtures.sh /work/build/repo-pre15-g3
python3 -B tests/pre15/fixtures/g3.py verify \
--output /work/build/repo-pre15-g3
sha256sum /work/build/repo-pre15-g3/G3-MANIFEST.json
```
The maintained generator is `tests/pre15/fixtures/g3.py`. The compatibility
entry point `tests/g3_fixtures.py` delegates to it. Normal generation does not
read `tests/results/`; B01 separately proves that the migrated metadata and
final-review sets match both archived scripts byte for byte.
`G3-MANIFEST.json` records the generator SHA256, erofs-utils identity, exact
`mkfs.erofs` argv, canonical source-tree inventories, image hashes, and expected
directory sets. Record this manifest, all checksum files, the exact source
commit, and the KLD SHA256. Transfer the fixtures, sources, and only these native
probes to the FreeBSD 15 guest:
```text
readdir_probe.c g3_vfs_probe.c stat_special.c nfs_fh_tool.c
mmap_fault.c sparse_hole_probe.c
```
## Guest Preparation
Build the KLD natively from an archive exported from the recorded commit. Build
the probes directly:
```sh
cc -O2 -Wall -Wextra -Werror -std=c17 readdir_probe.c -o readdir_probe
cc -O2 -Wall -Wextra -Werror -std=c17 g3_vfs_probe.c -o g3_vfs_probe
cc -O2 -Wall -Wextra -Werror -std=c17 stat_special.c -o stat_special
cc -O2 -Wall -Wextra -Werror -std=c17 nfs_fh_tool.c -o nfs_fh_tool
cc -O2 -Wall -Wextra -Werror -std=c17 mmap_fault.c -o mmap_fault
cc -O2 -Wall -Wextra -Werror -std=c17 sparse_hole_probe.c \
-o sparse_hole_probe
```
Before the first test, require no EROFS mount, no md provider, and no stale
EROFS KLD. Load only the exact KLD under test. For each test, attach a fresh
dynamic vnode md, mount read-only, execute that numbered Markdown procedure,
then unmount and detach before continuing.
The report records the literal command, exit status or syscall errno, relevant
fixture/data/KLD hashes, dmesg delta, and cleanup state for every test.
## Common Mount Pattern
```sh
mkdir -p /tmp/repo-pre15-g3/mnt
unit=$(mdconfig -a -t vnode -f IMAGE)
mount -t erofs -o ro "/dev/$unit" /tmp/repo-pre15-g3/mnt
# Execute exactly one numbered test.
umount /tmp/repo-pre15-g3/mnt
mdconfig -d -u "$unit"
```
TC153 additionally creates and removes its documented multi-terabyte sparse
provider. Final cleanup requires zero EROFS mounts, zero owned md providers, the
test KLD unloaded, and all guest-generated sparse files removed.
+88
View File
@@ -0,0 +1,88 @@
# G4 xattr, ACL, and metabox manual setup
This setup is shared only by TC005, TC067-TC083, TC117, TC134-TC140, and
TC142. It does not consume an existing image or report from `/work/build`.
## Host fixture generation
The host must provide erofs-utils 1.8.6 and Linux `user.*` xattrs. The helper
creates the source trees, invokes `mkfs.erofs`, performs structured on-disk
transformations, and then reopens every output to verify its fields and hashes.
```sh
mkfs.erofs -V
run=/work/build/repo22-g4-$(date -u +%Y%m%dT%H%M%SZ)
python3 tests/g4_fixtures.py make --output "$run"
python3 tests/g4_fixtures.py verify --output "$run"
sha256sum -c "$run/IMAGE-SHA256SUMS"
```
`mkfs.erofs -V` must report 1.8.6. `SOURCE-SHA256`,
`IMAGE-SHA256SUMS`, and `fixture-manifest.json` are required evidence. The
manifest records the complete source inventory, each mkfs command, image and
provider sizes, and every transformed field as offset/size/before/after bytes.
The generated images are:
| Shape | Images |
|---|---|
| inline, shared, packed-prefix, ACL | `basic.erofs` |
| primary prefix fallback | `prefix-primary.erofs` |
| plain, compressed, fragment metabox | `metabox-plain.erofs`, `metabox-compressed.erofs`, `metabox-fragment.erofs` |
| malformed xattr | `bad-inline-entry.erofs`, `bad-shared-entry.erofs` |
| declared bounds | `bad-shared-declared-bounds.erofs`, `bad-prefix-declared-bounds.erofs` |
| superblock validation | `bad-metabox-truncated-extension.erofs`, `bad-ishare-prefix-id.erofs` |
| fragment safety | `bad-fragment-self-loop.erofs`, `bad-fragment-range.erofs`, `bad-metabox-recursive-nid.erofs`, `bad-packed-recursive-nid.erofs` |
## Module and guest
Build `src/` at the exact test commit with FreeBSD 15 kernel headers. Record
the commit, header revision/branch, compiler target, `WITH_ZSTDIO`, and KLD
SHA256. Copy only the new KLD and generated images to an isolated FreeBSD 15
guest. Load the KLD and verify guest hashes before the first case.
```sh
freebsd-version
uname -a
sha256 /tmp/repo22-g4/erofs.ko /tmp/repo22-g4/images/*.erofs
kldload /tmp/repo22-g4/erofs.ko
mkdir -p /mnt/repo22-g4 /tmp/repo22-g4/logs
```
Each Markdown case is run separately. Attach its stated image, mount read-only,
run only the stated observations, then unmount and detach before the next case.
```sh
unit=$(mdconfig -a -t vnode -f /tmp/repo22-g4/images/IMAGE.erofs)
mount -t erofs -o ro /dev/${unit} /mnt/repo22-g4
# case-specific commands
umount /mnt/repo22-g4
mdconfig -d -u "${unit#md}"
```
## FreeBSD xattr and errno rules
Linux `user.*` appears in FreeBSD namespace `user` without `user.` in the
attribute name. Linux `trusted.*`, `security.*`, and POSIX ACL xattrs appear in
FreeBSD namespace `system`; trusted/security retain their full names, while ACL
names are `posix_acl_access` and `posix_acl_default`. Use `lsextattr` and
`getextattr`, not Linux `getfattr` syntax.
Use `getextattr -qq -x` for byte-exact output. The utility's exit status is not
authoritative for all failures, so capture the kernel result with `truss` and
require errno 87 (`ENOATTR`), 97 (`EINTEGRITY`), 5 (`EIO`), or 30 (`EROFS`) as
specified by the case. Negative mounts must show `nmount(...)=ERR#97` or an
equivalent normalized `EIO` at a boundary where the VFS maps integrity errors.
## Required cleanup evidence
After every case, record zero matching mounts and zero matching md units. At
the end, require no active EROFS allocation, unload the KLD, and stop the
dedicated VM.
```sh
mount | grep repo22-g4 || true
mdconfig -l
vmstat -m | grep erofs
kldunload erofs
```
+118
View File
@@ -0,0 +1,118 @@
# G5 Compression Manual Setup
This is the canonical fixture and execution contract for exactly these 24
tests:
`TC003`, `TC004`, `TC084`-`TC092`, `TC102`-`TC110`, and `TC143`-`TC146`.
The commands and paths here replace placeholder image names, blind corruption
offsets, and non-source-compared reads in the individual test descriptions.
Do not use old images or reports as fixture inputs.
## Host fixture generation
Requirements are `mkfs.erofs`, `dump.erofs`, and `fsck.erofs` 1.8.6. Generate
into a new path; the helper rejects an existing output directory.
```sh
out=/work/build/repo22-g5-fixtures-a
python3 tests/g5_fixtures.py create \
--output "$out" \
--erofs-utils-source /path/to/erofs-utils-1.8.6
python3 tests/g5_fixtures.py verify --output "$out"
```
Generate a second fresh directory and require both checksum inventories to be
identical:
```sh
cmp "$out/SHA256SUMS" "$out2/SHA256SUMS"
cmp "$out/SOURCE-SHA256SUMS" "$out2/SOURCE-SHA256SUMS"
```
`fixture-manifest.json` records every mkfs option, source and image hash,
inode/NID/size/layout, compressed map-header offset, advise bits, algorithm
nibbles, extent summaries, partial-reference indexes and physical blocks, and
corruption pcluster/patch ranges. Creation reopens every transformed image and
the separate `verify` command repeats the structured checks.
The partial-reference transformer changes the HEAD pblk, sets
`Z_EROFS_LI_PARTIAL_REF`, and copies the complete source pcluster's
`D0_CBLKCNT`. Corruption is applied only after `dump.erofs` and the byte parser
agree on the target algorithm and physical extent.
## Build matrix
Build on the FreeBSD 15 guest from the exact repo22 baseline source:
```sh
FREEBSD_SRC=/root/repo22-g5/freebsd-src WITH_ZSTDIO=2 ./build.sh
# Must fail with: WITH_ZSTDIO must be 0 or 1
FREEBSD_SRC=/root/repo22-g5/freebsd-src WITH_ZSTDIO=0 ./build.sh
cp build/erofs.ko /root/repo22-g5/erofs-nozstd.ko
nm -u /root/repo22-g5/erofs-nozstd.ko
FREEBSD_SRC=/root/repo22-g5/freebsd-src WITH_ZSTDIO=1 ./build.sh
cp build/erofs.ko /root/repo22-g5/erofs-zstdio.ko
nm -u /root/repo22-g5/erofs-zstdio.ko
```
The disabled module must have no `ZSTD_*` or `bcmp` reference. The enabled
module may reference only the formal FreeBSD ZSTD API names. Load by full path,
obtain the file ID from the matching `kldstat` path row, and unload that ID.
## Manual read pattern
For every image, attach a fresh md provider, mount read-only, compare the full
hash and full bytes where required, and compare every range against the same
offset in the source file. `tests/read_probe.c` provides deterministic `pread`
and errno checks; it is a probe, not a runner.
```sh
unit=$(mdconfig -a -t vnode -f "$image")
mount -t erofs -o ro "/dev/$unit" "$mnt"
sha256 -q "$source"
sha256 -q "$mnt/$name"
cmp "$source" "$mnt/$name"
read_probe pread "$mnt/$name" "$offset" "$length" guest.bin
read_probe pread "$source" "$offset" "$length" source.bin
cmp source.bin guest.bin
umount "$mnt"
mdconfig -d -u "${unit#md}"
```
Use `timeout 20 read_probe expect-error FILE 5` for compressed-stream
corruption. Also compare `control.bin` from the same corrupted image, then
require no mount/md remains and EROFS active allocations return to zero.
## Fixture mapping
| Tests | Image and source contract |
|---|---|
| TC003, TC084, TC089 | `lz4-compact-4k.erofs` or `lz4-full-4k.erofs`; `sources/lz4/compressed.bin` |
| TC085 | `lz4-large.erofs`; 268435456-byte `sources/large/large.bin` |
| TC086, TC087, TC090 | `lz4-compact-64k.erofs`; fixed source offsets |
| TC088 | 4K, 64K, and 256K compact LZ4 images; same source bytes |
| TC091, TC092 | `lz4-ztail.erofs`; inline target plus four edge controls |
| TC004 | `lzma-level6.erofs`; full, middle, and EOF reads |
| TC102-TC104 | distinct DEFLATE level 1, 6, and 9 images |
| TC105-TC107 | distinct ZSTD level 1, 15, and 22 images |
| TC108 | `lzma-large.erofs`; 104857601-byte LZMA level 6 source |
| TC109 | `microlzma-edge.erofs`; actual 1B, 4K, and compressed 16K files |
| TC110, TC144 | LZMA partial/corrupt pair plus same-image `control.bin` |
| TC143 | DEFLATE and ZSTD partial/corrupt pairs plus controls |
| TC145 | both KLDs, LZ4 control, and `zstd-level1.erofs` gate/read |
| TC146 HEAD2 | `head2.erofs` and targeted corrupt copy; boundary from manifest |
| TC146 interlaced | `interlaced.erofs`; first compressed/plain transition from manifest |
| TC146 explicit extent | `extent-attempt.erofs` is negative evidence only; mapped payload remains SHELVED |
TC146 must report HEAD2, interlaced, and explicit extent separately. A normal
full-index image produced with `--max-extent-bytes` is not explicit-extent
coverage.
## Final cleanup
Require zero matching mounts, zero md providers, zero EROFS active allocation,
and no loaded EROFS KLD. Compare pre/post dmesg, verify guest responsiveness,
power off the dedicated VM, and remove Python bytecode caches before commit.
+164
View File
@@ -0,0 +1,164 @@
# G6 Chunk and Multi-Device Manual Setup
This setup applies only to `TC006`, `TC093` through `TC101`, and `TC118`.
There are exactly 11 test cases. The commands below generate new inputs; no
fixture or result from an earlier run is an input.
## Host prerequisites
- Linux host with erofs-utils 1.8.6 (`mkfs.erofs` and `fsck.erofs`).
- Python 3.11 or newer.
- QEMU with qcow2 support.
- A clean FreeBSD 15 amd64 base disk used only as the backing file for a new
per-run overlay.
Set a new run directory and generate the fixtures twice:
```sh
REPO=/path/to/worktree/repo-community/repo22
RUN=/work/build/repo22-g6-$(date -u +%Y%m%dT%H%M%SZ)
mkdir -p "$RUN"
cd "$REPO"
python3 -B tests/g6_multidev_fixtures.py generate \
--output "$RUN/fixtures-a"
python3 -B tests/g6_multidev_fixtures.py generate \
--output "$RUN/fixtures-b"
cmp "$RUN/fixtures-a/manifest.json" "$RUN/fixtures-b/manifest.json"
cmp "$RUN/fixtures-a/SHA256SUMS" "$RUN/fixtures-b/SHA256SUMS"
python3 -B tests/g6_multidev_fixtures.py verify "$RUN/fixtures-a"
```
`generate` refuses an existing output directory, fixes source bytes, UUIDs,
timestamps, worker count, and every binary transformation, and asserts the
old field before each patch. `verify` checks every artifact size and SHA256,
then reparses superblock, device-table, and chunk-index fields from disk.
The manifest records two erofs-utils 1.8.6 limitations. Its fsck qualifies
the mkfs split image, single-index image, explicit 2/3-slot images, table-at-0,
`uniaddr=0`, fragment image, and original two-block LZ4 pcluster. Flatdev and
device-ID-0 unified relocation are qualified by the FreeBSD kernel reads in
TC094 and TC101 because this fsck release does not implement those mappings.
## Dedicated FreeBSD 15 VM
Create a new overlay and use only SSH port 9226 for this run:
```sh
qemu-img create -f qcow2 -F qcow2 \
-b /work/build/vm-freebsd-dev-base.qcow2 \
"$RUN/freebsd15-overlay.qcow2"
qemu-system-x86_64 -accel tcg,thread=multi -cpu qemu64 \
-m 6144 -smp 4 \
-drive file="$RUN/freebsd15-overlay.qcow2",if=virtio,format=qcow2 \
-netdev user,id=net0,hostfwd=tcp:127.0.0.1:9226-:22 \
-device virtio-net-pci,netdev=net0 -display none \
-serial file:"$RUN/freebsd15-serial.log" -monitor none \
-pidfile "$RUN/freebsd15-qemu.pid" \
-D "$RUN/freebsd15-qemu.log" -daemonize
```
Record the guest identity before installing test artifacts:
```sh
uname -a
freebsd-version -ku
sysctl -n kern.osreldate
sha256 /boot/kernel/kernel
mdconfig -l
mount -p | awk '$3 == "erofs"'
```
The initial `mdconfig` and EROFS mount outputs must be empty.
## Exact-source KLD
On the host, record and archive the exact worktree source:
```sh
git rev-parse HEAD | tee "$RUN/source.commit"
git status --short
git archive --format=tar HEAD repo-community/repo22 | \
gzip -n > "$RUN/repo22-source.tar.gz"
git archive --format=tar HEAD dev-freebsd-releng/sys | \
gzip -n > "$RUN/freebsd-sys-source.tar.gz"
tar -C "$RUN/fixtures-a" -czf "$RUN/g6-fixtures.tar.gz" \
SHA256SUMS manifest.json images sources
```
Transfer both archives to the new guest. Authentication details remain
outside the repository:
```sh
scp -O -P 9226 "$RUN/repo22-source.tar.gz" \
"$RUN/freebsd-sys-source.tar.gz" \
"$RUN/g6-fixtures.tar.gz" [email protected]:/root/
```
Build natively in the guest, with no source edits:
```sh
mkdir -p /root/freebsd-src /root/repo22-g6-src /root/repo22-g6
tar -xzf /root/freebsd-sys-source.tar.gz -C /root/freebsd-src \
--strip-components 1
tar -xzf /root/repo22-source.tar.gz -C /root/repo22-g6-src \
--strip-components 2
tar -xzf /root/g6-fixtures.tar.gz -C /root/repo22-g6
cd /root/repo22-g6-src
grep -E '^(REVISION|BRANCH)=' /root/freebsd-src/sys/conf/newvers.sh
env WITH_ZSTDIO=1 FREEBSD_SRC=/root/freebsd-src ./build.sh
sha256 build/erofs.ko
file build/erofs.ko
cp build/erofs.ko /root/repo22-g6/erofs.ko
```
At baseline `6b33b4afb490be7d6fec70e499469c306a58435d`, the tracked sys tree is
15.0-RELEASE-p9 and the clean guest is p8; both report OSREL 1500068. Record
this source/guest distinction rather than claiming they are the same patch
level. Also record `source.commit`, `WITH_ZSTDIO=1`, FreeBSD source archive
SHA256, KLD SHA256, kernel SHA256, and all guest values in the report.
## Manual evidence rules
Run the commands in each TC Markdown directly. Do not use a runner, CI job,
or test wrapper. Before each test:
```sh
mkdir -p /mnt/g6
kldload /root/repo22-g6/erofs.ko
dmesg | tail -40 > /tmp/g6-dmesg-before
```
For a negative mount or read, capture the syscall result with `truss` and
record the named errno, not only command exit status:
```sh
truss -f -o /tmp/operation.truss command arguments
tail -20 /tmp/operation.truss
```
After every TC, unmount first, detach external providers in descending slot
order, detach the primary, and unload the module. All four checks must report
zero:
```sh
mount -p | awk '$3 == "erofs" { print }'
mdconfig -l
kldstat -n erofs 2>/dev/null || true
sysctl -n kern.geom.conftxt | \
awk '/Geom name: md9[0-3]$|Consumers:|Providers:|erofs/ { print }'
```
Also compare the new dmesg suffix and reject any panic, trap, assertion,
watchdog, or EROFS error not expected by the current negative operation.
## FreeBSD and Linux behavior
Linux EROFS accepts a device table at byte offset zero and excludes a slot
whose `uniaddr` is zero from device-ID-0 unified lookup; a nonzero device ID
still selects that slot. FreeBSD uses explicit `device.<slot>=/dev/<provider>`
mount options because GEOM providers are not discovered from Linux block
device tags. FreeBSD also holds one read-only GEOM consumer per provider, so
normal `mdconfig -d` returns `EBUSY` while mounted. A forced GEOM orphan makes
later cold I/O return `ENXIO`; unmount must still release vnode, cdev, and GEOM
references. These lifecycle details have no direct Linux loop-device
equivalent and are checked in TC006 and TC118.
+226
View File
@@ -0,0 +1,226 @@
# G7 Boundary and Stress Manual Setup
This setup applies to exactly `TC120` through `TC130`: 11 tests, with no
additional test IDs. Run each test's Markdown commands directly. Do not use a
CI job, runner, or test wrapper.
## Deterministic fixtures
The host requires Python 3, erofs-utils 1.8.6, GNU tar, QEMU, and at least
2 GiB of free working space. Generate two fresh fixture directories; the
helper rejects an existing output path.
```sh
REPO=/path/to/worktree/repo-community/repo22
RUN=/work/build/repo22-g7-$(date -u +%Y%m%dT%H%M%SZ)
mkdir -p "$RUN"
cd "$REPO"
python3 -B tests/g7_fixtures.py create --output "$RUN/fixtures-a"
python3 -B tests/g7_fixtures.py create --output "$RUN/fixtures-b"
python3 -B tests/g7_fixtures.py verify --output "$RUN/fixtures-a"
cmp "$RUN/fixtures-a/SOURCE-INVENTORY.tsv" \
"$RUN/fixtures-b/SOURCE-INVENTORY.tsv"
cmp "$RUN/fixtures-a/SOURCE-SHA256SUMS" \
"$RUN/fixtures-b/SOURCE-SHA256SUMS"
cmp "$RUN/fixtures-a/SHA256SUMS" "$RUN/fixtures-b/SHA256SUMS"
sha256sum "$RUN/fixtures-a/SOURCE-INVENTORY.tsv" \
"$RUN/fixtures-a/SOURCE-SHA256SUMS" \
"$RUN/fixtures-a/SHA256SUMS" \
"$RUN/fixtures-a/fixture-manifest.json"
```
`SOURCE-INVENTORY.tsv` records the exact relative path, size, and SHA256 of
every source file. The helper re-hashes the complete inventory, checks exact
counts and names, checks all sparse markers and allocated-block usage, runs
`fsck.erofs`, reopens the 4 GiB boundary inode with `dump.erofs`, and verifies
the image checksums. `boundaries.erofs` covers TC120-TC125;
`workloads.erofs` covers TC126-TC130.
The practical sparse boundary is 4 GiB + 4097 bytes. It crosses signed 32-bit,
2 GiB, unsigned 32-bit, 4 GiB, block, hole, and EOF boundaries without
claiming that a 16 TiB image is practical in this VM. The source remains
sparse and every selected range is compared to that exact source in TC121.
## Dedicated FreeBSD 15 VM
Create a new qcow2 overlay and use only SSH port 9227:
```sh
qemu-img create -f qcow2 -F qcow2 \
-b /work/build/vm-freebsd-dev-base.qcow2 \
"$RUN/freebsd15-overlay.qcow2"
qemu-system-x86_64 -accel tcg,thread=multi -cpu qemu64 \
-m 6144 -smp 4 \
-drive file="$RUN/freebsd15-overlay.qcow2",if=virtio,format=qcow2 \
-netdev user,id=net0,hostfwd=tcp:127.0.0.1:9227-:22 \
-device virtio-net-pci,netdev=net0 -display none \
-serial file:"$RUN/freebsd15-serial.log" -monitor none \
-pidfile "$RUN/freebsd15-qemu.pid" \
-D "$RUN/freebsd15-qemu.log" -daemonize
```
Record the initial guest identity and resource-control state:
```sh
uname -a
freebsd-version -ku
sysctl -n kern.osreldate
sha256 /boot/kernel/kernel
sysctl kern.racct.enable
rctl
vmstat -H 1 3
mdconfig -l
mount -p | awk '$3 == "erofs"'
```
If RACCT/RCTL is disabled, enable the FreeBSD loader tunable in this overlay
and reboot it. Do not use a jail as a memory-limit command.
```sh
grep -q '^kern.racct.enable=' /boot/loader.conf || \
printf 'kern.racct.enable="1"\n' >> /boot/loader.conf
grep '^kern.racct.enable=' /boot/loader.conf
shutdown -r now
```
After reconnecting, require `sysctl -n kern.racct.enable` to print `1` and
record `rctl` plus `vmstat -H 1 3` again.
## Exact-source KLD and guest inputs
Commit the helper and corrected Markdown before building. Then archive the
exact commit and the tracked FreeBSD 15 sys tree. The final report may be a
later documentation-only commit, but its `repo22/src` tree hash must equal the
build input tree hash.
```sh
cd /path/to/worktree
BUILD_COMMIT=$(git rev-parse HEAD)
git status --short
printf '%s\n' "$BUILD_COMMIT" > "$RUN/source.commit"
git rev-parse "$BUILD_COMMIT:repo-community/repo22/src" \
> "$RUN/repo22-src.tree"
git archive --format=tar "$BUILD_COMMIT" repo-community/repo22 | \
gzip -n > "$RUN/repo22-source.tar.gz"
git archive --format=tar "$BUILD_COMMIT" dev-freebsd-releng/sys | \
gzip -n > "$RUN/freebsd-sys-source.tar.gz"
tar --sparse --format=gnu -C "$RUN/fixtures-a" \
-cf "$RUN/boundaries-source.tar" sources/boundaries
tar --sparse --format=gnu -C "$RUN/fixtures-a" \
-cf "$RUN/workloads-source.tar" sources/workloads
gzip -n "$RUN/boundaries-source.tar"
gzip -n "$RUN/workloads-source.tar"
sha256sum "$RUN/repo22-source.tar.gz" \
"$RUN/freebsd-sys-source.tar.gz" \
"$RUN/boundaries-source.tar.gz" \
"$RUN/workloads-source.tar.gz" > "$RUN/source-archives.sha256"
```
Transfer the archives, images, and fixture metadata to the dedicated guest.
Authentication configuration stays outside the repository.
```sh
ssh -p 9227 [email protected] 'mkdir -p /root/repo22-g7-transfer'
scp -O -P 9227 "$RUN/repo22-source.tar.gz" \
"$RUN/freebsd-sys-source.tar.gz" \
"$RUN/boundaries-source.tar.gz" "$RUN/workloads-source.tar.gz" \
"$RUN/fixtures-a/images/boundaries.erofs" \
"$RUN/fixtures-a/images/workloads.erofs" \
"$RUN/fixtures-a/SOURCE-INVENTORY.tsv" \
"$RUN/fixtures-a/SOURCE-SHA256SUMS" \
"$RUN/fixtures-a/SHA256SUMS" \
"$RUN/fixtures-a/fixture-manifest.json" \
"$RUN/fixtures-a/DEEP-PATH" "$RUN/fixtures-a/LONG-NAME" \
"$RUN/fixtures-a/SPARSE-RANGES.tsv" \
"$RUN/source.commit" "$RUN/repo22-src.tree" \
"$RUN/source-archives.sha256" \
[email protected]:/root/repo22-g7-transfer/
```
Build and verify natively in the guest:
```sh
mkdir -p /root/freebsd-src /root/repo22-g7-src \
/root/repo22-g7/fixtures/images /root/repo22-g7/fixtures/sources \
/root/repo22-g7/evidence
tar -xzf /root/repo22-g7-transfer/freebsd-sys-source.tar.gz \
-C /root/freebsd-src --strip-components 1
tar -xzf /root/repo22-g7-transfer/repo22-source.tar.gz \
-C /root/repo22-g7-src --strip-components 2
tar -xzf /root/repo22-g7-transfer/boundaries-source.tar.gz \
-C /root/repo22-g7/fixtures
tar -xzf /root/repo22-g7-transfer/workloads-source.tar.gz \
-C /root/repo22-g7/fixtures
cp /root/repo22-g7-transfer/*.erofs /root/repo22-g7/fixtures/images/
cp /root/repo22-g7-transfer/SOURCE-INVENTORY.tsv \
/root/repo22-g7-transfer/SOURCE-SHA256SUMS \
/root/repo22-g7-transfer/SHA256SUMS \
/root/repo22-g7-transfer/fixture-manifest.json \
/root/repo22-g7-transfer/DEEP-PATH \
/root/repo22-g7-transfer/LONG-NAME \
/root/repo22-g7-transfer/SPARSE-RANGES.tsv \
/root/repo22-g7/fixtures/
cd /root/repo22-g7/fixtures
sha256sum -c SHA256SUMS
sha256sum -c SOURCE-SHA256SUMS
stat -f 'size=%z blocks=%b blocksize=%k name=%N' \
sources/boundaries/maximum/sparse-boundary.bin
cd /root/repo22-g7-src
grep -E '^(REVISION|BRANCH)=' /root/freebsd-src/sys/conf/newvers.sh
env WITH_ZSTDIO=1 FREEBSD_SRC=/root/freebsd-src ./build.sh
cp build/erofs.ko /root/repo22-g7/erofs.ko
cc -std=c11 -O2 -Wall -Wextra -Werror \
-o /root/repo22-g7/g7_probe tests/g7_probe.c
sha256 /root/repo22-g7/erofs.ko /root/repo22-g7/g7_probe
file /root/repo22-g7/erofs.ko
```
Record the build commit, source tree hash, `WITH_ZSTDIO=1`, archive hashes,
FreeBSD source `REVISION`/`BRANCH`, KLD SHA256, probe SHA256, guest identity,
kernel SHA256, and TCG/QEMU configuration in the report.
## Manual lifecycle
Load the exact module by full path and record its file ID and pathname:
```sh
kldload /root/repo22-g7/erofs.ko
kldstat -v | tee /root/repo22-g7/evidence/kldstat-loaded.txt
KLD_ID=$(kldstat -v | awk '$NF == "(/root/repo22-g7/erofs.ko)" { print $1 }')
test -n "$KLD_ID"
printf '%s\n' "$KLD_ID" > /root/repo22-g7/evidence/kld.id
dmesg > /root/repo22-g7/evidence/dmesg-before.txt
```
Each TC attaches one fresh vnode-backed md provider, mounts read-only, records
all correctness and metrics evidence, unmounts, detaches that exact provider,
and verifies both are gone. Never use `killall`; concurrent tests persist
every child PID, wait every child, and record every exit code.
TC128 and TC129 metrics are recording-only under QEMU TCG. Their PASS gate is
read completion and exact source hash/byte comparison, not a fixed MB/s,
IOPS, or latency threshold. TC127 uses FreeBSD RCTL `vmemoryuse:deny` with
cooperating allocation probes, records allocation failure and reclamation,
and verifies reads while pressure is held.
## Final cleanup
After TC130, require zero child processes, EROFS mounts, md providers, and
RCTL rules created by G7. Unload the exact KLD file ID, compare dmesg, and
power off the dedicated guest.
```sh
mount -p | awk '$3 == "erofs" { print }'
mdconfig -l
rctl
kldstat -v | grep -A2 -B2 '/root/repo22-g7/erofs.ko'
KLD_ID=$(cat /root/repo22-g7/evidence/kld.id)
kldunload -i "$KLD_ID"
test -z "$(kldstat -v | grep '/root/repo22-g7/erofs.ko')"
dmesg > /root/repo22-g7/evidence/dmesg-after.txt
shutdown -p now
```
Remove the host overlay, QEMU logs/PID file, source tars, temporary sparse
probe data, and Python bytecode caches only after evidence has been copied
into the committed report. Confirm port 9227 is released.
+229
View File
@@ -0,0 +1,229 @@
# Test Result Summary Template
## Test Execution Information
- **Date**: YYYY-MM-DD
- **Tester**: Name
- **FreeBSD Version**: 13.x / 14.x
- **Kernel**: uname -a output
- **erofs Module Version**: kldstat output
- **Test Duration**: X hours Y minutes
## Overall Statistics
```
Total Test Cases: 153 executable cases (plus TC000 template)
Executed: ___
Passed: ___
Failed: ___
Skipped: ___
Blocked: ___
Pass Rate: ___%
```
## Phase Results
### Phase 1: Basic Functionality (15 tests)
```
Passed: ___/15
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 2: Inode & Data Layouts (21 tests)
```
Passed: ___/21
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 3: Directory & VFS (26 tests)
```
Passed: ___/26
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 4: Extended Attributes (17 tests)
```
Passed: ___/17
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 5: Compression (24 tests)
```
Passed: ___/24
Failed: ___
Critical Issues: ___
```
**LZMA Tests** (repo18 NEW):
- TC004: [PASS/FAIL]
- TC108: [PASS/FAIL]
- TC109: [PASS/FAIL]
- TC110: [PASS/FAIL]
Failed Tests:
- TC###: Description - Reason
### Phase 6: Multi-Device (9 tests)
```
Passed: ___/9
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 7: Error Handling (8 tests)
```
Passed: ___/8
Failed: ___
Critical Issues: ___
```
Failed Tests:
- TC###: Description - Reason
### Phase 8: Boundary & Stress (11 tests)
```
Passed: ___/11
Failed: ___
Critical Issues: ___
```
**Performance Results**:
- TC128 (Sequential): ___ MB/s
- TC129 (Random): ___ IOPS
- TC130 (Mixed): ___ req/s
Failed Tests:
- TC###: Description - Reason
### Phase 9: Documentation (1 test)
```
Passed: ___/1
Failed: ___
```
## Critical Failures
Priority: Critical failures that prevent basic functionality
| Test ID | Description | Root Cause | Impact | Status |
|---------|-------------|------------|--------|--------|
| TC### | Brief | Reason | High/Medium/Low | Open/Fixed |
## Known Issues
Non-critical issues or limitations
| Test ID | Description | Workaround | Severity | Tracked |
|---------|-------------|------------|----------|---------|
| TC### | Brief | Workaround if any | Medium/Low | Issue #X |
## Performance Summary
| Metric | Result | Baseline | Delta | Status |
|--------|--------|----------|-------|--------|
| Sequential Read | ___ MB/s | 500 MB/s | ___% | PASS/FAIL |
| Random Read | ___ IOPS | 5000 IOPS | ___% | PASS/FAIL |
| Large File (10GB) | ___ s | 20 s | ___% | PASS/FAIL |
| Many Files (10K) | ___ s | 30 s | ___% | PASS/FAIL |
## Feature Coverage
```
Total Features: 65
Tested: ___
Passed: ___
Failed: ___
Coverage: ___%
```
### Feature Status
- [x] Mount operations: PASS
- [x] Inode formats: PASS/FAIL
- [x] Data layouts: PASS/FAIL
- [x] LZ4 compression: PASS/FAIL
- [x] DEFLATE compression: PASS/FAIL
- [x] zstd compression: PASS/FAIL
- [x] **LZMA compression (NEW)**: PASS/FAIL
- [x] Extended attributes: PASS/FAIL
- [x] Multi-device: PASS/FAIL
- [x] Directory operations: PASS/FAIL
- [x] VFS integration: PASS/FAIL
## Regression Check
Comparison with repo17 (64 features, 91% complete)
| Feature | repo17 | repo18 | Status |
|---------|--------|--------|--------|
| Basic mount | PASS | PASS | No regression |
| LZ4 | PASS | PASS | No regression |
| DEFLATE | PASS | PASS | No regression |
| zstd | PASS | PASS | No regression |
| **LZMA** | NOT IMPL | PASS | **NEW** |
| ... | ... | ... | ... |
## repo18 Iteration 1 Validation
**Goal**: LZMA compression support
### LZMA-Specific Results
- TC004 (Basic LZMA): [PASS/FAIL]
- TC108 (Large file): [PASS/FAIL]
- TC109 (MicroLZMA): [PASS/FAIL]
- TC110 (Corrupted): [PASS/FAIL]
**Data Integrity**: [PASS/FAIL]
- SHA256 checksums match: [YES/NO]
- Random access works: [YES/NO]
- Large file (100MB+) decompressed correctly: [YES/NO]
**Implementation Validation**:
- Self-contained decoder (232 lines): [YES/NO]
- No external dependencies: [YES/NO]
- Unified interface: [YES/NO]
**Iteration 1 Status**: [COMPLETE/INCOMPLETE]
## System Stability
- Kernel panics: [YES/NO] - Count: ___
- Memory leaks: [DETECTED/NONE]
- File descriptor leaks: [DETECTED/NONE]
- dmesg errors: [COUNT]
## Test Environment
```
CPU: ___
Memory: ___ GB
Disk: ___ (type)
Load during tests: ___
```
## Recommendations
1. [Action item 1]
2. [Action item 2]
3. ...
## Sign-Off
- [ ] All critical tests passed
- [ ] No regressions from repo17
- [ ] LZMA implementation validated (repo18 iter 1)
- [ ] Performance acceptable
- [ ] Known issues documented
**Tested by**: _______________
**Reviewed by**: _______________
**Date**: _______________
**Approved**: [YES/NO]
## Attachments
- [ ] Full test logs
- [ ] dmesg output
- [ ] Performance graphs
- [ ] Failure screenshots/dumps
+36
View File
@@ -0,0 +1,36 @@
# Test Case Template
**Test ID**: TC###-brief-name
**Category**: [Mount Operations | Filesystem Metadata | Inode Operations | Data Layouts | Compression | Directory Operations | Symlink Operations | Extended Attributes | Multi-Device | VFS Integration | Read-Only Enforcement | VM Integration | Error Handling | Performance & Stress]
**Priority**: [Critical | High | Medium | Low]
**Regression**: [None | Issue #XXX]
## Objective
Brief description of what this test validates.
## Preconditions
- EROFS image requirements (e.g., "Image with LZ4-compressed files")
- System requirements (e.g., "FreeBSD 13.0+")
- Test data setup
## Test Steps
1. Step one with specific commands
2. Step two with expected intermediate state
3. ...
## Expected Results
- Specific expected outcomes
- Expected output values
- Expected file contents
## Verification Method
How to verify the test passed:
- Command outputs to check
- Files to inspect
- Performance metrics (if applicable)
## Cleanup
Steps to reset the environment after the test.
## Notes
Any additional considerations or known limitations.
+39
View File
@@ -0,0 +1,39 @@
# Test Case: Basic Mount and Unmount
**Test ID**: TC001-mount-basic
## Objective
Verify that the exact G1 KLD mounts a valid image read-only, exposes source
data exactly, and unmounts cleanly.
## Fixture
Use `images/compact.erofs` and `source/compact/root.txt` generated by
`tests/prepare_g1_fixtures.sh`. Follow `tests/G1-MANUAL-SETUP.md` and record the
image, source, and KLD SHA256 values.
## Procedure
```sh
image=/tmp/repo22-g1/images/compact.erofs
source=/tmp/repo22-g1/source/compact/root.txt
mnt=/mnt/repo22-g1-001
md=$(mdconfig -a -t vnode -f "$image")
mkdir -p "$mnt"
kldstat | grep -i erofs
mount -t erofs -o ro "/dev/$md" "$mnt"
mount -p | awk -v p="$mnt" '$2 == p'
cmp "$source" "$mnt/root.txt"
sha256 "$source" "$mnt/root.txt"
./statfs_probe "$mnt"
umount "$mnt"
mdconfig -d -u "${md#md}"
rmdir "$mnt"
```
## Expected Results
- Mount, `cmp`, `statfs_probe`, unmount, and detach all return zero.
- The mount entry is `erofs` and read-only; both file hashes are identical.
- Cleanup leaves neither this mount point nor its md provider.
+66
View File
@@ -0,0 +1,66 @@
# Test Case: Superblock CRC32C Validation
**Test ID**: TC002-superblock-crc32c
**Category**: Filesystem Metadata
**Priority**: Critical
## Objective
Verify the real EROFS superblock checksum field and both equivalent checksum
ranges, then prove that a valid image mounts while an equal-length image with
one covered byte changed and no checksum update is rejected.
## Fixture Qualification
Run from the repo22 root with erofs-utils 1.8.6:
```sh
work=$(mktemp -d /tmp/repo22-tc002.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
python3 tests/erofs_fixture.py inspect "$work/fixtures/valid-plain.erofs"
python3 tests/erofs_fixture.py inspect "$work/fixtures/bad-super-crc.erofs"
grep '^bad-super-crc ' "$work/fixtures/fixture-evidence.txt"
(cd "$work/fixtures" && sha256 -c SHA256SUMS)
```
For a 4096-byte block, the canonical calculation clears the little-endian
checksum at absolute byte 1028 and calculates CRC32C over bytes `[1024,4096)`
with initial value `0xffffffff`. The production verifier uses seed
`0x5045b54a` over `[1032,4096)`. The helper must print identical canonical and
kernel values, `equivalent=True`, and `valid=True` for the control. The bad
image changes byte 1088, preserves provider length, does not recompute CRC, and
must print `valid=False`.
## FreeBSD Procedure
```sh
mkdir "$work/mnt"
unit=$(mdconfig -a -t vnode -f "$work/fixtures/valid-plain.erofs")
mount -t erofs -o ro "/dev/$unit" "$work/mnt"
cmp "$work/fixtures/source/control.txt" "$work/mnt/control.txt"
umount "$work/mnt"
mdconfig -d -u "${unit#md}"
unit=$(mdconfig -a -t vnode -f "$work/fixtures/bad-super-crc.erofs")
set +e
mount -t erofs -o ro "/dev/$unit" "$work/mnt" \
>"$work/bad-mount.out" 2>"$work/bad-mount.err"
mount_status=$?
set -e
printf 'mount_status=%s\n' "$mount_status"
test "$mount_status" -ne 0
set +e
truss -o "$work/bad-mount.truss" mount -t erofs -o ro "/dev/$unit" \
"$work/mnt" >/dev/null 2>&1
set -e
grep -E 'nmount.*ERR#97' "$work/bad-mount.truss"
! mount -p | awk -v path="$work/mnt" '$2 == path { found=1 }
END { exit found ? 0 : 1 }'
mdconfig -d -u "${unit#md}"
```
## Expected Results
The control mounts and reads exactly. The bad image fails with FreeBSD errno
97 (`EINTEGRITY`) and no mount remains. Record both image hashes, provider
lengths, checksum values/ranges, command status, new dmesg lines, and cleanup.
+87
View File
@@ -0,0 +1,87 @@
# Test Case: LZ4 Compressed File Read
**Test ID**: TC003-lz4-compressed-read
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: Critical
**Regression**: None
## Objective
Verify correct decompression and reading of LZ4-compressed files.
## Preconditions
- EROFS image with LZ4-compressed files: `test-lz4.erofs`
- Known reference file: `original.txt` (uncompressed source)
- Compressed file in image: `/compressed/test.txt`
- Mount point: `/mnt/test`
## Test Steps
1. Mount the LZ4 test image:
```
mount -t erofs /dev/md0 /mnt/test
```
2. Verify file exists:
```
ls -lh /mnt/test/compressed/test.txt
```
3. Read the entire file:
```
cat /mnt/test/compressed/test.txt > /tmp/decompressed.txt
```
4. Calculate checksum:
```
sha256 /tmp/decompressed.txt
sha256 original.txt
```
5. Compare byte-for-byte:
```
cmp /tmp/decompressed.txt original.txt
```
6. Test partial read:
```
dd if=/mnt/test/compressed/test.txt of=/tmp/partial.txt bs=1024 count=1
```
7. Verify partial read matches:
```
cmp -n 1024 /tmp/partial.txt original.txt
```
## Expected Results
- Step 1: Mount succeeds
- Step 2: File size matches original uncompressed size
- Step 4: SHA256 checksums are identical
- Step 5: `cmp` returns 0 (files identical)
- Step 7: First 1024 bytes match
## Verification Method
- Exact byte-for-byte match with original file
- No corruption in decompressed data
- File attributes (size, timestamps) preserved correctly
- Check inode compression format:
```
# Using custom debug tool if available
erofs_inspect /dev/md0 /compressed/test.txt
# Should show: z_algorithmformat[0:2] = Z_EROFS_COMPRESSION_LZ4
```
## Cleanup
```
umount /mnt/test
mdconfig -d -u md0
rm /tmp/decompressed.txt /tmp/partial.txt
```
## Notes
- LZ4 is the most common compression algorithm in EROFS
- Test should cover files of various sizes: small (<4KB), medium (4KB-1MB), large (>1MB)
- LZ4 pcluster size is typically 4KB or 64KB
- Related: TC004 (ztailpacking), TC005 (pcluster mapping)
+96
View File
@@ -0,0 +1,96 @@
# Test Case: LZMA Compressed File Read
**Test ID**: TC004-lzma-compressed-read
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: High
**Regression**: None
## Objective
Verify correct decompression and reading of LZMA/MicroLZMA-compressed files (newly implemented in repo18 iteration 1).
## Preconditions
- EROFS image with LZMA-compressed files: `test-lzma.erofs`
- Known reference file: `original-large.txt` (uncompressed source)
- Compressed file in image: `/compressed/large.txt`
- Mount point: `/mnt/test`
## Test Steps
1. Mount the LZMA test image:
```sh
unit=$(mdconfig -a -t vnode -f test-lzma.erofs)
mount -t erofs -o ro /dev/${unit} /mnt/test
```
2. Verify file exists and check size:
```
ls -lh /mnt/test/compressed/large.txt
stat -f "Size: %z bytes" /mnt/test/compressed/large.txt
```
3. Read entire compressed file:
```
cat /mnt/test/compressed/large.txt > /tmp/lzma-decompressed.txt
```
4. Verify decompression correctness:
```
sha256 /tmp/lzma-decompressed.txt
sha256 original-large.txt
```
5. Compare byte-for-byte:
```
cmp /tmp/lzma-decompressed.txt original-large.txt
echo $?
```
6. Test random access read:
```
dd if=/mnt/test/compressed/large.txt of=/tmp/lzma-middle.txt bs=1k skip=100 count=10
dd if=original-large.txt of=/tmp/orig-middle.txt bs=1k skip=100 count=10
cmp /tmp/lzma-middle.txt /tmp/orig-middle.txt
```
7. Test end-of-file read:
```
tail -c 1000 /mnt/test/compressed/large.txt > /tmp/lzma-tail.txt
tail -c 1000 original-large.txt > /tmp/orig-tail.txt
cmp /tmp/lzma-tail.txt /tmp/orig-tail.txt
```
## Expected Results
- Step 1: Mount succeeds
- Step 2: File size matches original uncompressed size exactly
- Step 4: SHA256 checksums are identical
- Step 5: Exit code 0 (files identical)
- Step 6: Middle section matches (random access works)
- Step 7: Tail matches (EOF handling correct)
## Verification Method
- Complete data integrity check via SHA256
- Partial read correctness (random access)
- No memory corruption or crashes during decompression
- Verify LZMA decoder internal state:
- 1846 probability models initialized
- Range decoder normalization correct
- Dictionary buffer within bounds
## Cleanup
```
umount /mnt/test
mdconfig -d -u "${unit}"
rm /tmp/lzma-*.txt /tmp/orig-*.txt
```
## Notes
- **NEW in repo18**: This is the first iteration with LZMA support
- LZMA provides maximum compression ratio (~30-50% smaller than LZ4)
- MicroLZMA is a variant without header, commonly used in EROFS
- Self-contained implementation in `src/decompressor_lzma.c`
- Reference implementation: XZ Embedded minimal decoder
- Critical test for repo18 iteration 1 validation
- Regression marker: First LZMA implementation, high priority for validation
+32
View File
@@ -0,0 +1,32 @@
# Test Case: Inline user xattrs
**Test ID**: TC005-inline-xattr-user
**Fixture**: `basic.erofs`, `/inline-user`
## Objective
Verify inline `user` namespace list/get and binary-value handling on FreeBSD.
Generate and qualify the image as described in `G4-MANUAL-SETUP.md`; the
manifest must classify `comment` and `special.chars` as inline entries.
## Manual steps
```sh
unit=$(mdconfig -a -t vnode -f /tmp/repo22-g4/images/basic.erofs)
mount -t erofs -o ro /dev/${unit} /mnt/repo22-g4
stat -f 'mode=%Sp size=%z inode=%i' /mnt/repo22-g4/inline-user
lsextattr user /mnt/repo22-g4/inline-user
getextattr -qq -x user comment /mnt/repo22-g4/inline-user
getextattr -qq -x user special.chars /mnt/repo22-g4/inline-user
truss -o /tmp/tc005.truss getextattr -qq user missing /mnt/repo22-g4/inline-user
grep extattr_get_file /tmp/tc005.truss
umount /mnt/repo22-g4
mdconfig -d -u "${unit#md}"
```
## Expected results
The names appear once. `comment` is hex
`696e6c696e652d757365722d76616c7565007461696c`; `special.chars` is
`7370656369616c2d76616c7565`. The missing name returns `ENOATTR` (87), and
cleanup leaves no mount or md provider.
+61
View File
@@ -0,0 +1,61 @@
# Test Case: Real Multi-Device Chunk Read
**Test ID**: TC006-multidev-chunk-read
**Category**: Multi-Device / Chunk-Based
**Priority**: Critical
## Objective
Prove that 8-byte chunk indexes read file data from a live external GEOM
provider, including a range crossing a chunk boundary.
## Fixture
Generate and verify the fresh G6 set as described in `G6-MANUAL-SETUP.md`.
Use `mkfs-blob-primary.erofs` and `mkfs-blob-slot1.blob`. The manifest must
show primary blocks `1`, slot-1 blocks `224`, and every `/tc006.bin` index as
device ID `1`. The 4096-byte primary cannot contain the 98304-byte source.
## Manual procedure
```sh
I=/root/repo22-g6/images
S=/root/repo22-g6/sources/chunk
mkdir -p /mnt/g6
kldload /root/repo22-g6/erofs.ko
mdconfig -a -t vnode -f "$I/mkfs-blob-primary.erofs" -u 90
mdconfig -a -t vnode -f "$I/mkfs-blob-slot1.blob" -u 91
mount -t erofs -o ro -o device.1=/dev/md91 /dev/md90 /mnt/g6
sha256 "$S/tc006.bin" /mnt/g6/tc006.bin
cmp "$S/tc006.bin" /mnt/g6/tc006.bin
dd if="$S/tc006.bin" of=/tmp/tc006.expected bs=1 skip=28672 count=8192 2>/dev/null
dd if=/mnt/g6/tc006.bin of=/tmp/tc006.actual bs=1 skip=28672 count=8192 2>/dev/null
cmp /tmp/tc006.expected /tmp/tc006.actual
truss -f -o /tmp/tc006-ebusy.truss mdconfig -d -u 91
tail -20 /tmp/tc006-ebusy.truss
sysctl -n kern.geom.conftxt | grep -A8 -B2 'Geom name: md91'
```
The detach must fail with `EBUSY`, proving a live external consumer.
## Expected results
- Mount, full-file `cmp`, SHA256, and the 8192-byte cross-boundary range pass.
- The external detach returns exactly `EBUSY` while mounted.
- No panic, trap, hang, or unexpected EROFS dmesg message occurs.
## Cleanup and zero-state check
```sh
umount /mnt/g6
mdconfig -d -u 91
mdconfig -d -u 90
kldunload erofs
rm -f /tmp/tc006.expected /tmp/tc006.actual
mount -p | awk '$3 == "erofs" { print }'
mdconfig -l
kldstat -n erofs 2>/dev/null || true
sysctl -n kern.geom.conftxt | grep -E 'md9[01]|erofs' || true
```
All four final outputs must be empty.
+48
View File
@@ -0,0 +1,48 @@
# Test Case: Concurrent Mount Operations
**Test ID**: TC007-concurrent-mount
## Objective
Verify two simultaneous mounts of the same deterministic EROFS image through
independent md providers, including source-exact reads from both mounts.
## Fixture
Use `images/compact.erofs` and `source/compact/testfile.txt` from the G1
fixture set. Follow `tests/G1-MANUAL-SETUP.md`.
## Procedure
```sh
image=/tmp/repo22-g1/images/compact.erofs
source=/tmp/repo22-g1/source/compact/testfile.txt
mnt1=/mnt/repo22-g1-007a
mnt2=/mnt/repo22-g1-007b
md1=$(mdconfig -a -t vnode -f "$image")
md2=$(mdconfig -a -t vnode -f "$image")
mkdir -p "$mnt1" "$mnt2"
set +e
mount -t erofs -o ro "/dev/$md1" "$mnt1" & p1=$!
mount -t erofs -o ro "/dev/$md2" "$mnt2" & p2=$!
wait "$p1"; rc1=$?
wait "$p2"; rc2=$?
set -e
printf 'mount_rc=%d,%d providers=%s,%s\n' "$rc1" "$rc2" "$md1" "$md2"
test "$rc1" -eq 0 -a "$rc2" -eq 0
cmp "$source" "$mnt1/testfile.txt"
cmp "$source" "$mnt2/testfile.txt"
cmp "$mnt1/testfile.txt" "$mnt2/testfile.txt"
sha256 "$source" "$mnt1/testfile.txt" "$mnt2/testfile.txt"
umount "$mnt1"
umount "$mnt2"
mdconfig -d -u "${md1#md}"
mdconfig -d -u "${md2#md}"
rmdir "$mnt1" "$mnt2"
```
## Expected Results
- Both independently waited mount processes return zero.
- All three comparisons and SHA256 values match.
- No panic, trap, EROFS diagnostic, mount, or md provider remains.
+77
View File
@@ -0,0 +1,77 @@
# Test Case: Mount Error Conditions
**Test ID**: TC008-mount-errors
**Category**: Mount Operations
**Priority**: High
## Objective
Verify graceful rejection of three distinct provider conditions: a
checksum-valid bad magic at the correct field, a 1023-byte provider ending
before the superblock, and a zero-length provider.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc008.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
grep -E '^(bad-magic|truncated-before-super|empty-provider) ' \
"$work/fixtures/fixture-evidence.txt"
python3 tests/erofs_fixture.py inspect "$work/fixtures/bad-magic.erofs"
wc -c "$work/fixtures/bad-magic.erofs" \
"$work/fixtures/truncated-before-super.erofs" \
"$work/fixtures/empty-provider.erofs"
(cd "$work/fixtures" && sha256 -c SHA256SUMS)
```
The bad-magic variant changes only absolute bytes 1024 through 1027 and
preserves media size. It intentionally retains the original checksum: the
production verifier's fixed-magic suffix calculation remains valid, while the
canonical checksum over the now-invalid magic does not. Magic is rejected
before checksum verification. The short and empty fixtures are intentional
provider-size cases, not same-size corruption variants.
## FreeBSD Procedure
Create a fresh mount directory. Execute each case separately and record whether
failure occurs at `mdconfig` or `nmount`; never assume a fixed md number.
```sh
mkdir "$work/mnt"
for image in bad-magic.erofs truncated-before-super.erofs empty-provider.erofs
do
set +e
unit=$(mdconfig -a -t vnode -f "$work/fixtures/$image" \
2>"$work/$image.md.err")
md_status=$?
set -e
printf '%s md_status=%s unit=%s\n' "$image" "$md_status" "$unit"
if test "$md_status" -eq 0; then
set +e
mount -t erofs -o ro "/dev/$unit" "$work/mnt" \
>"$work/$image.out" 2>"$work/$image.mount.err"
mount_status=$?
set -e
printf '%s mount_status=%s\n' "$image" "$mount_status"
test "$mount_status" -ne 0
set +e
truss -o "$work/$image.mount.truss" mount -t erofs -o ro \
"/dev/$unit" "$work/mnt" >/dev/null 2>&1
set -e
grep -E 'nmount.*ERR#' "$work/$image.mount.truss"
mdconfig -d -u "${unit#md}"
else
cat "$work/$image.md.err"
fi
! mount -p | awk -v path="$work/mnt" '$2 == path { found=1 }
END { exit found ? 0 : 1 }'
done
```
## Expected Results
All three cases fail without panic or hang. Bad magic reaches `nmount` and
returns `EINVAL`. A 1023-byte vnode provider reaches `nmount` and returns
`ENXIO`; a zero-byte provider is rejected by `mdconfig` with `EINVAL`. Record
the observed stage and errno rather than fabricating a mount result. No md unit
or mount may remain after each case.
+42
View File
@@ -0,0 +1,42 @@
# Test Case: Basic statfs Information
**Test ID**: TC009-statfs-basic
## Objective
Verify FreeBSD `statfs(2)` fields against the qualified image superblock.
## Fixture
Use `images/compact.erofs`. Before transfer, record `dump.erofs -s` and the
`compact` superblock row in `fixture-evidence.txt`. Follow
`tests/G1-MANUAL-SETUP.md` and compile `tests/statfs_probe.c` in the guest.
## Procedure
Host qualification:
```sh
dump.erofs -s /work/build/repo22-g1/images/compact.erofs
python3 tests/erofs_fixture.py inspect /work/build/repo22-g1/images/compact.erofs
```
Guest dynamic check after mounting the image on `$mnt`:
```sh
./statfs_probe "$mnt"
df -kT "$mnt"
df -iT "$mnt"
mount -p | awk -v p="$mnt" '$2 == p'
```
Unmount and detach the exact dynamic md unit.
## Expected Results
- `fstype=erofs`, `bsize=4096`, `iosize=4096`, and `readonly=1`.
- `blocks` and `files` equal the image's declared block and inode counts.
- `bfree`, `bavail`, and `ffree` are zero; `df` conversions agree.
- `mount -p` contains `ro`; cleanup succeeds.
`stat -f` is not a `statfs(2)` probe and must not be substituted.
+60
View File
@@ -0,0 +1,60 @@
# Test Case: statfs with 48-bit Block Count
**Test ID**: TC010-statfs-48bit
**Category**: Filesystem Metadata
**Priority**: Medium
## Objective
Verify a nonzero 48-bit block-count high word survives mount validation and is
exported without truncation through FreeBSD `statfs(2)` and `df(1)`.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc010.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
python3 tests/erofs_fixture.py inspect \
"$work/fixtures/48bit-statfs-prefix.erofs"
grep '^48bit-statfs-prefix ' "$work/fixtures/fixture-evidence.txt"
sha256 -q "$work/fixtures/48bit-statfs-prefix.erofs"
```
Read `total_blocks` and `required_provider_length` from the evidence. The
fixture sets incompat bit `0x80`, `rb.blocks_hi=1`, and a nonzero
`rootnid_8b`, then recomputes CRC32C. Its small prefix hash identifies the
metadata; the qualified provider is that exact prefix followed by sparse zeros
to the required media size.
## FreeBSD Procedure
```sh
provider="$work/48bit-statfs-provider.raw"
cp "$work/fixtures/48bit-statfs-prefix.erofs" "$provider"
truncate -s "$required_provider_length" "$provider"
stat -f 'size=%z blocks=%b' "$provider"
unit=$(mdconfig -a -t vnode -f "$provider")
diskinfo -v "/dev/$unit"
mkdir "$work/mnt"
mount -t erofs -o ro "/dev/$unit" "$work/mnt"
df -kT "$work/mnt"
df -iT "$work/mnt"
actual_1k=$(df -k "$work/mnt" | awk 'NR == 2 { print $2 }')
used_1k=$(df -k "$work/mnt" | awk 'NR == 2 { print $3 }')
avail_1k=$(df -k "$work/mnt" | awk 'NR == 2 { print $4 }')
expected_1k=$((total_blocks * 4))
test "$actual_1k" -eq "$expected_1k"
test "$used_1k" -eq "$expected_1k"
test "$avail_1k" -eq 0
umount "$work/mnt"
mdconfig -d -u "${unit#md}"
rm -f "$provider"
```
## Expected Results
`diskinfo` reports at least the declared 16 TiB-plus media size. Mount succeeds,
the `df -k` total equals `total_blocks * 4`, Used equals total because
`f_bfree=0`, and Avail is zero.
If the filesystem cannot create or attach the sparse provider, record SHELVED
with all failed commands; a short-provider `ENXIO` is not PASS.
+36
View File
@@ -0,0 +1,36 @@
# Test Case: Root Vnode Lookup
**Test ID**: TC011-root-vnode
## Objective
Verify the mounted root vnode has the encoded root NID, directory attributes,
and the same top-level names as the fixture source.
## Fixture
Use `images/compact.erofs` and `source/compact`. The structured evidence records
the root NID and compact inode size. Follow `tests/G1-MANUAL-SETUP.md`.
## Procedure
After mounting on `$mnt`:
```sh
stat -f 'ino=%i type=%HT mode=%Lp nlink=%l' "$mnt"
stat -f '%i' "$mnt" > /tmp/tc011-root-nid
ls -la "$mnt"
(cd /tmp/repo22-g1/source/compact && ls -1A | sort) > /tmp/tc011-source-names
(cd "$mnt" && ls -1A | sort) > /tmp/tc011-mount-names
diff -u /tmp/tc011-source-names /tmp/tc011-mount-names
cat "$mnt/root.txt"
```
Compare `/tmp/tc011-root-nid` with `root_nid` from `fixture-evidence.txt`, then
unmount, detach, and remove the two temporary name lists.
## Expected Results
- Root inode equals the encoded NID and reports a traversable directory.
- The complete top-level name lists match and `root.txt` is readable.
- All operations and cleanup return zero.
+39
View File
@@ -0,0 +1,39 @@
# Test Case: VFS Vget Normal Operation
**Test ID**: TC012-vget-normal
## Objective
Exercise `VFS_VGET` through a real FreeBSD file handle and verify the returned
vnode identifies and reads the same EROFS inode as pathname lookup.
## Fixture
Use `images/compact.erofs`, `source/compact/testfile.txt`, and
`tests/nfs_fh_tool.c`. Compile the helper as described in
`tests/G1-MANUAL-SETUP.md`; run as root.
## Procedure
After mounting on `$mnt`:
```sh
stat -f 'path_ino=%i gen=%v size=%z' "$mnt/testfile.txt"
./nfs_fh_tool capture "$mnt/testfile.txt" /tmp/tc012-a.fh
./nfs_fh_tool capture "$mnt/testfile.txt" /tmp/tc012-b.fh
./nfs_fh_tool compare /tmp/tc012-a.fh /tmp/tc012-b.fh
./nfs_fh_tool describe /tmp/tc012-a.fh
./nfs_fh_tool stat /tmp/tc012-a.fh
./nfs_fh_tool cat /tmp/tc012-a.fh /tmp/tc012-fh.out
cmp /tmp/repo22-g1/source/compact/testfile.txt /tmp/tc012-fh.out
sha256 /tmp/repo22-g1/source/compact/testfile.txt /tmp/tc012-fh.out
```
Remove the handles/output, then unmount and detach.
## Expected Results
- Both captures produce the same handle.
- Handle NID, `fhstat` inode, and pathname inode agree.
- `fhopen` output matches the deterministic source exactly.
- No stale/duplicate vnode error or cleanup failure occurs.
+39
View File
@@ -0,0 +1,39 @@
# Test Case: Vget with Invalid NID
**Test ID**: TC013-vget-invalid
## Objective
Verify a checksum-valid directory entry that points outside the image reaches
the cold lookup/vget path and fails consistently with `EINTEGRITY`.
## Fixture
Use `images/invalid-dirent-nid.erofs`. `fixture-evidence.txt` records the exact
dirent offset, old NID, out-of-range NID, and valid recomputed CRC. Compile
`tests/read_probe.c` in the guest.
## Procedure
Mount the image without first listing the root, then run:
```sh
./read_probe expect-error "$mnt/invalid-target.txt" 97
./read_probe expect-error "$mnt/invalid-target.txt" 97
set +e
truss -o /tmp/tc013.truss stat "$mnt/invalid-target.txt"
rc=$?
set -e
printf 'stat_rc=%d\n' "$rc"
tail -20 /tmp/tc013.truss
```
Record dmesg before/after, remove the trace, then unmount and detach.
## Expected Results
- Mount succeeds because only the child dirent is malformed.
- Both cold/repeated opens fail at pathname acquisition with FreeBSD errno 97
(`EINTEGRITY`); `truss` shows the same syscall errno.
- The second failure is not converted to `ENOENT`; no panic or stale vnode
appears, and cleanup succeeds.
+42
View File
@@ -0,0 +1,42 @@
# Test Case: Superblock Parsing
**Test ID**: TC014-superblock-parse
## Objective
Verify mandatory superblock fields at their real on-disk offsets and compare
the mounted values exposed by FreeBSD.
## Fixture
Use `images/compact.erofs`, its image hash, `fixture-evidence.txt`, and
`tests/statfs_probe.c`.
## Procedure
Host inspection:
```sh
od -An -tx4 -j 1024 -N 4 /work/build/repo22-g1/images/compact.erofs
od -An -tu1 -j 1036 -N 1 /work/build/repo22-g1/images/compact.erofs
od -An -tu2 -j 1038 -N 2 /work/build/repo22-g1/images/compact.erofs
od -An -tu8 -j 1040 -N 8 /work/build/repo22-g1/images/compact.erofs
od -An -tu4 -j 1060 -N 8 /work/build/repo22-g1/images/compact.erofs
dump.erofs -s /work/build/repo22-g1/images/compact.erofs
python3 tests/erofs_fixture.py inspect /work/build/repo22-g1/images/compact.erofs
```
Mount the same hashed image in FreeBSD, then run:
```sh
./statfs_probe "$mnt"
stat -f 'root_ino=%i type=%HT' "$mnt"
mount -p | awk -v p="$mnt" '$2 == p'
```
## Expected Results
- Magic is `0xe0f5e1e2`, `blkszbits=12`, and checksum validation is true.
- Root NID, block count, and inode count agree with root `stat` and
`statfs_probe`; filesystem type is `erofs` and read-only.
- No parser error appears in the dmesg delta; cleanup succeeds.
+63
View File
@@ -0,0 +1,63 @@
# Test Case: Corrupted Superblock Fields
**Test ID**: TC015-superblock-corrupted
**Category**: Error Handling
**Priority**: High
## Objective
Verify ordered rejection of an unsupported block size, an image-bounds-invalid
48-bit root NID, and an unknown incompat feature. Every variant preserves
provider length and carries a checksum valid for its encoded fields.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc015.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
grep -E '^(bad-block-size|bad-root-nid|unsupported-feature) ' \
"$work/fixtures/fixture-evidence.txt"
python3 tests/erofs_fixture.py inspect "$work/fixtures/bad-block-size.erofs"
python3 tests/erofs_fixture.py inspect "$work/fixtures/bad-root-nid.erofs"
python3 tests/erofs_fixture.py inspect "$work/fixtures/unsupported-feature.erofs"
(cd "$work/fixtures" && sha256 -c SHA256SUMS)
```
`bad-block-size` changes `blkszbits` at 1036 from 12 to 13 and recomputes the
checksum over `[1024,8192)`. `bad-root-nid` selects the 48-bit union form and
sets `rootnid_8b` beyond the declared inode area. `unsupported-feature` sets
unknown incompat bit `0x80000000`.
## FreeBSD Procedure
For each image, attach a dynamic vnode md unit, run mount directly and under
`truss`, assert nonzero status and no mount entry, then detach that exact unit.
```sh
mkdir "$work/mnt"
for image in bad-block-size.erofs bad-root-nid.erofs unsupported-feature.erofs
do
unit=$(mdconfig -a -t vnode -f "$work/fixtures/$image")
set +e
mount -t erofs -o ro "/dev/$unit" "$work/mnt" \
>"$work/$image.out" 2>"$work/$image.err"
status=$?
set -e
printf '%s status=%s\n' "$image" "$status"
test "$status" -ne 0
set +e
truss -o "$work/$image.truss" mount -t erofs -o ro "/dev/$unit" \
"$work/mnt" >/dev/null 2>&1
set -e
grep -E 'nmount.*ERR#' "$work/$image.truss"
! mount -p | awk -v path="$work/mnt" '$2 == path { found=1 }
END { exit found ? 0 : 1 }'
mdconfig -d -u "${unit#md}"
done
```
## Expected Results
Bad block size returns `EINVAL`, bad root NID returns `EINTEGRITY`, and the
unknown incompat bit returns `EOPNOTSUPP`. Record syscall errno, image hash,
provider length, new dmesg lines, and cleanup for all three subcases.
+52
View File
@@ -0,0 +1,52 @@
# Test Case: Superblock CRC Field Mismatch
**Test ID**: TC016-superblock-crc-invalid
**Category**: Error Handling
**Priority**: High
## Objective
Verify that changing only the stored superblock checksum field causes
`EINTEGRITY`, with all protected bytes and provider length unchanged.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc016.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
grep '^bad-checksum-field ' "$work/fixtures/fixture-evidence.txt"
python3 tests/erofs_fixture.py inspect \
"$work/fixtures/bad-checksum-field.erofs"
wc -c "$work/fixtures/valid-plain.erofs" \
"$work/fixtures/bad-checksum-field.erofs"
sha256 -q "$work/fixtures/bad-checksum-field.erofs"
```
The mutator flips bit 0 of the little-endian checksum at absolute byte 1028,
does not recompute it, asserts checksum invalidity, and changes no other byte.
## FreeBSD Procedure
```sh
mkdir "$work/mnt"
unit=$(mdconfig -a -t vnode -f "$work/fixtures/bad-checksum-field.erofs")
set +e
mount -t erofs -o ro "/dev/$unit" "$work/mnt" \
>"$work/mount.out" 2>"$work/mount.err"
status=$?
set -e
test "$status" -ne 0
set +e
truss -o "$work/mount.truss" mount -t erofs -o ro "/dev/$unit" \
"$work/mnt" >/dev/null 2>&1
set -e
grep -E 'nmount.*ERR#97' "$work/mount.truss"
! mount -p | awk -v path="$work/mnt" '$2 == path { found=1 }
END { exit found ? 0 : 1 }'
mdconfig -d -u "${unit#md}"
```
## Expected Results
Mount returns errno 97 (`EINTEGRITY`) with a checksum diagnostic. No mount or
md consumer remains.
+44
View File
@@ -0,0 +1,44 @@
# Test Case: 48-bit Block Count Parsing
**Test ID**: TC017-48bit-blocks-parse
**Category**: Filesystem Metadata
**Priority**: Medium
## Objective
Verify the Linux EROFS union rule dynamically: with incompat bit `0x80` and a
nonzero `rootnid_8b`, combine `blocks_lo` with `rb.blocks_hi << 32` and preserve
the complete count through mount.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc017.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
python3 tests/erofs_fixture.py inspect \
"$work/fixtures/48bit-statfs-prefix.erofs"
od -An -tx2 -j 1038 -N 2 "$work/fixtures/48bit-statfs-prefix.erofs"
od -An -tx4 -j 1060 -N 4 "$work/fixtures/48bit-statfs-prefix.erofs"
od -An -tx8 -j 1136 -N 8 "$work/fixtures/48bit-statfs-prefix.erofs"
grep '^48bit-statfs-prefix ' "$work/fixtures/fixture-evidence.txt"
```
Require `blocks_hi=1`, `rootnid_8b != 0`, a valid recomputed CRC, and a recorded
prefix SHA256. The expected count is `blocks_lo | (1 << 32)`.
## FreeBSD Procedure
Create and attach the sparse provider exactly as in TC010. Record `diskinfo`,
mount it read-only, compare `df -k` total with `total_blocks * 4`, read
`control.txt`, then unmount and detach the dynamic md unit.
Also attach the unextended small prefix once and require mount failure with
`ENXIO`; record this only as the negative media-size guard, not the positive
parse result.
## Expected Results
The sparse provider mount succeeds and reports the full nonzero-high-word count.
The small prefix fails `ENXIO`. If no qualifying provider can be attached,
record the metadata parsing evidence separately and mark the dynamic portion
SHELVED, never PASS.
+66
View File
@@ -0,0 +1,66 @@
# Test Case: Large Filesystem with 48-bit Addressing
**Test ID**: TC018-48bit-large-fs
**Category**: Filesystem Metadata
**Priority**: Low
## Objective
Verify a real FLAT_PLAIN read whose extended inode has `startblk_hi=1`, so the
provider I/O occurs above 16 TiB rather than only reporting a large `statfs`
total.
## Fixture Qualification
```sh
work=$(mktemp -d /tmp/repo22-tc018.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
python3 tests/erofs_fixture.py inspect \
"$work/fixtures/48bit-high-file-prefix.erofs" --path /high-offset.txt
grep '^48bit-high-file-prefix ' "$work/fixtures/fixture-evidence.txt"
sha256 -q "$work/fixtures/48bit-high-file-prefix.erofs"
```
Require an extended nonempty FLAT_PLAIN inode, `startblk_hi=1`, a physical
`data_offset >= 17592186044416`, a valid CRC, and an end offset within
`required_provider_length`. The mutator zeros the original low-block payload,
so a reader that ignores `startblk_hi` cannot return the expected source bytes.
Read `required_provider_length`, `start_block`, `data_offset`,
`low_decoy_offset`, and `file_size` from the evidence line.
## FreeBSD Procedure
```sh
provider="$work/48bit-high-file-provider.raw"
cp "$work/fixtures/48bit-high-file-prefix.erofs" "$provider"
truncate -s "$required_provider_length" "$provider"
dd if="$work/fixtures/source/high-offset.txt" of="$provider" bs=4096 \
seek="$start_block" conv=notrunc
cc -O2 -Wall -Wextra -std=c17 tests/read_probe.c -o "$work/read_probe"
"$work/read_probe" pread "$provider" "$low_decoy_offset" "$file_size" \
"$work/raw-low-offset.txt"
"$work/read_probe" pread "$provider" "$data_offset" "$file_size" \
"$work/raw-high-offset.txt"
dd if=/dev/zero of="$work/zero.bin" bs="$file_size" count=1
cmp "$work/zero.bin" "$work/raw-low-offset.txt"
! cmp -s "$work/fixtures/source/high-offset.txt" "$work/raw-low-offset.txt"
cmp "$work/fixtures/source/high-offset.txt" "$work/raw-high-offset.txt"
unit=$(mdconfig -a -t vnode -f "$provider")
diskinfo -v "/dev/$unit"
mkdir "$work/mnt"
mount -t erofs -o ro "/dev/$unit" "$work/mnt"
cmp "$work/fixtures/source/high-offset.txt" "$work/mnt/high-offset.txt"
cmp "$work/fixtures/source/control.txt" "$work/mnt/control.txt"
df -h "$work/mnt"
umount "$work/mnt"
mdconfig -d -u "${unit#md}"
rm -f "$provider"
```
## Expected Results
The raw provider probe and mounted file both return the exact source bytes from
above 16 TiB, while a low-offset control remains valid. Record the prefix hash,
sparse media size/allocation, high offset, source/data hash, dmesg delta, and
cleanup. If sparse high-offset I/O is unavailable, mark SHELVED with all
attempts; a large `df` result alone cannot pass TC018.
+44
View File
@@ -0,0 +1,44 @@
# Test Case: Nonzero 48-bit Root NID
**Test ID**: TC019-48bit-root-nid
## Objective
Verify the `48BIT && rootnid_8b != 0` selector uses the complete 64-bit root
field and interprets the two-byte union as `blocks_hi`.
## Fixture
Use `images/root8-48bit.erofs`. The structured transformer requires incompat
bit `0x80`, nonzero image-bounded `rootnid_8b`, `blocks_hi=0`, valid CRC32C,
and unchanged provider-sized `blocks_lo`. Production fsck.erofs 1.8.6 does not
recognize this feature; FreeBSD mount/read is mandatory.
## Procedure
Host field check:
```sh
python3 tests/erofs_fixture.py inspect /work/build/repo22-g1/images/root8-48bit.erofs --path=/root.txt
od -An -tu2 -j 1038 -N 2 /work/build/repo22-g1/images/root8-48bit.erofs
od -An -tx4 -j 1104 -N 4 /work/build/repo22-g1/images/root8-48bit.erofs
od -An -tu8 -j 1136 -N 8 /work/build/repo22-g1/images/root8-48bit.erofs
```
Mount the same hashed image in FreeBSD, then run:
```sh
stat -f 'root_ino=%i type=%HT' "$mnt"
cmp /tmp/repo22-g1/source/compact/root.txt "$mnt/root.txt"
sha256 /tmp/repo22-g1/source/compact/root.txt "$mnt/root.txt"
./statfs_probe "$mnt"
```
## Expected Results
- `feature_incompat & 0x80` is set, `rootnid_8b` is nonzero, and observed root
inode equals that complete field.
- Root content matches the source and statfs uses `blocks_lo` plus zero
`blocks_hi`; mount and cleanup succeed.
- TC150's zero-`rootnid_8b` fallback is a separate selector and is not accepted
as evidence for this TC.
+32
View File
@@ -0,0 +1,32 @@
# Test Case: Compact Inode Decoding
**Test ID**: TC020-compact-inode-basic
## Objective
Verify a real 32-byte compact regular inode exposes correct mode, link count,
size, allocation, and source-exact data.
## Fixture
Use `images/compact.erofs` and `source/compact/small.txt`. The evidence records
the NID and asserts `inode_size=32`.
## Procedure
After mounting on `$mnt`:
```sh
stat -f 'ino=%i size=%z mode=%p nlink=%l blocks=%b mtime=%m' "$mnt/small.txt"
wc -c /tmp/repo22-g1/source/compact/small.txt "$mnt/small.txt"
cmp /tmp/repo22-g1/source/compact/small.txt "$mnt/small.txt"
sha256 /tmp/repo22-g1/source/compact/small.txt "$mnt/small.txt"
```
Unmount and detach the exact md unit.
## Expected Results
- The inode is compact, regular mode `0644`, link count 1, timestamp 0, and
size equal to the source.
- Full content and hashes match; all operations and cleanup return zero.
+28
View File
@@ -0,0 +1,28 @@
# Test Case: Compact Inode I_NLINK_1
**Test ID**: TC021-compact-inode-nlink1
## Objective
Verify bit 4 on a non-directory compact inode forces `st_nlink=1` while the
`i_nb` union remains available for address high bits.
## Fixture
Use `images/compact-nlink1.erofs` and `source/compact/single.txt`.
`fixture-evidence.txt` asserts a 32-byte regular inode, bit 4 set,
`i_nb=0x1234`, and valid recomputed CRC32C.
## Procedure
```sh
stat -f 'ino=%i nlink=%l size=%z mode=%p' "$mnt/single.txt"
cmp /tmp/repo22-g1/source/compact/single.txt "$mnt/single.txt"
sha256 /tmp/repo22-g1/source/compact/single.txt "$mnt/single.txt"
```
## Expected Results
- `st_nlink=1` despite on-disk `i_nb=0x1234`.
- Inline file data remains source-exact; mount and cleanup succeed.
- An `i_nb` value of zero is not used as a substitute for the bit-4 encoding.
+31
View File
@@ -0,0 +1,31 @@
# Test Case: Compact Special Inodes and Linux dev_t Decode
**Test ID**: TC022-compact-inode-special
## Objective
Verify compact char/block/FIFO inodes and Linux `new_decode_dev` conversion to
FreeBSD `dev_t`.
## Fixture
Use `images/compact.erofs`. Source metadata records real nodes
`char-large=2748:344865`, `block-large=2748:344865`, and `fifo`; structured
evidence asserts compact inodes and raw on-disk `i_u.rdev=0x543abc21` for both
devices. Compile `tests/stat_special.c` natively in FreeBSD.
## Procedure
```sh
./stat_special char "$mnt/char-large" 2748 344865
./stat_special block "$mnt/block-large" 2748 344865
./stat_special fifo "$mnt/fifo"
stat -f '%N mode=%p rdev=%r' "$mnt/char-large" "$mnt/block-large" "$mnt/fifo"
```
## Expected Results
- Char and block helpers report major 2748, minor 344865, and raw FreeBSD
`st_rdev=0xa430005bc21`.
- FIFO type is correct and `st_rdev=NODEV`.
- A raw little-endian cast would differ; all helper checks and cleanup pass.
+27
View File
@@ -0,0 +1,27 @@
# Test Case: Extended Inode Decoding
**Test ID**: TC023-extended-inode-normal
## Objective
Verify a real 64-byte extended regular inode, including size, full-width
metadata, timestamps, and source-exact content.
## Fixture
Use `images/extended.erofs` and `source/extended/large-file.bin`.
`fixture-evidence.txt` records the NID, byte offset, size, and `inode_size=64`.
## Procedure
```sh
stat -f 'ino=%i size=%z mode=%p nlink=%l mtime=%m ctime=%c blocks=%b' "$mnt/large-file.bin"
cmp /tmp/repo22-g1/source/extended/large-file.bin "$mnt/large-file.bin"
sha256 /tmp/repo22-g1/source/extended/large-file.bin "$mnt/large-file.bin"
```
## Expected Results
- The file reports the evidence/source size, regular `0644`, link count 1,
deterministic timestamps, and a 64-byte on-disk inode.
- Full source compare and SHA256 match; cleanup succeeds.
+38
View File
@@ -0,0 +1,38 @@
# Test Case: Extended Inode File Above 4 GiB
**Test ID**: TC024-extended-inode-large
## Objective
Verify a 64-bit extended `i_size` above 4 GiB without materializing a multi-GiB
payload, using deterministic FLAT_PLAIN hole reads at distant offsets.
## Fixture
Use `images/extended-large-hole.erofs` and
`expected/large-hole-zero-64k.bin`. Structured evidence asserts a 64-byte
inode, size `4294971393`, layout 0, `startblk=0xffffffffffff`, valid CRC, and
an all-zero content descriptor.
## Procedure
After mounting, run three bounded reads:
```sh
stat -f 'size=%z blocks=%b mode=%p' "$mnt/huge-sparse.dat"
./read_probe pread "$mnt/huge-sparse.dat" 0 65536 /tmp/tc024-start
./read_probe pread "$mnt/huge-sparse.dat" 2147483648 65536 /tmp/tc024-middle
./read_probe pread "$mnt/huge-sparse.dat" 4294905857 65536 /tmp/tc024-end
cmp /tmp/repo22-g1/expected/large-hole-zero-64k.bin /tmp/tc024-start
cmp /tmp/repo22-g1/expected/large-hole-zero-64k.bin /tmp/tc024-middle
cmp /tmp/repo22-g1/expected/large-hole-zero-64k.bin /tmp/tc024-end
sha256 /tmp/tc024-start /tmp/tc024-middle /tmp/tc024-end
```
Remove outputs, unmount, and detach.
## Expected Results
- Size is exactly `4294971393`, not truncated to 32 bits.
- Start, middle, and final 64 KiB ranges match the deterministic zero source.
- Reads are bounded, no large allocation/panic occurs, and cleanup succeeds.
+35
View File
@@ -0,0 +1,35 @@
# Test Case: Compact Inode Link-Count Rules
**Test ID**: TC025-nlink1-handling
## Objective
Compare explicit compact link counts, bit-4 single-link encoding, hard links,
and directory link counts without conflating directory dot omission.
## Fixture
Use `images/compact.erofs` and `images/compact-nlink1.erofs`. Evidence asserts
explicit `single.txt i_nb=1`, patched bit 4 plus `i_nb=0x1234`, identical
hard-link NID with `i_nb=2`, and explicit directory nlink.
## Procedure
Mount each image separately and record:
```sh
stat -f '%N ino=%i nlink=%l mode=%p' \
"$mnt/single.txt" "$mnt/hard-a.txt" "$mnt/hard-b.txt" "$mnt/dotdir"
cmp /tmp/repo22-g1/source/compact/single.txt "$mnt/single.txt"
cmp /tmp/repo22-g1/source/compact/hard-a.txt "$mnt/hard-a.txt"
cmp "$mnt/hard-a.txt" "$mnt/hard-b.txt"
```
Unmount and detach before attaching the second image.
## Expected Results
- Baseline and patched `single.txt` both report nlink 1 for different valid
encodings.
- `hard-a.txt` and `hard-b.txt` share one inode and report nlink 2.
- `dotdir` uses its explicit directory link count; all data compares pass.
+33
View File
@@ -0,0 +1,33 @@
# Test Case: Directory with dot_omitted
**Test ID**: TC026-dot-omitted-with
## Objective
Verify a bit-4 directory omits only `.` on disk, retains explicit `..`, and
receives a synthetic `.` from FreeBSD readdir.
## Fixture
Use `images/compact-dot-omitted.erofs`. Structured evidence records bit 4,
on-disk names `..,child.txt`, 48-bit incompat selection with nonzero
`rootnid_8b`, `blocks_hi=0`, and valid CRC. Production fsck.erofs 1.8.6 cannot
qualify this newer format.
## Procedure
After mounting in FreeBSD:
```sh
stat -f 'ino=%i nlink=%l mode=%p' "$mnt/dotdir"
ls -lai "$mnt/dotdir"
(cd "$mnt/dotdir" && test "$(pwd)" = "$mnt/dotdir")
cmp /tmp/repo22-g1/source/compact/dotdir/child.txt "$mnt/dotdir/child.txt"
```
## Expected Results
- User-visible listing contains both `.` and `..`; `.` has the directory NID.
- Structured evidence, not the listing, proves `.` is absent on disk and bit 4
is set; `..` remains an on-disk entry.
- Child data, navigation, mount, and cleanup succeed.
+35
View File
@@ -0,0 +1,35 @@
# Test Case: Directory without dot_omitted
**Test ID**: TC027-dot-omitted-without
## Objective
Verify a bit-4-clear directory reads explicit on-disk `.` and `..` entries.
## Fixture
Use `images/compact.erofs` and `/dotdir`. Structured evidence asserts bit 4 is
clear and names are `.,..,child.txt`; production `dump.erofs --ls` also
qualifies this baseline.
## Procedure
Host:
```sh
dump.erofs --ls --path=/dotdir /work/build/repo22-g1/images/compact.erofs
```
Guest after mount:
```sh
stat -f 'ino=%i nlink=%l mode=%p' "$mnt/dotdir"
ls -lai "$mnt/dotdir"
cmp /tmp/repo22-g1/source/compact/dotdir/child.txt "$mnt/dotdir/child.txt"
```
## Expected Results
- Host inspection and FreeBSD listing both contain explicit `.` and `..`.
- Child data matches the source, directory attributes are valid, and cleanup
succeeds.
+26
View File
@@ -0,0 +1,26 @@
# Test Case: FLAT_PLAIN Small File
**Test ID**: TC028-flat-plain-small
## Objective
Verify a sub-block regular file explicitly encoded as uncompressed
`FLAT_PLAIN` reads source-exactly.
## Fixture
Use `images/flat.erofs` and `source/flat/small.txt`. Structured evidence asserts
layout 0 and records NID and size.
## Procedure
```sh
stat -f 'size=%z blocks=%b mode=%p' "$mnt/small.txt"
cmp /tmp/repo22-g1/source/flat/small.txt "$mnt/small.txt"
sha256 /tmp/repo22-g1/source/flat/small.txt "$mnt/small.txt"
```
## Expected Results
- Evidence proves `FLAT_PLAIN`; size and data match the source.
- The read, hash, mount, and cleanup all succeed.
+30
View File
@@ -0,0 +1,30 @@
# Test Case: FLAT_PLAIN Medium File
**Test ID**: TC029-flat-plain-medium
## Objective
Verify complete and nonzero-offset reads of a multi-block `FLAT_PLAIN` file.
## Fixture
Use `images/flat.erofs`, `source/flat/medium.dat`, and
`expected/medium-10-5.bin`. Evidence asserts layout 0 and size 102417.
## Procedure
```sh
stat -f 'size=%z blocks=%b' "$mnt/medium.dat"
cmp /tmp/repo22-g1/source/flat/medium.dat "$mnt/medium.dat"
./read_probe pread "$mnt/medium.dat" 40960 20480 /tmp/tc029-range
cmp /tmp/repo22-g1/expected/medium-10-5.bin /tmp/tc029-range
sha256 /tmp/repo22-g1/source/flat/medium.dat "$mnt/medium.dat" \
/tmp/repo22-g1/expected/medium-10-5.bin /tmp/tc029-range
```
Remove the range output, unmount, and detach.
## Expected Results
- Full file and the five-block range match their deterministic sources.
- No block-boundary truncation or cleanup failure occurs.
+33
View File
@@ -0,0 +1,33 @@
# Test Case: FLAT_PLAIN Large File
**Test ID**: TC030-flat-plain-large
## Objective
Verify a greater-than-10-MiB contiguous `FLAT_PLAIN` file across thousands of
filesystem blocks.
## Fixture
Use `images/flat.erofs` and `source/flat/large.bin`. Evidence asserts layout 0
and size 10485791.
## Procedure
```sh
stat -f 'size=%z blocks=%b' "$mnt/large.bin"
dd if="$mnt/large.bin" of=/tmp/tc030-full bs=1m
cmp /tmp/repo22-g1/source/flat/large.bin /tmp/tc030-full
sha256 /tmp/repo22-g1/source/flat/large.bin /tmp/tc030-full
./read_probe pread "$mnt/large.bin" 5242880 1048576 /tmp/tc030-seek
dd if=/tmp/repo22-g1/source/flat/large.bin of=/tmp/tc030-source-seek \
bs=1m skip=5 count=1
cmp /tmp/tc030-source-seek /tmp/tc030-seek
```
Remove all outputs, unmount, and detach.
## Expected Results
- Reported size is exact; full and seeked data match the source.
- No mapping error, panic, or cleanup failure occurs.
+28
View File
@@ -0,0 +1,28 @@
# Test Case: FLAT_INLINE Small File
**Test ID**: TC031-flat-inline-normal
## Objective
Verify a small uncompressed `FLAT_INLINE` payload stored in the inode metadata
area and bounded by its metadata block.
## Fixture
Use `images/inline.erofs` and `source/inline/tiny.txt`. Evidence asserts layout
2, compact inode, NID, and size.
## Procedure
```sh
stat -f 'size=%z blocks=%b mode=%p' "$mnt/tiny.txt"
cmp /tmp/repo22-g1/source/inline/tiny.txt "$mnt/tiny.txt"
sha256 /tmp/repo22-g1/source/inline/tiny.txt "$mnt/tiny.txt"
```
## Expected Results
- Layout is 2 and size/content match the source.
- FreeBSD reports 8 allocated 512-byte sectors for the 4096-byte EROFS
allocation unit; zero blocks is not the current qualified semantic.
- Mount and cleanup succeed.
+29
View File
@@ -0,0 +1,29 @@
# Test Case: FLAT_INLINE Zero-Length File
**Test ID**: TC032-flat-inline-zero
## Objective
Verify an explicitly layout-2 zero-length compact inode returns clean EOF and
zero allocation.
## Fixture
Use `images/inline-zero.erofs` and `source/inline/empty.txt`. The transformer
asserts compact `FLAT_INLINE`, size 0, valid CRC, and records the image/source
hashes.
## Procedure
```sh
stat -f 'size=%z blocks=%b mode=%p' "$mnt/empty.txt"
cmp /tmp/repo22-g1/source/inline/empty.txt "$mnt/empty.txt"
test "$(wc -c < "$mnt/empty.txt")" -eq 0
sha256 /tmp/repo22-g1/source/inline/empty.txt "$mnt/empty.txt"
```
## Expected Results
- Size and byte count are zero, `st_blocks=0`, hashes match the empty source,
and no read error occurs.
- Mount and cleanup succeed.
+33
View File
@@ -0,0 +1,33 @@
# Test Case: FLAT_INLINE Tailpacking
**Test ID**: TC033-tailpacking-normal
## Objective
Verify a 5000-byte file reads exactly across its full data block and 904-byte
inline tail boundary.
## Fixture
Use `images/inline.erofs` and `source/inline/tailpacked.dat`. Evidence asserts
layout 2 and size 5000.
## Procedure
```sh
stat -f 'size=%z blocks=%b' "$mnt/tailpacked.dat"
cmp /tmp/repo22-g1/source/inline/tailpacked.dat "$mnt/tailpacked.dat"
sha256 /tmp/repo22-g1/source/inline/tailpacked.dat "$mnt/tailpacked.dat"
./read_probe pread "$mnt/tailpacked.dat" 4080 64 /tmp/tc033-mounted-range
dd if=/tmp/repo22-g1/source/inline/tailpacked.dat \
of=/tmp/tc033-source-range bs=1 skip=4080 count=64 2>/dev/null
cmp /tmp/tc033-source-range /tmp/tc033-mounted-range
```
Remove outputs, unmount, and detach.
## Expected Results
- Full file and the range crossing logical offset 4096 match the source.
- The transition from plain block data to metadata tail is seamless and
cleanup succeeds.
+35
View File
@@ -0,0 +1,35 @@
# Test Case: Tailpacking Block Boundaries
**Test ID**: TC034-tailpacking-boundary
## Objective
Verify the actual erofs-utils boundary choices at 4095, 4096, and 4097 bytes,
including the one-byte inline tail case.
## Fixture
Use `images/inline.erofs` and the three matching files under `source/inline`.
Structured evidence requires layouts `0,0,2` respectively. A 4095-byte payload
cannot fit beside a 32-byte inode in one 4096-byte metadata block, so it is not
claimed as fully inline.
## Procedure
```sh
for size in 4095 4096 4097; do
stat -f '%N size=%z blocks=%b' "$mnt/file-$size.dat"
cmp "/tmp/repo22-g1/source/inline/file-$size.dat" "$mnt/file-$size.dat"
sha256 "/tmp/repo22-g1/source/inline/file-$size.dat" "$mnt/file-$size.dat"
done
./read_probe pread "$mnt/file-4097.dat" 4088 9 /tmp/tc034-boundary
dd if=/tmp/repo22-g1/source/inline/file-4097.dat \
of=/tmp/tc034-source bs=1 skip=4088 count=9 2>/dev/null
cmp /tmp/tc034-source /tmp/tc034-boundary
```
## Expected Results
- Exact sizes and full hashes match for all three files.
- 4095 and 4096 are FLAT_PLAIN; 4097 is FLAT_INLINE with a one-byte tail.
- The nine-byte cross-boundary range matches; cleanup succeeds.
+29
View File
@@ -0,0 +1,29 @@
# Test Case: Sequential Regular-File Read
**Test ID**: TC035-file-read-sequential
## Objective
Verify buffered sequential reads return every byte of a deterministic
multi-block regular file.
## Fixture
Use `images/flat.erofs` and `source/flat/data.txt`. Evidence asserts
`FLAT_PLAIN`, NID, and size 262163.
## Procedure
```sh
stat -f 'size=%z blocks=%b' "$mnt/data.txt"
dd if="$mnt/data.txt" of=/tmp/tc035.out bs=4096
cmp /tmp/repo22-g1/source/flat/data.txt /tmp/tc035.out
sha256 /tmp/repo22-g1/source/flat/data.txt /tmp/tc035.out
```
Remove output, unmount, and detach.
## Expected Results
- `dd` reaches exact EOF and the complete output matches the source/hash.
- No short read, data corruption, dmesg error, or cleanup failure occurs.
+30
View File
@@ -0,0 +1,30 @@
# Test Case: Random Regular-File Reads
**Test ID**: TC036-file-read-random
## Objective
Verify independent start, middle, and end `pread(2)` ranges from a deterministic
1 MiB regular file.
## Fixture
Use `images/flat.erofs`, `source/flat/random-test.bin`, and the three
`expected/random-*.bin` files. Evidence asserts layout 0 and exact size.
## Procedure
```sh
./read_probe pread "$mnt/random-test.bin" 0 10240 /tmp/tc036-start
./read_probe pread "$mnt/random-test.bin" 512000 10240 /tmp/tc036-mid
./read_probe pread "$mnt/random-test.bin" 1024000 24576 /tmp/tc036-end
cmp /tmp/repo22-g1/expected/random-start.bin /tmp/tc036-start
cmp /tmp/repo22-g1/expected/random-mid.bin /tmp/tc036-mid
cmp /tmp/repo22-g1/expected/random-end.bin /tmp/tc036-end
sha256 /tmp/tc036-start /tmp/tc036-mid /tmp/tc036-end
```
## Expected Results
- Every bounded `pread` reports its requested byte count and exact offset.
- All three ranges match independently generated source ranges; cleanup passes.
+33
View File
@@ -0,0 +1,33 @@
# Test Case: Large Regular-File Read
**Test ID**: TC037-file-read-large
## Objective
Verify complete and distant-range reads of a deterministic file larger than
100 MiB without a correctness shortcut based only on throughput.
## Fixture
Use `images/flat.erofs`, `source/flat/huge-data.bin`, and
`expected/huge-sample.bin`. Evidence asserts layout 0 and size 104857857.
## Procedure
```sh
stat -f 'size=%z blocks=%b' "$mnt/huge-data.bin"
time dd if="$mnt/huge-data.bin" of=/tmp/tc037-full bs=1m
cmp /tmp/repo22-g1/source/flat/huge-data.bin /tmp/tc037-full
sha256 /tmp/repo22-g1/source/flat/huge-data.bin /tmp/tc037-full
./read_probe pread "$mnt/huge-data.bin" 52428800 5242880 /tmp/tc037-sample
cmp /tmp/repo22-g1/expected/huge-sample.bin /tmp/tc037-sample
sha256 /tmp/repo22-g1/expected/huge-sample.bin /tmp/tc037-sample
```
Remove outputs, unmount, and detach.
## Expected Results
- Exact size, full compare, and 50 MiB-offset sample compare all pass.
- Record elapsed time but impose no host-dependent QEMU throughput threshold.
- No memory exhaustion, panic, or cleanup failure occurs.
+30
View File
@@ -0,0 +1,30 @@
# Test Case: Short Symlink Target
**Test ID**: TC038-symlink-short
## Objective
Verify a short `FLAT_INLINE` symlink returns the exact source target and follows
to source-exact file data.
## Fixture
Use `images/inline.erofs`, `source/inline/link1`, and
`source/inline/target.txt`. Evidence asserts symlink mode, layout 2, and target
length 10.
## Procedure
```sh
test -L "$mnt/link1"
readlink /tmp/repo22-g1/source/inline/link1 > /tmp/tc038-source-target
readlink "$mnt/link1" > /tmp/tc038-mount-target
cmp /tmp/tc038-source-target /tmp/tc038-mount-target
cmp /tmp/repo22-g1/source/inline/target.txt "$mnt/link1"
sha256 /tmp/repo22-g1/source/inline/target.txt "$mnt/link1"
```
## Expected Results
- `readlink` returns exactly `target.txt`, without newline truncation ambiguity.
- Following the link yields the exact target source; cleanup succeeds.
+36
View File
@@ -0,0 +1,36 @@
# Test Case: Long Symlink Target
**Test ID**: TC039-symlink-long
## Objective
Verify a greater-than-60-byte EROFS symlink target is returned completely and
follows correctly.
## Fixture
Use `images/flat.erofs`, `source/flat/longlink`, and its nested target file.
Structured evidence records symlink size 73 and actual layout 2. EROFS does not
switch to a separate-data-block symlink merely at 60 bytes; that former fixture
assumption is invalid.
## Procedure
```sh
test -L "$mnt/longlink"
readlink /tmp/repo22-g1/source/flat/longlink > /tmp/tc039-source-target
readlink "$mnt/longlink" > /tmp/tc039-mount-target
cmp /tmp/tc039-source-target /tmp/tc039-mount-target
test "$(wc -c < /tmp/tc039-mount-target)" -eq 74
cmp /tmp/repo22-g1/source/flat/very/long/path/to/a/deeply/nested/deterministic/target/directory/file.txt \
"$mnt/longlink"
sha256 "$mnt/longlink" \
/tmp/repo22-g1/source/flat/very/long/path/to/a/deeply/nested/deterministic/target/directory/file.txt
```
The `wc` count is 73 target bytes plus the output newline.
## Expected Results
- Source and mounted targets match exactly and exceed 60 bytes.
- The link follows to source-exact data with no truncation; cleanup succeeds.
+30
View File
@@ -0,0 +1,30 @@
# Test Case: Broken Symlink
**Test ID**: TC040-symlink-broken
## Objective
Verify `readlink(2)` succeeds for a deterministic broken link while following
the target fails with exact `ENOENT`.
## Fixture
Use `images/inline.erofs` and `source/inline/brokenlink`. Evidence asserts
symlink mode, layout 2, and target length 29.
## Procedure
```sh
test -L "$mnt/brokenlink"
readlink /tmp/repo22-g1/source/inline/brokenlink > /tmp/tc040-source-target
readlink "$mnt/brokenlink" > /tmp/tc040-mount-target
cmp /tmp/tc040-source-target /tmp/tc040-mount-target
./read_probe expect-error "$mnt/brokenlink" 2
./read_probe expect-error "$mnt/brokenlink" 2
```
## Expected Results
- Both `readlink` targets equal `/nonexistent/repo22-g1-target`.
- Both follow attempts return errno 2 (`ENOENT`), while the symlink itself
remains visible; no kernel error or cleanup failure occurs.
+29
View File
@@ -0,0 +1,29 @@
# Test Case: Directory Entry Decoding - Normal
**Test ID**: TC041-dirent-decode-normal
**Category**: Directory Operations
**Priority**: High
## Objective
Decode every entry type and the 255-byte filename from the deterministic VFS
fixture through FreeBSD getdirentries(2), not inferred ls formatting.
## Procedure
Mount vfs/vfs-plain.erofs using G3-MANUAL-SETUP.md, then run:
./readdir_probe /tmp/repo22-g3/mnt/testdir 512
find /tmp/repo22-g3/mnt/testdir -maxdepth 1 -print |
sed 's#.*/##' | sort > actual-testdir.txt
cmp fixtures/vfs/expected-testdir.txt actual-testdir.txt
name=$(find /tmp/repo22-g3/mnt/testdir -maxdepth 1 -type f \
-name 'long-*')
test "$(basename "$name" | tr -d '\n' | wc -c)" -eq 255
## Expected Results
The probe reports 36 unique entries, correct DT_DIR/DT_REG/DT_LNK counts,
strictly increasing restartable cookies, and one deterministic name/type hash.
The 34 real names match the manifest exactly and the longest name is 255 bytes.
Record probe output, manifest/image hashes, and mount/md cleanup.
+28
View File
@@ -0,0 +1,28 @@
# Test Case: Directory Entry Decoding - Multi-Block Directory
**Test ID**: TC042-dirent-large-dir
**Category**: Directory Operations
**Priority**: High
## Objective
Decode a real multi-block directory without missing, duplicating, or
truncating entries across directory-block boundaries.
## Procedure
Mount metadata/namei-base.erofs and run:
./readdir_probe /tmp/repo22-g3/mnt/wide 128
find /tmp/repo22-g3/mnt/wide -maxdepth 1 -type f |
sed 's#.*/##' | sort > actual-wide.txt
cmp fixtures/metadata/expected-wide.txt actual-wide.txt
stat /tmp/repo22-g3/mnt/wide/entry-000-abcdefghijklmnopqrstuvwxyz.txt
stat /tmp/repo22-g3/mnt/wide/entry-159-abcdefghijklmnopqrstuvwxyz.txt
stat /tmp/repo22-g3/mnt/wide/entry-319-abcdefghijklmnopqrstuvwxyz.txt
## Expected Results
All 320 real files plus dot and dotdot are returned once. The helper reports
322 restartable kernel and libc positions and the three boundary samples stat
successfully. Record the image hash and cleanup.
+29
View File
@@ -0,0 +1,29 @@
# Test Case: Lookup - Existing Entries
**Test ID**: TC043-lookup-found
**Category**: Directory Operations
**Priority**: Critical
## Objective
Verify exact-name lookup of regular, directory, and symlink entries returns
stable vnode metadata and source-exact data.
## Procedure
Mount vfs/vfs-plain.erofs and run:
./g3_vfs_probe stat /tmp/repo22-g3/mnt/testdir/file.txt
./g3_vfs_probe stat /tmp/repo22-g3/mnt/testdir/subdir
./g3_vfs_probe stat /tmp/repo22-g3/mnt/testdir/shortlink
./g3_vfs_probe stat /tmp/repo22-g3/mnt/testdir/file.txt
cmp fixtures/vfs/source/testdir/file.txt \
/tmp/repo22-g3/mnt/testdir/file.txt
sha256 fixtures/vfs/source/testdir/file.txt \
/tmp/repo22-g3/mnt/testdir/file.txt
## Expected Results
All lookups succeed, repeated file lookup reports the same nonzero inode, each
type/mode/size matches fixture evidence, and source/mounted hashes are equal.
Record command statuses and cleanup.
+28
View File
@@ -0,0 +1,28 @@
# Test Case: Lookup - Missing Entry
**Test ID**: TC044-lookup-not-found
**Category**: Directory Operations
**Priority**: High
## Objective
Verify direct missing lookups return exact ENOENT without masking existing
entries or turning other errors into a miss.
## Procedure
Mount vfs/vfs-plain.erofs and run:
./g3_vfs_probe expect-error stat \
/tmp/repo22-g3/mnt/testdir/nonexistent.txt 2
./g3_vfs_probe expect-error stat \
/tmp/repo22-g3/mnt/testdir/nonexistent.txt 2
./g3_vfs_probe expect-error stat \
/tmp/repo22-g3/mnt/no-such-directory/file.txt 2
./g3_vfs_probe expect-success stat \
/tmp/repo22-g3/mnt/testdir/file.txt
## Expected Results
Each missing syscall reports errno 2 and the positive control succeeds.
Wrapper text alone is not evidence; record the helper output and cleanup.
+27
View File
@@ -0,0 +1,27 @@
# Test Case: Lookup - Case Sensitivity
**Test ID**: TC045-lookup-case-sensitive
**Category**: Directory Operations
**Priority**: High
## Objective
Verify four distinct case variants resolve independently and an unrecorded
case variant returns ENOENT.
## Procedure
Mount vfs/vfs-plain.erofs and run:
stat -f 'name=%N ino=%i size=%z' \
/tmp/repo22-g3/mnt/testdir/file.txt \
/tmp/repo22-g3/mnt/testdir/File.txt \
/tmp/repo22-g3/mnt/testdir/FILE.TXT \
/tmp/repo22-g3/mnt/testdir/FiLe.TxT
./g3_vfs_probe expect-error stat \
/tmp/repo22-g3/mnt/testdir/fILE.txt 2
## Expected Results
The four exact variants have four nonzero, distinct NIDs and their recorded
sizes. The unmatched spelling returns errno 2. Record output and cleanup.
+25
View File
@@ -0,0 +1,25 @@
# Test Case: Readdir - Basic Completeness
**Test ID**: TC046-readdir-basic
**Category**: Directory Operations
**Priority**: Critical
## Objective
Verify one complete getdirentries(2)/readdir(3) traversal returns the exact
fixture set, including dot and dotdot.
## Procedure
Mount vfs/vfs-plain.erofs and run:
./readdir_probe /tmp/repo22-g3/mnt/testdir 4096
find /tmp/repo22-g3/mnt/testdir -maxdepth 1 -mindepth 1 -print |
sed 's#.*/##' | sort > actual-testdir.txt
cmp fixtures/vfs/expected-testdir.txt actual-testdir.txt
## Expected Results
The manifest comparison is exact; the probe reports 36 unique entries and the
same ordered name/type hash through both APIs. Record the image hash and
cleanup.
+28
View File
@@ -0,0 +1,28 @@
# Test Case: Readdir - Multi-Block Completeness
**Test ID**: TC047-readdir-large
**Category**: Directory Operations
**Priority**: High
## Objective
Verify repeated small-buffer reads cover a 320-file multi-block directory
exactly once.
## Procedure
Mount metadata/namei-base.erofs and run:
./readdir_probe /tmp/repo22-g3/mnt/wide 128
find /tmp/repo22-g3/mnt/wide -maxdepth 1 -type f |
sed 's#.*/##' | sort > actual-wide.txt
test "$(wc -l < actual-wide.txt)" -eq 320
test "$(sort actual-wide.txt | uniq -d | wc -l)" -eq 0
Compare actual-wide.txt with fixtures/metadata/expected-wide.txt.
## Expected Results
The helper reports 322 unique entries and 322 valid restart positions with a
128-byte userspace buffer; the exact manifest has 320 names and no duplicates.
Record the probe hash and cleanup.
+31
View File
@@ -0,0 +1,31 @@
# Test Case: Readdir Cookies and seekdir Resume
**Test ID**: TC048-readdir-seek
**Category**: Directory Operations
**Priority**: Critical
## Objective
Verify every kernel d_off and libc telldir cookie resumes at the next exact
entry; complete second traversals are not substitutes.
## Procedure
Mount metadata/namei-base.erofs and run:
./readdir_probe /tmp/repo22-g3/mnt/wide 128 |
tee TC048-probe.out
## Probe Contract
The helper fails on non-increasing cookies, invalid records, duplicate names,
kernel/libc order or type differences, a restart at the wrong next name, or a
final cookie that does not reach EOF. Kernel d_off values are validated after
reopen/lseek; each libc telldir value is passed back to seekdir on the same
DIR stream that produced it, as required by the API contract.
## Expected Results
The helper exits zero and reports entries=322, d_off_restarts=322,
seekdir_restarts=322, buffer=128, the type counts, and one FNV-1a name/type
hash. Record literal output and cleanup.
+26
View File
@@ -0,0 +1,26 @@
# Test Case: Dot Entry Handling
**Test ID**: TC049-dot-handling
**Category**: Directory Operations
**Priority**: High
## Objective
Verify dot is returned as a directory entry and explicit dot lookup resolves
to the same vnode as its directory.
## Procedure
Mount vfs/vfs-plain.erofs and run:
./readdir_probe /tmp/repo22-g3/mnt/testdir 128
stat -f '%i %HT' /tmp/repo22-g3/mnt/testdir
stat -f '%i %HT' /tmp/repo22-g3/mnt/testdir/.
cmp fixtures/vfs/source/testdir/file.txt \
/tmp/repo22-g3/mnt/./testdir/./file.txt
## Expected Results
The traversal includes one dot entry with DT_DIR, both stat commands report
the same directory NID, and paths containing dot read source-exact data.
Record output and cleanup.
+26
View File
@@ -0,0 +1,26 @@
# Test Case: Dotdot Entry Handling
**Test ID**: TC050-dotdot-handling
**Category**: Directory Operations
**Priority**: High
## Objective
Verify dotdot is returned and resolves nested paths to the correct parent
vnode without lock-order failure.
## Procedure
Mount vfs/vfs-plain.erofs and run:
./readdir_probe /tmp/repo22-g3/mnt/parent/child 128
stat -f '%i %HT' /tmp/repo22-g3/mnt/parent
stat -f '%i %HT' /tmp/repo22-g3/mnt/parent/child/..
cmp fixtures/vfs/source/parent/file.txt \
/tmp/repo22-g3/mnt/parent/child/grandchild/../../file.txt
## Expected Results
The traversal contains one dotdot DT_DIR entry, both parent stat commands
report the same NID, and multiple dotdot components reach the expected file.
Record output, dmesg delta, and cleanup.
+29
View File
@@ -0,0 +1,29 @@
# Test Case: Repeated Positive Lookup Behavior
**Test ID**: TC051-namecache-hit
**Category**: VFS Integration
**Priority**: High
## Objective
Verify repeated positive pathname lookups remain stable within one mount.
This is behavior-level namecache integration coverage; timing differences do
not prove an internal cache hit.
## Procedure
Mount vfs/vfs-plain.erofs and run:
stat -f 'ino=%i mode=%p size=%z gen=%v' \
/tmp/repo22-g3/mnt/testdir/file.txt > TC051-first.txt
stat -f 'ino=%i mode=%p size=%z gen=%v' \
/tmp/repo22-g3/mnt/testdir/file.txt > TC051-second.txt
cmp TC051-first.txt TC051-second.txt
cmp fixtures/vfs/source/testdir/file.txt \
/tmp/repo22-g3/mnt/testdir/file.txt
## Expected Results
Both lookups succeed with byte-identical metadata and data. Record only this
observable behavior as PASS; do not claim a measured cache hit without kernel
instrumentation. Record output and cleanup.
+28
View File
@@ -0,0 +1,28 @@
# Test Case: Repeated Negative Lookup Behavior
**Test ID**: TC052-namecache-miss
**Category**: VFS Integration
**Priority**: High
## Objective
Verify repeated missing-name lookups consistently return ENOENT and do not
poison a later positive lookup. This is behavior-level negative-namecache
coverage, not proof of an internal cache hit.
## Procedure
Mount vfs/vfs-plain.erofs and run:
./g3_vfs_probe expect-error stat \
/tmp/repo22-g3/mnt/testdir/cache-missing 2
./g3_vfs_probe expect-error stat \
/tmp/repo22-g3/mnt/testdir/cache-missing 2
./g3_vfs_probe expect-success stat \
/tmp/repo22-g3/mnt/testdir/file.txt
## Expected Results
Both negative syscalls report errno 2 and the positive control succeeds.
Record the stable behavior honestly, with no latency-based cache assertion,
then clean the mount and md.
+30
View File
@@ -0,0 +1,30 @@
# Test Case: Stable Vnode Identity Behavior
**Test ID**: TC053-vfs-hash-integration
**Category**: VFS Integration
**Priority**: High
## Objective
Verify repeated resolutions of one EROFS NID expose one stable vnode identity
and file handle within a mount. This is behavior-level coverage of the
vfs_hash contract; it does not directly observe the internal hash bucket.
## Procedure
Mount vfs/vfs-plain.erofs and run:
stat -f 'ino=%i gen=%v size=%z' \
/tmp/repo22-g3/mnt/testdir/file.txt > TC053-a.txt
stat -f 'ino=%i gen=%v size=%z' \
/tmp/repo22-g3/mnt/testdir/./file.txt > TC053-b.txt
cmp TC053-a.txt TC053-b.txt
./nfs_fh_tool capture /tmp/repo22-g3/mnt/testdir/file.txt TC053-a.fh
./nfs_fh_tool capture /tmp/repo22-g3/mnt/testdir/./file.txt TC053-b.fh
./nfs_fh_tool compare TC053-a.fh TC053-b.fh
./nfs_fh_tool describe TC053-a.fh
## Expected Results
Metadata and handles are identical and the handle NID/generation matches stat.
Record this observable identity behavior, handle hash, and cleanup.
+30
View File
@@ -0,0 +1,30 @@
# Test Case: Parent Vnode Lookup Behavior
**Test ID**: TC054-vn-vget-ino
**Category**: VFS Integration
**Priority**: High
## Objective
Verify repeated dotdot lookups return the correct stable parent vnode without
deadlock. This is behavior-level coverage of the vn_vget_ino path; userspace
cannot prove the internal helper call by timing.
## Procedure
Mount vfs/vfs-plain.erofs and run:
stat -f 'ino=%i gen=%v type=%HT' \
/tmp/repo22-g3/mnt/parent > TC054-parent.txt
stat -f 'ino=%i gen=%v type=%HT' \
/tmp/repo22-g3/mnt/parent/child/.. > TC054-dotdot1.txt
stat -f 'ino=%i gen=%v type=%HT' \
/tmp/repo22-g3/mnt/parent/child/.. > TC054-dotdot2.txt
cmp TC054-parent.txt TC054-dotdot1.txt
cmp TC054-dotdot1.txt TC054-dotdot2.txt
## Expected Results
All commands complete, metadata is identical, and dmesg has no lock-order,
trap, or panic report. Record behavior and cleanup without claiming direct
internal instrumentation.
+30
View File
@@ -0,0 +1,30 @@
# Test Case: getattr Basic Metadata
**Test ID**: TC055-getattr-basic
**Category**: Vnode Operations
**Priority**: Critical
## Objective
Verify VOP_GETATTR returns fixture-exact type, mode, ownership, link count,
size, allocation, block size, inode, and generation for plain and compressed
regular files.
## Procedure
On separate fresh mounts of vfs/vfs-plain.erofs and vfs/vfs-lz4.erofs:
./g3_vfs_probe stat /tmp/repo22-g3/mnt/pager.bin
stat -f 'ino=%i mode=%p uid=%u gid=%g nlink=%l size=%z blocks=%b blksize=%k gen=%v type=%HT' \
/tmp/repo22-g3/mnt/pager.bin
sha256 /tmp/repo22-g3/mnt/pager.bin
For the plain mount also compare the mounted hash with
fixtures/vfs/source/pager.bin and fixture evidence.
## Expected Results
Both files are regular mode 0644, uid/gid 0, size 21211, block size 4096, with
nonzero stable NID/generation. Both logical hashes equal the source hash;
st_blocks records each inode's reported allocated sectors. Record both image
hashes and per-mount cleanup.
+28
View File
@@ -0,0 +1,28 @@
# Test Case: getattr Special Files
**Test ID**: TC056-getattr-special
**Category**: Vnode Operations
**Priority**: Critical
## Objective
Verify compact and extended char, block, and FIFO inodes expose exact types,
device numbers, zero size/allocation, and stable generation.
## Procedure
On fresh mounts of metadata/special-compact.erofs and
metadata/special-extended.erofs:
./stat_special char /tmp/repo22-g3/mnt/char-large 2748 344865
./stat_special block /tmp/repo22-g3/mnt/block-large 2748 344865
./stat_special fifo /tmp/repo22-g3/mnt/fifo
stat -f 'name=%N type=%HT size=%z blocks=%b gen=%v' \
/tmp/repo22-g3/mnt/char-large \
/tmp/repo22-g3/mnt/block-large \
/tmp/repo22-g3/mnt/fifo
## Expected Results
All type/rdev probes pass; each special inode has size and st_blocks zero and
a nonzero generation. Record both fixture hashes and clean each mount/md.
+24
View File
@@ -0,0 +1,24 @@
# Test Case: access - Allowed Operations
**Test ID**: TC057-access-allowed
**Category**: VFS Integration
**Priority**: High
## Objective
Verify a non-root credential receives read and execute access allowed by the
recorded mode bits on the read-only mount.
## Procedure
Mount vfs/vfs-plain.erofs and run with the native nobody credential:
su -m nobody -c './g3_vfs_probe expect-success access-read /tmp/repo22-g3/mnt/testdir/file.txt'
su -m nobody -c './g3_vfs_probe expect-success access-exec /tmp/repo22-g3/mnt/testdir'
su -m nobody -c './g3_vfs_probe expect-success access-exec /tmp/repo22-g3/mnt/testdir/script.sh'
su -m nobody -c './g3_vfs_probe expect-success open-read /tmp/repo22-g3/mnt/testdir/file.txt'
## Expected Results
All four direct checks report success with errno 0. Record the nobody uid,
fixture modes, command output, and cleanup.
+24
View File
@@ -0,0 +1,24 @@
# Test Case: access - Denied Operations
**Test ID**: TC058-access-denied
**Category**: VFS Integration
**Priority**: High
## Objective
Verify direct access checks distinguish permission denial from read-only
modification denial for a non-root credential.
## Procedure
Mount vfs/vfs-plain.erofs and run:
su -m nobody -c './g3_vfs_probe expect-error access-read /tmp/repo22-g3/mnt/testdir/rootonly.txt 13'
su -m nobody -c './g3_vfs_probe expect-error access-read /tmp/repo22-g3/mnt/testdir/writeonly.txt 13'
su -m nobody -c './g3_vfs_probe expect-error access-exec /tmp/repo22-g3/mnt/testdir/noexec.txt 13'
su -m nobody -c './g3_vfs_probe expect-error access-write /tmp/repo22-g3/mnt/testdir/file.txt 30'
## Expected Results
Mode-bit denials report EACCES (13); requested modification of the regular
file reports EROFS (30). Record exact helper output and cleanup.
+30
View File
@@ -0,0 +1,30 @@
# Test Case: readlink Exact Targets
**Test ID**: TC059-readlink
**Category**: VFS Integration
**Priority**: High
## Objective
Verify short, relative, broken, and long symlink targets are returned exactly,
including the long inline target length and bytes.
## Procedure
Mount vfs/vfs-plain.erofs and run:
./g3_vfs_probe readlink /tmp/repo22-g3/mnt/testdir/shortlink
./g3_vfs_probe readlink /tmp/repo22-g3/mnt/testdir/relative-link
./g3_vfs_probe readlink /tmp/repo22-g3/mnt/testdir/broken-link
./g3_vfs_probe readlink /tmp/repo22-g3/mnt/testdir/long-link
cmp fixtures/vfs/source/testdir/file.txt \
/tmp/repo22-g3/mnt/testdir/shortlink
./g3_vfs_probe expect-error stat \
/tmp/repo22-g3/mnt/testdir/broken-link 2
## Expected Results
The helper reports exact targets and hashes: shortlink is file.txt, the
relative link is subdir/child.txt, broken-link is missing-target, and long-link
has the recorded 119-byte target. Following valid links matches source; the
broken target returns errno 2. Record output and cleanup.
+25
View File
@@ -0,0 +1,25 @@
# Test Case: pathconf Queries
**Test ID**: TC060-pathconf
**Category**: VFS Integration
**Priority**: High
**Latest Result**: PASS on exact cdba7e54f KLD; see
tests/results/manual/2026-08-09T1120Z-tc060-fix/manual-test-report.md and the
resolved issues/TC060-pathconf-standard-values.md.
## Objective
Verify direct pathconf(2) results for EROFS limits and read-only conventions.
## Procedure
Mount vfs/vfs-plain.erofs and run:
./g3_vfs_probe pathconf /tmp/repo22-g3/mnt/testdir
## Expected Results
The helper must exit zero and report name_max=255, a positive path_max,
filesizebits=64, a positive link_max, no_trunc=1, and chown_restricted=1.
EINVAL for either standard query is a kernel failure, not an environmental
skip. Record every value/errno and cleanup.
+24
View File
@@ -0,0 +1,24 @@
# Test Case: chmod Rejection
**Test ID**: TC061-setattr-chmod-reject
**Category**: Read-Only Enforcement
**Priority**: Critical
## Objective
Verify a direct chmod request returns EROFS and leaves metadata unchanged.
## Procedure
Mount vfs/vfs-plain.erofs and run:
./g3_vfs_probe stat /tmp/repo22-g3/mnt/testdir/file.txt > TC061-before.txt
./g3_vfs_probe expect-error chmod \
/tmp/repo22-g3/mnt/testdir/file.txt 30
./g3_vfs_probe stat /tmp/repo22-g3/mnt/testdir/file.txt > TC061-after.txt
cmp TC061-before.txt TC061-after.txt
## Expected Results
chmod reports errno 30 and complete metadata remains unchanged. Record output,
fixture hash, and cleanup.
+25
View File
@@ -0,0 +1,25 @@
# Test Case: chown Rejection
**Test ID**: TC062-setattr-chown-reject
**Category**: Read-Only Enforcement
**Priority**: Critical
## Objective
Verify a privileged direct chown request returns EROFS and leaves ownership
unchanged.
## Procedure
Mount vfs/vfs-plain.erofs as root and run:
./g3_vfs_probe stat /tmp/repo22-g3/mnt/testdir/file.txt > TC062-before.txt
./g3_vfs_probe expect-error chown \
/tmp/repo22-g3/mnt/testdir/file.txt 30
./g3_vfs_probe stat /tmp/repo22-g3/mnt/testdir/file.txt > TC062-after.txt
cmp TC062-before.txt TC062-after.txt
## Expected Results
chown to uid/gid 65534 reports errno 30 and metadata remains unchanged. Record
output and cleanup.
+30
View File
@@ -0,0 +1,30 @@
# Test Case: Write, Truncate, and Create Rejection
**Test ID**: TC063-setattr-write-reject
**Category**: Read-Only Enforcement
**Priority**: Critical
## Objective
Verify direct open-for-write, truncate, and create requests return EROFS and
leave the mounted tree unchanged.
## Procedure
Mount vfs/vfs-plain.erofs and run:
sha256 /tmp/repo22-g3/mnt/testdir/file.txt > TC063-before.txt
./g3_vfs_probe expect-error open-rdwr \
/tmp/repo22-g3/mnt/testdir/file.txt 30
./g3_vfs_probe expect-error truncate \
/tmp/repo22-g3/mnt/testdir/file.txt 30
./g3_vfs_probe expect-error create \
/tmp/repo22-g3/mnt/testdir/newfile.txt 30
sha256 /tmp/repo22-g3/mnt/testdir/file.txt > TC063-after.txt
cmp TC063-before.txt TC063-after.txt
test ! -e /tmp/repo22-g3/mnt/testdir/newfile.txt
## Expected Results
All mutation syscalls report errno 30, the file hash is unchanged, and no new
entry exists. Record output and cleanup.
+25
View File
@@ -0,0 +1,25 @@
# Test Case: FreeBSD 15 Local Vnode Pager
**Test ID**: TC064-vop-getpages-normal
**Category**: VM Integration
**Priority**: Critical
## Objective
Verify the exact KLD resolves FreeBSD 15 local pager entry points and real
plain/LZ4 vnode mappings fault data through the pager.
## Procedure
Record unresolved KLD symbols with nm -u. On separate fresh mounts of
vfs/vfs-plain.erofs and vfs/vfs-lz4.erofs, run:
./mmap_fault /tmp/repo22-g3/mnt/pager.bin
## Expected Results
The KLD loads with vnode_pager_local_getpages and
vnode_pager_local_getpages_async resolved. Both helpers report identical
21211-byte FNV hashes, six random faults, partial-EOF zeroing, expected child
SIGBUS, denied writable shared mapping, private COW, and O_RDWR EROFS. Record
both image hashes, dmesg, and per-mount cleanup.
+24
View File
@@ -0,0 +1,24 @@
# Test Case: mmap Through VOP_GETPAGES
**Test ID**: TC065-vop-getpages-mmap
**Category**: VM Integration
**Priority**: Critical
## Objective
Verify page-fault-driven reads, EOF semantics, and private/shared mapping
behavior for plain and compressed EROFS files.
## Procedure
On separate fresh mounts of vfs/vfs-plain.erofs and vfs/vfs-lz4.erofs:
./mmap_fault /tmp/repo22-g3/mnt/pager.bin
## Expected Results
Random and sequential mapped bytes match pread exactly; the partial EOF page
is zero, the next full page produces the helper's expected child SIGBUS,
writable MAP_SHARED is EACCES, MAP_PRIVATE COW succeeds, and O_RDWR is EROFS.
The plain and LZ4 logical FNV hashes are identical. Direct read utilities are
not acceptance evidence. Record dmesg and cleanup.
+24
View File
@@ -0,0 +1,24 @@
# Test Case: Unsupported BMAP with Pager Fallback
**Test ID**: TC066-vop-bmap-unsupported
**Category**: VM Integration
**Priority**: High
## Objective
Verify the exact source KLD keeps erofs_bmap as EOPNOTSUPP while FreeBSD's
local vnode pager successfully faults plain and compressed files through
VOP_READ.
## Procedure
Audit the exact archived source registration and record KLD symbols. Then, on
fresh mounts of vfs/vfs-plain.erofs and vfs/vfs-lz4.erofs, run:
./mmap_fault /tmp/repo22-g3/mnt/pager.bin
## Expected Results
The source returns EOPNOTSUPP from erofs_bmap and registers both local pager
entry points. Both real mmap tests pass; dmesg has no "No strategy for
buffer", assertion, trap, or panic. Record source/KLD/image hashes and cleanup.
+28
View File
@@ -0,0 +1,28 @@
# Test Case: Shared xattr scan found
**Test ID**: TC067-shared-xattr-scan-found
**Fixture**: `basic.erofs`, `/shared-a`, `/shared-b`
## Objective
Verify that shared IDs are scanned after inline entries and return exact data.
The generator manifest must show `shared_key` in the shared table and `local`
inline on `/shared-a`.
## Manual steps
Mount `basic.erofs` using `G4-MANUAL-SETUP.md`, then run:
```sh
lsextattr user /mnt/repo22-g4/shared-a
getextattr -qq -x user shared_key /mnt/repo22-g4/shared-a
getextattr -qq -x user shared_key /mnt/repo22-g4/shared-b
getextattr -qq -x user local /mnt/repo22-g4/shared-a
stat -f 'size=%z inode=%i' /mnt/repo22-g4/shared-a
```
## Expected results
Both shared reads equal `shared-value\0exact` byte-for-byte, the inline read is
`in-bounds-local`, and list output contains all four expected user names.
Unmount and detach before the next TC.
+26
View File
@@ -0,0 +1,26 @@
# Test Case: Shared xattr scan not found
**Test ID**: TC068-shared-xattr-scan-notfound
**Fixture**: `basic.erofs`, `/shared-a`
## Objective
Verify clean exhaustion of inline and shared scans and namespace isolation.
## Manual steps
Mount `basic.erofs`, list the user namespace, then capture both misses:
```sh
lsextattr user /mnt/repo22-g4/shared-a
truss -o /tmp/tc068-name.truss \
getextattr -qq user nonexistent /mnt/repo22-g4/shared-a
truss -o /tmp/tc068-ns.truss \
getextattr -qq system shared_key /mnt/repo22-g4/shared-a
grep extattr_get_file /tmp/tc068-*.truss
```
## Expected results
Both kernel calls return `ENOATTR` (87), with no `EINTEGRITY`, delay, or name
leak into the FreeBSD `system` namespace. Unmount and detach the image.
+23
View File
@@ -0,0 +1,23 @@
# Test Case: List multiple shared xattrs
**Test ID**: TC069-shared-xattr-list-multiple
**Fixture**: `basic.erofs`, `/shared-multi`
## Objective
Verify FreeBSD's length-prefixed extattr list for several shared user entries.
## Manual steps
```sh
lsextattr user /mnt/repo22-g4/shared-multi
getextattr -qq -x user shared_key /mnt/repo22-g4/shared-multi
getextattr -qq -x user shared_comment /mnt/repo22-g4/shared-multi
getextattr -qq -x user shared_binary /mnt/repo22-g4/shared-multi
```
## Expected results
The list contains exactly `shared_key`, `shared_comment`, and `shared_binary`,
without duplicates. Values are `shared-value\0exact`, `shared-comment`, and
bytes `00` through `0f`. Perform the standard unmount/md cleanup.
+24
View File
@@ -0,0 +1,24 @@
# Test Case: Shared trusted xattr
**Test ID**: TC070-shared-trusted-xattr
**Fixture**: `basic.erofs`, `/trusted-shared-a`, `/trusted-shared-b`
## Objective
Verify a shared Linux `trusted.*` entry through FreeBSD extattr semantics.
The transformer must record `e_name_index: 1 -> 4` for the shared record.
## Manual steps
```sh
lsextattr system /mnt/repo22-g4/trusted-shared-a
getextattr -qq -x system trusted.config /mnt/repo22-g4/trusted-shared-a
getextattr -qq -x system trusted.config /mnt/repo22-g4/trusted-shared-b
su -m nobody -c 'getextattr system trusted.config /mnt/repo22-g4/trusted-shared-a'
```
## Expected results
Root receives `trusted-shared-value` from both files. FreeBSD exposes the full
`trusted.config` name in namespace `system`, not a namespace named `trusted`.
The unprivileged system-namespace request is denied. Clean up the mount/md.
+25
View File
@@ -0,0 +1,25 @@
# Test Case: Shared security xattr
**Test ID**: TC071-shared-security-xattr
**Fixture**: `basic.erofs`, `/security-shared-a`, `/security-shared-b`
## Objective
Verify a shared Linux `security.*` entry mapped to FreeBSD namespace `system`.
The transformer must record `e_name_index: 1 -> 6`.
## Manual steps
```sh
lsextattr system /mnt/repo22-g4/security-shared-a
getextattr -qq -x system security.selinux /mnt/repo22-g4/security-shared-a
getextattr -qq -x system security.selinux /mnt/repo22-g4/security-shared-b
truss -o /tmp/tc071.truss getextattr -qq system security.missing \
/mnt/repo22-g4/security-shared-a
grep extattr_get_file /tmp/tc071.truss
```
## Expected results
Both values are exactly `system_u:object_r:shared_repo22_t:s0\0`; the missing
name returns `ENOATTR` (87). Unmount and detach.
+26
View File
@@ -0,0 +1,26 @@
# Test Case: List the FreeBSD system namespace
**Test ID**: TC072-shared-system-xattr-list
**Fixture**: `basic.erofs`
## Objective
Verify that trusted, security, and POSIX ACL indexes are listed only through
FreeBSD namespace `system` with their ABI names.
## Manual steps
```sh
lsextattr system /mnt/repo22-g4/trusted-shared-a
lsextattr system /mnt/repo22-g4/security-shared-a
lsextattr system /mnt/repo22-g4/acl-unordered
lsextattr user /mnt/repo22-g4/trusted-shared-a
stat -f '%HT %Sp' /mnt/repo22-g4/trusted-shared-a \
/mnt/repo22-g4/security-shared-a /mnt/repo22-g4/acl-unordered
```
## Expected results
The three system lists contain `trusted.config`, `security.selinux`, and
`posix_acl_access`, respectively. The trusted file's user list is empty.
Unmount and detach.
+30
View File
@@ -0,0 +1,30 @@
# Test Case: Plain metabox container
**Test ID**: TC073-metabox-container
**Fixture**: `metabox-plain.erofs`, `/metabox-a`, `/metabox-b`
## Objective
Verify metadata and xattrs read from a plain regular metabox carrier. Before
guest use, require manifest fields `METABOX`, `sb_extslots=1`, relocated
`meta_blkaddr`, `metabox_nid`, bit-63 dirent NIDs, and image/source hashes.
## Manual steps
```sh
unit=$(mdconfig -a -t vnode -f /tmp/repo22-g4/images/metabox-plain.erofs)
mount -t erofs -o ro /dev/${unit} /mnt/repo22-g4
stat -f 'mode=%Sp size=%z inode=%i' /mnt/repo22-g4/metabox-a \
/mnt/repo22-g4/metabox-b
sha256 -q /mnt/repo22-g4/metabox-a /mnt/repo22-g4/metabox-b
lsextattr user /mnt/repo22-g4/metabox-a
getextattr -qq -x user metaboxshared /mnt/repo22-g4/metabox-a
getextattr -qq -x user metaboxshared /mnt/repo22-g4/metabox-b
umount /mnt/repo22-g4
mdconfig -d -u "${unit#md}"
```
## Expected results
The inode numbers retain bit 63; file hashes match the generated source;
`metaboxshared` is `nonzero-base` for both files. No mount/md remains.
+24
View File
@@ -0,0 +1,24 @@
# Test Case: Multiple inline user xattrs
**Test ID**: TC074-inline-user-xattr-multiple
**Fixture**: `basic.erofs`, `/inline-multi`
## Objective
Verify list/get across several inline user entries, including embedded NUL and
binary values. Qualify their inline offsets in `fixture-manifest.json` first.
## Manual steps
```sh
lsextattr user /mnt/repo22-g4/inline-multi
getextattr -qq -x user attr1 /mnt/repo22-g4/inline-multi
getextattr -qq -x user attr2 /mnt/repo22-g4/inline-multi
getextattr -qq -x user attr3 /mnt/repo22-g4/inline-multi
stat -f 'mode=%Sp size=%z' /mnt/repo22-g4/inline-multi
```
## Expected results
Names are `attr1`, `attr2`, `attr3`; values are `one`, `two\0binary`, and
bytes `00` through `1f`. Unmount and detach.
+24
View File
@@ -0,0 +1,24 @@
# Test Case: Inline trusted xattr
**Test ID**: TC075-inline-trusted-xattr
**Fixture**: `basic.erofs`, `/inline-trusted`
## Objective
Verify an inline entry transformed to Linux index 4 and exposed through the
FreeBSD system namespace.
## Manual steps
```sh
lsextattr system /mnt/repo22-g4/inline-trusted
getextattr -qq -x system trusted.admin /mnt/repo22-g4/inline-trusted
truss -o /tmp/tc075.truss getextattr -qq user admin \
/mnt/repo22-g4/inline-trusted
grep extattr_get_file /tmp/tc075.truss
```
## Expected results
`trusted.admin` equals `trusted-inline-value`; the user-namespace lookup
returns `ENOATTR` (87). Perform standard cleanup.
+22
View File
@@ -0,0 +1,22 @@
# Test Case: Inline security xattrs
**Test ID**: TC076-inline-security-xattr
**Fixture**: `basic.erofs`, `/inline-security`
## Objective
Verify inline Linux index 6 entries and binary-value preservation through
FreeBSD namespace `system`.
## Manual steps
```sh
lsextattr system /mnt/repo22-g4/inline-security
getextattr -qq -x system security.capability /mnt/repo22-g4/inline-security
getextattr -qq -x system security.selinux /mnt/repo22-g4/inline-security
```
## Expected results
Capability bytes are `0100000200000000aabbccdd`; SELinux bytes are
`system_u:object_r:repo22_t:s0\0`. Unmount and detach.
+25
View File
@@ -0,0 +1,25 @@
# Test Case: Long-prefix user xattr
**Test ID**: TC077-long-prefix-user-xattr
**Fixture**: `basic.erofs`, `/prefix-user-0`, `/prefix-user-1`
## Objective
Verify reconstruction of a long user name from packed prefix ID 0. The helper
must self-check prefix record base index 1 and infix
`repo22.application.component.`.
## Manual steps
```sh
lsextattr user /mnt/repo22-g4/prefix-user-0
getextattr -qq -x user repo22.application.component.setting \
/mnt/repo22-g4/prefix-user-0
getextattr -qq -x user repo22.application.component.setting \
/mnt/repo22-g4/prefix-user-1
```
## Expected results
The full name is listed once and the values are `prefix-value-0` and
`prefix-value-1`. Unmount and detach.
+24
View File
@@ -0,0 +1,24 @@
# Test Case: Long-prefix trusted xattr
**Test ID**: TC078-long-prefix-trusted-xattr
**Fixture**: `basic.erofs`, `/prefix-trusted-0`, `/prefix-trusted-1`
## Objective
Verify packed long-prefix reconstruction after the transformer changes prefix
record 1 from user base index 1 to trusted base index 4.
## Manual steps
```sh
lsextattr system /mnt/repo22-g4/prefix-trusted-0
getextattr -qq -x system trusted.repo22.trusted.deep.setting \
/mnt/repo22-g4/prefix-trusted-0
getextattr -qq -x system trusted.repo22.trusted.deep.setting \
/mnt/repo22-g4/prefix-trusted-1
```
## Expected results
The full trusted name appears in FreeBSD namespace `system`; values are
`trusted-prefix-value-0` and `trusted-prefix-value-1`. Clean up.
+33
View File
@@ -0,0 +1,33 @@
# Test Case: Packed prefix table
**Test ID**: TC079-packed-prefix-table
**Fixture**: `basic.erofs`
## Objective
Verify a non-plain prefix table carried by the packed inode generated by
erofs-utils 1.8.6.
## Layout qualification
```sh
dump.erofs -s "$run/images/basic.erofs"
python3 tests/g4_fixtures.py verify --output "$run"
```
Require `xattr_prefix_count=2`, nonzero `packed_nid`, prefix start 0, user
record `(base=1, infix=repo22.application.component.)`, trusted record
`(base=4, infix=repo22.trusted.deep.)`, and matching image hash.
## Guest observations
```sh
getextattr -qq -x user repo22.application.component.setting \
/mnt/repo22-g4/prefix-user-2
getextattr -qq -x system trusted.repo22.trusted.deep.setting \
/mnt/repo22-g4/prefix-trusted-2
stat -f 'mode=%Sp inode=%i' /mnt/repo22-g4/prefix-user-2 \
/mnt/repo22-g4/prefix-trusted-2
```
Both values must end in `-2`; cleanup must be empty.
+25
View File
@@ -0,0 +1,25 @@
# Test Case: Prefix table lookup
**Test ID**: TC080-prefix-table-lookup
**Fixture**: `basic.erofs`, `/prefix-user-0` through `/prefix-user-3`
## Objective
Verify repeated prefix-ID lookup and a clean missing-suffix result.
## Manual steps
Run the following lookup separately for suffix files 0, 1, 2, and 3:
```sh
getextattr -qq -x user repo22.application.component.setting \
/mnt/repo22-g4/prefix-user-N
truss -o /tmp/tc080.truss getextattr -qq user \
repo22.application.component.missing /mnt/repo22-g4/prefix-user-0
grep extattr_get_file /tmp/tc080.truss
```
## Expected results
Values are `prefix-value-N` for each file; the missing suffix is `ENOATTR`
(87), not an integrity failure. Unmount and detach.
+26
View File
@@ -0,0 +1,26 @@
# Test Case: Metabox-backed shared xattrs
**Test ID**: TC081-metabox-backed-xattr
**Fixture**: `metabox-plain.erofs`, `/metabox-a`, `/metabox-b`
## Objective
Verify shared entries, inline entries, and long-prefix shared entries from a
metabox logical stream. Require `SHARED_EA_IN_METABOX`, `xattr_blkaddr=1`,
carrier size/bounds, shared IDs, and prefix fields in the generator manifest.
## Manual steps
```sh
lsextattr user /mnt/repo22-g4/metabox-a
getextattr -qq -x user metaboxshared /mnt/repo22-g4/metabox-a
getextattr -qq -x user metaboxshared /mnt/repo22-g4/metabox-b
getextattr -qq -x user shared-prefix-key /mnt/repo22-g4/metabox-a
getextattr -qq -x user repo22.application.component.setting \
/mnt/repo22-g4/metabox-a
```
## Expected results
Values are `nonzero-base`, `nonzero-base`, `shared-value`, and
`long-prefix-value`; all names list once. Clean up.
+25
View File
@@ -0,0 +1,25 @@
# Test Case: FreeBSD system namespace subset
**Test ID**: TC082-system-namespace-subset
**Fixture**: `basic.erofs`, `/acl-unordered`
## Objective
Verify POSIX ACL exposure in FreeBSD namespace `system` and isolation from
namespace `user`.
## Manual steps
```sh
lsextattr system /mnt/repo22-g4/acl-unordered
getextattr -qq -x system posix_acl_access /mnt/repo22-g4/acl-unordered
getfacl -n /mnt/repo22-g4/acl-unordered
truss -o /tmp/tc082.truss getextattr -qq user posix_acl_access \
/mnt/repo22-g4/acl-unordered
grep extattr_get_file /tmp/tc082.truss
```
## Expected results
Raw ACL starts with little-endian version 2; `getfacl` shows named users 3002
then 2002. The user-namespace request returns `ENOATTR` (87). Clean up.
+25
View File
@@ -0,0 +1,25 @@
# Test Case: User namespace subset
**Test ID**: TC083-user-namespace-subset
**Fixture**: `basic.erofs`, `/user-subset`
## Objective
Verify several application user xattrs without exposing them through FreeBSD
namespace `system`.
## Manual steps
```sh
lsextattr user /mnt/repo22-g4/user-subset
getextattr -qq -x user comment /mnt/repo22-g4/user-subset
getextattr -qq -x user author /mnt/repo22-g4/user-subset
getextattr -qq -x user checksum /mnt/repo22-g4/user-subset
getextattr -qq -x user com.repo22.app.setting /mnt/repo22-g4/user-subset
lsextattr system /mnt/repo22-g4/user-subset
```
## Expected results
The user values are `subset-comment`, `repo22`,
`sha256:0123456789abcdef`, and `enabled`. The system list is empty. Clean up.
+71
View File
@@ -0,0 +1,71 @@
# Test Case: LZ4 Compression Basic Support
**Test ID**: TC084-lz4-basic
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: Critical
**Regression**: None
## Objective
Verify basic LZ4 compression support for reading compressed files.
## Preconditions
- EROFS image with LZ4 compression: `test-lz4-basic.erofs`
- Test file: `/files/test-lz4.txt` (1MB, highly compressible)
- Mount point: `/mnt/test`
## Test Steps
1. Mount the LZ4-compressed image:
```
mount -t erofs /dev/md0 /mnt/test
```
2. Read entire compressed file:
```
cat /mnt/test/files/test-lz4.txt > /tmp/output.txt
```
3. Verify file size:
```
stat -f %z /mnt/test/files/test-lz4.txt
```
4. Compare with reference (uncompressed):
```
cmp /tmp/output.txt /tmp/reference.txt
```
5. Test multiple LZ4-compressed files:
```
for f in /mnt/test/files/lz4-*.txt; do
cat "$f" > /dev/null
done
```
## Expected Results
- Step 1: Mount succeeds
- Step 2: File read successfully, decompressed transparently
- Step 3: Size matches original uncompressed size (1048576 bytes)
- Step 4: Files identical (cmp returns 0)
- Step 5: All LZ4 files read without errors
## Verification Method
- Verify decompression transparent to application
- Confirm data integrity after decompression
- Test LZ4 decompression performance acceptable
- Verify compressed file sizes smaller than uncompressed
## Cleanup
```
umount /mnt/test
mdconfig -d -u md0
rm /tmp/output.txt
```
## Notes
- LZ4 provides fast decompression with moderate compression ratio
- EROFS uses LZ4 for general-purpose compression
- Related tests: TC003 (LZ4 compressed read), TC085 (large files)
+78
View File
@@ -0,0 +1,78 @@
# Test Case: LZ4 Compression Large File Handling
**Test ID**: TC085-lz4-large-file
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: High
**Regression**: None
## Objective
Verify LZ4 decompression correctness for large files (>100MB).
## Preconditions
- Generated image: `images/lz4-large.erofs`
- Test file: `/large.bin`, exactly 268435456 bytes
- Reference: `sources/large/large.bin` and its generated SHA256
- Mount point: `/mnt/test`
## Test Steps
1. Mount the image:
```
mount -t erofs /dev/md0 /mnt/test
```
2. Verify file size reported correctly:
```
stat -f %z /mnt/test/files/large-lz4.bin
```
3. Read entire file and compute checksum:
```
sha256 -q /mnt/test/large.bin
sha256 -q sources/large/large.bin
cmp sources/large/large.bin /mnt/test/large.bin
```
4. Test partial reads at various offsets:
```
read_probe pread /mnt/test/large.bin 52428800 10485760 guest.bin
read_probe pread sources/large/large.bin 52428800 10485760 source.bin
cmp source.bin guest.bin
read_probe pread /mnt/test/large.bin 209715200 10485760 guest.bin
read_probe pread sources/large/large.bin 209715200 10485760 source.bin
cmp source.bin guest.bin
```
5. Test random access reads:
```
# Compare both guest ranges with the same source offsets.
read_probe pread /mnt/test/large.bin 4096000 4096 chunk1
read_probe pread /mnt/test/large.bin 204800000 4096 chunk2
```
## Expected Results
- Step 2: Size is 268435456 bytes (256MB)
- Step 3: Checksum matches reference value
- Step 4: Partial reads succeed, no errors
- Step 5: Random access works correctly
## Verification Method
- Verify data integrity via checksum
- Confirm partial/random reads decompress correctly
- Test memory usage during decompression reasonable
- Verify no memory leaks or buffer overflows
## Cleanup
```
umount /mnt/test
mdconfig -d -u "${unit#md}"
rm /tmp/chunk1 /tmp/chunk2
```
## Notes
- Large file decompression tests pcluster handling
- LZ4 decompression should be streaming (low memory)
- Related tests: TC089 (pcluster mapping)
+57
View File
@@ -0,0 +1,57 @@
# Test Case: LZ4 Sequential Read Correctness
**Test ID**: TC086-lz4-sequential-read
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: High
## Objective
Verify complete sequential reads of an LZ4-compressed file with two request
sizes against the original source bytes. Record timings only; repo22 exposes no
filesystem-specific DTrace provider and this test has no fixed throughput
threshold.
## Fixture
```sh
work=$(mktemp -d /tmp/repo22-tc086.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
dump.erofs -s "$work/fixtures/valid-lz4.erofs"
dump.erofs --path=/compressed.bin -e "$work/fixtures/valid-lz4.erofs"
```
The dump must identify `/compressed.bin` as compressed LZ4 data with real
physical extents.
## FreeBSD Procedure
```sh
mkdir "$work/mnt"
unit=$(mdconfig -a -t vnode -f "$work/fixtures/valid-lz4.erofs")
mount -t erofs -o ro "/dev/$unit" "$work/mnt"
expected=$(sha256 -q "$work/fixtures/source/compressed.bin")
actual=$(sha256 -q "$work/mnt/compressed.bin")
test "$actual" = "$expected"
/usr/bin/time -p dd if="$work/mnt/compressed.bin" \
of="$work/read-4k.bin" bs=4096 2>"$work/time-4k.txt"
cmp "$work/fixtures/source/compressed.bin" "$work/read-4k.bin"
/usr/bin/time -p dd if="$work/mnt/compressed.bin" \
of="$work/read-1m.bin" bs=1048576 2>"$work/time-1m.txt"
cmp "$work/fixtures/source/compressed.bin" "$work/read-1m.bin"
cat "$work/time-4k.txt" "$work/time-1m.txt"
umount "$work/mnt"
mdconfig -d -u "${unit#md}"
```
## Expected Results
Both complete outputs compare byte-for-byte with the source and both hashes
match. Timing values are recorded with guest CPU, VM, and storage context; one
buffer size is not required to outperform the other.
+53
View File
@@ -0,0 +1,53 @@
# Test Case: LZ4 Deterministic Random-Offset Reads
**Test ID**: TC087-lz4-random-read
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: High
## Objective
Verify non-sequential reads from deterministic offsets in an LZ4-compressed
file and compare every returned byte with the source. `$RANDOM` and timing-only
checks are not evidence of data correctness.
## Fixture and Helper
```sh
work=$(mktemp -d /tmp/repo22-tc087.XXXXXX)
tests/prepare_error_fixtures.sh "$work/fixtures"
cc -O2 -Wall -Wextra -std=c17 tests/read_probe.c -o "$work/read_probe"
dump.erofs --path=/compressed.bin -e "$work/fixtures/valid-lz4.erofs"
```
## FreeBSD Procedure
```sh
mkdir "$work/mnt"
unit=$(mdconfig -a -t vnode -f "$work/fixtures/valid-lz4.erofs")
mount -t erofs -o ro "/dev/$unit" "$work/mnt"
size=$(stat -f %z "$work/fixtures/source/compressed.bin")
test "$size" -gt 1052672
index=0
for offset in 0 4096 65536 1048576 $((size - 4096)); do
"$work/read_probe" pread "$work/mnt/compressed.bin" \
"$offset" 4096 "$work/guest-$index.bin"
"$work/read_probe" pread "$work/fixtures/source/compressed.bin" \
"$offset" 4096 "$work/source-$index.bin"
cmp "$work/source-$index.bin" "$work/guest-$index.bin"
index=$((index + 1))
done
umount "$work/mnt"
mdconfig -d -u "${unit#md}"
```
## Expected Results
All five fixed reads, including the final 4 KiB of the file, return exactly
4096 bytes and compare equal to their corresponding source ranges. No
performance ordering is asserted.
+85
View File
@@ -0,0 +1,85 @@
# Test Case: LZ4 Compression Configuration Handling
**Test ID**: TC088-lz4-config-handling
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: Medium
**Regression**: None
## Objective
Verify correct handling of different LZ4 compression configurations.
## Preconditions
- Multiple EROFS images with different LZ4 configs:
- `lz4-pcluster-4k.erofs` (4KB pcluster)
- `lz4-pcluster-64k.erofs` (64KB pcluster)
- `lz4-pcluster-256k.erofs` (256KB pcluster)
- Test file in each: `/files/test.dat` (identical content)
- Mount points: `/mnt/test1`, `/mnt/test2`, `/mnt/test3`
- Generate the images explicitly with `mkfs.erofs 1.8.6`:
```
mkfs.erofs -zlz4 -C4096 lz4-pcluster-4k.erofs source
mkfs.erofs -zlz4 -C65536 lz4-pcluster-64k.erofs source
mkfs.erofs -zlz4 -C262144 lz4-pcluster-256k.erofs source
```
- Confirm the requested feature/configuration with `dump.erofs -s` before
treating an image as a valid fixture.
## Test Steps
1. Mount all three images:
```
mount -t erofs /dev/md0 /mnt/test1
mount -t erofs /dev/md1 /mnt/test2
mount -t erofs /dev/md2 /mnt/test3
```
2. Read from each and verify identical output:
```
sha256 /mnt/test1/files/test.dat
sha256 /mnt/test2/files/test.dat
sha256 /mnt/test3/files/test.dat
```
3. Record image sizes without assuming a monotonic compression ratio:
```
stat -f %z /root/lz4-pcluster-4k.erofs
stat -f %z /root/lz4-pcluster-64k.erofs
stat -f %z /root/lz4-pcluster-256k.erofs
```
4. Verify the same offset read with each pcluster size. Timing is diagnostic
only and is not a functional pass criterion:
```
time dd if=/mnt/test1/files/test.dat of=/dev/null bs=4K skip=100 count=10
time dd if=/mnt/test2/files/test.dat of=/dev/null bs=4K skip=100 count=10
time dd if=/mnt/test3/files/test.dat of=/dev/null bs=4K skip=100 count=10
```
## Expected Results
- Step 1: All mounts succeed
- Step 2: All three checksums identical (data integrity)
- Step 3: Image sizes are recorded for the exact corpus and mkfs version
- Step 4: All reads succeed and return identical data at the tested offset
## Verification Method
- Verify all pcluster configurations work correctly
- Record pcluster size effects without assuming they are monotonic for every
corpus or under a QEMU TCG guest
- Test driver handles different configs transparently
## Cleanup
```
umount /mnt/test1 /mnt/test2 /mnt/test3
mdconfig -d -u "${unit1#md}"
mdconfig -d -u "${unit2#md}"
mdconfig -d -u "${unit3#md}"
```
## Notes
- Pcluster size is compression unit (physical cluster)
- Larger pcluster -> better compression, worse random access
- Typical sizes: 4KB, 16KB, 64KB, 256KB
- Related tests: TC089 (pcluster mapping)
+69
View File
@@ -0,0 +1,69 @@
# Test Case: LZ4 Compressed Physical Cluster Mapping (4KB)
**Test ID**: TC089-lz4-pcluster-4k
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: High
**Regression**: None
## Objective
Verify correct physical cluster (pcluster) mapping for LZ4 compression with 4KB pcluster size.
## Preconditions
- EROFS image with LZ4, 4KB pcluster: `test-lz4-pcluster-4k.erofs`
- Test file: `/files/test-4k.dat` (aligned to 4KB boundaries)
- Mount point: `/mnt/test`
## Test Steps
1. Mount the image:
```
mount -t erofs /dev/md0 /mnt/test
```
2. Read aligned to pcluster boundary (offset 0):
```
dd if=/mnt/test/files/test-4k.dat of=/tmp/out1 bs=4K count=1 skip=0
```
3. Read aligned to pcluster boundary (offset 4K):
```
dd if=/mnt/test/files/test-4k.dat of=/tmp/out2 bs=4K count=1 skip=1
```
4. Read unaligned (crosses pcluster boundary):
```
dd if=/mnt/test/files/test-4k.dat of=/tmp/out3 bs=2K count=2 skip=1
```
5. Read spanning multiple pclusters:
```
dd if=/mnt/test/files/test-4k.dat of=/tmp/out4 bs=16K count=1
```
## Expected Results
- Step 2: Single pcluster decompressed, correct data
- Step 3: Adjacent pcluster decompressed, correct data
- Step 4: Two pclusters decompressed for unaligned read
- Step 5: Four pclusters decompressed (16K / 4K = 4)
## Verification Method
- Verify pcluster boundaries handled correctly
- Confirm unaligned reads work (may decompress extra pclusters)
- Test each pcluster independently decompressible
- Verify correct data returned for all read patterns
## Cleanup
```
umount /mnt/test
mdconfig -d -u md0
rm /tmp/out1 /tmp/out2 /tmp/out3 /tmp/out4
```
## Notes
- 4KB pcluster provides good random access performance
- Each 4KB logical region compressed independently
- Small pcluster = less compression, better random access
- Related tests: TC090 (64KB pcluster)
+72
View File
@@ -0,0 +1,72 @@
# Test Case: LZ4 Compressed Physical Cluster Mapping (64KB)
**Test ID**: TC090-lz4-pcluster-64k
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: High
**Regression**: None
## Objective
Verify correct physical cluster mapping for LZ4 compression with 64KB pcluster size.
## Preconditions
- EROFS image with LZ4, 64KB pcluster: `test-lz4-pcluster-64k.erofs`
- Test file: `/files/test-64k.dat` (1MB, highly compressible)
- Mount point: `/mnt/test`
- Generate with `mkfs.erofs -zlz4 -C65536 test-lz4-pcluster-64k.erofs source`
and confirm `compr_cfgs big_pcluster` with `dump.erofs -s`.
## Test Steps
1. Mount the image:
```
mount -t erofs /dev/md0 /mnt/test
```
2. Read first pcluster (64KB):
```
dd if=/mnt/test/files/test-64k.dat of=/tmp/out1 bs=64K count=1
```
3. Read small portion requiring full pcluster decompression:
```
dd if=/mnt/test/files/test-64k.dat of=/tmp/out2 bs=4K count=1 skip=1
```
4. Read crossing pcluster boundary:
```
dd if=/mnt/test/files/test-64k.dat of=/tmp/cross bs=1 skip=65504 count=64
dd if=/root/reference/test-64k.dat of=/tmp/cross.expected bs=1 skip=65504 count=64
cmp /tmp/cross /tmp/cross.expected
```
5. Record the physical extent layout:
```
dump.erofs --path=/files/test-64k.dat -e test-lz4-pcluster-64k.erofs
```
## Expected Results
- Step 2: Full 64KB pcluster decompressed correctly
- Step 3: Reading 4KB requires decompressing entire 64KB pcluster
- Step 4: Boundary crossing handled correctly
- Step 5: The fixture contains a real compressed big-pcluster extent
## Verification Method
- Verify large pcluster decompression works
- Confirm small reads still trigger full pcluster decompression
- Test memory usage reasonable for 64KB decompression
- Do not require a better compression ratio than 4KB for every corpus
## Cleanup
```
umount /mnt/test
mdconfig -d -u md0
rm /tmp/out* /tmp/cross /tmp/cross.expected
```
## Notes
- 64KB pcluster provides better compression at cost of random access
- Common pcluster size for read-heavy workloads
- Related tests: TC089 (4KB pcluster), TC088 (config handling)
+76
View File
@@ -0,0 +1,76 @@
# Test Case: LZ4 ztailpacking Data Path
**Test ID**: TC091-ztailpacking-basic
**G5 fixture contract**: Use [G5-MANUAL-SETUP.md](G5-MANUAL-SETUP.md).
Its generated paths, source comparisons, structured corruption offsets, and
cleanup rules supersede placeholder examples in this file.
**Category**: Compression
**Priority**: High
**Regression**: None
## Objective
Verify correct operation of ztailpacking (compressed tail packing) for LZ4-compressed files.
## Preconditions
- EROFS image with ztailpacking enabled: `test-ztailpacking.erofs`
- Test file: `/inline.dat` (64KB deterministic compressible data)
- Mount point: `/mnt/test`
- Generate with:
```
mkfs.erofs -zlz4 -C4096 -Eztailpacking test-ztailpacking.erofs source
```
- Before mounting, require `dump.erofs -s` to report `ztailpacking` and
`dump.erofs --path=/inline.dat -e` to show the compressed physical extent
inside the metadata block. Enabling the mkfs option alone does not prove
that the target file was tail-packed.
## Test Steps
1. Mount the image:
```
mount -t erofs /dev/md0 /mnt/test
```
2. Read file with packed tail:
```
cat /mnt/test/inline.dat > /tmp/output.dat
```
3. Verify file size correct:
```
stat -f %z /mnt/test/inline.dat
```
4. Read file tail specifically:
```
tail -c 1024 /mnt/test/inline.dat > /tmp/tail.dat
```
5. Verify data integrity:
```
sha256 /tmp/output.dat
```
## Expected Results
- Step 2: File read successfully with tail decompressed
- Step 3: Size matches original uncompressed size
- Step 4: Tail data correct
- Step 5: Checksum matches reference
## Verification Method
- Verify tail-packed data decompressed correctly
- Confirm storage efficiency (tail stored inline or in special area)
- Test tail packing transparent to reader
- Verify boundary between main data and packed tail handled correctly
## Cleanup
```
umount /mnt/test
mdconfig -d -u md0
rm /tmp/output.dat /tmp/tail.dat
```
## Notes
- ztailpacking stores compressed file tail inline with inode or in special area
- Reduces fragmentation and improves small file efficiency
- Related tests: TC092 (edge cases), TC033 (uncompressed tailpacking)

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