Added support for ATA commands using Windows 2000/XP undocumented

IOCTL that predates Windows 2003 documented one.
This commit is contained in:
2017-09-10 23:20:59 +01:00
parent d60060d266
commit fe1c3e13b4
5 changed files with 185 additions and 1 deletions

View File

@@ -144,6 +144,23 @@ namespace DiscImageChef.Devices
{ {
case Interop.PlatformID.Win32NT: case Interop.PlatformID.Win32NT:
{ {
if(
( // Windows XP <= SP1
Environment.OSVersion.Version.Major == 5 &&
Environment.OSVersion.Version.Minor == 1 &&
(Environment.OSVersion.ServicePack == "Service Pack 1" || Environment.OSVersion.ServicePack == "")) ||
( // Windows 2000
Environment.OSVersion.Version.Major == 5 &&
Environment.OSVersion.Version.Minor == 0)
)
return Windows.Command.SendIdeCommand((SafeFileHandle)fd, registers, out errorRegisters,
protocol, ref buffer, timeout, out duration, out sense);
// Windows NT 4 or earlier, requires special ATA pass thru SCSI. But DiscImageChef cannot run there (or can it?)
if(
Environment.OSVersion.Version.Major <= 4
)
throw new InvalidOperationException("Windows NT 4.0 or earlier is not supported.");
return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters,
protocol, ref buffer, timeout, out duration, out sense); protocol, ref buffer, timeout, out duration, out sense);
} }
@@ -177,6 +194,23 @@ namespace DiscImageChef.Devices
{ {
case Interop.PlatformID.Win32NT: case Interop.PlatformID.Win32NT:
{ {
if(
( // Windows XP <= SP1
Environment.OSVersion.Version.Major == 5 &&
Environment.OSVersion.Version.Minor == 1 &&
(Environment.OSVersion.ServicePack == "Service Pack 1" || Environment.OSVersion.ServicePack == "")) ||
( // Windows 2000
Environment.OSVersion.Version.Major == 5 &&
Environment.OSVersion.Version.Minor == 0)
)
return Windows.Command.SendIdeCommand((SafeFileHandle)fd, registers, out errorRegisters,
protocol, ref buffer, timeout, out duration, out sense);
// Windows NT 4 or earlier, requires special ATA pass thru SCSI. But DiscImageChef cannot run there (or can it?)
if(
Environment.OSVersion.Version.Major <= 4
)
throw new InvalidOperationException("Windows NT 4.0 or earlier is not supported.");
return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters,
protocol, ref buffer, timeout, out duration, out sense); protocol, ref buffer, timeout, out duration, out sense);
} }
@@ -210,6 +244,7 @@ namespace DiscImageChef.Devices
{ {
case Interop.PlatformID.Win32NT: case Interop.PlatformID.Win32NT:
{ {
// No check for Windows version. A 48-bit ATA disk simply does not work on earlier systems
return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters,
protocol, ref buffer, timeout, out duration, out sense); protocol, ref buffer, timeout, out duration, out sense);
} }

View File

