Skip to content

Commit 0df31bc

Browse files
authored
Include correct HResult in IOException during preallocation when the disk is full (#110281)
1 parent 3bbd403 commit 0df31bc

File tree

2 files changed

+44
-3
lines changed

2 files changed

+44
-3
lines changed

src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,11 @@ private static unsafe void Preallocate(string fullPath, long preallocationSize,
136136
int errorCode = Marshal.GetLastPInvokeError();
137137

138138
// Only throw for errors that indicate there is not enough space.
139-
if (errorCode == Interop.Errors.ERROR_DISK_FULL ||
140-
errorCode == Interop.Errors.ERROR_FILE_TOO_LARGE)
139+
// SetFileInformationByHandle fails with ERROR_DISK_FULL in certain cases when the size is disallowed by filesystem,
140+
// such as >4GB on FAT32 volume. We cannot distinguish them currently.
141+
if (errorCode is Interop.Errors.ERROR_DISK_FULL or
142+
Interop.Errors.ERROR_FILE_TOO_LARGE or
143+
Interop.Errors.ERROR_INVALID_PARAMETER)
141144
{
142145
fileHandle.Dispose();
143146

@@ -147,7 +150,7 @@ private static unsafe void Preallocate(string fullPath, long preallocationSize,
147150
throw new IOException(SR.Format(errorCode == Interop.Errors.ERROR_DISK_FULL
148151
? SR.IO_DiskFull_Path_AllocationSize
149152
: SR.IO_FileTooLarge_Path_AllocationSize,
150-
fullPath, preallocationSize));
153+
fullPath, preallocationSize), Win32Marshal.MakeHRFromErrorCode(errorCode));
151154
}
152155
}
153156
}

src/libraries/System.Runtime/tests/System.IO.FileSystem.Tests/File/OpenHandle.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,5 +95,43 @@ public void DeleteOnClose_FileDeletedAfterSafeHandleDispose(FileOptions options)
9595
}
9696
Assert.False(File.Exists(path));
9797
}
98+
99+
[Fact]
100+
[PlatformSpecific(TestPlatforms.Windows)]
101+
public void PreallocationSizeVeryLargeThrowsCorrectHResult()
102+
{
103+
const long VeryLargeFileSize = (long)128 * 1024 * 1024 * 1024 * 1024; // 128TB
104+
105+
// The largest file size depends on cluster size.
106+
// See https://learn.microsoft.com/en-us/windows-server/storage/file-server/ntfs-overview#support-for-large-volumes
107+
108+
109+
const int ERROR_INVALID_PARAMETER = unchecked((int)0x80070057);
110+
const int ERROR_DISK_FULL = unchecked((int)0x80070070);
111+
112+
string path = GetTestFilePath();
113+
if (!IOServices.IsDriveNTFS(Path.GetPathRoot(path)))
114+
{
115+
// Skip the test for non-NTFS filesystems
116+
return;
117+
}
118+
119+
if (new DriveInfo(path).TotalFreeSpace >= VeryLargeFileSize)
120+
{
121+
// Skip the test if somehow the drive is really big.
122+
return;
123+
}
124+
125+
try
126+
{
127+
using (File.OpenHandle(path, mode: FileMode.Create, access: FileAccess.ReadWrite, preallocationSize: VeryLargeFileSize)) { }
128+
Assert.Fail("File.OpenHandle should throw due to failure to preallocate a very large file.");
129+
}
130+
catch (IOException ex)
131+
{
132+
// Accept both results since we cannot assume the cluster size of testing volume
133+
Assert.True(ex.HResult is ERROR_INVALID_PARAMETER or ERROR_DISK_FULL);
134+
}
135+
}
98136
}
99137
}

0 commit comments

Comments
 (0)