Skip to content

Commit

Permalink
Include correct HResult in IOException during preallocation when …
Browse files Browse the repository at this point in the history
…the disk is full (#110281)
karakasa authored Jan 10, 2025
1 parent 3bbd403 commit 0df31bc
Showing 2 changed files with 44 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -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));
}
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
}

0 comments on commit 0df31bc

Please sign in to comment.