# Pre10 Batch B: Bounded Metadata Helpers ## Scope and baseline Batch B is a source-only extraction based on commit `3c7c774b296a2ee90578a78f372aad5fd2edbec6`. It changes only `src/super.c`, `src/data.c`, and `src/inode.c`, plus this report. No build, QEMU smoke, or feature test was run. The extraction keeps ownership at the original FreeBSD lifecycle boundaries: - `erofs_mountfs()` still owns mount allocation, the primary GEOM transfer, extent-cache lifecycle, device scanning, internal inode setup, publication, and the single `erofs_sb_free()` failure path. - `erofs_map_dev()` still owns device selection, range and overflow checks, flat-device behavior, and all `ENODEV`/`EINTEGRITY` returns. - `erofs_vget()` still owns vnode allocation, locking, mount association, hash insertion, construction state, publication, and failure cleanup. ## Superblock helper `erofs_read_superblock()` now reads the on-disk superblock into a temporary buffer, copies the fixed 144-byte structure to caller-owned stack storage, releases the temporary buffer, and performs the existing validation and field decode in the original order. It also retains the existing device-size, checksum, generation-seed, metabox-NID, and compression-configuration checks. The helper does not allocate or publish the mount, open extra devices, create internal inodes, initialize xattrs, or own failure cleanup. `erofs_mountfs()` still calls, in order: ```text z_erofs_extent_cache_init erofs_read_superblock erofs_scan_devices shared-EA/metabox combination check erofs_init_packed_inode erofs_init_metabox_inode erofs_xattr_prefixes_init mount publication and flag setup ``` All prior superblock outcomes remain at the same semantic boundary: | Condition | Preserved result | | --- | --- | | read failure | underlying `erofs_bread()` error | | invalid magic or block geometry | `EINVAL` | | unsupported directory blocks or feature bits | `EOPNOTSUPP` | | invalid xattr, timestamp, or metabox metadata | `EINTEGRITY` | | invalid device geometry | existing `EINVAL`, `EINTEGRITY`, or `ENXIO` | | checksum mismatch | `EINTEGRITY` | | generation/config read failure | existing underlying error | Copying the superblock before releasing its read buffer makes its lifetime explicit. The copy remains available to `erofs_scan_devices()` and volume-name publication, while checksum and compression configuration reads continue to use their existing independent I/O helpers. ## Device-map helper `erofs_fill_map_dev()` is a pure assignment helper for `m_em`, `m_dif`, and `m_pa`. It has no branches, errors, allocation, I/O, or ownership effects. The caller retains all explicit-device and unified-range selection, arithmetic overflow checks, physical range validation, flat-device handling, and external device availability checks. In particular: - flat explicit mappings still use the primary device and add the unified offset to the existing physical address; - flat implicit mappings still retain the primary device and unified address; - non-flat explicit mappings select the requested extra device only after all validation succeeds; and - non-flat implicit misses still return success with the initial primary map. ## Vnode helper `erofs_fill_vnode()` sets only the inode-derived vnode type, FIFO operation vector, and `VV_ROOT` flag. It runs after `erofs_read_inode()` succeeds and immediately before `VSTATE_CONSTRUCTED`, while `erofs_vget()` still holds the exclusive vnode lock. Hash lookup/insertion, allocation, `insmntque()`, locking, race handling, failure `vgone()`/`vput()`, shared-lock downgrade, and `*vpp` publication remain in `erofs_vget()` in their original order. ## Known pre-existing risk The `insmntque()` failure branch remains unchanged. FreeBSD's `insmntque()` may reclaim and release the vnode on failure, while the existing EROFS branch then accesses `vp->v_data`. This possible use-after-release is outside a helper-only batch and was deliberately not mixed into this commit. Consequently, this report does not claim that the pre-existing vnode cleanup path is correct. ## Static validation | Check | Result | Evidence | | --- | --- | --- | | Changed-path allowlist | PASS | Only three allowed source files and this report changed. | | Helper ownership | PASS | Each helper is `static` and has one direct caller. | | Mount lifecycle order | PASS | Cache init, device scan, internal inode/xattr setup, publication, and `erofs_sb_free()` remain in `erofs_mountfs()`. | | Device-map errors | PASS | All range, overflow, device-selection, `ENODEV`, and `EINTEGRITY` branches remain in `erofs_map_dev()`. | | Vnode lifecycle order | PASS | `insmntque()`, hash insertion, inode read, construction, downgrade, and publication retain their order. | | `git diff --check` | PASS | No whitespace errors. | | Build/QEMU/feature test | NOT RUN | Reserved for later aggregate Pre10 validation. | The pre-existing `insmntque()` concern is deferred; no new errno, logging, ABI, Linux lifecycle facade, or Pre9 decoded-cache change was introduced.