diff --git a/src/libraries/System.IO.FileSystem/tests/LegacyTests/runtimeconfig.template.json b/src/libraries/System.IO.FileSystem/tests/LegacyTests/runtimeconfig.template.json
index 010891ed8fc6c..0c1a3482aba4f 100644
--- a/src/libraries/System.IO.FileSystem/tests/LegacyTests/runtimeconfig.template.json
+++ b/src/libraries/System.IO.FileSystem/tests/LegacyTests/runtimeconfig.template.json
@@ -1,5 +1,5 @@
{
"configProperties": {
- "System.IO.UseLegacyFileStream": true
+ "System.IO.UseNet5CompatFileStream": false
}
}
diff --git a/src/libraries/System.IO/tests/LegacyTests/runtimeconfig.template.json b/src/libraries/System.IO/tests/LegacyTests/runtimeconfig.template.json
index 010891ed8fc6c..0c1a3482aba4f 100644
--- a/src/libraries/System.IO/tests/LegacyTests/runtimeconfig.template.json
+++ b/src/libraries/System.IO/tests/LegacyTests/runtimeconfig.template.json
@@ -1,5 +1,5 @@
{
"configProperties": {
- "System.IO.UseLegacyFileStream": true
+ "System.IO.UseNet5CompatFileStream": false
}
}
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 7aed1af79c2b2..44f01e2821e27 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
@@ -392,8 +392,6 @@
-
-
@@ -405,12 +403,10 @@
-
-
@@ -426,6 +422,11 @@
+
+
+
+
+
@@ -1635,17 +1636,17 @@
-
-
-
-
-
-
+
+
+
+
+
+
@@ -1846,13 +1847,13 @@
-
-
-
-
+
+
+
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs
index 20e5f73bb0efd..439f00c0c5c8f 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.IO.Strategies;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Threading;
@@ -46,7 +47,7 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS
{
ValidateHandle(safeHandle, access, bufferSize, isAsync);
- _strategy = WrapIfDerivedType(FileStreamHelpers.ChooseStrategy(safeHandle, access, bufferSize, isAsync));
+ _strategy = FileStreamHelpers.ChooseStrategy(this, safeHandle, access, bufferSize, isAsync);
}
catch
{
@@ -65,22 +66,27 @@ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferS
private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
{
if (handle.IsInvalid)
+ {
throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle));
-
- if (access < FileAccess.Read || access > FileAccess.ReadWrite)
+ }
+ else if (access < FileAccess.Read || access > FileAccess.ReadWrite)
+ {
throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum);
- if (bufferSize <= 0)
+ }
+ else if (bufferSize <= 0)
+ {
throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
-
- if (handle.IsClosed)
+ }
+ else if (handle.IsClosed)
+ {
ThrowHelper.ThrowObjectDisposedException_FileClosed();
- if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.GetValueOrDefault())
+ }
+ else if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.GetValueOrDefault())
+ {
throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle));
+ }
}
- private FileStreamStrategy WrapIfDerivedType(FileStreamStrategy impl)
- => GetType() == typeof(FileStream) ? impl : new DerivedFileStreamStrategy(this, impl);
-
public FileStream(SafeFileHandle handle, FileAccess access)
: this(handle, access, DefaultBufferSize)
{
@@ -95,56 +101,76 @@ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool
{
ValidateHandle(handle, access, bufferSize, isAsync);
- _strategy = WrapIfDerivedType(FileStreamHelpers.ChooseStrategy(handle, access, bufferSize, isAsync));
+ _strategy = FileStreamHelpers.ChooseStrategy(this, handle, access, bufferSize, isAsync);
}
- public FileStream(string path, FileMode mode) :
- this(path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, DefaultShare, DefaultBufferSize, DefaultIsAsync)
- { }
+ public FileStream(string path, FileMode mode)
+ : this(path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, DefaultShare, DefaultBufferSize, DefaultIsAsync)
+ {
+ }
- public FileStream(string path, FileMode mode, FileAccess access) :
- this(path, mode, access, DefaultShare, DefaultBufferSize, DefaultIsAsync)
- { }
+ public FileStream(string path, FileMode mode, FileAccess access)
+ : this(path, mode, access, DefaultShare, DefaultBufferSize, DefaultIsAsync)
+ {
+ }
- public FileStream(string path, FileMode mode, FileAccess access, FileShare share) :
- this(path, mode, access, share, DefaultBufferSize, DefaultIsAsync)
- { }
+ public FileStream(string path, FileMode mode, FileAccess access, FileShare share)
+ : this(path, mode, access, share, DefaultBufferSize, DefaultIsAsync)
+ {
+ }
- public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) :
- this(path, mode, access, share, bufferSize, DefaultIsAsync)
- { }
+ public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
+ : this(path, mode, access, share, bufferSize, DefaultIsAsync)
+ {
+ }
- public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) :
- this(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
- { }
+ public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
+ : this(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
+ {
+ }
public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
{
if (path == null)
+ {
throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path);
- if (path.Length == 0)
+ }
+ else if (path.Length == 0)
+ {
throw new ArgumentException(SR.Argument_EmptyPath, nameof(path));
+ }
// don't include inheritable in our bounds check for share
FileShare tempshare = share & ~FileShare.Inheritable;
string? badArg = null;
if (mode < FileMode.CreateNew || mode > FileMode.Append)
+ {
badArg = nameof(mode);
+ }
else if (access < FileAccess.Read || access > FileAccess.ReadWrite)
+ {
badArg = nameof(access);
+ }
else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete))
+ {
badArg = nameof(share);
+ }
if (badArg != null)
+ {
throw new ArgumentOutOfRangeException(badArg, SR.ArgumentOutOfRange_Enum);
+ }
// NOTE: any change to FileOptions enum needs to be matched here in the error validation
if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0)
+ {
throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_Enum);
-
- if (bufferSize <= 0)
+ }
+ else if (bufferSize <= 0)
+ {
throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
+ }
// Write access validation
if ((access & FileAccess.Write) == 0)
@@ -157,14 +183,15 @@ public FileStream(string path, FileMode mode, FileAccess access, FileShare share
}
if ((access & FileAccess.Read) != 0 && mode == FileMode.Append)
+ {
throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(access));
-
- if ((access & FileAccess.Write) == FileAccess.Write)
+ }
+ else if ((access & FileAccess.Write) == FileAccess.Write)
{
SerializationInfo.ThrowIfDeserializationInProgress("AllowFileWrites", ref s_cachedSerializationSwitch);
}
- _strategy = WrapIfDerivedType(FileStreamHelpers.ChooseStrategy(path, mode, access, share, bufferSize, options));
+ _strategy = FileStreamHelpers.ChooseStrategy(this, path, mode, access, share, bufferSize, options);
}
[Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. https://go.microsoft.com/fwlink/?linkid=14202")]
@@ -177,8 +204,7 @@ public virtual void Lock(long position, long length)
{
throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
}
-
- if (_strategy.IsClosed)
+ else if (_strategy.IsClosed)
{
ThrowHelper.ThrowObjectDisposedException_FileClosed();
}
@@ -193,8 +219,7 @@ public virtual void Unlock(long position, long length)
{
throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
}
-
- if (_strategy.IsClosed)
+ else if (_strategy.IsClosed)
{
ThrowHelper.ThrowObjectDisposedException_FileClosed();
}
@@ -208,7 +233,7 @@ public override Task FlushAsync(CancellationToken cancellationToken)
{
return Task.FromCanceled(cancellationToken);
}
- if (_strategy.IsClosed)
+ else if (_strategy.IsClosed)
{
ThrowHelper.ThrowObjectDisposedException_FileClosed();
}
@@ -230,10 +255,13 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
ValidateBufferArguments(buffer, offset, count);
if (cancellationToken.IsCancellationRequested)
+ {
return Task.FromCanceled(cancellationToken);
-
- if (_strategy.IsClosed)
+ }
+ else if (_strategy.IsClosed)
+ {
ThrowHelper.ThrowObjectDisposedException_FileClosed();
+ }
return _strategy.ReadAsync(buffer, offset, count, cancellationToken);
}
@@ -244,8 +272,7 @@ public override ValueTask ReadAsync(Memory buffer, CancellationToken
{
return ValueTask.FromCanceled(cancellationToken);
}
-
- if (_strategy.IsClosed)
+ else if (_strategy.IsClosed)
{
ThrowHelper.ThrowObjectDisposedException_FileClosed();
}
@@ -267,10 +294,13 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati
ValidateBufferArguments(buffer, offset, count);
if (cancellationToken.IsCancellationRequested)
+ {
return Task.FromCanceled(cancellationToken);
-
- if (_strategy.IsClosed)
+ }
+ else if (_strategy.IsClosed)
+ {
ThrowHelper.ThrowObjectDisposedException_FileClosed();
+ }
return _strategy.WriteAsync(buffer, offset, count, cancellationToken);
}
@@ -281,8 +311,7 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationTo
{
return ValueTask.FromCanceled(cancellationToken);
}
-
- if (_strategy.IsClosed)
+ else if (_strategy.IsClosed)
{
ThrowHelper.ThrowObjectDisposedException_FileClosed();
}
@@ -305,7 +334,10 @@ public override void Flush()
///
public virtual void Flush(bool flushToDisk)
{
- if (_strategy.IsClosed) ThrowHelper.ThrowObjectDisposedException_FileClosed();
+ if (_strategy.IsClosed)
+ {
+ ThrowHelper.ThrowObjectDisposedException_FileClosed();
+ }
_strategy.Flush(flushToDisk);
}
@@ -324,7 +356,9 @@ private void ValidateReadWriteArgs(byte[] buffer, int offset, int count)
{
ValidateBufferArguments(buffer, offset, count);
if (_strategy.IsClosed)
+ {
ThrowHelper.ThrowObjectDisposedException_FileClosed();
+ }
}
/// Sets the length of this stream to the given value.
@@ -332,13 +366,21 @@ private void ValidateReadWriteArgs(byte[] buffer, int offset, int count)
public override void SetLength(long value)
{
if (value < 0)
+ {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
- if (_strategy.IsClosed)
+ }
+ else if (_strategy.IsClosed)
+ {
ThrowHelper.ThrowObjectDisposedException_FileClosed();
- if (!CanSeek)
+ }
+ else if (!CanSeek)
+ {
ThrowHelper.ThrowNotSupportedException_UnseekableStream();
- if (!CanWrite)
+ }
+ else if (!CanWrite)
+ {
ThrowHelper.ThrowNotSupportedException_UnwritableStream();
+ }
_strategy.SetLength(value);
}
@@ -356,8 +398,15 @@ public override long Length
{
get
{
- if (_strategy.IsClosed) ThrowHelper.ThrowObjectDisposedException_FileClosed();
- if (!CanSeek) ThrowHelper.ThrowNotSupportedException_UnseekableStream();
+ if (_strategy.IsClosed)
+ {
+ ThrowHelper.ThrowObjectDisposedException_FileClosed();
+ }
+ else if (!CanSeek)
+ {
+ ThrowHelper.ThrowNotSupportedException_UnseekableStream();
+ }
+
return _strategy.Length;
}
}
@@ -368,17 +417,22 @@ public override long Position
get
{
if (_strategy.IsClosed)
+ {
ThrowHelper.ThrowObjectDisposedException_FileClosed();
-
- if (!CanSeek)
+ }
+ else if (!CanSeek)
+ {
ThrowHelper.ThrowNotSupportedException_UnseekableStream();
+ }
return _strategy.Position;
}
set
{
if (value < 0)
+ {
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
+ }
_strategy.Seek(value, SeekOrigin.Begin);
}
@@ -397,7 +451,7 @@ public override long Position
/// The byte to write to the stream.
public override void WriteByte(byte value) => _strategy.WriteByte(value);
- protected override void Dispose(bool disposing) => _strategy?.DisposeInternal(disposing);
+ protected override void Dispose(bool disposing) => _strategy.DisposeInternal(disposing);
internal void DisposeInternal(bool disposing) => Dispose(disposing);
@@ -411,8 +465,15 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
ValidateBufferArguments(buffer, offset, count);
- if (_strategy.IsClosed) ThrowHelper.ThrowObjectDisposedException_FileClosed();
- if (!CanRead) ThrowHelper.ThrowNotSupportedException_UnreadableStream();
+
+ if (_strategy.IsClosed)
+ {
+ ThrowHelper.ThrowObjectDisposedException_FileClosed();
+ }
+ else if (!CanRead)
+ {
+ ThrowHelper.ThrowNotSupportedException_UnreadableStream();
+ }
return _strategy.BeginRead(buffer, offset, count, callback, state);
}
@@ -420,7 +481,9 @@ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, Asy
public override int EndRead(IAsyncResult asyncResult)
{
if (asyncResult == null)
+ {
throw new ArgumentNullException(nameof(asyncResult));
+ }
return _strategy.EndRead(asyncResult);
}
@@ -428,8 +491,15 @@ public override int EndRead(IAsyncResult asyncResult)
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
{
ValidateBufferArguments(buffer, offset, count);
- if (_strategy.IsClosed) ThrowHelper.ThrowObjectDisposedException_FileClosed();
- if (!CanWrite) ThrowHelper.ThrowNotSupportedException_UnwritableStream();
+
+ if (_strategy.IsClosed)
+ {
+ ThrowHelper.ThrowObjectDisposedException_FileClosed();
+ }
+ else if (!CanWrite)
+ {
+ ThrowHelper.ThrowNotSupportedException_UnwritableStream();
+ }
return _strategy.BeginWrite(buffer, offset, count, callback, state);
}
@@ -437,7 +507,9 @@ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, As
public override void EndWrite(IAsyncResult asyncResult)
{
if (asyncResult == null)
+ {
throw new ArgumentNullException(nameof(asyncResult));
+ }
_strategy.EndWrite(asyncResult);
}
@@ -479,21 +551,5 @@ internal IAsyncResult BaseBeginWrite(byte[] buffer, int offset, int count, Async
=> base.BeginWrite(buffer, offset, count, callback, state);
internal void BaseEndWrite(IAsyncResult asyncResult) => base.EndWrite(asyncResult);
-
- internal static bool IsIoRelatedException(Exception e) =>
- // These all derive from IOException
- // DirectoryNotFoundException
- // DriveNotFoundException
- // EndOfStreamException
- // FileLoadException
- // FileNotFoundException
- // PathTooLongException
- // PipeException
- e is IOException ||
- // Note that SecurityException is only thrown on runtimes that support CAS
- // e is SecurityException ||
- e is UnauthorizedAccessException ||
- e is NotSupportedException ||
- (e is ArgumentException && !(e is ArgumentNullException));
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/AsyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs
similarity index 99%
rename from src/libraries/System.Private.CoreLib/src/System/IO/AsyncWindowsFileStreamStrategy.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs
index 918d9afc88337..b02366ecfd13d 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/AsyncWindowsFileStreamStrategy.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/AsyncWindowsFileStreamStrategy.cs
@@ -6,7 +6,7 @@
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
-namespace System.IO
+namespace System.IO.Strategies
{
internal sealed partial class AsyncWindowsFileStreamStrategy : WindowsFileStreamStrategy, IFileStreamCompletionSourceStrategy
{
@@ -397,7 +397,7 @@ await FileStreamHelpers
finally
{
// Make sure the stream's current position reflects where we ended up
- if (!_fileHandle.IsClosed && CanSeek)
+ if (!_fileHandle.IsClosed && canSeek)
{
SeekCore(_fileHandle, 0, SeekOrigin.End);
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/BufferedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs
similarity index 99%
rename from src/libraries/System.Private.CoreLib/src/System/IO/BufferedFileStreamStrategy.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs
index eb802eb646efb..b2a239b20b73b 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/BufferedFileStreamStrategy.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/BufferedFileStreamStrategy.cs
@@ -6,7 +6,7 @@
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
-namespace System.IO
+namespace System.IO.Strategies
{
// this type exists so we can avoid duplicating the buffering logic in every FileStreamStrategy implementation
internal sealed class BufferedFileStreamStrategy : FileStreamStrategy
@@ -23,7 +23,7 @@ internal sealed class BufferedFileStreamStrategy : FileStreamStrategy
internal BufferedFileStreamStrategy(FileStreamStrategy strategy, int bufferSize)
{
- Debug.Assert(bufferSize > 1);
+ Debug.Assert(bufferSize > 1, "Buffering must not be enabled for smaller buffer sizes");
_strategy = strategy;
_bufferSize = bufferSize;
@@ -37,7 +37,7 @@ internal BufferedFileStreamStrategy(FileStreamStrategy strategy, int bufferSize)
// so we enforce it by passing always true
Dispose(true);
}
- catch (Exception e) when (FileStream.IsIoRelatedException(e))
+ catch (Exception e) when (FileStreamHelpers.IsIoRelatedException(e))
{
// On finalization, ignore failures from trying to flush the write buffer,
// e.g. if this stream is wrapping a pipe and the pipe is now broken.
@@ -74,7 +74,7 @@ public override long Position
{
Debug.Assert(!(_writePos > 0 && _readPos != _readLen), "Read and Write buffers cannot both have data in them at the same time.");
- return _strategy.Position + (_readPos - _readLen + _writePos);
+ return _strategy.Position + _readPos - _readLen + _writePos;
}
set
{
@@ -598,7 +598,7 @@ private void WriteByteSlow(byte value)
ClearReadBufferBeforeWrite();
EnsureBufferAllocated();
}
- else if (_writePos >= _bufferSize - 1)
+ else if (_writePos == _bufferSize - 1)
{
FlushWrite();
}
@@ -1055,8 +1055,6 @@ private void EnsureCanWrite()
private void EnsureBufferAllocated()
{
- Debug.Assert(_bufferSize > 0);
-
// BufferedFileStreamStrategy is not intended for multi-threaded use, so no worries about the get/set race on _buffer.
if (_buffer == null)
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/DerivedFileStreamStrategy.cs
similarity index 99%
rename from src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/DerivedFileStreamStrategy.cs
index e7dd824305f49..e6c63246d0a19 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/DerivedFileStreamStrategy.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/DerivedFileStreamStrategy.cs
@@ -5,7 +5,7 @@
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
-namespace System.IO
+namespace System.IO.Strategies
{
// this type exists so we can avoid GetType() != typeof(FileStream) checks in FileStream
// when FileStream was supposed to call base.Method() for such cases, we just call _fileStream.BaseMethod()
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamCompletionSource.Win32.cs
similarity index 99%
rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamCompletionSource.Win32.cs
index edee6fce1315d..c23f04dc64b12 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamCompletionSource.Win32.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamCompletionSource.Win32.cs
@@ -8,7 +8,7 @@
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
-namespace System.IO
+namespace System.IO.Strategies
{
// to avoid code duplicaiton of FileStreamCompletionSource for LegacyFileStreamStrategy and AsyncWindowsFileStreamStrategy
// we have created the following interface that is a common contract for both of them
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs
similarity index 93%
rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs
index 25a09f1ab8940..4d3639185feb4 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Unix.cs
@@ -8,16 +8,16 @@
using System.Threading;
using System.Threading.Tasks;
-namespace System.IO
+namespace System.IO.Strategies
{
// this type defines a set of stateless FileStream/FileStreamStrategy helper methods
- internal static class FileStreamHelpers
+ internal static partial class FileStreamHelpers
{
// in the future we are most probably going to introduce more strategies (io_uring etc)
- internal static FileStreamStrategy ChooseStrategy(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
+ private static FileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
=> new LegacyFileStreamStrategy(handle, access, bufferSize, isAsync);
- internal static FileStreamStrategy ChooseStrategy(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
+ private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
=> new LegacyFileStreamStrategy(path, mode, access, share, bufferSize, options);
internal static SafeFileHandle OpenHandle(string path, FileMode mode, FileAccess access, FileShare share, FileOptions options)
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
similarity index 97%
rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
index 840d62cb03b80..f565d1f1e0f3b 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamHelpers.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs
@@ -9,21 +9,17 @@
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
-namespace System.IO
+namespace System.IO.Strategies
{
// this type defines a set of stateless FileStream/FileStreamStrategy helper methods
- internal static class FileStreamHelpers
+ internal static partial class FileStreamHelpers
{
internal const int ERROR_BROKEN_PIPE = 109;
internal const int ERROR_NO_DATA = 232;
private const int ERROR_HANDLE_EOF = 38;
private const int ERROR_IO_PENDING = 997;
- // It's enabled by default. We are going to change that (by removing !) once we fix #16354, #25905 and #24847.
- internal static bool UseLegacyStrategy { get; }
- = !AppContextConfigHelper.GetBooleanConfig("System.IO.UseLegacyFileStream", "DOTNET_SYSTEM_IO_USELEGACYFILESTREAM");
-
- internal static FileStreamStrategy ChooseStrategy(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
+ private static FileStreamStrategy ChooseStrategyCore(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
{
if (UseLegacyStrategy)
{
@@ -37,7 +33,7 @@ internal static FileStreamStrategy ChooseStrategy(SafeFileHandle handle, FileAcc
return EnableBufferingIfNeeded(strategy, bufferSize);
}
- internal static FileStreamStrategy ChooseStrategy(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
+ private static FileStreamStrategy ChooseStrategyCore(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
{
if (UseLegacyStrategy)
{
@@ -146,7 +142,7 @@ internal static void VerifyHandleIsSync(SafeFileHandle handle)
// If we can't check the handle, just assume it is ok.
if (!(IsHandleSynchronous(handle, ignoreInvalid: false) ?? true))
- throw new ArgumentException(SR.Arg_HandleNotSync, nameof(handle));
+ ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(handle));
}
private static unsafe Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share)
@@ -316,7 +312,7 @@ internal static void GetFileTypeSpecificInformation(SafeFileHandle handle, out b
isPipe = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE;
}
- internal static unsafe void SetLength(SafeFileHandle handle, string? path, long length)
+ internal static unsafe void SetFileLength(SafeFileHandle handle, string? path, long length)
{
var eofInfo = new Interop.Kernel32.FILE_END_OF_FILE_INFO
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs
new file mode 100644
index 0000000000000..5eb9eabaeeb97
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Win32.SafeHandles;
+
+namespace System.IO.Strategies
+{
+ internal static partial class FileStreamHelpers
+ {
+ // It's enabled by default. We are going to change that once we fix #16354, #25905 and #24847.
+ internal static bool UseLegacyStrategy { get; } = GetLegacyFileStreamSetting();
+
+ private static bool GetLegacyFileStreamSetting()
+ {
+ if (AppContext.TryGetSwitch("System.IO.UseNet5CompatFileStream", out bool fileConfig))
+ {
+ return fileConfig;
+ }
+
+ string? envVar = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM");
+ return envVar is null
+ ? true // legacy is currently enabled by default;
+ : bool.IsTrueStringIgnoreCase(envVar) || envVar.Equals("1");
+ }
+
+ internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
+ => WrapIfDerivedType(fileStream, ChooseStrategyCore(handle, access, bufferSize, isAsync));
+
+ internal static FileStreamStrategy ChooseStrategy(FileStream fileStream, string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
+ => WrapIfDerivedType(fileStream, ChooseStrategyCore(path, mode, access, share, bufferSize, options));
+
+ private static FileStreamStrategy WrapIfDerivedType(FileStream fileStream, FileStreamStrategy strategy)
+ => fileStream.GetType() == typeof(FileStream)
+ ? strategy
+ : new DerivedFileStreamStrategy(fileStream, strategy);
+
+ internal static bool IsIoRelatedException(Exception e) =>
+ // These all derive from IOException
+ // DirectoryNotFoundException
+ // DriveNotFoundException
+ // EndOfStreamException
+ // FileLoadException
+ // FileNotFoundException
+ // PathTooLongException
+ // PipeException
+ e is IOException ||
+ // Note that SecurityException is only thrown on runtimes that support CAS
+ // e is SecurityException ||
+ e is UnauthorizedAccessException ||
+ e is NotSupportedException ||
+ (e is ArgumentException && !(e is ArgumentNullException));
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamStrategy.cs
similarity index 96%
rename from src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamStrategy.cs
index 416e48a01eac0..fdeba8f0df233 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStreamStrategy.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamStrategy.cs
@@ -3,7 +3,7 @@
using Microsoft.Win32.SafeHandles;
-namespace System.IO
+namespace System.IO.Strategies
{
internal abstract class FileStreamStrategy : Stream
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.OSX.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.OSX.cs
similarity index 95%
rename from src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.OSX.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.OSX.cs
index 599b51694d179..5d00486b26b02 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.OSX.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.OSX.cs
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-namespace System.IO
+namespace System.IO.Strategies
{
internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.Unix.cs
similarity index 97%
rename from src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.Unix.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.Unix.cs
index 5233dcdb7087f..d6a534ffb2595 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Lock.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Lock.Unix.cs
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-namespace System.IO
+namespace System.IO.Strategies
{
internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Unix.cs
similarity index 99%
rename from src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Unix.cs
index 26818014bbbcf..a9614b1d55386 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Unix.cs
@@ -8,7 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
-namespace System.IO
+namespace System.IO.Strategies
{
/// Provides an implementation of a file stream for Unix files.
internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy
@@ -172,7 +172,7 @@ protected override void Dispose(bool disposing)
{
FlushWriteBuffer();
}
- catch (Exception e) when (!disposing && FileStream.IsIoRelatedException(e))
+ catch (Exception e) when (!disposing && FileStreamHelpers.IsIoRelatedException(e))
{
// On finalization, ignore failures from trying to flush the write buffer,
// e.g. if this stream is wrapping a pipe and the pipe is now broken.
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Windows.cs
similarity index 99%
rename from src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Windows.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Windows.cs
index ab8cc977a5e05..8890bf3053474 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.Windows.cs
@@ -34,7 +34,7 @@
*
*/
-namespace System.IO
+namespace System.IO.Strategies
{
internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy, IFileStreamCompletionSourceStrategy
{
@@ -192,7 +192,7 @@ protected override void Dispose(bool disposing)
{
FlushWriteBuffer(!disposing);
}
- catch (Exception e) when (!disposing && FileStream.IsIoRelatedException(e))
+ catch (Exception e) when (!disposing && FileStreamHelpers.IsIoRelatedException(e))
{
// On finalization, ignore failures from trying to flush the write buffer,
// e.g. if this stream is wrapping a pipe and the pipe is now broken.
@@ -328,7 +328,7 @@ private unsafe void SetLengthCore(long value)
Debug.Assert(value >= 0, "value >= 0");
VerifyOSHandlePosition();
- FileStreamHelpers.SetLength(_fileHandle, _path, value);
+ FileStreamHelpers.SetFileLength(_fileHandle, _path, value);
if (_filePosition > value)
{
@@ -434,7 +434,7 @@ private unsafe int ReadNative(Span buffer)
else
{
if (errorCode == ERROR_INVALID_PARAMETER)
- throw new ArgumentException(SR.Arg_HandleNotSync, "_fileHandle");
+ ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(_fileHandle));
throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.cs
similarity index 99%
rename from src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.cs
index 5d90aecac6ac7..6d06e9614056e 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/LegacyFileStreamStrategy.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/LegacyFileStreamStrategy.cs
@@ -7,7 +7,7 @@
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
-namespace System.IO
+namespace System.IO.Strategies
{
// This type is partial so we can avoid code duplication between Windows and Unix Legacy implementations
internal sealed partial class LegacyFileStreamStrategy : FileStreamStrategy
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/SyncWindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs
similarity index 97%
rename from src/libraries/System.Private.CoreLib/src/System/IO/SyncWindowsFileStreamStrategy.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs
index 30d01134481cb..bcd9c67e7f8ae 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/SyncWindowsFileStreamStrategy.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/SyncWindowsFileStreamStrategy.cs
@@ -7,7 +7,7 @@
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
-namespace System.IO
+namespace System.IO.Strategies
{
internal sealed class SyncWindowsFileStreamStrategy : WindowsFileStreamStrategy
{
@@ -33,7 +33,7 @@ protected override void OnInitFromHandle(SafeFileHandle handle)
// If we can't check the handle, just assume it is ok.
if (!(FileStreamHelpers.IsHandleSynchronous(handle, ignoreInvalid: false) ?? true))
- throw new ArgumentException(SR.Arg_HandleNotSync, nameof(handle));
+ ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(handle));
}
public override int Read(byte[] buffer, int offset, int count) => ReadSpan(new Span(buffer, offset, count));
@@ -119,7 +119,7 @@ private unsafe int ReadSpan(Span destination)
else
{
if (errorCode == ERROR_INVALID_PARAMETER)
- throw new ArgumentException(SR.Arg_HandleNotSync, "_fileHandle");
+ ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(_fileHandle));
throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs
similarity index 99%
rename from src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs
rename to src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs
index 45917d0bc6d2c..d238bc23e4c4e 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/WindowsFileStreamStrategy.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/WindowsFileStreamStrategy.cs
@@ -6,7 +6,7 @@
using Microsoft.Win32.SafeHandles;
using System.Runtime.CompilerServices;
-namespace System.IO
+namespace System.IO.Strategies
{
// this type serves some basic functionality that is common for Async and Sync Windows File Stream Strategies
internal abstract class WindowsFileStreamStrategy : FileStreamStrategy
@@ -263,7 +263,7 @@ protected unsafe void SetLengthCore(long value)
Debug.Assert(value >= 0, "value >= 0");
VerifyOSHandlePosition();
- FileStreamHelpers.SetLength(_fileHandle, _path, value);
+ FileStreamHelpers.SetFileLength(_fileHandle, _path, value);
if (_filePosition > value)
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
index 27c29017c3846..d49e39d8b2f92 100644
--- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
@@ -219,6 +219,12 @@ internal static void ThrowArgumentException(ExceptionResource resource, Exceptio
throw GetArgumentException(resource, argument);
}
+ [DoesNotReturn]
+ internal static void ThrowArgumentException_HandleNotSync(string paramName)
+ {
+ throw new ArgumentException(SR.Arg_HandleNotSync, paramName);
+ }
+
[DoesNotReturn]
internal static void ThrowArgumentNullException(ExceptionArgument argument)
{