Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FileStream.ReadAsync() with isAsync: true is throwing IOException #54143

Closed
X9VoiD opened this issue Jun 14, 2021 · 13 comments
Closed

FileStream.ReadAsync() with isAsync: true is throwing IOException #54143

X9VoiD opened this issue Jun 14, 2021 · 13 comments
Assignees
Milestone

Comments

@X9VoiD
Copy link

X9VoiD commented Jun 14, 2021

Description

FileStreams opened with isAsync: true fails to perform asynchronous read operations. Not specifying isAsync: true runs completely fine.

The function used to get a FileStream:

private void InitializeFileStream(string filePath, bool exclusive, int readSize, int writeSize)
{
    FileAccess access = 0;
    if (readSize > 0)
        access |= FileAccess.Read;
    if (writeSize > 0)
        access |= FileAccess.Write;

    FileShare share = exclusive ? FileShare.None : (FileShare)access;
    FileStream = new FileStream(filePath, FileMode.Open, access, share, Math.Max(readSize, writeSize), true);
}

Run loop of the task:

private async void ReadPoll()
{
    while (_run)
    {
        int bytesReturned = await FileStream!.ReadAsync(_readBuffer);
        OnInput(_readBuffer.Slice(0, bytesReturned));
    }
}

Configuration

OS Version Windows 10 21H1
.NET Version .NET 6 preview 4

Regression?

I'm not sure...

Other information

Exception has occurred: CLR/System.IO.IOException
An exception of type 'System.IO.IOException' occurred in System.Private.CoreLib.dll but was not handled in user code: 'Incorrect function. : '\\?\hid#vid_28bd&pid_0906&mi_02#7&194ad48b&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}''
   at System.IO.Strategies.FileStreamHelpers.GetFileLength(SafeFileHandle handle, String path)
   at System.IO.Strategies.WindowsFileStreamStrategy.get_Length()
   at System.IO.Strategies.AsyncWindowsFileStreamStrategy.ReadAsyncInternal(Memory`1 destination, CancellationToken cancellationToken)
   at System.IO.Strategies.AsyncWindowsFileStreamStrategy.ReadAsync(Memory`1 destination, CancellationToken cancellationToken)
   at System.IO.Strategies.BufferedFileStreamStrategy.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
   at System.IO.FileStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)

The source repo is currently private, but if more information or testing is needed from my side then I'll do it willingly.

@dotnet-issue-labeler dotnet-issue-labeler bot added area-System.IO untriaged New issue has not been triaged by the area owner labels Jun 14, 2021
@ghost
Copy link

ghost commented Jun 14, 2021

Tagging subscribers to this area: @dotnet/area-system-io
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

FileStreams opened with isAsync: true fails to perform asynchronous read operations. Not specifying isAsync: true runs completely fine.

The function used to get a FileStream:

private void InitializeFileStream(string filePath, bool exclusive, int readSize, int writeSize)
{
    FileAccess access = 0;
    if (readSize > 0)
        access |= FileAccess.Read;
    if (writeSize > 0)
        access |= FileAccess.Write;

    FileShare share = exclusive ? FileShare.None : (FileShare)access;
    FileStream = new FileStream(filePath, FileMode.Open, access, share, Math.Max(readSize, writeSize), true);
}

Run loop of the task:

private async Task ReadPoll()
{
    while (_run)
    {
        int bytesReturned = await FileStream!.ReadAsync(_readBuffer);
        OnInput(_readBuffer.Slice(0, bytesReturned));
    }
}

Configuration

OS Version Windows 10 21H1
.NET Version .NET 6 preview 4

Regression?

I'm not sure...

Other information

