Skip to content

Add Filesystem::statx() for FUSE_STATX - #746

Merged
cberner merged 1 commit into
masterfrom
claude/xfstests-skip-investigation-s9gdr1
Aug 5, 2026
Merged

Add Filesystem::statx() for FUSE_STATX#746
cberner merged 1 commit into
masterfrom
claude/xfstests-skip-investigation-s9gdr1

Conversation

@cberner

@cberner cberner commented Aug 5, 2026

Copy link
Copy Markdown
Owner

The kernel sends FUSE_STATX for statx(2) once the connection is at ABI 7.38 or
above, and falls back to FUSE_GETATTR when the filesystem answers ENOSYS -
which it takes as permanent, the same way it does for tmpfile() and syncfs().

What it is worth implementing for

The creation time. fuse_attr has a field for it on macOS alone, so on Linux
there has been no way for a filesystem to report one and statx(2) answers with
whatever the kernel last cached.

StatxAttr carries it as an Option<SystemTime>, and the reply's stx_mask is
derived from what was actually filled in rather than supplied by the caller, so
it cannot claim a field that is not there.

What it is not worth implementing for

I went in expecting this to make chattr +i visible to statx(2), which would
have let generic/424 run. It does not, and the reason is worth recording since
the wire format suggests otherwise.

struct fuse_statx has an attributes field carrying the STATX_ATTR_* bits,
and this PR fills and sends it. The kernel ignores it. fuse_do_statx() copies
the mask, the creation time and the basic stats out of the reply and nothing
else:

stat->result_mask = sx->mask & (STATX_BASIC_STATS | STATX_BTIME);
stat->btime.tv_sec = sx->btime.tv_sec;
stat->btime.tv_nsec = min_t(u32, sx->btime.tv_nsec, NSEC_PER_SEC - 1);
fuse_fillattr(idmap, inode, &attr, stat);
stat->result_mask |= STATX_TYPE;

Measured before it was read, by setting IMMUTABLE | APPEND in a test filesystem
and calling statx(2) against it:

stx_attributes=0x0  stx_attributes_mask=0x203000  stx_mask=0x1fff  btime=1000000000

The creation time arrives; the attributes do not, and stx_attributes_mask holds
only the VFS's own MOUNT_ROOT | AUTOMOUNT | DAX. Since btime sits at offset 80
of the struct and size at 40, and both arrive intact, this is the kernel
discarding the field rather than a layout mistake on this side.

The field is kept and sent regardless: a filesystem that fills it is correct today
and needs no change if the kernel starts reading it. But it is documented as
inert on the field itself, in the CHANGELOG, and pinned by the test, so nobody
implements it expecting chattr +i to show up.

Testing

  • Two wire-format tests over a byte-exact FUSE_STATX request, for both
    endiannesses, covering the file handle being present and absent.
  • An integration test that mounts a filesystem and calls statx(2) for real,
    asserting the creation time survives the round trip and that the request
    reaches statx() rather than falling back to getattr(). It also asserts the
    attributes are dropped, so a kernel that starts honoring them fails here rather
    than silently outdating the documentation. Skipped below 7.38, as the tmpfile
    test is below 7.37.
  • A unit test for the device-number split, checked against /dev/null and
    /dev/fuse as stat reports them plus a minor number too large for its low
    byte, since struct statx wants a major/minor pair where fuse_attr carries
    the encoded number.

cargo test --all is 104 passed, 1 failed - mnt::test::mount_unmount_auto_unmount,
which fails the same way on unmodified master here for want of a fusermount3
binary. Clippy clean with --deny warnings, and
cargo check --target x86_64-apple-darwin --features=macos-no-mount clean, which
caught two things: a version constant that is only needed by the tests, and
nix::sys::stat::major/minor not existing on macOS - replaced with the
kernel's own new_decode_dev() split.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Q4hiZwHE9fEYdn7DK3ZrV2


Generated by Claude Code

The kernel sends FUSE_STATX for statx(2) once the connection is at ABI 7.38 or
above, falling back to FUSE_GETATTR when the filesystem answers ENOSYS - which it
takes as permanent, as it does for the other opcodes added this way.

What the opcode is worth implementing for is the creation time. fuse_attr has a
field for it on macOS alone, so on Linux there has been no way for a filesystem
to report one, and statx(2) answers with whatever the kernel last cached.
StatxAttr carries it as an Option, and the reply's mask is derived from what was
actually filled in rather than supplied by the caller, so it cannot claim a field
that is not there.

StatxAttr also carries the STATX_ATTR_* properties - immutable, append-only and
the rest - because the wire format has a field for them. The kernel does not use
it: fuse_do_statx() copies the mask, the creation time and the basic stats out of
the reply and ignores the attributes, so setting them does not make chattr +i
visible to statx(2). That is worth having anyway, since a filesystem that fills
the field is correct today and needs no change if the kernel starts reading it,
but it is documented on the field, in the CHANGELOG, and pinned by the test, so
that nobody implements it expecting otherwise.

The integration test goes through statx(2) itself rather than asserting against
the encoding, which is what showed the attributes being dropped. It also asserts
the drop, so a kernel that starts honoring the field fails the test rather than
silently outdating the documentation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4hiZwHE9fEYdn7DK3ZrV2
@cberner
cberner force-pushed the claude/xfstests-skip-investigation-s9gdr1 branch from 99a94fe to 2b64265 Compare August 5, 2026 01:38
@cberner
cberner merged commit e039dae into master Aug 5, 2026
9 checks passed
@cberner
cberner deleted the claude/xfstests-skip-investigation-s9gdr1 branch August 5, 2026 03:54
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.

2 participants