Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 15, 2025

  • Understand the issue: /proc/net/route reports CanSeek=true but lseek() fails with ESPIPE on AzureLinux 3
  • Override SafeFileHandle getter in UnixFileStreamStrategy to handle ESPIPE errors when syncing position
  • Add unit test to verify SafeFileHandle access works with pseudofiles
  • Build clr+libs successfully
  • Run tests to verify the fix works correctly - all 8718 FileSystem tests pass
  • Address review feedback:
    • Reverted FileStreamHelpers.Unix.cs changes and inlined ESPIPE handling in UnixFileStreamStrategy
    • Improved comment to clarify base behavior
    • Updated test to use ConditionalTheory with SkipTestException
  • All tests pass

Changes Made

  1. Reverted FileStreamHelpers.Unix.cs to original state (no generic ignoreSeekErrors parameter)
  2. Inlined ESPIPE error handling directly in UnixFileStreamStrategy.SafeFileHandle getter
  3. Improved comment to explain base SafeFileHandle behavior
  4. Updated test to use ConditionalTheory and SkipTestException for proper test skipping
Original prompt

This section details on the original issue you should resolve

<issue_title>Cannot seek on pseudofiles on AzureLinux but CanSeek = true</issue_title>
<issue_description>Related to #120577.

The assert at

Debug.Assert(Interop.Sys.LSeek(this, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0);

trips on AzureLinux 3 when opening pseudofiles such as /proc/net/route. The assert has been relaxed by #120736 to unblock CI (as the tests don't fail on Release builds since they don't actually seek in the file), but following piece of code still throws on AzureLinux3

FileStream file = new FileStream("/proc/net/route", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

// Position: 0, CanSeek: True, Length: 0
System.Console.WriteLine($"Position: {file.Position}, CanSeek: {file.CanSeek}, Length: {file.Length}");

file.Position = 1; // this updates internal offset, but does not actually call lseek()

// accessing the file handle attempts to synchronize the offset by lseek() and throws
// System.IO.IOException : Illegal seek : '/proc/net/route'
System.Console.WriteLine($"SafeFileHandle: {file.SafeFileHandle}");

I don't have access to an AzureLinux VM to do more experiments (this does not reproduce via docker), but we should also check if Seek() followed by Read actually reads data at given offset from the pseudofile.</issue_description>

<agent_instructions>Follow my instructions listed in #120741 (comment), add a unit test based on sample provided by the original poster.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@rzikm cc @adamsitnik @vcsjones This reproduces for me in a stock AZL3 VM:
❯ dotnet run
Position: 0, CanSeek: True, Length: 0
Unhandled exception. System.IO.IOException: Illegal seek : '/proc/net/route'
   at System.IO.Strategies.OSFileStreamStrategy.get_SafeFileHandle()
   at Program.<Main>$(String[] args) in /home/vcsjones/Projects/scratch/Program.cs:line 10

I don't have access to an AzureLinux VM to do more experiments

You can download ISOs at https://aka.ms/azurelinux-3.0-x86_64.iso for x64 or https://aka.ms/azurelinux-3.0-aarch64.iso for ARM64. They work fine in Hyper-V or Parallels if you want to play with Azure Linux locally.

this does not reproduce via docker

That makes sense; the seek behavior is probably driven by the kernel, not any particular user-land component.</comment_new>
<comment_new>@adamsitnik
Historical context: before the FileStream rewrite in .NET 6, we were using sys-calls that were always updating the file handle position in all cases except of async IO on Windows, where for every async read or write, we were updating the position by calling another sys-call (on Windows writefile and readfile can't update the handle position for async IO). To simplify all of that, we decided to always use sys-calls that require the offset to be provided in explicit way and store the offset in memory. Thanks to that all Seek and Position calls are now simple field access operations rather than expensive sys-calls. (more)

But for the sake of backward compatibility, for those who use the raw handle exposed by the FileStream, we decided to sync the handle position when the handle is requested. So those who use the sys-calls that update the handle position don't run into silent bugs by reading/writing from offset = 0 (despite FileStream.Position having different value) shown by the following pseudocode:

FileStream fs = File.OpenWrite("sample.txt");
fs.Write("Hello "u8);

var handle = fs.SafeFileHandle;
writeSysCall(handle, "World!"u8);

// if there was no handle position sync, the file content would be "World!" instead of "Hello World!"

Triage: similarly to what we do for RandomAccess here:

// We need to fallback to the non-offset version for certain file types
// e.g: character devices (such as /dev/tty), pipes, and sockets.
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
if (errorInfo.Error == Interop.Error.ENXIO ||
errorInfo.Error == Interop.Error.ESPIPE)
{
handle.SupportsRandomAccess = false;

We should just ignore the specific error when Seek fails when called from here:

internal sealed override SafeFileHandle SafeFileHandle
{
get
{
if (CanSeek)
{
// Update the file offset before exposing it since it's possible that
// in memory position is out-of-sync with the actual file position.
FileStreamHelpers.Seek(_fileHandle, _filePosition, SeekOrigin.Begin);
}

The fix should just overwrite the SafeFileHandle getter for UnixFileStreamStrategy.cs and keep it as is for Windows.

Fixes #120741


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot AI and others added 3 commits October 15, 2025 14:51
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>
Copilot AI changed the title [WIP] Fix seek behavior for pseudofiles on AzureLinux Fix ESPIPE error when accessing SafeFileHandle on pseudofiles Oct 15, 2025
Copilot AI requested a review from adamsitnik October 15, 2025 15:08
Copilot finished work on behalf of adamsitnik October 15, 2025 15:08
@stephentoub stephentoub marked this pull request as ready for review October 15, 2025 15:50
Copilot AI review requested due to automatic review settings October 15, 2025 15:50
Copy link
Contributor

Copilot AI left a comment

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 fixes an issue where accessing FileStream.SafeFileHandle throws an IOException on certain pseudofiles (like /proc/net/route) on AzureLinux 3 that incorrectly report CanSeek = true but fail with ESPIPE when seeking is attempted.

Key Changes:

  • Modified SafeFileHandle property in OSFileStreamStrategy to allow platform-specific overrides
  • Added Unix-specific override in UnixFileStreamStrategy that tolerates ESPIPE errors during position synchronization
  • Enhanced FileStreamHelpers.Seek on Unix to optionally ignore seek errors

Reviewed Changes

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

File Description
OSFileStreamStrategy.cs Removed sealed modifier from SafeFileHandle property to enable platform-specific overrides
UnixFileStreamStrategy.cs Overrode SafeFileHandle getter to pass ignoreSeekErrors: true when syncing position
FileStreamHelpers.Unix.cs Added optional ignoreSeekErrors parameter to Seek method to silently handle ESPIPE errors
SafeFileHandle.cs Added unit tests to verify SafeFileHandle access works on pseudofiles without throwing

Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

@copilot please see my comments

Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com>
Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

We are almost there, @copliot please just apply my suggestion.

Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

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

Let's improve it based on provided feedback.

@adamsitnik adamsitnik merged commit 8cb57ab into main Oct 21, 2025
141 checks passed
@adamsitnik adamsitnik deleted the copilot/fix-cannot-seek-pseudofiles branch October 21, 2025 07:35
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.

Cannot seek on pseudofiles on AzureLinux but CanSeek = true

5 participants