# 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.
