# EROFS for FreeBSD ## Build Kernel Module Build on FreeBSD 15 amd64 with a matching FreeBSD source tree. The default source path is `/usr/src`: ```sh WITH_ZSTDIO=0 ./build.sh ``` Use `FREEBSD_SRC=/path/to/freebsd-src` when the source tree is elsewhere. The script is a native FreeBSD wrapper around `src/Makefile` and `bsd.kmod.mk`; the Makefile is the authoritative module name, source list, architecture gate, and per-source flag definition. Each run rebuilds `build/obj` and writes `build/erofs.ko`. Only `MACHINE_ARCH=amd64` is currently qualified. Other architectures are rejected explicitly instead of inheriting amd64 ABI flags. Build ZSTD support with: ```sh WITH_ZSTDIO=1 ./build.sh ``` The enabled module references the FreeBSD kernel ZSTD API and therefore requires a running kernel built with `options ZSTDIO`. `WITH_ZSTDIO=0` builds a module without those references and rejects ZSTD-compressed images at mount. ## Mount EROFS is read-only. Mount a single-device image with: ```sh mount -t erofs -o ro /dev/md0 /mnt/erofs ``` For an image with external blob devices, map every one-based on-disk device slot explicitly with `device.=`: ```sh mount -t erofs -o ro \ -o device.2=/dev/md92 \ -o device.1=/dev/md91 \ /dev/md90 /mnt/erofs ``` Slot names make the mapping independent of option order. Every external slot must be present exactly once; assigning the same GEOM provider to multiple slots is rejected. The same names and values may be passed directly as `nmount(2)` iovec entries. There is no repo-local `mount_erofs` binary; FreeBSD's generic `/sbin/mount` frontend passes these distinct option names through to `nmount(2)`. The driver forces every successful mount read-only, so even `-o rw` produces a read-only mount rather than enabling writes. If an image has a device table but no `device.` options are supplied, the primary provider is treated as a Linux-compatible flatdev image. It must contain the external ranges at their declared `uniaddr` block offsets, for example a deterministic concatenation of the primary image and its blobs. ## Qualified Semantics - Compact/extended metadata, Linux device-number decode, plain/inline/chunk data, and LZ4/MicroLZMA/DEFLATE/ZSTD compressed reads. - Inline tails are confined to the inode metadata block and declared image or metabox backing bounds. - Directory lookup/readdir share strict validation while accepting Linux-style nonzero unused bytes after the final name NUL. - Compressed `st_blocks` reflects the inode's on-disk compressed block count; uncompressed and chunk files retain logical block rounding. - FreeBSD 15 local vnode pager sync/async entry points are used for real mmap faults. - NFS export uses full 64-bit NIDs and a generation derived from the superblock seed and inode metadata. Replacing metadata changes the generation and makes old handles stale; a metadata-identical, payload-only replacement is not guaranteed to return `ESTALE`. See `docs/features.md` for the bounded feature claim and `tests/results/manual/2026-08-09T1804Z-final-review-independent/manual-test-report.md` for the final-review evidence. ## Test Fixtures `tests/prepare_directory_fixtures.sh` creates the deterministic TC048/141/148 directory fixtures. `tests/prepare_error_fixtures.sh` and `tests/erofs_fixture.py` create and self-check the structured TC002/086/087/102 and TC112-TC116/119 fixtures. Generated trees, images, overlays, and `build/` outputs are test artifacts and are not committed.