diff --git a/DiscImageChef.Devices/Device/Constructor.cs b/DiscImageChef.Devices/Device/Constructor.cs index 1f820b57..051010e0 100644 --- a/DiscImageChef.Devices/Device/Constructor.cs +++ b/DiscImageChef.Devices/Device/Constructor.cs @@ -33,6 +33,8 @@ using System; using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; +using System.Text; +using DiscImageChef.Console; using DiscImageChef.Decoders.ATA; namespace DiscImageChef.Devices @@ -100,6 +102,7 @@ namespace DiscImageChef.Devices throw new SystemException(string.Format("Error {0} trying device.", lastError)); bool scsiSense = true; + string ntDevicePath = null; // Windows is answering SCSI INQUIRY for all device types so it needs to be detected first if(platformID == Interop.PlatformID.Win32NT) @@ -126,7 +129,20 @@ namespace DiscImageChef.Devices { Windows.StorageDeviceDescriptor descriptor = new Windows.StorageDeviceDescriptor(); + descriptor.Version = BitConverter.ToUInt32(descriptor_b, 0); + descriptor.Size = BitConverter.ToUInt32(descriptor_b, 4); + descriptor.DeviceType = descriptor_b[8]; + descriptor.DeviceTypeModifier= descriptor_b[9]; + descriptor.RemovableMedia = descriptor_b[10] > 0; + descriptor.CommandQueueing = descriptor_b[11] > 0; + descriptor.VendorIdOffset = BitConverter.ToUInt32(descriptor_b, 12); + descriptor.ProductIdOffset = BitConverter.ToUInt32(descriptor_b, 16); + descriptor.ProductRevisionOffset = BitConverter.ToUInt32(descriptor_b, 20); + descriptor.SerialNumberOffset = BitConverter.ToUInt32(descriptor_b, 24); descriptor.BusType = (Windows.StorageBusType)BitConverter.ToUInt32(descriptor_b, 28); + descriptor.RawPropertiesLength = BitConverter.ToUInt32(descriptor_b, 32); + descriptor.RawDeviceProperties = new byte[descriptor.RawPropertiesLength]; + Array.Copy(descriptor_b, 36, descriptor.RawDeviceProperties, 0, descriptor.RawPropertiesLength); switch(descriptor.BusType) { @@ -180,9 +196,12 @@ namespace DiscImageChef.Devices else manufacturer = "ATA"; } + // TODO: Get cached CID, CSD and SCR from kernel space } - + + ntDevicePath = Windows.Command.GetDevicePath((SafeFileHandle)fd); + DicConsole.DebugWriteLine("Windows devices", "NT device path: {0}", ntDevicePath); Marshal.FreeHGlobal(descriptorPtr); } else @@ -246,9 +265,12 @@ namespace DiscImageChef.Devices } #region USB - if (platformID == Interop.PlatformID.Linux) + + if(platformID == Interop.PlatformID.Linux) { - if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) + if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) { string devPath = devicePath.Substring(5); if(System.IO.Directory.Exists("/sys/block/" + devPath)) @@ -261,14 +283,15 @@ namespace DiscImageChef.Devices { resolvedLink = System.IO.Path.GetDirectoryName(resolvedLink); if(System.IO.File.Exists(resolvedLink + "/descriptors") && - System.IO.File.Exists(resolvedLink + "/idProduct") && - System.IO.File.Exists(resolvedLink + "/idVendor")) + System.IO.File.Exists(resolvedLink + "/idProduct") && + System.IO.File.Exists(resolvedLink + "/idVendor")) { System.IO.FileStream usbFs; System.IO.StreamReader usbSr; string usbTemp; - usbFs = new System.IO.FileStream(resolvedLink + "/descriptors", System.IO.FileMode.Open, System.IO.FileAccess.Read); + usbFs = new System.IO.FileStream(resolvedLink + "/descriptors", + System.IO.FileMode.Open, System.IO.FileAccess.Read); byte[] usbBuf = new byte[65536]; int usbCount = usbFs.Read(usbBuf, 0, 65536); usbDescriptors = new byte[usbCount]; @@ -277,12 +300,14 @@ namespace DiscImageChef.Devices usbSr = new System.IO.StreamReader(resolvedLink + "/idProduct"); usbTemp = usbSr.ReadToEnd(); - ushort.TryParse(usbTemp, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out usbProduct); + ushort.TryParse(usbTemp, System.Globalization.NumberStyles.HexNumber, + System.Globalization.CultureInfo.InvariantCulture, out usbProduct); usbSr.Close(); usbSr = new System.IO.StreamReader(resolvedLink + "/idVendor"); usbTemp = usbSr.ReadToEnd(); - ushort.TryParse(usbTemp, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out usbVendor); + ushort.TryParse(usbTemp, System.Globalization.NumberStyles.HexNumber, + System.Globalization.CultureInfo.InvariantCulture, out usbVendor); usbSr.Close(); if(System.IO.File.Exists(resolvedLink + "/manufacturer")) @@ -313,6 +338,10 @@ namespace DiscImageChef.Devices } } } + } + else if(platformID == Interop.PlatformID.Win32NT) + { + } // TODO: Implement for other operating systems else diff --git a/DiscImageChef.Devices/Windows/Command.cs b/DiscImageChef.Devices/Windows/Command.cs index 3452f498..8c7ec55d 100644 --- a/DiscImageChef.Devices/Windows/Command.cs +++ b/DiscImageChef.Devices/Windows/Command.cs @@ -32,8 +32,10 @@ // ****************************************************************************/ using System; +using System.Net.NetworkInformation; using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; +using System.Text; using DiscImageChef.Decoders.ATA; namespace DiscImageChef.Devices.Windows @@ -481,6 +483,93 @@ namespace DiscImageChef.Devices.Windows return error; } + + internal static uint GetDeviceNumber(SafeFileHandle deviceHandle) + { + StorageDeviceNumber sdn = new StorageDeviceNumber(); + sdn.deviceNumber = - 1; + uint k = 0; + if(!Extern.DeviceIoControlGetDeviceNumber(deviceHandle, WindowsIoctl.IOCTL_STORAGE_GET_DEVICE_NUMBER, IntPtr.Zero, + 0, ref sdn, (uint)Marshal.SizeOf(sdn), ref k, IntPtr.Zero)) + { + return uint.MaxValue; + } + + return (uint)sdn.deviceNumber; + } + + internal static string GetDevicePath(SafeFileHandle fd) + { + uint devNumber = GetDeviceNumber(fd); + + if(devNumber == uint.MaxValue) + return null; + + SafeFileHandle hDevInfo = Extern.SetupDiGetClassDevs(ref Consts.GUID_DEVINTERFACE_DISK, IntPtr.Zero, + IntPtr.Zero, DeviceGetClassFlags.Present | DeviceGetClassFlags.DeviceInterface); + + if(hDevInfo.IsInvalid) + return null; + + uint index = 0; + DeviceInterfaceData spdid = new DeviceInterfaceData(); + spdid.cbSize = Marshal.SizeOf(spdid); + + byte[] buffer; + + while(true) + { + buffer = new byte[2048]; + + if(!Extern.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref Consts.GUID_DEVINTERFACE_DISK, index, + ref spdid)) + break; + + uint size = 0; + + Extern.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref spdid, IntPtr.Zero, 0, ref size, IntPtr.Zero); + + if(size > 0 && size < buffer.Length) + { + buffer[0] = (byte)(IntPtr.Size == 8 ? IntPtr.Size : IntPtr.Size + Marshal.SystemDefaultCharSize); // Funny... + + IntPtr pspdidd = Marshal.AllocHGlobal(buffer.Length); + Marshal.Copy(buffer, 0, pspdidd, buffer.Length); + + bool result = + Extern.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref spdid, pspdidd, size, ref size, IntPtr.Zero); + + buffer = new byte[size]; + Marshal.Copy(pspdidd, buffer, 0, buffer.Length); + Marshal.FreeHGlobal(pspdidd); + + if(result) + { + string devicePath = Encoding.Unicode.GetString(buffer, 4, (int)size - 4); + SafeFileHandle hDrive = Extern.CreateFile(devicePath, 0, FileShare.Read | FileShare.Write, + IntPtr.Zero, FileMode.OpenExisting, 0, IntPtr.Zero); + + if(!hDrive.IsInvalid) + { + uint newDeviceNumber = GetDeviceNumber(hDrive); + + if(newDeviceNumber == devNumber) + { + Extern.CloseHandle(hDrive); + return devicePath; + } + } + + Extern.CloseHandle(hDrive); + } + } + + index++; + } + + Extern.SetupDiDestroyDeviceInfoList(hDevInfo); + return null; + } } } diff --git a/DiscImageChef.Devices/Windows/Enums.cs b/DiscImageChef.Devices/Windows/Enums.cs index 5b54fa3b..4e371b82 100644 --- a/DiscImageChef.Devices/Windows/Enums.cs +++ b/DiscImageChef.Devices/Windows/Enums.cs @@ -105,7 +105,7 @@ namespace DiscImageChef.Devices.Windows /// /// FILE_ATTRIBUTE_VIRTUAL /// - Virtual = 0x10000 + Virtual = 0x10000, } [Flags] @@ -271,6 +271,7 @@ namespace DiscImageChef.Devices.Windows IOCTL_SCSI_GET_ADDRESS = 0x41018, IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400, IOCTL_IDE_PASS_THROUGH = 0x4D028, + IOCTL_STORAGE_GET_DEVICE_NUMBER = 0x2D1080, } [Flags] @@ -349,4 +350,34 @@ namespace DiscImageChef.Devices.Windows FileBackedVirtual = 0xF, NVMe = 0x11, } + + [Flags] + enum DeviceGetClassFlags : uint + { + /// + /// DIGCF_DEFAULT + /// + Default = 0x01, + /// + /// DIGCF_PRESENT + /// + Present = 0x02, + /// + /// DIGCF_ALLCLASSES + /// + AllClasses = 0x04, + /// + /// DIGCF_PROFILE + /// + Profile = 0x08, + /// + /// DIGCF_DEVICEINTERFACE + /// + DeviceInterface = 0x10, + } + + static class Consts + { + public static Guid GUID_DEVINTERFACE_DISK = new Guid(0x53F56307, 0xB6BF, 0x11D0, 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B); + } } diff --git a/DiscImageChef.Devices/Windows/Extern.cs b/DiscImageChef.Devices/Windows/Extern.cs index 1356d868..44936be2 100644 --- a/DiscImageChef.Devices/Windows/Extern.cs +++ b/DiscImageChef.Devices/Windows/Extern.cs @@ -98,6 +98,49 @@ namespace DiscImageChef.Devices.Windows IntPtr Overlapped ); + + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControlGetDeviceNumber( + SafeFileHandle hDevice, + WindowsIoctl IoControlCode, + IntPtr InBuffer, + uint nInBufferSize, + ref StorageDeviceNumber OutBuffer, + uint nOutBufferSize, + ref uint pBytesReturned, + IntPtr Overlapped + ); + + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] + internal static extern SafeFileHandle SetupDiGetClassDevs( + ref Guid ClassGuid, + IntPtr Enumerator, + IntPtr hwndParent, + DeviceGetClassFlags Flags + ); + + [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool SetupDiEnumDeviceInterfaces( + SafeFileHandle hDevInfo, + IntPtr devInfo, + ref Guid interfaceClassGuid, + uint memberIndex, + ref DeviceInterfaceData deviceInterfaceData + ); + + [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool SetupDiGetDeviceInterfaceDetail( + SafeFileHandle hDevInfo, + ref DeviceInterfaceData deviceInterfaceData, + IntPtr deviceInterfaceDetailData, + UInt32 deviceInterfaceDetailDataSize, + ref UInt32 requiredSize, + IntPtr deviceInfoData + ); + + [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool SetupDiDestroyDeviceInfoList(SafeFileHandle hDevInfo); + [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] internal static extern bool CloseHandle(SafeFileHandle hDevice); } diff --git a/DiscImageChef.Devices/Windows/Structs.cs b/DiscImageChef.Devices/Windows/Structs.cs index c107fd6f..627ccaba 100644 --- a/DiscImageChef.Devices/Windows/Structs.cs +++ b/DiscImageChef.Devices/Windows/Structs.cs @@ -212,5 +212,31 @@ namespace DiscImageChef.Devices.Windows [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] public byte[] DataBuffer; } + + [StructLayout(LayoutKind.Sequential)] + struct StorageDeviceNumber + { + public int deviceType; + public int deviceNumber; + public int partitionNumber; + } + + [StructLayout(LayoutKind.Sequential)] + struct DeviceInfoData + { + public int cbSize; + public Guid classGuid; + public uint devInst; + public IntPtr reserved; + } + + [StructLayout(LayoutKind.Sequential)] + struct DeviceInterfaceData + { + public int cbSize; + public Guid interfaceClassGuid; + public uint flags; + private IntPtr reserved; + } }