diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadV.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadV.cs
new file mode 100644
index 00000000000000..10d21b40d22c20
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.ReadV.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadV", SetLastError = true)]
+ internal static unsafe partial long ReadV(SafeHandle fd, IOVector* vectors, int vectorCount);
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.WriteV.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.WriteV.cs
new file mode 100644
index 00000000000000..e890545f14ba39
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.WriteV.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_WriteV", SetLastError = true)]
+ internal static unsafe partial long WriteV(SafeHandle fd, IOVector* vectors, int vectorCount);
+ }
+}
diff --git a/src/libraries/System.Console/src/System.Console.csproj b/src/libraries/System.Console/src/System.Console.csproj
index 341c9947b9c829..34c35a2f62cd64 100644
--- a/src/libraries/System.Console/src/System.Console.csproj
+++ b/src/libraries/System.Console/src/System.Console.csproj
@@ -81,16 +81,8 @@
Link="Common\Interop\Unix\Interop.StdinReady.cs" />
-
-
-
-
-
-
-
- buffer) =>
_useReadLine ?
ConsolePal.StdInReader.ReadLine(buffer) :
#endif
- ConsolePal.Read(_handle, buffer);
+ RandomAccess.Read(_handle, buffer, fileOffset: 0);
public override void Write(ReadOnlySpan buffer) =>
ConsolePal.WriteFromConsoleStream(_handle, buffer);
diff --git a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs
index 57d4ef11fd62e3..8845acada13e2a 100644
--- a/src/libraries/System.Console/src/System/ConsolePal.Unix.cs
+++ b/src/libraries/System.Console/src/System/ConsolePal.Unix.cs
@@ -939,20 +939,6 @@ private static unsafe void EnsureInitializedCore()
}
}
- /// Reads data from the file descriptor into the buffer.
- /// The file descriptor.
- /// The buffer to read into.
- /// The number of bytes read, or an exception if there's an error.
- private static unsafe int Read(SafeFileHandle fd, Span buffer)
- {
- fixed (byte* bufPtr = buffer)
- {
- int result = Interop.CheckIo(Interop.Sys.Read(fd, bufPtr, buffer.Length));
- Debug.Assert(result <= buffer.Length);
- return result;
- }
- }
-
internal static void WriteToTerminal(ReadOnlySpan buffer, SafeFileHandle? handle = null, bool mayChangeCursorPosition = true)
{
handle ??= s_terminalHandle;
@@ -978,65 +964,35 @@ internal static unsafe void WriteFromConsoleStream(SafeFileHandle fd, ReadOnlySp
/// The file descriptor.
/// The buffer from which to write data.
/// Writing this buffer may change the cursor position.
- private static unsafe void Write(SafeFileHandle fd, ReadOnlySpan buffer, bool mayChangeCursorPosition = true)
+ private static void Write(SafeFileHandle fd, ReadOnlySpan buffer, bool mayChangeCursorPosition = true)
{
- fixed (byte* p = buffer)
- {
- byte* bufPtr = p;
- int count = buffer.Length;
- while (count > 0)
- {
- int cursorVersion = mayChangeCursorPosition ? Volatile.Read(ref s_cursorVersion) : -1;
+ int cursorVersion = mayChangeCursorPosition ? Volatile.Read(ref s_cursorVersion) : -1;
- int bytesWritten = Interop.Sys.Write(fd, bufPtr, count);
- if (bytesWritten < 0)
- {
- Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
- if (errorInfo.Error == Interop.Error.EPIPE)
- {
- // Broken pipe... likely due to being redirected to a program
- // that ended, so simply pretend we were successful.
- return;
- }
- else if (errorInfo.Error == Interop.Error.EAGAIN) // aka EWOULDBLOCK
- {
- // May happen if the file handle is configured as non-blocking.
- // In that case, we need to wait to be able to write and then
- // try again. We poll, but don't actually care about the result,
- // only the blocking behavior, and thus ignore any poll errors
- // and loop around to do another write (which may correctly fail
- // if something else has gone wrong).
- Interop.Sys.Poll(fd, Interop.PollEvents.POLLOUT, Timeout.Infinite, out Interop.PollEvents triggered);
- continue;
- }
- else
- {
- // Something else... fail.
- throw Interop.GetExceptionForIoErrno(errorInfo);
- }
- }
- else
- {
- if (mayChangeCursorPosition)
- {
- UpdatedCachedCursorPosition(bufPtr, bytesWritten, cursorVersion);
- }
- }
+ try
+ {
+ RandomAccess.Write(fd, buffer, fileOffset: 0);
+ }
+ catch (IOException ex) when (Interop.Sys.ConvertErrorPlatformToPal(ex.HResult) == Interop.Error.EPIPE)
+ {
+ // Broken pipe... likely due to being redirected to a program
+ // that ended, so simply pretend we were successful.
+ return;
+ }
- count -= bytesWritten;
- bufPtr += bytesWritten;
- }
+ if (mayChangeCursorPosition)
+ {
+ UpdatedCachedCursorPosition(buffer, cursorVersion);
}
}
- private static unsafe void UpdatedCachedCursorPosition(byte* bufPtr, int count, int cursorVersion)
+ private static void UpdatedCachedCursorPosition(ReadOnlySpan buffer, int cursorVersion)
{
lock (Console.Out)
{
int left, top;
if (cursorVersion != s_cursorVersion || // the cursor was changed during the write by another operation
!TryGetCachedCursorPosition(out left, out top) || // we don't have a cursor position
- count > InteractiveBufferSize) // limit the amount of bytes we are willing to inspect
+ buffer.Length > InteractiveBufferSize) // limit the amount of bytes we are willing to inspect
{
InvalidateCachedCursorPosition();
return;
@@ -1044,9 +1000,8 @@ private static unsafe void UpdatedCachedCursorPosition(byte* bufPtr, int count,
GetWindowSize(out int width, out int height);
- for (int i = 0; i < count; i++)
+ foreach (byte c in buffer)
{
- byte c = bufPtr[i];
if (c < 127 && c >= 32) // ASCII/UTF-8 characters that take up a single position
{
left++;
diff --git a/src/libraries/System.Console/src/System/ConsolePal.Wasi.cs b/src/libraries/System.Console/src/System/ConsolePal.Wasi.cs
index 70aec828947c03..53d20868d0d028 100644
--- a/src/libraries/System.Console/src/System/ConsolePal.Wasi.cs
+++ b/src/libraries/System.Console/src/System/ConsolePal.Wasi.cs
@@ -244,20 +244,6 @@ internal static void EnsureConsoleInitialized()
{
}
- /// Reads data from the file descriptor into the buffer.
- /// The file descriptor.
- /// The buffer to read into.
- /// The number of bytes read, or an exception if there's an error.
- private static unsafe int Read(SafeFileHandle fd, Span buffer)
- {
- fixed (byte* bufPtr = buffer)
- {
- int result = Interop.CheckIo(Interop.Sys.Read(fd, bufPtr, buffer.Length));
- Debug.Assert(result <= buffer.Length);
- return result;
- }
- }
-
internal static unsafe void WriteFromConsoleStream(SafeFileHandle fd, ReadOnlySpan buffer)
{
EnsureConsoleInitialized();
@@ -271,45 +257,16 @@ internal static unsafe void WriteFromConsoleStream(SafeFileHandle fd, ReadOnlySp
/// Writes data from the buffer into the file descriptor.
/// The file descriptor.
/// The buffer from which to write data.
- private static unsafe void Write(SafeFileHandle fd, ReadOnlySpan buffer)
+ private static void Write(SafeFileHandle fd, ReadOnlySpan buffer)
{
- fixed (byte* p = buffer)
+ try
+ {
+ RandomAccess.Write(fd, buffer, fileOffset: 0);
+ }
+ catch (IOException ex) when (Interop.Sys.ConvertErrorPlatformToPal(ex.HResult) == Interop.Error.EPIPE)
{
- byte* bufPtr = p;
- int count = buffer.Length;
- while (count > 0)
- {
- int bytesWritten = Interop.Sys.Write(fd, bufPtr, count);
- if (bytesWritten < 0)
- {
- Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
- if (errorInfo.Error == Interop.Error.EPIPE)
- {
- // Broken pipe... likely due to being redirected to a program
- // that ended, so simply pretend we were successful.
- return;
- }
- else if (errorInfo.Error == Interop.Error.EAGAIN) // aka EWOULDBLOCK
- {
- // May happen if the file handle is configured as non-blocking.
- // In that case, we need to wait to be able to write and then
- // try again. We poll, but don't actually care about the result,
- // only the blocking behavior, and thus ignore any poll errors
- // and loop around to do another write (which may correctly fail
- // if something else has gone wrong).
- Interop.Sys.Poll(fd, Interop.PollEvents.POLLOUT, Timeout.Infinite, out Interop.PollEvents triggered);
- continue;
- }
- else
- {
- // Something else... fail.
- throw Interop.GetExceptionForIoErrno(errorInfo);
- }
- }
-
- count -= bytesWritten;
- bufPtr += bytesWritten;
- }
+ // Broken pipe... likely due to being redirected to a program
+ // that ended, so simply pretend we were successful.
}
}
}
diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
index 8ef602113be272..22db2706974389 100644
--- a/src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
+++ b/src/libraries/System.Diagnostics.Process/tests/ProcessWaitingTests.cs
@@ -350,7 +350,7 @@ public void WaitForPeerProcess()
child2.StartInfo.RedirectStandardOutput = true;
child2.Start();
char[] output = new char[6];
- child2.StandardOutput.Read(output, 0, output.Length);
+ child2.StandardOutput.ReadBlock(output, 0, output.Length);
Assert.Equal("Signal", new string(output)); // wait for the signal before killing the peer
child1.Kill();
@@ -380,7 +380,7 @@ public async Task WaitAsyncForPeerProcess()
child2.StartInfo.RedirectStandardOutput = true;
child2.Start();
char[] output = new char[6];
- child2.StandardOutput.Read(output, 0, output.Length);
+ child2.StandardOutput.ReadBlock(output, 0, output.Length);
Assert.Equal("Signal", new string(output)); // wait for the signal before killing the peer
child1.Kill();
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index ece38286e289a0..7315408e420ca9 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -2558,6 +2558,9 @@
Common\Interop\Unix\System.Native\Interop.PRead.cs
+
+ Common\Interop\Unix\System.Native\Interop.ReadV.cs
+
Common\Interop\Unix\System.Native\Interop.PReadV.cs
@@ -2570,6 +2573,9 @@
Common\Interop\Unix\System.Native\Interop.Read.cs
+
+ Common\Interop\Unix\System.Native\Interop.WriteV.cs
+
Common\Interop\Unix\System.Native\Interop.ReadDir.cs
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs
index 1fb4140feac36c..cf2e56021b15a9 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs
@@ -29,7 +29,7 @@ internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span buffer
// The Windows implementation uses ReadFile, which ignores the offset if the handle
// isn't seekable. We do the same manually with PRead vs Read, in order to enable
// the function to be used by FileStream for all the same situations.
- int result;
+ int result = -1;
if (handle.IsAsync)
{
result = Interop.Sys.ReadFromNonblocking(handle, bufPtr, buffer.Length);
@@ -38,21 +38,14 @@ internal static unsafe int ReadAtOffset(SafeFileHandle handle, Span buffer
{
// Try pread for seekable files.
result = Interop.Sys.PRead(handle, bufPtr, buffer.Length, fileOffset);
- if (result == -1)
- {
- // 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;
- result = Interop.Sys.Read(handle, bufPtr, buffer.Length);
- }
+ if (result == -1 && ShouldFallBackToNonOffsetSyscall(Interop.Sys.GetLastErrorInfo()))
+ {
+ handle.SupportsRandomAccess = false; // Fall through to non-offset Read below.
}
}
- else
+
+ if (!handle.IsAsync && !handle.SupportsRandomAccess)
{
result = Interop.Sys.Read(handle, bufPtr, buffer.Length);
}
@@ -67,7 +60,7 @@ internal static unsafe long ReadScatterAtOffset(SafeFileHandle handle, IReadOnly
MemoryHandle[] handles = new MemoryHandle[buffers.Count];
Span vectors = buffers.Count <= IovStackThreshold ? stackalloc Interop.Sys.IOVector[IovStackThreshold] : new Interop.Sys.IOVector[buffers.Count];
- long result;
+ long result = -1;
try
{
int buffersCount = buffers.Count;
@@ -81,7 +74,20 @@ internal static unsafe long ReadScatterAtOffset(SafeFileHandle handle, IReadOnly
fixed (Interop.Sys.IOVector* pinnedVectors = &MemoryMarshal.GetReference(vectors))
{
- result = Interop.Sys.PReadV(handle, pinnedVectors, buffers.Count, fileOffset);
+ if (handle.SupportsRandomAccess)
+ {
+ result = Interop.Sys.PReadV(handle, pinnedVectors, buffers.Count, fileOffset);
+
+ if (result == -1 && ShouldFallBackToNonOffsetSyscall(Interop.Sys.GetLastErrorInfo()))
+ {
+ handle.SupportsRandomAccess = false; // Fall through to non-offset ReadV below.
+ }
+ }
+
+ if (!handle.SupportsRandomAccess)
+ {
+ result = Interop.Sys.ReadV(handle, pinnedVectors, buffers.Count);
+ }
}
}
finally
@@ -111,7 +117,7 @@ internal static unsafe void WriteAtOffset(SafeFileHandle handle, ReadOnlySpan 0)
{
- long bytesWritten;
+ long bytesWritten = -1;
Span left = vectors.Slice(buffersOffset);
fixed (Interop.Sys.IOVector* pinnedVectors = &MemoryMarshal.GetReference(left))
{
- bytesWritten = Interop.Sys.PWriteV(handle, pinnedVectors, left.Length, fileOffset);
+ if (handle.SupportsRandomAccess)
+ {
+ bytesWritten = Interop.Sys.PWriteV(handle, pinnedVectors, left.Length, fileOffset);
+
+ if (bytesWritten == -1 && ShouldFallBackToNonOffsetSyscall(Interop.Sys.GetLastErrorInfo()))
+ {
+ handle.SupportsRandomAccess = false; // Fall through to non-offset WriteV below.
+ }
+ }
+
+ if (!handle.SupportsRandomAccess)
+ {
+ bytesWritten = Interop.Sys.WriteV(handle, pinnedVectors, left.Length);
+ }
}
FileStreamHelpers.CheckFileCall(bytesWritten, handle.Path);
@@ -253,5 +265,12 @@ internal static ValueTask WriteAtOffsetAsync(SafeFileHandle handle, ReadOnlyMemo
private static ValueTask WriteGatherAtOffsetAsync(SafeFileHandle handle, IReadOnlyList> buffers, long fileOffset, CancellationToken cancellationToken)
=> handle.GetThreadPoolValueTaskSource().QueueWriteGather(buffers, fileOffset, cancellationToken);
+
+ ///
+ /// Checks the last error after a failed pread/pwrite/preadv/pwritev call
+ /// and returns true if the error indicates a non-seekable file type (ENXIO or ESPIPE).
+ ///
+ private static bool ShouldFallBackToNonOffsetSyscall(Interop.ErrorInfo lastError)
+ => lastError.Error == Interop.Error.ENXIO || lastError.Error == Interop.Error.ESPIPE;
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs
index 166d2faf3fbb14..154e8b4e5f8892 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.cs
@@ -22,7 +22,7 @@ public static partial class RandomAccess
/// The file does not support seeking (pipe or socket).
public static long GetLength(SafeFileHandle handle)
{
- ValidateInput(handle, fileOffset: 0);
+ ValidateInput(handle, fileOffset: 0, allowUnseekableHandles: false);
return handle.GetFileLength();
}
@@ -39,7 +39,7 @@ public static long GetLength(SafeFileHandle handle)
/// is negative.
public static void SetLength(SafeFileHandle handle, long length)
{
- ValidateInput(handle, fileOffset: 0);
+ ValidateInput(handle, fileOffset: 0, allowUnseekableHandles: false);
if (length < 0)
{
@@ -54,12 +54,12 @@ public static void SetLength(SafeFileHandle handle, long length)
///
/// The file handle.
/// A region of memory. When this method returns, the contents of this region are replaced by the bytes read from the file.
- /// The file position to read from.
+ /// The file position to read from. In .NET 11 and later versions, for a file that does not support seeking (pipe or socket), it's ignored.
/// The total number of bytes read into the buffer. This can be less than the number of bytes allocated in the buffer if that many bytes are not currently available, or zero (0) if the end of the file has been reached.
/// is .
/// is invalid.
/// The file is closed.
- /// The file does not support seeking (pipe or socket).
+ /// In .NET 10 and earlier versions, the file does not support seeking (pipe or socket).
/// is negative.
/// was not opened for reading.
/// An I/O error occurred.
@@ -76,12 +76,12 @@ public static int Read(SafeFileHandle handle, Span buffer, long fileOffset
///
/// The file handle.
/// A list of memory buffers. When this method returns, the contents of the buffers are replaced by the bytes read from the file.
- /// The file position to read from.
+ /// The file position to read from. In .NET 11 and later versions, for a file that does not support seeking (pipe or socket), it's ignored.
/// The total number of bytes read into the buffers. This can be less than the number of bytes allocated in the buffers if that many bytes are not currently available, or zero (0) if the end of the file has been reached.
/// or is .
/// is invalid.
/// The file is closed.
- /// The file does not support seeking (pipe or socket).
+ /// In .NET 10 and earlier versions, the file does not support seeking (pipe or socket).
/// is negative.
/// was not opened for reading.
/// An I/O error occurred.
@@ -99,13 +99,13 @@ public static long Read(SafeFileHandle handle, IReadOnlyList> buffe
///
/// The file handle.
/// A region of memory. When this method returns, the contents of this region are replaced by the bytes read from the file.
- /// The file position to read from.
+ /// The file position to read from. In .NET 11 and later versions, for a file that does not support seeking (pipe or socket), it's ignored.
/// The token to monitor for cancellation requests. The default value is .
/// The total number of bytes read into the buffer. This can be less than the number of bytes allocated in the buffer if that many bytes are not currently available, or zero (0) if the end of the file has been reached.
/// is .
/// is invalid.
/// The file is closed.
- /// The file does not support seeking (pipe or socket).
+ /// In .NET 10 and earlier versions, the file does not support seeking (pipe or socket).
/// is negative.
/// was not opened for reading.
/// An I/O error occurred.
@@ -127,13 +127,13 @@ public static ValueTask ReadAsync(SafeFileHandle handle, Memory buffe
///
/// The file handle.
/// A list of memory buffers. When this method returns, the contents of these buffers are replaced by the bytes read from the file.
- /// The file position to read from.
+ /// The file position to read from. In .NET 11 and later versions, for a file that does not support seeking (pipe or socket), it's ignored.
/// The token to monitor for cancellation requests. The default value is .
/// The total number of bytes read into the buffers. This can be less than the number of bytes allocated in the buffers if that many bytes are not currently available, or zero (0) if the end of the file has been reached.
/// or is .
/// is invalid.
/// The file is closed.
- /// The file does not support seeking (pipe or socket).
+ /// In .NET 10 and earlier versions, the file does not support seeking (pipe or socket).
/// is negative.
/// was not opened for reading.
/// An I/O error occurred.
@@ -156,11 +156,11 @@ public static ValueTask ReadAsync(SafeFileHandle handle, IReadOnlyList
/// The file handle.
/// A region of memory. This method copies the contents of this region to the file.
- /// The file position to write to.
+ /// The file position to write to. In .NET 11 and later versions, for a file that does not support seeking (pipe or socket), it's ignored.
/// is .
/// is invalid.
/// The file is closed.
- /// The file does not support seeking (pipe or socket).
+ /// In .NET 10 and earlier versions, the file does not support seeking (pipe or socket).
/// is negative.
/// was not opened for writing.
/// An I/O error occurred.
@@ -177,11 +177,11 @@ public static void Write(SafeFileHandle handle, ReadOnlySpan buffer, long
///
/// The file handle.
/// A list of memory buffers. This method copies the contents of these buffers to the file.
- /// The file position to write to.
+ /// The file position to write to. In .NET 11 and later versions, for a file that does not support seeking (pipe or socket), it's ignored.
/// or is .
/// is invalid.
/// The file is closed.
- /// The file does not support seeking (pipe or socket).
+ /// In .NET 10 and earlier versions, the file does not support seeking (pipe or socket).
/// is negative.
/// was not opened for writing.
/// An I/O error occurred.
@@ -199,13 +199,13 @@ public static void Write(SafeFileHandle handle, IReadOnlyList
/// The file handle.
/// A region of memory. This method copies the contents of this region to the file.
- /// The file position to write to.
+ /// The file position to write to. In .NET 11 and later versions, for a file that does not support seeking (pipe or socket), it's ignored.
/// The token to monitor for cancellation requests. The default value is .
/// A task representing the asynchronous completion of the write operation.
/// is .
/// is invalid.
/// The file is closed.
- /// The file does not support seeking (pipe or socket).
+ /// In .NET 10 and earlier versions, the file does not support seeking (pipe or socket).
/// is negative.
/// was not opened for writing.
/// An I/O error occurred.
@@ -227,13 +227,13 @@ public static ValueTask WriteAsync(SafeFileHandle handle, ReadOnlyMemory b
///
/// The file handle.
/// A list of memory buffers. This method copies the contents of these buffers to the file.
- /// The file position to write to.
+ /// The file position to write to. In .NET 11 and later versions, for a file that does not support seeking (pipe or socket), it's ignored.
/// The token to monitor for cancellation requests. The default value is .
/// A task representing the asynchronous completion of the write operation.
/// or is .
/// is invalid.
/// The file is closed.
- /// The file does not support seeking (pipe or socket).
+ /// In .NET 10 and earlier versions, the file does not support seeking (pipe or socket).
/// is negative.
/// was not opened for writing.
/// An I/O error occurred.
@@ -276,12 +276,12 @@ public static void FlushToDisk(SafeFileHandle handle)
// Unix does NOT support unseekable handles however, the code that ultimately runs on Unix when we
// call FileStreamHelpers.FlushToDisk() later below, will silently ignore those errors, effectively
// making FlushToDisk() a no-op on Unix when used with unseekable handles.
- ValidateInput(handle, fileOffset: 0, allowUnseekableHandles: true);
+ ValidateInput(handle, fileOffset: 0);
FileStreamHelpers.FlushToDisk(handle);
}
- private static void ValidateInput(SafeFileHandle handle, long fileOffset, bool allowUnseekableHandles = false)
+ private static void ValidateInput(SafeFileHandle handle, long fileOffset, bool allowUnseekableHandles = true)
{
if (handle is null)
{
@@ -291,18 +291,13 @@ private static void ValidateInput(SafeFileHandle handle, long fileOffset, bool a
{
ThrowHelper.ThrowArgumentException_InvalidHandle(nameof(handle));
}
- else if (!handle.CanSeek)
+ else if (handle.IsClosed)
{
- // CanSeek calls IsClosed, we don't want to call it twice for valid handles
- if (handle.IsClosed)
- {
- ThrowHelper.ThrowObjectDisposedException_FileClosed();
- }
-
- if (!allowUnseekableHandles)
- {
- ThrowHelper.ThrowNotSupportedException_UnseekableStream();
- }
+ ThrowHelper.ThrowObjectDisposedException_FileClosed();
+ }
+ else if (!allowUnseekableHandles && !handle.CanSeek)
+ {
+ ThrowHelper.ThrowNotSupportedException_UnseekableStream();
}
else if (fileOffset < 0)
{
diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/RandomAccess/Base.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/RandomAccess/Base.cs
index 9506d954d28374..3a0a23020d7bc8 100644
--- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/RandomAccess/Base.cs
+++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/RandomAccess/Base.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
-using System.IO.Pipes;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using Xunit;
@@ -15,8 +14,6 @@ public abstract class RandomAccess_Base : FileSystemTest
protected virtual bool UsesOffsets => true;
- protected virtual bool ThrowsForUnseekableFile => true;
-
public static IEnumerable