An unhandled exception of type 'System.IO.IOException' occurred in System.Private.CoreLib.dll: 'Incorrect function. : '\\?\hid#vid_28bd&pid_0906&mi_02#7&194ad48b&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}''
   at System.IO.Strategies.FileStreamHelpers.GetFileLength(SafeFileHandle handle, String path)
   at System.IO.Strategies.WindowsFileStreamStrategy.get_Length()
   at System.IO.Strategies.AsyncWindowsFileStreamStrategy.ReadAsyncInternal(Memory`1 destination, CancellationToken cancellationToken)
   at System.IO.Strategies.AsyncWindowsFileStreamStrategy.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.Stream.Read(Span`1 buffer)
   at System.IO.Strategies.BufferedFileStreamStrategy.ReadSpan(Span`1 destination, ArraySegment`1 arraySegment)
   at System.IO.FileStream.Read(Span`1 buffer)

The source repo is currently private, but if more information or testing is needed from my side then I'll do it willingly.

Author: X9VoiD
Assignees: -
Labels:

area-System.IO, untriaged

Milestone: -

@adamsitnik
Copy link
Member

Hi @X9VoiD

Are you sure that you have provided the right example and stack trace?

In the source code I can see a call to ReadAsync

await FileStream!.ReadAsync(_readBuffer);

While in the stack trace I can see a call to Read (sync, not async):

System.IO.FileStream.Read(Span`1 buffer)

The method that should be invoked:

return _strategy.ReadAsync(buffer, cancellationToken);

@X9VoiD
Copy link
Author

X9VoiD commented Jun 14, 2021

My bad, here's the correct stack trace:

Exception has occurred: CLR/System.IO.IOException
An exception of type 'System.IO.IOException' occurred in System.Private.CoreLib.dll but was not handled in user code: 'Incorrect function. : '\\?\hid#vid_28bd&pid_0906&mi_02#7&194ad48b&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}''
   at System.IO.Strategies.FileStreamHelpers.GetFileLength(SafeFileHandle handle, String path)
   at System.IO.Strategies.WindowsFileStreamStrategy.get_Length()
   at System.IO.Strategies.AsyncWindowsFileStreamStrategy.ReadAsyncInternal(Memory`1 destination, CancellationToken cancellationToken)
   at System.IO.Strategies.AsyncWindowsFileStreamStrategy.ReadAsync(Memory`1 destination, CancellationToken cancellationToken)
   at System.IO.Strategies.BufferedFileStreamStrategy.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
   at System.IO.FileStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)

Also updated the stack trace in issue.

@adamsitnik
Copy link
Member

@X9VoiD Could you please try to run your example with the following env var enabled:

set DOTNET_SYSTEM_IO_USENET5COMPATFILESTREAM=1

And see if it works?

Which Windows version are you using? Is the file a local file or a file located on a remote machine? What is the file system?

@X9VoiD
Copy link
Author

X9VoiD commented Jun 14, 2021

I'm running Windows 10 21H1, the latest one, and the file is a local HID device found through SetupDi functions.

I've set the environment variable on either the cmd prompt or globally like this;
image

Then I rebuilt it just to be sure, but both still failed with this stack trace:

Exception has occurred: CLR/System.IO.IOException
An exception of type 'System.IO.IOException' occurred in System.Private.CoreLib.dll but was not handled in user code: 'Incorrect function. : '\\?\hid#vid_28bd&pid_0906&mi_02#7&194ad48b&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}''
   at System.IO.Strategies.FileStreamHelpers.GetFileLength(SafeFileHandle handle, String path)
   at System.IO.Strategies.WindowsFileStreamStrategy.get_Length()
   at System.IO.Strategies.AsyncWindowsFileStreamStrategy.ReadAsyncInternal(Memory`1 destination, CancellationToken cancellationToken)
   at System.IO.Strategies.AsyncWindowsFileStreamStrategy.ReadAsync(Memory`1 destination, CancellationToken cancellationToken)
   at System.IO.Strategies.BufferedFileStreamStrategy.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
   at System.IO.FileStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)

@stephentoub
Copy link
Member

stephentoub commented Jun 14, 2021

but both still failed with this stack trace

If that's the exact stack trace you saw in both cases, then the environment variable didn't properly configure things. Can you share the exact stack trace you got with the environment variable set?

@stephentoub
Copy link
Member

Regression? I'm not sure...

Does it work for you with .NET 5 or any other version of .NET besides .NET 6?

@adamsitnik
Copy link
Member

@X9VoiD could you please run the following app by providing the path as first argument?

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>
</Project>
using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;

namespace Issue54143
{
    class Program
    {
        static void Main(string[] args)
        {
            Test(args[0]);
            Test(Path.GetFullPath(args[0]));
        }

