Skip to content

kernel: handle AT_EMPTY_PATH in fstatat so File#stat on an fd works#865

Open
brandonpayton wants to merge 1 commit into
mainfrom
fix/fstatat-empty-path
Open

kernel: handle AT_EMPTY_PATH in fstatat so File#stat on an fd works#865
brandonpayton wants to merge 1 commit into
mainfrom
fix/fstatat-empty-path

Conversation

@brandonpayton

Copy link
Copy Markdown
Member

Purpose

fstatat(fd, "", AT_EMPTY_PATH) must stat the open fd itself — equivalent to fstat(fd). glibc/musl's fstat and Ruby's File#stat / IO#stat route through this form. The kernel did not recognize AT_EMPTY_PATH, so the empty path fell through to resolve_at_path, which returns ENOTDIR when dirfd is a non-directory fd. As a result, File#stat on any regular-file fd failed with Errno::ENOTDIR.

This is a real POSIX gap, surfaced by running upstream Homebrew inside Kandelo: brew's download-lock code opens the lock file (RDWR|CREAT) and calls File#stat on the resulting fd. That raised ENOTDIR and aborted every brew install / download before fetching anything.

Fix

Detect AT_EMPTY_PATH with an empty path and a real dirfd up front in sys_fstatat and delegate to sys_fstat. sys_statx already delegates to sys_fstatat, so statx-based callers (glibc's modern fstat) are fixed by the same change.

Linux semantics preserved: without AT_EMPTY_PATH, an empty path against a file fd still returns ENOTDIR.

Validation

  • New unit test test_fstatat_empty_path_on_file: empty-path stat on a regular-file fd and on a pipe fd both succeed and match plain fstat; without the flag, empty path still yields ENOTDIR.
  • Full kernel lib suite: cargo test -p kandelo --target aarch64-apple-darwin --liball pass (964 on main + this test).
  • scripts/check-abi-version.sh: snapshot in sync — no ABI change (pure kernel behavior).
  • End-to-end: with this fix, brew install inside Kandelo advances past the download-lock File#stat and reaches the fetch stage (previously it aborted at lock acquisition).

🤖 Generated with Claude Code

`fstatat(fd, "", AT_EMPTY_PATH)` must stat the open fd itself, equivalent
to `fstat(fd)`. glibc/musl's `fstat` and Ruby's `File#stat`/`IO#stat` route
through this form. Previously the empty path fell through to
`resolve_at_path`, which returns ENOTDIR when dirfd is a non-directory fd —
so `File#stat` on any regular-file fd failed with Errno::ENOTDIR.

This broke Homebrew inside Kandelo: `brew`'s download-lock code opens the
lock file (RDWR|CREAT) and calls `File#stat` on the fd, which raised
ENOTDIR and aborted every `brew install`/download before fetching.

Detect AT_EMPTY_PATH with an empty path and a real dirfd up front and
delegate to sys_fstat. `sys_statx` already delegates to `sys_fstatat`, so
statx-based callers are fixed too. Pure kernel behavior; no ABI change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant