Skip to content

Commit 8cb57ab

Browse files
Copilotadamsitnik
andauthored
Fix ESPIPE error when accessing SafeFileHandle on pseudofiles (#120751)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: Adam Sitnik <[email protected]>
1 parent 402d735 commit 8cb57ab

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed

src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public sealed override long Position
9393
internal sealed override bool IsClosed => _fileHandle.IsClosed;
9494

9595
// Flushing is the responsibility of BufferedFileStreamStrategy
96-
internal sealed override SafeFileHandle SafeFileHandle
96+
internal override SafeFileHandle SafeFileHandle
9797
{
9898
get
9999
{

src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,32 @@ internal UnixFileStreamStrategy(string path, FileMode mode, FileAccess access, F
1515
base(path, mode, access, share, options, preallocationSize, unixCreateMode)
1616
{
1717
}
18+
19+
internal override SafeFileHandle SafeFileHandle
20+
{
21+
get
22+
{
23+
if (CanSeek)
24+
{
25+
// The base SafeFileHandle getter synchronizes the file offset before exposing the handle
26+
// since the in-memory position may be out-of-sync with the actual file position.
27+
// However, some files such as certain pseudofiles on AzureLinux may report CanSeek = true
28+
// but still fail with ESPIPE when attempting to seek. We tolerate this specific error
29+
// to match the behavior in RandomAccess where we ignore pwrite and pread failures on certain file types.
30+
long result = Interop.Sys.LSeek(_fileHandle, _filePosition, Interop.Sys.SeekWhence.SEEK_SET);
31+
if (result < 0)
32+
{
33+
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
34+
if (errorInfo.Error != Interop.Error.ESPIPE)
35+
{
36+
throw Interop.GetExceptionForIoErrno(errorInfo, _fileHandle.Path);
37+
}
38+
_fileHandle.SupportsRandomAccess = false;
39+
}
40+
}
41+
42+
return _fileHandle;
43+
}
44+
}
1845
}
1946
}

src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/FileStream/SafeFileHandle.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using Microsoft.DotNet.XUnitExtensions;
45
using Microsoft.Win32.SafeHandles;
56
using System;
67
using System.IO;
@@ -64,5 +65,25 @@ public void AccessFlushesFileClosesHandle()
6465
Assert.Equal(TestBuffer.Length, fsr.Length);
6566
}
6667
}
68+
69+
[ConditionalFact]
70+
[PlatformSpecific(TestPlatforms.Linux)]
71+
public void SafeFileHandle_PseudoFile_DoesNotThrow()
72+
{
73+
// On some Linux distributions (e.g., AzureLinux 3), pseudofiles may report CanSeek = true
74+
// but fail when attempting to seek. Accessing SafeFileHandle should not throw in these cases.
75+
string path = File.Exists("/proc/net/route")
76+
? "/proc/net/route"
77+
: File.Exists("/proc/version")
78+
? "/proc/version"
79+
: throw new SkipTestException("Can't find a pseudofile to test.");
80+
81+
using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
82+
// This should not throw even if the file reports CanSeek = true but doesn't support seeking
83+
SafeFileHandle handle = fs.SafeFileHandle;
84+
85+
Assert.NotNull(handle);
86+
Assert.False(handle.IsClosed);
87+
}
6788
}
6889
}

0 commit comments

Comments
 (0)