        private static unsafe void Test(string path)
        {
            Console.WriteLine($"Path: '{path}'");
            using (SafeFileHandle handle = CreateFile(path, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, (int)FileOptions.Asynchronous, IntPtr.Zero))
            {
                Console.WriteLine($"IsInvalid: {handle.IsInvalid}");
                Console.WriteLine($"GetFileType(): {GetFileType(handle)}");

                FILE_STANDARD_INFO info;
                if (!GetFileInformationByHandleEx(handle, 1 /* FILE_STANDARD_INFO */, &info, (uint)sizeof(FILE_STANDARD_INFO)))
                {
                    Console.WriteLine($"Marshal.GetLastWin32Error(): {Marshal.GetLastWin32Error()}");
                }
                else
                {
                    Console.WriteLine($"EndOfFile: {info.EndOfFile}");
                }
            }
        }

        [DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false, ExactSpelling = true)]
        private static extern unsafe SafeFileHandle CreateFile(
             string lpFileName,
             FileAccess dwDesiredAccess,
             FileShare dwShareMode,
             IntPtr lpSecurityAttributes,
             FileMode dwCreationDisposition,
             int dwFlagsAndAttributes,
             IntPtr hTemplateFile);

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern int GetFileType(SafeHandle handle);

        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        internal static extern unsafe bool GetFileInformationByHandleEx(SafeFileHandle hFile, int FileInformationClass, void* lpFileInformation, uint dwBufferSize);

        internal enum BOOL : int
        {
            FALSE = 0,
            TRUE = 1,
        }

        internal struct FILE_STANDARD_INFO
        {
            internal long AllocationSize;
            internal long EndOfFile;
            internal uint NumberOfLinks;
            internal BOOL DeletePending;
            internal BOOL Directory;
        }
    }
}
dotnet run -- "thePathGoesHere"

@adamsitnik adamsitnik self-assigned this Jun 15, 2021
@X9VoiD
Copy link
Author

X9VoiD commented Jun 15, 2021

dotnet run -- "\\?\hid#vid_28bd&pid_0906&mi_02#7&194ad48b&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
Path: '\\?\hid#vid_28bd&pid_0906&mi_02#7&194ad48b&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}'
IsInvalid: False
GetFileType(): 0
Marshal.GetLastWin32Error(): 1
Path: '\\?\hid#vid_28bd&pid_0906&mi_02#7&194ad48b&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}'
IsInvalid: False
GetFileType(): 0
Marshal.GetLastWin32Error(): 1

@adamsitnik
Copy link
Member

@X9VoiD big thanks for providing all the information. Looking at the results you have provided we have most probably never supported this scenario (GetFileType() returns 0 which means UNKNOWN file type) but I think that we should be able to get it working by recognizing such files as non-seekable files. The problem is that I can't repro the problem locally...

