diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_END_OF_FILE_INFO.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_END_OF_FILE_INFO.cs
new file mode 100644
index 00000000000000..5683bb20653c08
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FILE_END_OF_FILE_INFO.cs
@@ -0,0 +1,17 @@
+// 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
+ {
+ // From FILE_INFO_BY_HANDLE_CLASS
+ // Use for SetFileInformationByHandle
+ internal const int FileEndOfFileInfo = 6;
+
+ internal struct FILE_END_OF_FILE_INFO
+ {
+ internal long EndOfFile;
+ }
+ }
+}
diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetEndOfFile.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetEndOfFile.cs
deleted file mode 100644
index 7ba5014d62feb8..00000000000000
--- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SetEndOfFile.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-// 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;
-using System.Runtime.InteropServices;
-
-internal static partial class Interop
-{
- internal static partial class Kernel32
- {
- [DllImport(Libraries.Kernel32, SetLastError = true)]
- internal static extern bool SetEndOfFile(SafeFileHandle hFile);
- }
-}
diff --git a/src/libraries/System.IO.FileSystem/tests/ManualTests/ManualTests.cs b/src/libraries/System.IO.FileSystem/tests/ManualTests/ManualTests.cs
index 0e8ae5b968044c..c363e654e73ac9 100644
--- a/src/libraries/System.IO.FileSystem/tests/ManualTests/ManualTests.cs
+++ b/src/libraries/System.IO.FileSystem/tests/ManualTests/ManualTests.cs
@@ -75,5 +75,66 @@ public static void Throw_FileStreamDispose_WhenRemoteMountRunsOutOfSpace()
destinationStream.Dispose();
});
}
+
+
+ const long InitialFileSize = 1024;
+
+ [ConditionalFact(nameof(ManualTestsEnabled))]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public static void SetLength_DoesNotAlterPositionWhenNativeCallFails()
+ {
+ /* This test verifies that Position is not altered when SetLength fails when a "disk out of space" error occurs.
+
+ Setup environment to have a drive with less than 1k available space:
+ - Create an 8mb fixed size VHD.
+ - Open Computer Management -> Storage -> Disk Management
+ - Follow these instructions:
+ https://docs.microsoft.com/en-us/windows-server/storage/disk-management/manage-virtual-hard-disks
+
+ - Restrict the space available in the VHD.
+ - Create a 512 bytes quota in the VHD created above using cmd:
+ fsutil quota modify E: 512 512 SYSTEM
+ fsutil quota modify E: 512 512 YourUser
+
+ - Run the test. If configured correctly, the SetLength operation should fail at least once.
+ */
+
+ using FileStream fs = File.Open("E:/dummy_file.txt", FileMode.OpenOrCreate);
+
+ // Position was less than new Length; should remain the same.
+ fs.Seek(0, SeekOrigin.Begin);
+ VerifySetLength(fs);
+ Assert.Equal(0, fs.Position);
+ Assert.True(fs.Position < fs.Length);
+
+ // Position was larger than new Length; should be adjusted to the Length.
+ fs.Seek(InitialFileSize + 1, SeekOrigin.Begin);
+ VerifySetLength(fs);
+ Assert.Equal(fs.Length, fs.Position);
+ }
+
+ private static void VerifySetLength(FileStream fs)
+ {
+ long originalPosition = fs.Position;
+ bool success = false;
+ long size = InitialFileSize;
+
+ while (!success)
+ {
+ try
+ {
+ Console.WriteLine($"Attempting to write {size} bytes...");
+ fs.SetLength(size);
+ Console.WriteLine("Success!");
+ success = true;
+ }
+ catch (IOException)
+ {
+ Console.WriteLine("Failed.");
+ Assert.Equal(originalPosition, fs.Position);
+ size = (long)(size * 0.9);
+ }
+ }
+ }
}
}
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 4ac94b06136feb..561518834ad44d 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
@@ -1307,6 +1307,9 @@
Common\Interop\Windows\Kernel32\Interop.ExpandEnvironmentStrings.cs
+
+ Common\Interop\Windows\Kernel32\Interop.FILE_END_OF_FILE_INFO.cs
+
Common\Interop\Windows\Kernel32\Interop.FILE_STANDARD_INFO.cs
@@ -1475,8 +1478,8 @@
Common\Interop\Windows\Kernel32\Interop.SetCurrentDirectory.cs
-
- Common\Interop\Windows\Kernel32\Interop.SetEndOfFile.cs
+
+ Common\Interop\Windows\Kernel32\Interop.SetFileInformationByHandle.cs
Common\Interop\Windows\Kernel32\Interop.SetFilePointerEx.cs
diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Windows.cs
index 3cfdf8de38f0b9..85b6e7794fae26 100644
--- a/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Windows.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileStream.Windows.cs
@@ -379,28 +379,31 @@ private void SetLengthInternal(long value)
// We absolutely need this method broken out so that WriteInternalCoreAsync can call
// a method without having to go through buffering code that might call FlushWrite.
- private void SetLengthCore(long value)
+ private unsafe void SetLengthCore(long value)
{
Debug.Assert(value >= 0, "value >= 0");
- long origPos = _filePosition;
-
VerifyOSHandlePosition();
- if (_filePosition != value)
- SeekCore(_fileHandle, value, SeekOrigin.Begin);
- if (!Interop.Kernel32.SetEndOfFile(_fileHandle))
+
+ var eofInfo = new Interop.Kernel32.FILE_END_OF_FILE_INFO
+ {
+ EndOfFile = value
+ };
+
+ if (!Interop.Kernel32.SetFileInformationByHandle(
+ _fileHandle,
+ Interop.Kernel32.FileEndOfFileInfo,
+ &eofInfo,
+ (uint)sizeof(Interop.Kernel32.FILE_END_OF_FILE_INFO)))
{
int errorCode = Marshal.GetLastWin32Error();
if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER)
throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_FileLengthTooBig);
throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
}
- // Return file pointer to where it was before setting length
- if (origPos != value)
+
+ if (_filePosition > value)
{
- if (origPos < value)
- SeekCore(_fileHandle, origPos, SeekOrigin.Begin);
- else
- SeekCore(_fileHandle, 0, SeekOrigin.End);
+ SeekCore(_fileHandle, 0, SeekOrigin.End);
}
}