diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs deleted file mode 100644 index 0233626ee73c6..0000000000000 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_ALLOCATION_INFO.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -internal static partial class Interop -{ - internal static partial class Kernel32 - { - // Value taken from https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle#remarks: - internal const int FileAllocationInfo = 5; - - internal struct FILE_ALLOCATION_INFO - { - internal long AllocationSize; - } - } -} diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.Browser.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.Browser.cs deleted file mode 100644 index 3475c8999ca6f..0000000000000 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.Browser.cs +++ /dev/null @@ -1,16 +0,0 @@ -// 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.Tests -{ - public partial class FileStream_ctor_options_as : FileStream_ctor_options_as_base - { - protected override long PreallocationSize => 10; - - protected override long InitialLength => 10; - - private long GetExpectedFileLength(long preallocationSize) => preallocationSize; - - private long GetActualPreallocationSize(FileStream fileStream) => fileStream.Length; - } -} diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.Unix.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.Unix.cs deleted file mode 100644 index 12e8f1641bac0..0000000000000 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.Unix.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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.Tests -{ - public partial class FileStream_ctor_options_as : FileStream_ctor_options_as_base - { - protected override long PreallocationSize => 10; - - protected override long InitialLength => 10; - - private long GetExpectedFileLength(long preallocationSize) => preallocationSize; - - private long GetActualPreallocationSize(FileStream fileStream) - { - // On Unix posix_fallocate modifies file length and we are using fstat to get it for verificaiton - Interop.Sys.FStat(fileStream.SafeFileHandle, out Interop.Sys.FileStatus fileStatus); - return fileStatus.Size; - } - } -} diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.Windows.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.Windows.cs index bde8cd5e4692a..b716bcb3de25b 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.Windows.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.Windows.cs @@ -1,29 +1,16 @@ // 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.Pipes; using System.Runtime.InteropServices; using System.Text; +using System.Threading.Tasks; using Xunit; namespace System.IO.Tests { - public partial class FileStream_ctor_options_as : FileStream_ctor_options_as_base + public partial class FileStream_ctor_options_as { - protected override long PreallocationSize => 10; - - protected override long InitialLength => 0; // Windows modifies AllocationSize, but not EndOfFile (file length) - - private long GetExpectedFileLength(long preallocationSize) => 0; // Windows modifies AllocationSize, but not EndOfFile (file length) - - private unsafe long GetActualPreallocationSize(FileStream fileStream) - { - Interop.Kernel32.FILE_STANDARD_INFO info; - - Assert.True(Interop.Kernel32.GetFileInformationByHandleEx(fileStream.SafeFileHandle, Interop.Kernel32.FileStandardInfo, &info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO))); - - return info.AllocationSize; - } - [Theory] [InlineData(@"\\?\")] [InlineData(@"\??\")] @@ -36,7 +23,24 @@ public void ExtendedPathsAreSupported(string prefix) using (var fs = new FileStream(filePath, GetOptions(FileMode.CreateNew, FileAccess.Write, FileShare.None, FileOptions.None, preallocationSize))) { - Assert.True(GetActualPreallocationSize(fs) >= preallocationSize, $"Provided {preallocationSize}, actual: {GetActualPreallocationSize(fs)}"); + Assert.Equal(preallocationSize, fs.Length); + } + } + + [Fact] + public async Task PreallocationSizeIsIgnoredForNonSeekableFiles() + { + string pipeName = GetNamedPipeServerStreamName(); + string pipePath = Path.GetFullPath($@"\\.\pipe\{pipeName}"); + + FileStreamOptions options = new() { Mode = FileMode.Open, Access = FileAccess.Write, Share = FileShare.None, PreallocationSize = 123 }; + + using (var server = new NamedPipeServerStream(pipeName, PipeDirection.In)) + using (var clienStream = new FileStream(pipePath, options)) + { + await server.WaitForConnectionAsync(); + + Assert.False(clienStream.CanSeek); } } diff --git a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.cs b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.cs index f36e55ea16d2d..b23ccb1573983 100644 --- a/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.cs +++ b/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options_as.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Linq; +using System.Security.Cryptography; using Xunit; namespace System.IO.Tests @@ -68,6 +70,10 @@ public partial class NoParallelTests { } [Collection("NoParallelTests")] public partial class FileStream_ctor_options_as : FileStream_ctor_options_as_base { + protected override long PreallocationSize => 10; + + protected override long InitialLength => 10; + [Fact] public virtual void NegativePreallocationSizeThrows() { @@ -80,40 +86,51 @@ public virtual void NegativePreallocationSizeThrows() [InlineData(FileMode.Create, 0L)] [InlineData(FileMode.CreateNew, 0L)] [InlineData(FileMode.OpenOrCreate, 0L)] - public void WhenFileIsCreatedWithoutPreallocationSizeSpecifiedThePreallocationSizeIsNotSet(FileMode mode, long preallocationSize) + public void WhenFileIsCreatedWithoutPreallocationSizeSpecifiedItsLengthIsZero(FileMode mode, long preallocationSize) { using (var fs = new FileStream(GetPathToNonExistingFile(), GetOptions(mode, FileAccess.Write, FileShare.None, FileOptions.None, preallocationSize))) { - Assert.Equal(0, GetActualPreallocationSize(fs)); Assert.Equal(0, fs.Length); Assert.Equal(0, fs.Position); } } [Theory] - [InlineData(FileMode.Open, 0L)] - [InlineData(FileMode.Open, 1L)] - [InlineData(FileMode.OpenOrCreate, 0L)] - [InlineData(FileMode.OpenOrCreate, 1L)] - [InlineData(FileMode.Append, 0L)] - [InlineData(FileMode.Append, 1L)] - public void WhenExistingFileIsBeingOpenedWithPreallocationSizeSpecifiedThePreallocationSizeIsNotChanged(FileMode mode, long preallocationSize) + [InlineData(FileMode.Open, 20L)] + [InlineData(FileMode.Open, 5L)] + [InlineData(FileMode.Append, 20L)] + [InlineData(FileMode.Append, 5L)] + public void PreallocationSizeIsIgnoredForFileModeOpenAndAppend(FileMode mode, long preallocationSize) { - const int initialSize = 1; + const int initialSize = 10; string filePath = GetPathToNonExistingFile(); File.WriteAllBytes(filePath, new byte[initialSize]); - long initialPreallocationSize; - using (var fs = new FileStream(filePath, GetOptions(mode, FileAccess.Write, FileShare.None, FileOptions.None, 0))) // preallocationSize NOT provided + using (var fs = new FileStream(filePath, GetOptions(mode, FileAccess.Write, FileShare.None, FileOptions.None, preallocationSize))) { - initialPreallocationSize = GetActualPreallocationSize(fs); // just read it to ensure it's not being changed + Assert.Equal(initialSize, fs.Length); // it has NOT been changed + Assert.Equal(mode == FileMode.Append ? initialSize : 0, fs.Position); } + } - using (var fs = new FileStream(filePath, GetOptions(mode, FileAccess.Write, FileShare.None, FileOptions.None, preallocationSize))) + [Theory] + [InlineData(FileMode.OpenOrCreate, 20L)] // preallocationSize > initialSize + [InlineData(FileMode.OpenOrCreate, 5L)] // preallocationSize < initialSize + public void WhenExistingFileIsBeingOpenedWithOpenOrCreateModeTheLengthRemainsUnchanged(FileMode mode, long preallocationSize) + { + const int initialSize = 10; + string filePath = GetPathToNonExistingFile(); + byte[] initialData = RandomNumberGenerator.GetBytes(initialSize); + File.WriteAllBytes(filePath, initialData); + + using (var fs = new FileStream(filePath, GetOptions(mode, FileAccess.ReadWrite, FileShare.None, FileOptions.None, preallocationSize))) { - Assert.Equal(initialPreallocationSize, GetActualPreallocationSize(fs)); // it has NOT been changed - Assert.Equal(initialSize, fs.Length); - Assert.Equal(mode == FileMode.Append ? initialSize : 0, fs.Position); + Assert.Equal(initialSize, fs.Length); // it was not changed + Assert.Equal(0, fs.Position); + + byte[] actualContent = new byte[initialData.Length]; + Assert.Equal(actualContent.Length, fs.Read(actualContent)); + AssertExtensions.SequenceEqual(initialData, actualContent); // the initial content was not changed } } @@ -121,16 +138,16 @@ public void WhenExistingFileIsBeingOpenedWithPreallocationSizeSpecifiedThePreall [InlineData(FileMode.Create)] [InlineData(FileMode.CreateNew)] [InlineData(FileMode.OpenOrCreate)] - public void WhenFileIsCreatedWithPreallocationSizeSpecifiedThePreallocationSizeIsSet(FileMode mode) + public void WhenFileIsCreatedWithPreallocationSizeSpecifiedTheLengthIsSetAndTheContentIsZeroed(FileMode mode) { const long preallocationSize = 123; - using (var fs = new FileStream(GetPathToNonExistingFile(), GetOptions(mode, FileAccess.Write, FileShare.None, FileOptions.None, preallocationSize))) + using (var fs = new FileStream(GetPathToNonExistingFile(), GetOptions(mode, FileAccess.ReadWrite, FileShare.None, FileOptions.None, preallocationSize))) { - // OS might allocate MORE than we have requested - Assert.True(GetActualPreallocationSize(fs) >= preallocationSize, $"Provided {preallocationSize}, actual: {GetActualPreallocationSize(fs)}"); - Assert.Equal(GetExpectedFileLength(preallocationSize), fs.Length); + Assert.Equal(preallocationSize, fs.Length); Assert.Equal(0, fs.Position); + + AssertFileContentHasBeenZeroed(0, (int)fs.Length, fs); } } @@ -153,7 +170,7 @@ public void WhenDiskIsFullTheErrorMessageContainsAllDetails(FileMode mode) Assert.Contains(filePath, ex.Message); Assert.Contains(tooMuch.ToString(), ex.Message); - // ensure it was NOT created (provided OOTB by Windows, emulated on Unix) + // ensure it was NOT created bool exists = File.Exists(filePath); if (exists) { @@ -163,37 +180,20 @@ public void WhenDiskIsFullTheErrorMessageContainsAllDetails(FileMode mode) } [Fact] - public void WhenFileIsTruncatedWithoutPreallocationSizeSpecifiedThePreallocationSizeIsNotSet() - { - const int initialSize = 10_000; - - string filePath = GetPathToNonExistingFile(); - File.WriteAllBytes(filePath, new byte[initialSize]); - - using (var fs = new FileStream(filePath, GetOptions(FileMode.Truncate, FileAccess.Write, FileShare.None, FileOptions.None, 0))) - { - Assert.Equal(0, GetActualPreallocationSize(fs)); - Assert.Equal(0, fs.Length); - Assert.Equal(0, fs.Position); - } - } - - [Fact] - public void WhenFileIsTruncatedWithPreallocationSizeSpecifiedThePreallocationSizeIsSet() + public void WhenFileIsTruncatedWithPreallocationSizeSpecifiedTheLengthIsSetAndTheContentIsZeroed() { const int initialSize = 10_000; // this must be more than 4kb which seems to be minimum allocation size on Windows const long preallocationSize = 100; string filePath = GetPathToNonExistingFile(); - File.WriteAllBytes(filePath, new byte[initialSize]); + File.WriteAllBytes(filePath, Enumerable.Repeat((byte)1, initialSize).ToArray()); - using (var fs = new FileStream(filePath, GetOptions(FileMode.Truncate, FileAccess.Write, FileShare.None, FileOptions.None, preallocationSize))) + using (var fs = new FileStream(filePath, GetOptions(FileMode.Truncate, FileAccess.ReadWrite, FileShare.None, FileOptions.None, preallocationSize))) { - Assert.True(GetActualPreallocationSize(fs) >= preallocationSize, $"Provided {preallocationSize}, actual: {GetActualPreallocationSize(fs)}"); - // less than initial file size (file got truncated) - Assert.True(GetActualPreallocationSize(fs) < initialSize, $"initialSize {initialSize}, actual: {GetActualPreallocationSize(fs)}"); - Assert.Equal(GetExpectedFileLength(preallocationSize), fs.Length); + Assert.Equal(preallocationSize, fs.Length); Assert.Equal(0, fs.Position); + + AssertFileContentHasBeenZeroed(0, (int)fs.Length, fs); } } @@ -208,5 +208,16 @@ private string GetPathToNonExistingFile() return filePath; } + + private static void AssertFileContentHasBeenZeroed(int from, int to, FileStream fs) + { + int expectedByteCount = to - from; + int extraByteCount = 1; + byte[] content = Enumerable.Repeat((byte)1, expectedByteCount + extraByteCount).ToArray(); + fs.Position = from; + Assert.Equal(expectedByteCount, fs.Read(content)); + Assert.All(content.SkipLast(extraByteCount), @byte => Assert.Equal(0, @byte)); + Assert.Equal(to, fs.Position); + } } } diff --git a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj index 745ff60368b2e..0343ddaf70edc 100644 --- a/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/Net5CompatTests/System.IO.FileSystem.Net5Compat.Tests.csproj @@ -1,4 +1,4 @@ - + true true @@ -14,7 +14,6 @@ - @@ -24,8 +23,6 @@ - - diff --git a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj index bb7cac297d73a..e31a2fda63722 100644 --- a/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj +++ b/src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj @@ -70,9 +70,7 @@ - - @@ -88,8 +86,6 @@ - - @@ -103,7 +99,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs index ee1def85b9e02..777997444848f 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Unix.cs @@ -324,7 +324,7 @@ private void Init(string path, FileMode mode, FileAccess access, FileShare share } // If preallocationSize has been provided for a creatable and writeable file - if (FileStreamHelpers.ShouldPreallocate(preallocationSize, access, mode)) + if (FileStreamHelpers.ShouldPreallocate(preallocationSize, access, mode, this)) { int fallocateResult = Interop.Sys.PosixFAllocate(this, 0, preallocationSize); if (fallocateResult != 0) diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs index a7c4751a09478..3dde1e2e89a6b 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs @@ -34,7 +34,7 @@ internal static unsafe SafeFileHandle Open(string fullPath, FileMode mode, FileA // of converting DOS to NT file paths (RtlDosPathNameToRelativeNtPathName_U_WithStatus is not documented) SafeFileHandle fileHandle = CreateFile(fullPath, mode, access, share, options); - if (FileStreamHelpers.ShouldPreallocate(preallocationSize, access, mode)) + if (FileStreamHelpers.ShouldPreallocate(preallocationSize, access, mode, fileHandle)) { Preallocate(fullPath, preallocationSize, fileHandle); } @@ -108,33 +108,18 @@ private static unsafe SafeFileHandle CreateFile(string fullPath, FileMode mode, private static unsafe void Preallocate(string fullPath, long preallocationSize, SafeFileHandle fileHandle) { - var allocationInfo = new Interop.Kernel32.FILE_ALLOCATION_INFO - { - AllocationSize = preallocationSize - }; - - if (!Interop.Kernel32.SetFileInformationByHandle( - fileHandle, - Interop.Kernel32.FileAllocationInfo, - &allocationInfo, - (uint)sizeof(Interop.Kernel32.FILE_ALLOCATION_INFO))) - { - int errorCode = Marshal.GetLastPInvokeError(); - - // we try to mimic the atomic NtCreateFile here: - // if preallocation fails, close the handle and delete the file + // preallocationSize must be ignored for non-seekable files, unsupported file systems + // and other failures other than ERROR_DISK_FULL and ERROR_FILE_TOO_LARGE + if (!FileStreamHelpers.TrySetFileLength(fileHandle, preallocationSize, out int errorCode) + && errorCode == Interop.Errors.ERROR_DISK_FULL || errorCode == Interop.Errors.ERROR_FILE_TOO_LARGE) + { + // Windows does not modify the file size if the request can't be satisfied in atomic way. + // posix_fallocate (Unix) implementation might consume all available disk space and fail after that. + // To ensure that the behaviour is the same for every OS (no incomplete or empty file), we close the handle and delete the file. fileHandle.Dispose(); Interop.Kernel32.DeleteFile(fullPath); - switch (errorCode) - { - case Interop.Errors.ERROR_DISK_FULL: - throw new IOException(SR.Format(SR.IO_DiskFull_Path_AllocationSize, fullPath, preallocationSize)); - case Interop.Errors.ERROR_FILE_TOO_LARGE: - throw new IOException(SR.Format(SR.IO_FileTooLarge_Path_AllocationSize, fullPath, preallocationSize)); - default: - throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); - } + throw new IOException(SR.Format(errorCode == Interop.Errors.ERROR_DISK_FULL ? SR.IO_DiskFull_Path_AllocationSize : SR.IO_FileTooLarge_Path_AllocationSize, fullPath, preallocationSize)); } } 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 751a0748a35ce..6430f2d1db1db 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 @@ -1453,9 +1453,6 @@ Common\Interop\Windows\Kernel32\Interop.FILE_BASIC_INFO.cs - - Common\Interop\Windows\Kernel32\Interop.FILE_ALLOCATION_INFO.cs - Common\Interop\Windows\Kernel32\Interop.FILE_END_OF_FILE_INFO.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs index 779acce937371..5ba493ca9ca21 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.Windows.cs @@ -123,6 +123,16 @@ internal static void Unlock(SafeFileHandle handle, long position, long length) } internal static unsafe void SetFileLength(SafeFileHandle handle, long length) + { + if (!TrySetFileLength(handle, length, out int errorCode)) + { + throw errorCode == Interop.Errors.ERROR_INVALID_PARAMETER + ? new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_FileLengthTooBig) + : Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); + } + } + + internal static unsafe bool TrySetFileLength(SafeFileHandle handle, long length, out int errorCode) { var eofInfo = new Interop.Kernel32.FILE_END_OF_FILE_INFO { @@ -135,11 +145,12 @@ internal static unsafe void SetFileLength(SafeFileHandle handle, long length) &eofInfo, (uint)sizeof(Interop.Kernel32.FILE_END_OF_FILE_INFO))) { - int errorCode = Marshal.GetLastPInvokeError(); - if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) - throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_FileLengthTooBig); - throw Win32Marshal.GetExceptionForWin32Error(errorCode, handle.Path); + errorCode = Marshal.GetLastPInvokeError(); + return false; } + + errorCode = Interop.Errors.ERROR_SUCCESS; + return true; } internal static unsafe int ReadFileNative(SafeFileHandle handle, Span bytes, NativeOverlapped* overlapped, out int errorCode) 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 index d86c70a621ca1..981776da32b81 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Strategies/FileStreamHelpers.cs @@ -58,10 +58,11 @@ e is UnauthorizedAccessException || e is NotSupportedException || (e is ArgumentException && !(e is ArgumentNullException)); - internal static bool ShouldPreallocate(long preallocationSize, FileAccess access, FileMode mode) + internal static bool ShouldPreallocate(long preallocationSize, FileAccess access, FileMode mode, SafeFileHandle fileHandle) => preallocationSize > 0 && (access & FileAccess.Write) != 0 - && mode != FileMode.Open && mode != FileMode.Append; + && mode != FileMode.Open && mode != FileMode.Append + && (mode != FileMode.OpenOrCreate || (fileHandle.CanSeek && RandomAccess.GetFileLength(fileHandle) == 0)); // allow to extend only new files internal static void ValidateArguments(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize) {