Files
erofs-freebsd-out-tree/docs/pre15-stage0/P15-019.md
T
2026-08-18 09:20:44 +02:00

5.1 KiB

P15-019 Stage0 Decision

Status: GO. G03 authorizes B16 for P15-019 only. P15-046 remains the B01 test-only legacy xattr prefix fallback contract; it neither changes this decision nor authorizes any exact-header or xattr behavior change.

The decision is bound to DUT BASE d645feb720c7022d2138d2a62eb72c022eb75351, FreeBSD source HEAD 106727738dcfb6c001b46f25363b91cece970085, erofs-utils source HEAD 7db78788b000999e2de88decd2ba90654f26171c, and mkfs.erofs (erofs-utils) 1.8.6.

Authoritative Run

The gate ran from /work/erofs-freebsd-pre/repo-pre-15 as:

umask 077
timeout -k 10 240 tests/pre15/gates/P15-019.sh \
    --base d645feb720c7022d2138d2a62eb72c022eb75351 \
    --output /work/erofs-freebsd-pre-evidence/pre15/\
20260815T093800Z-G03-P15-019-umask-fix/gate-output

It returned zero with GO. The authoritative hashes are:

  • gate script: afa20cbd28ca9dfc8064596a312248756ed7cde54398efb5ef713818cc04ea54
  • gate input: 291bf12838981ef650e16bbf7381eaa9b2921de957e97dec46e6a573750a19b8
  • result: bb52e1a88cb09554a51432ec545ed98d5509a529832141ed0ce83b0c336be5bb
  • cases: 0c12cdce8b3ed37e85f349af0715a728d4acabbb758451fb65546dd9836066b5
  • fixture manifest/set: 5e99e5ad9112508991e78ae6e65928973d6b0da78285a059894bed06010cf373
  • semantics: 090e6b0cd5eaa197f17da12daf29f30bd8f337d3d4ae2f0f4f5819ad41e906f9
  • cleanup: 8e3b52610ee1492262494d00c48bf66ea04d313ab9ac1ef27565fa0e607b9a97

An additional independent replay from umask 077 produced the same result, cases, fixture manifest, semantics, and cleanup hashes byte for byte. The gate sets umask 022 internally because erofs-utils records the packed fragment inode's source mode; this makes the fixture set independent of its caller.

Real EROFS Fixture Oracle

The gate creates 35 deterministic EROFS images and seven external chunk blobs in owned output. Nothing is committed as an image, blob, binary, or KLD. Each of the five required storage paths has these seven cases:

  • normal short target;
  • normal 1,024-byte MAXPATHLEN target;
  • zero-byte target;
  • embedded NUL at the first, middle, and last byte;
  • 1,025-byte over-limit target.

Inline positives are native mkfs symlinks. Plain, chunk, compact compressed ztailpacking, and whole-file fragment cases start as real mkfs regular-file carriers because the host cannot create a symlink containing NUL and mkfs does not select those layouts for symlinks. The transform changes only inode mode, the root dirent type, and, for empty cases, inode size, then recomputes the real EROFS checksum. Chunk data remains on a real --blobdev; fragment data remains in the real packed inode. Changed-byte offsets are recorded for every case.

The independent Python parser reads the superblock, checksum span, inode, dirent, layout, and target bytes directly. It resolves chunk indexes and the external blob, decodes compact inline-pcluster raw LZ4 without a DUT helper, and resolves the whole-fragment offset through the packed inode. It does not compile, call, or inject values into any DUT internal function. dump.erofs is only a layout/type cross-check. All legal targets also pass fsck.erofs --extract and reproduce the exact source bytes.

The independent decision model returns positive FreeBSD EINTEGRITY (97) for empty or embedded-NUL targets, positive ENAMETOOLONG (63) before target I/O for size greater than MAXPATHLEN, and success for exact non-NUL bytes. No trailing NUL is required. All 35 expected results match, so every layout has a bounded positive and negative oracle as required by G03.

Linux and FreeBSD Semantics

Linux erofs_fill_symlink() validates only flat-inline fast symlinks while building i_link; it uses kmemdup_nul()/strlen() and returns negative -EFSCORRUPTED. Non-inline Linux symlinks use page_get_link. That page/cache shape is not a FreeBSD implementation contract.

FreeBSD enters through VOP_READLINK on a vnode and currently streams through erofs_readlink_target, the common map/read path, GEOM or the compressed backing path, and uiomove. It has no Linux i_link or page-get-link path and uses positive errno. B16 must therefore validate the complete immutable target before any uiomove, use the existing layout-specific map/read machinery, and keep the scan bounded by MAXPATHLEN=1024.

The 1,024-byte case is a successful readlink(2) byte target. Pathname follow may still return ENAMETOOLONG when that target plus the remaining pathname exceeds FreeBSD's namei buffer; that VFS result is distinct from the on-disk target validator. The 1,025-byte inode is rejected by B16 before target I/O.

Scope and Cleanup

The gate did not modify src/**, build a KLD, start QEMU, or run a feature suite. It observed but did not signal or otherwise alter protected PID 26318, port 9222, or /work/debug-qemu/local/vm-freebsd-build.qcow2.bp; their recorded identity was unchanged before and after the run. Owned temporary source and extract directories were removed, with zero leftovers.

B16 may now modify only its planned source/test write set and must pass the specified host TC166, D, zstdio0 build, and targeted TC166 QEMU acceptance.