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 91b863e22f7a51..5b87aba0918650 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 @@ -136,8 +136,11 @@ private static unsafe void Preallocate(string fullPath, long preallocationSize, int errorCode = Marshal.GetLastPInvokeError(); // Only throw for errors that indicate there is not enough space. - if (errorCode == Interop.Errors.ERROR_DISK_FULL || - errorCode == Interop.Errors.ERROR_FILE_TOO_LARGE) + // SetFileInformationByHandle fails with ERROR_DISK_FULL in certain cases when the size is disallowed by filesystem, + // such as >4GB on FAT32 volume. We cannot distinguish them currently. + if (errorCode is Interop.Errors.ERROR_DISK_FULL or + Interop.Errors.ERROR_FILE_TOO_LARGE or + Interop.Errors.ERROR_INVALID_PARAMETER) { fileHandle.Dispose(); @@ -147,7 +150,7 @@ private static unsafe void Preallocate(string fullPath, long preallocationSize, throw new IOException(SR.Format(errorCode == Interop.Errors.ERROR_DISK_FULL ? SR.IO_DiskFull_Path_AllocationSize : SR.IO_FileTooLarge_Path_AllocationSize, - fullPath, preallocationSize)); + fullPath, preallocationSize), Win32Marshal.MakeHRFromErrorCode(errorCode)); } } } diff --git a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/OpenHandle.cs b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/OpenHandle.cs index 36349e8b993b7c..3d7bcb303b4ce7 100644 --- a/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/OpenHandle.cs +++ b/src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/OpenHandle.cs @@ -95,5 +95,43 @@ public void DeleteOnClose_FileDeletedAfterSafeHandleDispose(FileOptions options) } Assert.False(File.Exists(path)); } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public void PreallocationSizeVeryLargeThrowsCorrectHResult() + { + const long VeryLargeFileSize = (long)128 * 1024 * 1024 * 1024 * 1024; // 128TB + + // The largest file size depends on cluster size. + // See https://learn.microsoft.com/en-us/windows-server/storage/file-server/ntfs-overview#support-for-large-volumes + + + const int ERROR_INVALID_PARAMETER = unchecked((int)0x80070057); + const int ERROR_DISK_FULL = unchecked((int)0x80070070); + + string path = GetTestFilePath(); + if (!IOServices.IsDriveNTFS(Path.GetPathRoot(path))) + { + // Skip the test for non-NTFS filesystems + return; + } + + if (new DriveInfo(path).TotalFreeSpace >= VeryLargeFileSize) + { + // Skip the test if somehow the drive is really big. + return; + } + + try + { + using (File.OpenHandle(path, mode: FileMode.Create, access: FileAccess.ReadWrite, preallocationSize: VeryLargeFileSize)) { } + Assert.Fail("File.OpenHandle should throw due to failure to preallocate a very large file."); + } + catch (IOException ex) + { + // Accept both results since we cannot assume the cluster size of testing volume + Assert.True(ex.HResult is ERROR_INVALID_PARAMETER or ERROR_DISK_FULL); + } + } } }