Skip to content

Use statx syscall when available - #132144

Open
adamsitnik with Copilot wants to merge 11 commits into
mainfrom
copilot/fix-runtime-issue-131395
Open

Use statx syscall when available#132144
adamsitnik with Copilot wants to merge 11 commits into
mainfrom
copilot/fix-runtime-issue-131395

Conversation

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Pull request created by AI Agent

created to investigate what it would take to fix #131395 without updating minimum required libc version

Copilot AI and others added 3 commits August 11, 2026 14:12
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.
@adamsitnik adamsitnik changed the title Ongoing task refinement and development Aug 11, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Unix PAL to use the Linux statx syscall (when available) to surface file birth/creation time, and adjusts System.IO filesystem tests to account for platforms/filesystems where birth time is (or isn’t) reported.

Changes:

  • Add a Linux statx syscall path in SystemNative_{Stat,FStat,LStat} to populate FileStatus.BirthTime* when supported by the kernel + filesystem.
  • Add a runtime probe (IOInputs.SupportsBirthTime) to detect whether birth time is actually observable on the current Linux environment.
  • Split/guard Linux creation-time tests based on whether birth time is supported.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/native/libs/System.Native/pal_io.c Adds statx syscall support to retrieve and expose birth time on Linux with fallback to stat/fstat/lstat.
src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/PortedCommon/IOInputs.cs Adds a Linux probe to determine whether birth time is observable for conditional test gating.
src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/GetSetTimes.cs Updates/extends Linux creation-time tests to cover both “birth time unavailable” and “birth time available” behaviors.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-io
See info in area-owners.md if you want to be subscribed.

@adamsitnik adamsitnik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel confident about merging this change for RC1/2. I would prefer to just wait for .NET 12 and bump the minimum required glibc version and use statx and the structures provided by libc.

Comment thread src/native/libs/System.Native/pal_io.c Outdated
Comment thread src/native/libs/System.Native/pal_io.c Outdated

@adamsitnik adamsitnik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot please address the feedback

Comment thread src/native/libs/System.Native/pal_io.c Outdated
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 11, 2026 17:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review any files in this pull request.

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please address the feedback

Addressed by reverting the statx experiment and associated test changes in a45ccf2; this is deferred to the libc-backed .NET 12 approach.

@adamsitnik

Copy link
Copy Markdown
Member

Addressed by reverting the statx experiment and associated test changes in a45ccf2; this is deferred to the libc-backed .NET 12 approach.

@copilot revert the revert NOW and just add the right include and use the statx structures defined in linux headers. This is what I meant by asking you to address #131395 (comment).

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 11, 2026 18:18

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Addressed by reverting the statx experiment and associated test changes in a45ccf2; this is deferred to the libc-...

Reverted the revert in 30d6acc. The implementation now includes <linux/stat.h> and uses its struct statx and STATX_* definitions; it no longer defines the statx layout or syscall numbers locally.

@adamsitnik adamsitnik added this to the 12.0.0 milestone Aug 19, 2026
Comment thread src/native/libs/System.Native/pal_io.c Outdated
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 26, 2026 11:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/GetSetTimes.cs:87

  • This test assumes the underlying Linux filesystem reports a birth time (so that changing LastWriteTime won’t affect CreationTime). On some Linux environments (e.g., filesystems/layers that don’t expose btime), CreationTimeUtc will legitimately fall back to the oldest of ctime/mtime, and this assertion can fail even when the implementation is correct. Consider making this a ConditionalFact gated on whether Interop.Sys.Stat reports HasBirthTime for a newly-created file, and skip otherwise.
        [Fact]
        [PlatformSpecific(TestPlatforms.Linux)]
        public void CreationTimeGet_ReturnsBirthTime_WhenAvailable()
        {
            // When the birth time is available, it is returned as the CreationTime,
            // independently of the write time.

            DateTime beforeCreationUtc = DateTime.UtcNow.AddMinutes(-1);
            string path = GetExistingItem();
            SetLastWriteTimeUtc(path, DateTime.UtcNow.AddMinutes(-30));

            Assert.InRange(GetCreationTimeUtc(path), beforeCreationUtc, DateTime.UtcNow.AddMinutes(1));
        }
Comment thread src/native/libs/System.Native/pal_io.c
Comment thread src/native/libs/System.Native/pal_io.c

@adamsitnik adamsitnik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot address feedback

Comment thread src/native/libs/System.Native/pal_io.c
Comment thread src/native/libs/System.Native/pal_io.c
Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 27, 2026 13:42

Copilot AI commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

@copilot address feedback

