# Pre11 Batch C: ACL Parsing Responsibilities

## Scope

This batch mechanically separates POSIX ACL wire-format parsing from the
FreeBSD vnode and xattr acquisition path.

Modified files:

- `src/xattr.c`
- `docs/pre11-batch-c.md`

No xattr body loading, header-only xattr behavior, namespace handling, public
VOP interface, build configuration, or test infrastructure was changed.

## Responsibility Split

`erofs_get_acl()` continues to own:

- mount option and ACL type validation;
- the default-ACL directory restriction;
- no-ACL filter handling and mode fallback;
- xattr namespace and name selection;
- both `erofs_getxattr()` stages;
- FreeBSD `uio` and `iovec` setup;
- `ENOATTR` fallback and the stack input buffer.

The new `erofs_posix_acl_from_xattr()` receives an already-read buffer and
owns only wire-format validation and ACL population. It borrows both the input
buffer and output ACL and performs no allocation, release, lookup, or vnode
operation.

`erofs_acl_from_mode()` now receives the inode mode directly. This preserves
the original fallback writes while preventing the parser from depending on an
`erofs_node`.

## Moved-Code Mapping

The parsing block formerly at the end of `erofs_get_acl()` was moved in the
same statement order into `erofs_posix_acl_from_xattr()`:

1. Header length and entry-size remainder validation.
2. Header copy and version validation.
3. Entry count calculation and maximum check.
4. Zero-entry mode fallback.
5. `acl_cnt` assignment and phase initialization.
6. Permission validation before tag-phase validation.
7. Tag phase transitions and `UINT32_MAX` rules.
8. Duplicate named user and group ID checks.
9. ACL entry writes and undefined-ID mapping.
10. The short-circuit `phase != 6 || acl_posix1e_check(aclp) != 0` check.

No ACL zeroing or additional field initialization was added. Partial ACL
mutation on malformed input remains possible exactly as before.

## Branch Matrix

| Input or state | Preserved result |
| --- | --- |
| POSIX ACL mount option disabled | `EOPNOTSUPP` in caller |
| Unsupported ACL type | `EINVAL` in caller |
| Default ACL requested for non-directory | `EINVAL` in caller |
| No-ACL filter match | Mode-derived ACL in caller |
| ACL xattr absent | Mode-derived ACL in caller |
| Xattr lookup/read failure | Original error from caller |
| Oversized xattr or nonzero UIO residual | `EINTEGRITY` in caller |
| Short or misaligned wire value | `EINTEGRITY` in parser |
| Unsupported wire version | `EINTEGRITY` in parser |
| Entry count above limit | `EINTEGRITY` in parser |
| Zero entries | Mode-derived ACL in parser |
| Invalid permission, phase, tag, or ID | `EINTEGRITY` in parser |
| Duplicate named user/group ID | `EINTEGRITY` in parser |
| Missing required mask or incomplete phase | `EINTEGRITY` in parser |
| Valid ACL | Identical ACL entry population and success |

## Resource And Error Audit

- The input buffer remains stack-owned by `erofs_get_acl()`.
- The parser does not allocate, free, or retain pointers.
- There are no new cleanup paths.
- Xattr errors, UIO residual errors, wire errors, and ACL validation errors
  retain their previous ordering.
- `erofs_xattr_load_body()` and its release paths were not modified.
- The optional header validation helper was intentionally omitted to avoid
  touching the known header-only behavior.

## Static Verification

The batch is validated with:

- a strict two-file path whitelist;
- unique definitions of `erofs_get_acl()`, `erofs_acl_from_mode()`, and
  `erofs_posix_acl_from_xattr()`;
- pre/post comparison of parser returns, phase transitions, ACL writes, and
  duplicate-ID checks;
- `git diff --check`;
- confirmation that `erofs_xattr_load_body()` is absent from the source diff.

No build, QEMU smoke test, or feature test was run for this mechanical split.