Is there any chance that you could tell me which sys-calls do you use exactly to get such a path? Or help me to create a repro? This is what I got so far:

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace DeviceIds
{
    class Program
    {
        static void Main(string[] args)
        {
            const int DIGCF_PRESENT = 0x2;
            const int DIGCF_ALLCLASSES = 0x4;

            IntPtr deviceInfoSet = SetupDiGetClassDevs(IntPtr.Zero, "HID", IntPtr.Zero, DIGCF_ALLCLASSES | DIGCF_PRESENT);

            try
            {
                SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA();
                deviceInfoData.cbSize = (uint)Marshal.SizeOf(deviceInfoData);

                int deviceIndex = 0;
                while (SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex++, ref deviceInfoData))
                {
                    StringBuilder deviceInstanceId = new StringBuilder(1024);
                    if (SetupDiGetDeviceInstanceId(deviceInfoSet, ref deviceInfoData, deviceInstanceId, deviceInstanceId.Capacity, out int requiredSize))
                    {
                        Console.WriteLine(deviceInstanceId.ToString());
                    }
                }
            }
            finally
            {
                SetupDiDestroyDeviceInfoList(deviceInfoSet);
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        struct SP_DEVICE_INTERFACE_DATA
        {
            public int cbSize;
            public Guid interfaceClassGuid;
            public int flags;
            private UIntPtr reserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct SP_DEVINFO_DATA
        {
            public uint cbSize;
            public Guid ClassGuid;
            public uint DevInst;
            public IntPtr Reserved;
        }

        [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SetupDiGetClassDevs(IntPtr ClassGuid, [MarshalAs(UnmanagedType.LPTStr)] string Enumerator, IntPtr hwndParent, int Flags);

        [DllImport("setupapi.dll", SetLastError = true)]
        public static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

        [DllImport("setupapi.dll", SetLastError = true)]
        internal static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, int MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, StringBuilder DeviceInstanceId, int DeviceInstanceIdSize, out int RequiredSize);
    }
}

Which gives me a list of device IDs:

HID\VID_0B0E&PID_24C8&MI_03&COL04\B&92AE56&0&0003
HID\VID_0B0E&PID_24C8&MI_03&COL01\B&92AE56&0&0000
HID\VID_045E&PID_07A5&MI_02&COL03\A&EF954E6&0&0002
HID\VID_045E&PID_07A5&MI_02&COL02\A&EF954E6&0&0001
HID\VID_045E&PID_07A5&MI_02&COL01\A&EF954E6&0&0000
HID\VID_0B0E&PID_24C8&MI_03&COL02\B&92AE56&0&0001
HID\VID_045E&PID_07A5&MI_00\A&1B0D0663&0&0000
HID\VID_0424&PID_274C\8&12BBBD2C&0&0000
HID\VID_045E&PID_07A5&MI_01&COL01\A&32D092A8&0&0000
HID\VID_0B0E&PID_24C8&MI_03&COL03\B&92AE56&0&0002
HID\VID_045E&PID_07A5&MI_01&COL02\A&32D092A8&0&0001

But I don't know how to get a path to a file like yours.

@X9VoiD
Copy link
Author

X9VoiD commented Jun 23, 2021

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;

namespace DeviceIds
{
    class Program
    {
        static void Main(string[] args)
        {
            const int DIGCF_PRESENT = 0x2;
            const int DIGCF_DEVICEINTERFACE = 0x10;
            const int ERROR_NO_MORE_ITEMS = 259;
            HidD_GetHidGuid(out Guid HidGuid);

            IntPtr deviceInfoSet = SetupDiGetClassDevs(in HidGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

            try
            {
                SP_DEVINFO_DATA deviceInfoData = new SP_DEVINFO_DATA();
                deviceInfoData.cbSize = (uint)Marshal.SizeOf(deviceInfoData);

                uint deviceIndex = 0;
                while (SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex++, ref deviceInfoData))
                {
                    if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
                        break;

                    SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
                    deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData);

                    if (!SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, in HidGuid, deviceIndex, ref deviceInterfaceData))
                    {
                        // throw new Win32Exception("Failed to get device interface data");
                    }

                    SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();
                    deviceInterfaceDetailData.cbSize = IntPtr.Size == 8 ? 8 : 6;

                    uint size = (uint)Marshal.SizeOf(deviceInterfaceDetailData);

                    if (!SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, ref deviceInterfaceDetailData, size, ref size, IntPtr.Zero))
                    {
                        // throw new Exception("Failed to get device interface detail");
                    }

                    Console.WriteLine(deviceInterfaceDetailData.DevicePath);
                }
            }
            finally
            {
                SetupDiDestroyDeviceInfoList(deviceInfoSet);
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        struct SP_DEVICE_INTERFACE_DATA
        {
            public int cbSize;
            public Guid interfaceClassGuid;
            public int flags;
            private UIntPtr reserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct SP_DEVINFO_DATA
        {
            public uint cbSize;
            public Guid ClassGuid;
            public uint DevInst;
            public IntPtr Reserved;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        struct SP_DEVICE_INTERFACE_DETAIL_DATA
        {
            public int cbSize;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public string DevicePath;
        }

        [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SetupDiGetClassDevs(IntPtr ClassGuid, [MarshalAs(UnmanagedType.LPTStr)] string Enumerator, IntPtr hwndParent, int Flags);

        [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SetupDiGetClassDevs(in Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, int Flags);

        [DllImport("setupapi.dll", SetLastError = true)]
        static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

        [DllImport("setupapi.dll", SetLastError = true)]
        static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet, uint MemberIndex, ref SP_DEVINFO_DATA DeviceInfoData);

        [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, StringBuilder DeviceInstanceId, int DeviceInstanceIdSize, out int RequiredSize);

        [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern bool SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, in Guid interfaceClassGuid, uint memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);

        [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, uint deviceInterfaceDetailDataSize, ref uint requiredSize, IntPtr deviceInfoData);

        [DllImport("hid.dll", SetLastError = true)]
        static extern void HidD_GetHidGuid(out Guid Guid);
    }
}

This gives me:

\\?\hid#vid_1ea7&pid_0907&mi_00#7&5b581ed&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd
\\?\hid#hid&col05#1&2d595ca7&0&0004#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_1ea7&pid_0907&mi_02&col04#7&65e2f90&0&0003#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#{3c316a9d-e0f1-4138-9ae1-72ab823e022f}&col03#1&ec462fd&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd
\\?\hid#vid_28bd&pid_0906&mi_00&col03#7&255e8608&1&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd
\\?\hid#vid_046d&pid_c084&mi_01&col02#8&39e92dbc&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_046d&pid_c084&mi_01&col03#8&39e92dbc&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_046d&pid_c084&mi_01&col04#8&39e92dbc&0&0003#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_28bd&pid_0906&mi_00&col01#7&255e8608&1&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_1ea7&pid_0907&mi_02&col03#7&65e2f90&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd
\\?\hid#vid_046d&pid_c084&mi_00#8&2225a177&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_046d&pid_c084&mi_01&col05#8&39e92dbc&0&0004#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_28bd&pid_0906&mi_00&col02#7&255e8608&1&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_1ea7&pid_0907&mi_01#7&1e21bbd5&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_046d&pid_c084&mi_01&col01#8&39e92dbc&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}\kbd
\\?\hid#{3c316a9d-e0f1-4138-9ae1-72ab823e022f}&col01#1&ec462fd&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_1ea7&pid_0907&mi_02&col01#7&65e2f90&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#{3c316a9d-e0f1-4138-9ae1-72ab823e022f}&col02#1&ec462fd&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_1ea7&pid_0907&mi_02&col02#7&65e2f90&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#hpq6001#3&3229b8bb&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#hid&col01#1&2d595ca7&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#vid_28bd&pid_0906&mi_02#7&194ad48b&1&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#hid&col02#1&2d595ca7&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}
\\?\hid#hid&col03#1&2d595ca7&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}

Which should be a valid path. Some of these file paths though are exclusively opened by Windows, for example, all of the paths ending in \kbd

@adamsitnik
Copy link
Member

@X9VoiD big thanks for providing the repro!

I was able to confirm that it was not a regression, because it was throwing the same exception for .NET 5:

image

The good news is that it works with latest .NET 6 preview as it got fixed by #53669

Some explanation:

Now if GetFileType() returns UNKNOWN (0):

internal int GetFileType()
{
int fileType = _fileType;
if (fileType == -1)
{
_fileType = fileType = Interop.Kernel32.GetFileType(this);
Debug.Assert(fileType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK
|| fileType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE
|| fileType == Interop.Kernel32.FileTypes.FILE_TYPE_CHAR,
$"Unknown file type: {fileType}");
}
return fileType;
}

SafeFileHandle.CanSeek returns false:

internal bool CanSeek => !IsClosed && GetFileType() == Interop.Kernel32.FileTypes.FILE_TYPE_DISK;

And because of that we don't access the problematic GetFileLength:

if (CanSeek)
{
long len = Length;
if (positionBefore + destination.Length > len)
{
destination = positionBefore <= len ?
destination.Slice(0, (int)(len - positionBefore)) :
default;
}

I am going to add a test that ensures that it keeps working.

@X9VoiD in theory it should work with the Preview 6 bits but it should definitely work with latest bits. The links come from https://github.com/dotnet/installer

@adamsitnik adamsitnik removed the untriaged New issue has not been triaged by the area owner label Jun 24, 2021
@adamsitnik adamsitnik added this to the 6.0.0 milestone Jun 24, 2021
@adamsitnik
Copy link
Member

Since this works thanks to #53669 and I've added tests to ensure it keeps working in #54673, I am closing the issue.

@X9VoiD once again big thanks for all the help with creating the repro!

@ghost ghost locked as resolved and limited conversation to collaborators Jul 31, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants