未验证 提交 1ae7c5f8 编写于 作者: V Victor Irzak 提交者: GitHub

Handle FileStream.Length for devices (#73708)

Co-authored-by: NTheodore Tsirpanis <teo@tsirpanis.gr>
Co-authored-by: NAdam Sitnik <adam.sitnik@gmail.com>
Co-authored-by: NJan Kotas <jkotas@microsoft.com>
上级 ae24786f
......@@ -12,14 +12,17 @@ internal static partial class Kernel32
// https://docs.microsoft.com/windows/win32/api/winioctl/ni-winioctl-fsctl_get_reparse_point
internal const int FSCTL_GET_REPARSE_POINT = 0x000900a8;
// https://docs.microsoft.com/windows-hardware/drivers/ddi/ntddstor/ni-ntddstor-ioctl_storage_read_capacity
internal const int IOCTL_STORAGE_READ_CAPACITY = 0x002D5140;
[LibraryImport(Libraries.Kernel32, EntryPoint = "DeviceIoControl", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool DeviceIoControl(
internal static unsafe partial bool DeviceIoControl(
SafeHandle hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
void* lpInBuffer,
uint nInBufferSize,
byte[] lpOutBuffer,
void* lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped);
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.InteropServices;
internal static partial class Interop
{
internal static partial class Kernel32
{
// https://docs.microsoft.com/en-us/windows/win32/devio/storage-read-capacity
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct STORAGE_READ_CAPACITY
{
internal uint Version;
internal uint Size;
internal uint BlockLength;
internal long NumberOfBlocks;
internal long DiskLength;
}
}
}
......@@ -36,5 +36,22 @@ public void ReturnsExactSizeForNonEmptyFiles(FileOptions options)
Assert.Equal(fileSize, RandomAccess.GetLength(handle));
}
}
[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindowsAndElevated))]
[MemberData(nameof(GetSyncAsyncOptions))]
public void ReturnsActualLengthForDevices(FileOptions options)
{
// both File.Exists and Path.Exists return false when "\\?\PhysicalDrive0" exists
// that is why we just try and swallow the exception when it occurs
try
{
using (SafeFileHandle handle = File.OpenHandle(@"\\?\PhysicalDrive0", FileMode.Open, options: options))
{
long length = RandomAccess.GetLength(handle);
Assert.True(length > 0);
}
}
catch (FileNotFoundException) { }
}
}
}
......@@ -6,6 +6,7 @@
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Buffers;
namespace Microsoft.Win32.SafeHandles
{
......@@ -286,12 +287,43 @@ unsafe long GetFileLengthCore()
{
Interop.Kernel32.FILE_STANDARD_INFO info;
if (!Interop.Kernel32.GetFileInformationByHandleEx(this, Interop.Kernel32.FileStandardInfo, &info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO)))
if (Interop.Kernel32.GetFileInformationByHandleEx(this, Interop.Kernel32.FileStandardInfo, &info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO)))
{
return info.EndOfFile;
}
// In theory when GetFileInformationByHandleEx fails, then
// a) IsDevice can modify last error (not true today, but can be in the future),
// b) DeviceIoControl can succeed (last error set to ERROR_SUCCESS) but return fewer bytes than requested.
// The error is stored and in such cases exception for the first failure is going to be thrown.
int lastError = Marshal.GetLastWin32Error();
if (Path is null || !PathInternal.IsDevice(Path))
{
throw Win32Marshal.GetExceptionForWin32Error(lastError, Path);
}
Interop.Kernel32.STORAGE_READ_CAPACITY storageReadCapacity;
bool success = Interop.Kernel32.DeviceIoControl(
this,
dwIoControlCode: Interop.Kernel32.IOCTL_STORAGE_READ_CAPACITY,
lpInBuffer: null,
nInBufferSize: 0,
lpOutBuffer: &storageReadCapacity,
nOutBufferSize: (uint)sizeof(Interop.Kernel32.STORAGE_READ_CAPACITY),
out uint bytesReturned,
IntPtr.Zero);
if (!success)
{
throw Win32Marshal.GetExceptionForLastWin32Error(Path);
}
else if (bytesReturned != sizeof(Interop.Kernel32.STORAGE_READ_CAPACITY))
{
throw Win32Marshal.GetExceptionForWin32Error(lastError, Path);
}
return info.EndOfFile;
return storageReadCapacity.DiskLength;
}
}
}
......
......@@ -1720,6 +1720,9 @@
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.SECURITY_ATTRIBUTES.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.STORAGE_READ_CAPACITY.cs">
<Link>Common\Interop\Windows\Kernel32\Interop.STORAGE_READ_CAPACITY.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs">
<Link>Common\Interop\Windows\Interop.UNICODE_STRING.cs</Link>
</Compile>
......
......@@ -574,15 +574,20 @@ internal static void CreateSymbolicLink(string path, string pathToTarget, bool i
byte[] buffer = ArrayPool<byte>.Shared.Rent(Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
try
{
bool success = Interop.Kernel32.DeviceIoControl(
handle,
dwIoControlCode: Interop.Kernel32.FSCTL_GET_REPARSE_POINT,
lpInBuffer: IntPtr.Zero,
nInBufferSize: 0,
lpOutBuffer: buffer,
nOutBufferSize: Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
out _,
IntPtr.Zero);
bool success;
fixed (byte* pBuffer = buffer)
{
success = Interop.Kernel32.DeviceIoControl(
handle,
dwIoControlCode: Interop.Kernel32.FSCTL_GET_REPARSE_POINT,
lpInBuffer: null,
nInBufferSize: 0,
lpOutBuffer: pBuffer,
nOutBufferSize: Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
out _,
IntPtr.Zero);
}
if (!success)
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册