Addressed in bc6ce5f - documented in pal_io.c why no per-architecture __NR_statx definition or run-time fallback is required (the syscall exists since Linux 4.11). Details in the review thread reply. The remaining runtime pipeline failure is infra-only ("Monitor Helix Jobs"), not a compile error; all native build legs succeeded.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/GetSetTimes.cs:79

  • This test is named "WhenAvailable" but it runs unconditionally on Linux. On file systems that don’t report STATX_BTIME, CreationTime is synthesized from ctime/mtime, and after forcing mtime 30 minutes into the past this assertion will fail. Make the test a ConditionalFact and skip when birth time isn’t actually reported by the underlying file system.
        [Fact]
        [PlatformSpecific(TestPlatforms.Linux)]
        public void CreationTimeGet_ReturnsBirthTime_WhenAvailable()
        {
            // When the birth time is available, it is returned as the CreationTime,

src/native/libs/System.Native/pal_io.c:286

  • TryStatxFileStatus currently treats any statx failure as fatal. If statx is blocked (e.g., by seccomp) or the requested flags aren’t accepted and the syscall fails with EPERM/EINVAL, we’ll return -1 and won’t fall back to stat(2)/fstat(2)/lstat(2), potentially breaking file operations that previously worked. Treat these errors as a soft failure and return 0 so the caller can fall back.
    return -1;

@adamsitnik adamsitnik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Behavioral contract — SetCreationTime no longer round-trips on btime-capable Linux filesystems

With btime now reported, Linux File.SetCreationTime* still mutates mtime via the existing Unix fallback, but GetCreationTime* will return the real birth time instead of the mtime that was just written. That means the old Linux round-trip behavior disappears on btime-capable filesystems while the call still changes LastWriteTime as a side effect.

This may be an acceptable consequence of exposing the real birth time, especially since Linux has no general API for setting btime. I would still have a human reviewer explicitly confirm the desired behavior and whether docs/tests should acknowledge it.

@copilot what is going to happen when the user calls File.SetCreationTime* on Linux using file system that supports btime? Is the updateCreationTime going to be true:

bool updateCreationTime = checkCreationTime && (_fileCache.Flags & Interop.Sys.FileStatusFlags.HasBirthTime) != 0 &&
(buf[1].TvSec < _fileCache.BirthTime || (buf[1].TvSec == _fileCache.BirthTime && buf[1].TvNsec < _fileCache.BirthTimeNsec));
InvalidateCaches();
if (updateCreationTime)
{
Interop.Error error = SetCreationTimeCore(handle, path, _fileCache.BirthTime, _fileCache.BirthTimeNsec);

and then throw InvalidOperationException here:

private static Interop.Error SetCreationTimeCore(SafeFileHandle? handle, string? path, long seconds, long nanoseconds) =>
throw new InvalidOperationException();

We need to ensure that File.SetCreationTime* throws in such situations (and/or consider always throwing on Linux).

Copilot AI commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

💡 Behavioral contract — SetCreationTime no longer round-trips on btime-capable Linux filesystems

With btime now reported, Linux File.SetCreationTime* still mutates mtime via the exis...

Short answer: no, nothing throws today — updateCreationTime is always false on Linux, so SetCreationTimeCore (the throw new InvalidOperationException() stub) is unreachable.

Why: on non-Apple Unix, FileStatus.SetTimes.OtherUnix.cs routes SetCreationTimeSetLastWriteTimeSetAccessOrWriteTime, and that overload hard-codes checkCreationTime: false:

private void SetAccessOrWriteTime(SafeFileHandle? handle, string? path, DateTimeOffset time, bool isAccessTime, bool asDirectory) =>
    SetAccessOrWriteTimeCore(handle, path, time, isAccessTime, checkCreationTime: false, asDirectory);

and updateCreationTime = checkCreationTime && ... short-circuits on that first term (FileStatus.Unix.cs:424). checkCreationTime: true is only passed from the Apple-specific file. So the HasBirthTime flag that this PR starts setting on Linux does not reach that path at all.

So with this PR the observable Linux behavior is: SetCreationTime* still silently rewrites mtime (and bumps ctime), and a subsequent GetCreationTime* returns the real btime instead of what was just written — i.e. a silent no-op with an mtime side effect, which is exactly the contract problem you're pointing at.

On making it throw — I'd like your call before I implement it, because it's a behavioral break that reaches beyond this PR:

  1. Always throw on Linux (deterministic, my preference of the two): File/Directory/FileInfo/DirectoryInfo SetCreationTime* would throw PlatformNotSupportedException. But FileStatus.SetTimes.OtherUnix.cs is compiled for all non-Apple Unix (Linux, FreeBSD, Android, Haiku, wasm), so it needs an explicit Linux-only gate, and it breaks existing in-tree callers/tests, e.g. WriteFileContext.DuplicateFileAttributes in System.Configuration.ConfigurationManager (File.SetCreationTimeUtc on a copied file) and FileSystemWatcher_File_NotifyFilter_CreationTime, which uses File.SetCreationTime on Linux to trigger the notification. Any cross-platform "copy metadata" code in the ecosystem would start throwing.
  2. Throw only when btime is available: avoids some of the fallout, but makes the behavior filesystem-dependent (ext4/btrfs throw, an NFS/overlay mount without btime doesn't), which I think is worse than either extreme.

Both options need a breaking-change doc, and given the "one concern per PR" guidance it may be cleaner as a follow-up so this PR stays a pure statx change. Tell me which you want (and whether here or in a follow-up) and I'll do it.

Note

This comment was generated by GitHub Copilot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

4 participants