diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs index 1f24874266c454..0ed531d7185f81 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/File.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/File.cs @@ -379,7 +379,15 @@ public static void WriteAllBytes(string path, byte[] bytes) fs.Write(bytes, 0, bytes.Length); #else using SafeFileHandle sfh = OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read); - RandomAccess.WriteAtOffset(sfh, bytes, 0); + + long offset = 0; + Span buffer = bytes; + do + { + int bytesWritten = RandomAccess.WriteAtOffset(sfh, buffer, offset); + offset += bytesWritten; + buffer = buffer.Slice(bytesWritten); + } while (!buffer.IsEmpty); #endif } public static string[] ReadAllLines(string path) @@ -840,7 +848,15 @@ static async Task Core(string path, byte[] bytes, CancellationToken cancellation await fs.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false); #else using SafeFileHandle sfh = OpenHandle(path, FileMode.Create, FileAccess.Write, FileShare.Read, FileOptions.Asynchronous | FileOptions.SequentialScan); - await RandomAccess.WriteAtOffsetAsync(sfh, bytes, 0, cancellationToken).ConfigureAwait(false); + + long offset = 0; + Memory buffer = bytes; + do + { + int bytesWritten = await RandomAccess.WriteAtOffsetAsync(sfh, buffer, offset, cancellationToken).ConfigureAwait(false); + offset += bytesWritten; + buffer = buffer.Slice(bytesWritten); + } while (!buffer.IsEmpty); #endif } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs index c83b58921c4e64..3f78bfbf6d177f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs @@ -64,27 +64,22 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => WriteAsyncInternal(buffer, cancellationToken); - private unsafe ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) + private async ValueTask WriteAsyncInternal(ReadOnlyMemory source, CancellationToken cancellationToken) { if (!CanWrite) { ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } - long positionBefore = _filePosition; - if (CanSeek) + do { - // When using overlapped IO, the OS is not supposed to - // touch the file pointer location at all. We will adjust it - // ourselves, but only in memory. This isn't threadsafe. - _filePosition += source.Length; - UpdateLengthOnChangePosition(); - } + int bytesWritten = await RandomAccess.WriteAtOffsetAsync(_fileHandle, source, _filePosition, cancellationToken).ConfigureAwait(false); + _filePosition += bytesWritten; + source = source.Slice(bytesWritten); - (SafeFileHandle.OverlappedValueTaskSource? vts, int errorCode) = RandomAccess.QueueAsyncWriteFile(_fileHandle, source, positionBefore, cancellationToken); - return vts != null - ? new ValueTask(vts, vts.Version) - : (errorCode == 0) ? ValueTask.CompletedTask : ValueTask.FromException(HandleIOError(positionBefore, errorCode)); + // we update it now, because if the write was incomplete, the next try might fail with an exception + UpdateLengthOnChangePosition(); + } while (!source.IsEmpty); } private Exception HandleIOError(long positionBefore, int errorCode) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs index b49b8ce2f33f8d..2ebf59501bb333 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/OSFileStreamStrategy.cs @@ -290,11 +290,18 @@ public sealed override void Write(ReadOnlySpan buffer) ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } - int r = RandomAccess.WriteAtOffset(_fileHandle, buffer, _filePosition); - Debug.Assert(r >= 0, $"RandomAccess.WriteAtOffset returned {r}."); - _filePosition += r; + do + { + int bytesWritten = RandomAccess.WriteAtOffset(_fileHandle, buffer, _filePosition); + Debug.Assert(bytesWritten >= 0, $"RandomAccess.WriteAtOffset returned {bytesWritten}."); + + _filePosition += bytesWritten; + buffer = buffer.Slice(bytesWritten); + + // we update it now, because if the write was incomplete, the next try might fail with an exception + UpdateLengthOnChangePosition(); + } while (!buffer.IsEmpty); - UpdateLengthOnChangePosition(); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs index c88665bf0b8d8f..3a2bd3937f27f0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/UnixFileStreamStrategy.cs @@ -58,28 +58,21 @@ public override void EndWrite(IAsyncResult asyncResult) => TaskToApm.End(asyncResult); public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => - WriteAsyncCore(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); + WriteAsync(new ReadOnlyMemory(buffer, offset, count), cancellationToken).AsTask(); - public override ValueTask WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken) => -#pragma warning disable CA2012 // The analyzer doesn't know the internal AsValueTask is safe. - WriteAsyncCore(source, cancellationToken).AsValueTask(); -#pragma warning restore CA2012 - - private ValueTask WriteAsyncCore(ReadOnlyMemory source, CancellationToken cancellationToken) + public override ValueTask WriteAsync(ReadOnlyMemory source, CancellationToken cancellationToken) { if (!CanWrite) { ThrowHelper.ThrowNotSupportedException_UnwritableStream(); } - long filePositionBefore = -1; - if (CanSeek) + do { - filePositionBefore = _filePosition; - _filePosition += source.Length; - } - - return RandomAccess.WriteAtOffsetAsync(_fileHandle, source, filePositionBefore, cancellationToken); + int bytesWritten = await RandomAccess.WriteAtOffsetAsync(_fileHandle, source, _filePosition, cancellationToken).ConfigureAwait(false); + _filePosition += bytesWritten; + source = source.Slice(bytesWritten); + } while (!source.IsEmpty); } /// Provides a reusable ValueTask-backing object for implementing ReadAsync.