@@ -363,6 +363,124 @@ namespace DiscImageChef.Devices.Windows
return error; return error;
} }
internal static int SendIdeCommand(SafeFileHandle fd, AtaRegistersCHS registers, out AtaErrorRegistersCHS errorRegisters,
AtaProtocol protocol, ref byte[] buffer, uint timeout, out double duration, out bool sense)
{
duration = 0;
sense = false;
errorRegisters = new AtaErrorRegistersCHS();
if(buffer == null || buffer.Length > 512)
return -1;
IdePassThroughDirect iptd = new IdePassThroughDirect
{
CurrentTaskFile = new AtaTaskFile
{
Command = registers.command,
CylinderHigh = registers.cylinderHigh,
CylinderLow = registers.cylinderLow,
DeviceHead = registers.deviceHead,
Features = registers.feature,
SectorCount = registers.sectorCount,
SectorNumber = registers.sector
},
DataBufferSize = 512,
DataBuffer = new byte[512],
};
uint k = 0;
int error = 0;
Array.Copy(buffer, 0, iptd.DataBuffer, 0, buffer.Length);
DateTime start = DateTime.Now;
sense = !Extern.DeviceIoControlIde(fd, WindowsIoctl.IOCTL_IDE_PASS_THROUGH, ref iptd, (uint)Marshal.SizeOf(iptd), ref iptd,
(uint)Marshal.SizeOf(iptd), ref k, IntPtr.Zero);
DateTime end = DateTime.Now;
if(sense)
error = Marshal.GetLastWin32Error();
buffer = new byte[k - 12];
Array.Copy(iptd.DataBuffer, 0, buffer, 0, buffer.Length);
duration = (end - start).TotalMilliseconds;
errorRegisters.command = iptd.CurrentTaskFile.Command;
errorRegisters.cylinderHigh = iptd.CurrentTaskFile.CylinderHigh;
errorRegisters.cylinderLow = iptd.CurrentTaskFile.CylinderLow;
errorRegisters.deviceHead = iptd.CurrentTaskFile.DeviceHead;
errorRegisters.error = iptd.CurrentTaskFile.Error;
errorRegisters.sector = iptd.CurrentTaskFile.SectorNumber;
errorRegisters.sectorCount = iptd.CurrentTaskFile.SectorCount;
errorRegisters.status = iptd.CurrentTaskFile.Status;
sense = errorRegisters.error != 0 || (errorRegisters.status & 0xA5) != 0;
return error;
}
internal static int SendIdeCommand(SafeFileHandle fd, AtaRegistersLBA28 registers, out AtaErrorRegistersLBA28 errorRegisters,
AtaProtocol protocol, ref byte[] buffer, uint timeout, out double duration, out bool sense)
{
duration = 0;
sense = false;
errorRegisters = new AtaErrorRegistersLBA28();
if(buffer == null)
return -1;
uint offsetForBuffer = (uint)(Marshal.SizeOf(typeof(AtaPassThroughDirect)) + Marshal.SizeOf(typeof(uint)));
IdePassThroughDirect iptd = new IdePassThroughDirect
{
CurrentTaskFile = new AtaTaskFile
{
Command = registers.command,
CylinderHigh = registers.lbaHigh,
CylinderLow = registers.lbaMid,
DeviceHead = registers.deviceHead,
Features = registers.feature,
SectorCount = registers.sectorCount,
SectorNumber = registers.lbaLow
},
DataBufferSize = 512,
DataBuffer = new byte[512],
};
uint k = 0;
int error = 0;
Array.Copy(buffer, 0, iptd.DataBuffer, 0, buffer.Length);
DateTime start = DateTime.Now;
sense = !Extern.DeviceIoControlIde(fd, WindowsIoctl.IOCTL_IDE_PASS_THROUGH, ref iptd, (uint)Marshal.SizeOf(iptd), ref iptd,
(uint)Marshal.SizeOf(iptd), ref k, IntPtr.Zero);
DateTime end = DateTime.Now;
if(sense)
error = Marshal.GetLastWin32Error();
buffer = new byte[k - 12];
Array.Copy(iptd.DataBuffer, 0, buffer, 0, buffer.Length);
duration = (end - start).TotalMilliseconds;
errorRegisters.command = iptd.CurrentTaskFile.Command;
errorRegisters.lbaHigh = iptd.CurrentTaskFile.CylinderHigh;
errorRegisters.lbaMid = iptd.CurrentTaskFile.CylinderLow;
errorRegisters.deviceHead = iptd.CurrentTaskFile.DeviceHead;
errorRegisters.error = iptd.CurrentTaskFile.Error;
errorRegisters.lbaLow = iptd.CurrentTaskFile.SectorNumber;
errorRegisters.sectorCount = iptd.CurrentTaskFile.SectorCount;
errorRegisters.status = iptd.CurrentTaskFile.Status;
sense = errorRegisters.error != 0 || (errorRegisters.status & 0xA5) != 0;
return error;
}
} }
} }

View File

@@ -270,6 +270,7 @@ namespace DiscImageChef.Devices.Windows
/// </summary> /// </summary>
IOCTL_SCSI_GET_ADDRESS = 0x41018, IOCTL_SCSI_GET_ADDRESS = 0x41018,
IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400, IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400,
IOCTL_IDE_PASS_THROUGH = 0x4D028,
} }
[Flags] [Flags]

View File

@@ -84,7 +84,19 @@ namespace DiscImageChef.Devices.Windows
uint nOutBufferSize, uint nOutBufferSize,
ref uint pBytesReturned, ref uint pBytesReturned,
IntPtr Overlapped IntPtr Overlapped
); );
[DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)]
internal static extern bool DeviceIoControlIde(
SafeFileHandle hDevice,
WindowsIoctl IoControlCode,
ref IdePassThroughDirect InBuffer,
uint nInBufferSize,
ref IdePassThroughDirect OutBuffer,
uint nOutBufferSize,
ref uint pBytesReturned,
IntPtr Overlapped
);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern bool CloseHandle(SafeFileHandle hDevice); internal static extern bool CloseHandle(SafeFileHandle hDevice);

View File

@@ -194,5 +194,23 @@ namespace DiscImageChef.Devices.Windows
public uint RawPropertiesLength; public uint RawPropertiesLength;
public byte[] RawDeviceProperties; public byte[] RawDeviceProperties;
} }
[StructLayout(LayoutKind.Sequential)]
struct IdePassThroughDirect
{
/// <summary>
/// ATA registers
/// </summary>
public AtaTaskFile CurrentTaskFile;
/// <summary>
/// Size of data buffer
/// </summary>
public uint DataBufferSize;
/// <summary>
/// Data buffer
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
public byte[] DataBuffer;
}
} }