Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,27 @@ internal static long CheckFileCall(long result, string? path, bool ignoreNotSupp
return result;
}

internal static long Seek(SafeFileHandle handle, long offset, SeekOrigin origin) =>
CheckFileCall(Interop.Sys.LSeek(handle, offset, (Interop.Sys.SeekWhence)(int)origin), handle.Path); // SeekOrigin values are the same as Interop.libc.SeekWhence values
internal static long Seek(SafeFileHandle handle, long offset, SeekOrigin origin, bool ignoreSeekErrors = false)
{
long result = Interop.Sys.LSeek(handle, offset, (Interop.Sys.SeekWhence)(int)origin); // SeekOrigin values are the same as Interop.libc.SeekWhence values
if (result < 0)
{
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();

// Some files, such as certain pseudofiles on AzureLinux, may report CanSeek = true
// but still fail with ESPIPE when attempting to seek. When ignoreSeekErrors is true,
// we ignore this specific error to match the behavior in RandomAccess where we tolerate seek failures.
// The return value in this case is not meaningful and should not be used by the caller.
if (ignoreSeekErrors && errorInfo.Error == Interop.Error.ESPIPE)
{
return 0; // Return value is not used when ignoreSeekErrors is true
}

throw Interop.GetExceptionForIoErrno(errorInfo, handle.Path);
}

return result;
}

internal static void ThrowInvalidArgument(SafeFileHandle handle) =>
throw Interop.GetExceptionForIoErrno(new Interop.ErrorInfo(Interop.Error.EINVAL), handle.Path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public sealed override long Position
internal sealed override bool IsClosed => _fileHandle.IsClosed;

// Flushing is the responsibility of BufferedFileStreamStrategy
internal sealed override SafeFileHandle SafeFileHandle
internal override SafeFileHandle SafeFileHandle
{
get
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,23 @@ internal UnixFileStreamStrategy(string path, FileMode mode, FileAccess access, F
base(path, mode, access, share, options, preallocationSize, unixCreateMode)
{
}

internal 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.
// Some files, such as certain pseudofiles on AzureLinux, may report CanSeek = true
// but still fail with ESPIPE when attempting to seek. We ignore this specific error
// to match the behavior in RandomAccess where we tolerate seek failures.
FileStreamHelpers.Seek(_fileHandle, _filePosition, SeekOrigin.Begin, ignoreSeekErrors: true);
}

return _fileHandle;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,27 @@ public void AccessFlushesFileClosesHandle()
Assert.Equal(TestBuffer.Length, fsr.Length);
}
}

[Theory]
[InlineData("/proc/net/route")]
[InlineData("/proc/version")]
[PlatformSpecific(TestPlatforms.Linux)]
public void SafeFileHandle_PseudoFile_DoesNotThrow(string path)
{
// On some Linux distributions (e.g., AzureLinux 3), pseudofiles may report CanSeek = true
// but fail when attempting to seek. Accessing SafeFileHandle should not throw in these cases.
if (!File.Exists(path))
{
return; // Skip if the file doesn't exist
}

using FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

// This should not throw even if the file reports CanSeek = true but doesn't support seeking
SafeFileHandle handle = fs.SafeFileHandle;

Assert.NotNull(handle);
Assert.False(handle.IsClosed);
}
}
}
Loading