Renamed project files and folders

This commit is contained in:
2020-02-26 19:10:46 +00:00
parent e133798583
commit f5b199e483
1417 changed files with 109 additions and 109 deletions

View File

@@ -0,0 +1,558 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Command.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Windows direct device access.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains a high level representation of the Windows syscalls used to
// directly interface devices.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2020 Natalia Portillo
// ****************************************************************************/
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using DiscImageChef.Decoders.ATA;
using Microsoft.Win32.SafeHandles;
namespace DiscImageChef.Devices.Windows
{
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
internal static class Command
{
/// <summary>
/// Sends a SCSI command
/// </summary>
/// <returns>0 if no error occurred, otherwise, errno</returns>
/// <param name="fd">File handle</param>
/// <param name="cdb">SCSI CDB</param>
/// <param name="buffer">Buffer for SCSI command response</param>
/// <param name="senseBuffer">Buffer with the SCSI sense</param>
/// <param name="timeout">Timeout in seconds</param>
/// <param name="direction">SCSI command transfer direction</param>
/// <param name="duration">Time it took to execute the command in milliseconds</param>
/// <param name="sense">
/// <c>True</c> if SCSI error returned non-OK status and <paramref name="senseBuffer" /> contains SCSI
/// sense
/// </param>
internal static int SendScsiCommand(SafeFileHandle fd, byte[] cdb, ref byte[] buffer,
out byte[] senseBuffer,
uint timeout, ScsiIoctlDirection direction, out double duration,
out bool sense)
{
senseBuffer = null;
duration = 0;
sense = false;
if (buffer == null) return -1;
var sptdSb = new ScsiPassThroughDirectAndSenseBuffer
{
SenseBuf = new byte[32],
sptd = new ScsiPassThroughDirect
{
Cdb = new byte[16],
CdbLength = (byte) cdb.Length,
SenseInfoLength = 32,
DataIn = direction,
DataTransferLength = (uint) buffer.Length,
TimeOutValue = timeout,
DataBuffer = Marshal.AllocHGlobal(buffer.Length)
}
};
sptdSb.sptd.Length = (ushort) Marshal.SizeOf(sptdSb.sptd);
sptdSb.sptd.SenseInfoOffset = (uint) Marshal.SizeOf(sptdSb.sptd);
Array.Copy(cdb, sptdSb.sptd.Cdb, cdb.Length);
uint k = 0;
var error = 0;
Marshal.Copy(buffer, 0, sptdSb.sptd.DataBuffer, buffer.Length);
var start = DateTime.Now;
var hasError = !Extern.DeviceIoControlScsi(fd, WindowsIoctl.IoctlScsiPassThroughDirect, ref sptdSb,
(uint) Marshal.SizeOf(sptdSb), ref sptdSb,
(uint) Marshal.SizeOf(sptdSb), ref k, IntPtr.Zero);
var end = DateTime.Now;
if (hasError) error = Marshal.GetLastWin32Error();
Marshal.Copy(sptdSb.sptd.DataBuffer, buffer, 0, buffer.Length);
sense |= sptdSb.sptd.ScsiStatus != 0;
senseBuffer = new byte[32];
Array.Copy(sptdSb.SenseBuf, senseBuffer, 32);
duration = (end - start).TotalMilliseconds;
Marshal.FreeHGlobal(sptdSb.sptd.DataBuffer);
return error;
}
/// <summary>
/// Sends an ATA command in CHS mode
/// </summary>
/// <returns>0 if no error occurred, otherwise, errno</returns>
/// <param name="fd">File handle</param>
/// <param name="buffer">Buffer for SCSI command response</param>
/// <param name="timeout">Timeout in seconds</param>
/// <param name="duration">Time it took to execute the command in milliseconds</param>
/// <param name="sense"><c>True</c> if ATA error returned non-OK status</param>
/// <param name="registers">Registers to send to drive</param>
/// <param name="errorRegisters">Registers returned by drive</param>
/// <param name="protocol">ATA protocol to use</param>
internal static int SendAtaCommand(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) return -1;
var offsetForBuffer = (uint) (Marshal.SizeOf(typeof(AtaPassThroughEx)) + Marshal.SizeOf(typeof(uint)));
var aptdBuf = new AtaPassThroughExBuffer
{
aptd = new AtaPassThroughEx
{
TimeOutValue = timeout,
DataBufferOffset = (IntPtr) offsetForBuffer,
Length = (ushort) Marshal.SizeOf(typeof(AtaPassThroughEx)),
DataTransferLength = (uint) buffer.Length,
PreviousTaskFile = new AtaTaskFile(),
CurrentTaskFile = new AtaTaskFile
{
Command = registers.Command,
CylinderHigh = registers.CylinderHigh,
CylinderLow = registers.CylinderLow,
DeviceHead = registers.DeviceHead,
Features = registers.Feature,
SectorCount = registers.SectorCount,
SectorNumber = registers.Sector
}
},
dataBuffer = new byte[64 * 512]
};
switch (protocol)
{
case AtaProtocol.PioIn:
case AtaProtocol.UDmaIn:
case AtaProtocol.Dma:
aptdBuf.aptd.AtaFlags = AtaFlags.DataIn;
break;
case AtaProtocol.PioOut:
case AtaProtocol.UDmaOut:
aptdBuf.aptd.AtaFlags = AtaFlags.DataOut;
break;
}
switch (protocol)
{
case AtaProtocol.Dma:
case AtaProtocol.DmaQueued:
case AtaProtocol.FpDma:
case AtaProtocol.UDmaIn:
case AtaProtocol.UDmaOut:
aptdBuf.aptd.AtaFlags |= AtaFlags.Dma;
break;
}
// Unknown if needed
aptdBuf.aptd.AtaFlags |= AtaFlags.DrdyRequired;
uint k = 0;
var error = 0;
Array.Copy(buffer, 0, aptdBuf.dataBuffer, 0, buffer.Length);
var start = DateTime.Now;
sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThrough, ref aptdBuf,
(uint) Marshal.SizeOf(aptdBuf), ref aptdBuf,
(uint) Marshal.SizeOf(aptdBuf), ref k, IntPtr.Zero);
var end = DateTime.Now;
if (sense) error = Marshal.GetLastWin32Error();
Array.Copy(aptdBuf.dataBuffer, 0, buffer, 0, buffer.Length);
duration = (end - start).TotalMilliseconds;
errorRegisters.CylinderHigh = aptdBuf.aptd.CurrentTaskFile.CylinderHigh;
errorRegisters.CylinderLow = aptdBuf.aptd.CurrentTaskFile.CylinderLow;
errorRegisters.DeviceHead = aptdBuf.aptd.CurrentTaskFile.DeviceHead;
errorRegisters.Error = aptdBuf.aptd.CurrentTaskFile.Error;
errorRegisters.Sector = aptdBuf.aptd.CurrentTaskFile.SectorNumber;
errorRegisters.SectorCount = aptdBuf.aptd.CurrentTaskFile.SectorCount;
errorRegisters.Status = aptdBuf.aptd.CurrentTaskFile.Status;
sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0;
return error;
}
/// <summary>
/// Sends an ATA command in 28-bit LBA mode
/// </summary>
/// <returns>0 if no error occurred, otherwise, errno</returns>
/// <param name="fd">File handle</param>
/// <param name="buffer">Buffer for SCSI command response</param>
/// <param name="timeout">Timeout in seconds</param>
/// <param name="duration">Time it took to execute the command in milliseconds</param>
/// <param name="sense"><c>True</c> if ATA error returned non-OK status</param>
/// <param name="registers">Registers to send to drive</param>
/// <param name="errorRegisters">Registers returned by drive</param>
/// <param name="protocol">ATA protocol to use</param>
internal static int SendAtaCommand(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;
var offsetForBuffer = (uint) (Marshal.SizeOf(typeof(AtaPassThroughEx)) + Marshal.SizeOf(typeof(uint)));
var aptdBuf = new AtaPassThroughExBuffer
{
aptd = new AtaPassThroughEx
{
TimeOutValue = timeout,
DataBufferOffset = (IntPtr) offsetForBuffer,
Length = (ushort) Marshal.SizeOf(typeof(AtaPassThroughEx)),
DataTransferLength = (uint) buffer.Length,
PreviousTaskFile = new AtaTaskFile(),
CurrentTaskFile = new AtaTaskFile
{
Command = registers.Command,
CylinderHigh = registers.LbaHigh,
CylinderLow = registers.LbaMid,
DeviceHead = registers.DeviceHead,
Features = registers.Feature,
SectorCount = registers.SectorCount,
SectorNumber = registers.LbaLow
}
},
dataBuffer = new byte[64 * 512]
};
switch (protocol)
{
case AtaProtocol.PioIn:
case AtaProtocol.UDmaIn:
case AtaProtocol.Dma:
aptdBuf.aptd.AtaFlags = AtaFlags.DataIn;
break;
case AtaProtocol.PioOut:
case AtaProtocol.UDmaOut:
aptdBuf.aptd.AtaFlags = AtaFlags.DataOut;
break;
}
switch (protocol)
{
case AtaProtocol.Dma:
case AtaProtocol.DmaQueued:
case AtaProtocol.FpDma:
case AtaProtocol.UDmaIn:
case AtaProtocol.UDmaOut:
aptdBuf.aptd.AtaFlags |= AtaFlags.Dma;
break;
}
// Unknown if needed
aptdBuf.aptd.AtaFlags |= AtaFlags.DrdyRequired;
uint k = 0;
var error = 0;
Array.Copy(buffer, 0, aptdBuf.dataBuffer, 0, buffer.Length);
var start = DateTime.Now;
sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThrough, ref aptdBuf,
(uint) Marshal.SizeOf(aptdBuf), ref aptdBuf,
(uint) Marshal.SizeOf(aptdBuf), ref k, IntPtr.Zero);
var end = DateTime.Now;
if (sense) error = Marshal.GetLastWin32Error();
Array.Copy(aptdBuf.dataBuffer, 0, buffer, 0, buffer.Length);
duration = (end - start).TotalMilliseconds;
errorRegisters.LbaHigh = aptdBuf.aptd.CurrentTaskFile.CylinderHigh;
errorRegisters.LbaMid = aptdBuf.aptd.CurrentTaskFile.CylinderLow;
errorRegisters.DeviceHead = aptdBuf.aptd.CurrentTaskFile.DeviceHead;
errorRegisters.Error = aptdBuf.aptd.CurrentTaskFile.Error;
errorRegisters.LbaLow = aptdBuf.aptd.CurrentTaskFile.SectorNumber;
errorRegisters.SectorCount = aptdBuf.aptd.CurrentTaskFile.SectorCount;
errorRegisters.Status = aptdBuf.aptd.CurrentTaskFile.Status;
sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0;
return error;
}
/// <summary>
/// Sends an ATA command in 48-bit LBA mode
/// </summary>
/// <returns>0 if no error occurred, otherwise, errno</returns>
/// <param name="fd">File handle</param>
/// <param name="buffer">Buffer for SCSI command response</param>
/// <param name="timeout">Timeout in seconds</param>
/// <param name="duration">Time it took to execute the command in milliseconds</param>
/// <param name="sense"><c>True</c> if ATA error returned non-OK status</param>
/// <param name="registers">Registers to send to drive</param>
/// <param name="errorRegisters">Registers returned by drive</param>
/// <param name="protocol">ATA protocol to use</param>
internal static int SendAtaCommand(SafeFileHandle fd, AtaRegistersLba48 registers,
out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol,
ref byte[] buffer, uint timeout,
out double duration, out bool sense)
{
duration = 0;
sense = false;
errorRegisters = new AtaErrorRegistersLba48();
if (buffer == null) return -1;
var offsetForBuffer = (uint) (Marshal.SizeOf(typeof(AtaPassThroughEx)) + Marshal.SizeOf(typeof(uint)));
var aptdBuf = new AtaPassThroughExBuffer
{
aptd = new AtaPassThroughEx
{
TimeOutValue = timeout,
DataBufferOffset = (IntPtr) offsetForBuffer,
Length = (ushort) Marshal.SizeOf(typeof(AtaPassThroughEx)),
DataTransferLength = (uint) buffer.Length,
PreviousTaskFile =
new AtaTaskFile
{
CylinderHigh = (byte) ((registers.LbaHigh & 0xFF00) >> 8),
CylinderLow = (byte) ((registers.LbaMid & 0xFF00) >> 8),
Features = (byte) ((registers.Feature & 0xFF00) >> 8),
SectorCount = (byte) ((registers.SectorCount & 0xFF00) >> 8),
SectorNumber = (byte) ((registers.LbaLow & 0xFF00) >> 8)
},
CurrentTaskFile = new AtaTaskFile
{
Command = registers.Command,
CylinderHigh = (byte) (registers.LbaHigh & 0xFF),
CylinderLow = (byte) (registers.LbaMid & 0xFF),
DeviceHead = registers.DeviceHead,
Features = (byte) (registers.Feature & 0xFF),
SectorCount = (byte) (registers.SectorCount & 0xFF),
SectorNumber = (byte) (registers.LbaLow & 0xFF)
}
},
dataBuffer = new byte[64 * 512]
};
switch (protocol)
{
case AtaProtocol.PioIn:
case AtaProtocol.UDmaIn:
case AtaProtocol.Dma:
aptdBuf.aptd.AtaFlags = AtaFlags.DataIn;
break;
case AtaProtocol.PioOut:
case AtaProtocol.UDmaOut:
aptdBuf.aptd.AtaFlags = AtaFlags.DataOut;
break;
}
switch (protocol)
{
case AtaProtocol.Dma:
case AtaProtocol.DmaQueued:
case AtaProtocol.FpDma:
case AtaProtocol.UDmaIn:
case AtaProtocol.UDmaOut:
aptdBuf.aptd.AtaFlags |= AtaFlags.Dma;
break;
}
aptdBuf.aptd.AtaFlags |= AtaFlags.ExtendedCommand;
// Unknown if needed
aptdBuf.aptd.AtaFlags |= AtaFlags.DrdyRequired;
uint k = 0;
var error = 0;
Array.Copy(buffer, 0, aptdBuf.dataBuffer, 0, buffer.Length);
var start = DateTime.Now;
sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThrough, ref aptdBuf,
(uint) Marshal.SizeOf(aptdBuf), ref aptdBuf,
(uint) Marshal.SizeOf(aptdBuf), ref k, IntPtr.Zero);
var end = DateTime.Now;
if (sense) error = Marshal.GetLastWin32Error();
Array.Copy(aptdBuf.dataBuffer, 0, buffer, 0, buffer.Length);
duration = (end - start).TotalMilliseconds;
errorRegisters.SectorCount = (ushort) ((aptdBuf.aptd.PreviousTaskFile.SectorCount << 8) +
aptdBuf.aptd.CurrentTaskFile.SectorCount);
errorRegisters.LbaLow = (ushort) ((aptdBuf.aptd.PreviousTaskFile.SectorNumber << 8) +
aptdBuf.aptd.CurrentTaskFile.SectorNumber);
errorRegisters.LbaMid = (ushort) ((aptdBuf.aptd.PreviousTaskFile.CylinderLow << 8) +
aptdBuf.aptd.CurrentTaskFile.CylinderLow);
errorRegisters.LbaHigh = (ushort) ((aptdBuf.aptd.PreviousTaskFile.CylinderHigh << 8) +
aptdBuf.aptd.CurrentTaskFile.CylinderHigh);
errorRegisters.DeviceHead = aptdBuf.aptd.CurrentTaskFile.DeviceHead;
errorRegisters.Error = aptdBuf.aptd.CurrentTaskFile.Error;
errorRegisters.Status = aptdBuf.aptd.CurrentTaskFile.Status;
sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0;
return error;
}
/// <summary>
/// Gets the device number for a specified handle
/// </summary>
/// <param name="deviceHandle">Device handle</param>
/// <returns>Device number</returns>
private static uint GetDeviceNumber(SafeFileHandle deviceHandle)
{
var sdn = new StorageDeviceNumber {deviceNumber = -1};
uint k = 0;
if (!Extern.DeviceIoControlGetDeviceNumber(deviceHandle, WindowsIoctl.IoctlStorageGetDeviceNumber,
IntPtr.Zero, 0, ref sdn, (uint) Marshal.SizeOf(sdn), ref k,
IntPtr.Zero)) return uint.MaxValue;
return (uint) sdn.deviceNumber;
}
/// <summary>
/// Returns true if the specified handle is controlled by a SFFDISK (aka SDHCI) driver
/// </summary>
/// <param name="fd">Device handle</param>
/// <returns><c>true</c> if SDHCI, false otherwise</returns>
internal static bool IsSdhci(SafeFileHandle fd)
{
var queryData1 = new SffdiskQueryDeviceProtocolData();
queryData1.size = (ushort) Marshal.SizeOf(queryData1);
Extern.DeviceIoControl(fd, WindowsIoctl.IoctlSffdiskQueryDeviceProtocol, IntPtr.Zero, 0, ref queryData1,
queryData1.size, out _, IntPtr.Zero);
return queryData1.protocolGuid.Equals(Consts.GuidSffProtocolSd) ||
queryData1.protocolGuid.Equals(Consts.GuidSffProtocolMmc);
}
/// <summary>
/// Sends a MMC/SD command
/// </summary>
/// <returns>The result of the command.</returns>
/// <param name="fd">File handle</param>
/// <param name="command">MMC/SD opcode</param>
/// <param name="buffer">Buffer for MMC/SD command response</param>
/// <param name="timeout">Timeout in seconds</param>
/// <param name="duration">Time it took to execute the command in milliseconds</param>
/// <param name="sense"><c>True</c> if MMC/SD returned non-OK status</param>
/// <param name="write"><c>True</c> if data is sent from host to card</param>
/// <param name="isApplication"><c>True</c> if command should be preceded with CMD55</param>
/// <param name="flags">Flags indicating kind and place of response</param>
/// <param name="blocks">How many blocks to transfer</param>
/// <param name="argument">Command argument</param>
/// <param name="response">Response registers</param>
/// <param name="blockSize">Size of block in bytes</param>
internal static int SendMmcCommand(SafeFileHandle fd, MmcCommands command, bool write,
bool isApplication,
MmcFlags flags, uint argument, uint blockSize,
uint blocks,
ref byte[] buffer, out uint[] response, out double duration,
out bool sense,
uint timeout = 0)
{
var commandData = new SffdiskDeviceCommandData();
var commandDescriptor = new SdCmdDescriptor();
commandData.size = (ushort) Marshal.SizeOf(commandData);
commandData.command = SffdiskDcmd.DeviceCommand;
commandData.protocolArgumentSize = (ushort) Marshal.SizeOf(commandDescriptor);
commandData.deviceDataBufferSize = blockSize * blocks;
commandDescriptor.commandCode = (byte) command;
commandDescriptor.cmdClass = isApplication ? SdCommandClass.AppCmd : SdCommandClass.Standard;
commandDescriptor.transferDirection = write ? SdTransferDirection.Write : SdTransferDirection.Read;
commandDescriptor.transferType = flags.HasFlag(MmcFlags.CommandAdtc)
? SdTransferType.SingleBlock
: SdTransferType.CmdOnly;
commandDescriptor.responseType = 0;
if (flags.HasFlag(MmcFlags.ResponseR1) || flags.HasFlag(MmcFlags.ResponseSpiR1))
commandDescriptor.responseType = SdResponseType.R1;
if (flags.HasFlag(MmcFlags.ResponseR1B) || flags.HasFlag(MmcFlags.ResponseSpiR1B))
commandDescriptor.responseType = SdResponseType.R1b;
if (flags.HasFlag(MmcFlags.ResponseR2) || flags.HasFlag(MmcFlags.ResponseSpiR2))
commandDescriptor.responseType = SdResponseType.R2;
if (flags.HasFlag(MmcFlags.ResponseR3) || flags.HasFlag(MmcFlags.ResponseSpiR3))
commandDescriptor.responseType = SdResponseType.R3;
if (flags.HasFlag(MmcFlags.ResponseR4) || flags.HasFlag(MmcFlags.ResponseSpiR4))
commandDescriptor.responseType = SdResponseType.R4;
if (flags.HasFlag(MmcFlags.ResponseR5) || flags.HasFlag(MmcFlags.ResponseSpiR5))
commandDescriptor.responseType = SdResponseType.R5;
if (flags.HasFlag(MmcFlags.ResponseR6)) commandDescriptor.responseType = SdResponseType.R6;
var commandB = new byte[commandData.size + commandData.protocolArgumentSize +
commandData.deviceDataBufferSize];
Array.Copy(buffer, 0, commandB, commandData.size + commandData.protocolArgumentSize, buffer.Length);
var hBuf = Marshal.AllocHGlobal(commandB.Length);
Marshal.StructureToPtr(commandData, hBuf, true);
var descriptorOffset = IntPtr.Add(hBuf, commandData.size);
Marshal.StructureToPtr(commandDescriptor, descriptorOffset, true);
Marshal.Copy(hBuf, commandB, 0, commandB.Length);
Marshal.FreeHGlobal(hBuf);
var error = 0;
var start = DateTime.Now;
sense = !Extern.DeviceIoControl(fd, WindowsIoctl.IoctlSffdiskDeviceCommand, commandB,
(uint) commandB.Length,
commandB, (uint) commandB.Length, out _, IntPtr.Zero);
var end = DateTime.Now;
if (sense) error = Marshal.GetLastWin32Error();
buffer = new byte[blockSize * blocks];
Buffer.BlockCopy(commandB, commandB.Length - buffer.Length, buffer, 0, buffer.Length);
response = new uint[4];
duration = (end - start).TotalMilliseconds;
return error;
}
}
}

View File

@@ -0,0 +1,550 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Enums.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Windows direct device access.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains enumerations necessary for directly interfacing devices under
// Windows.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2020 Natalia Portillo
// ****************************************************************************/
using System;
using System.Diagnostics.CodeAnalysis;
namespace DiscImageChef.Devices.Windows
{
[Flags]
internal enum FileAttributes : uint
{
/// <summary>
/// FILE_ATTRIBUTE_ARCHIVE
/// </summary>
Archive = 0x20,
/// <summary>
/// FILE_ATTRIBUTE_COMPRESSED
/// </summary>
Compressed = 0x800,
/// <summary>
/// FILE_ATTRIBUTE_DEVICE
/// </summary>
Device = 0x40,
/// <summary>
/// FILE_ATTRIBUTE_DIRECTORY
/// </summary>
Directory = 0x10,
/// <summary>
/// FILE_ATTRIBUTE_ENCRYPTED
/// </summary>
Encrypted = 0x4000,
/// <summary>
/// FILE_ATTRIBUTE_HIDDEN
/// </summary>
Hidden = 0x02,
/// <summary>
/// FILE_ATTRIBUTE_INTEGRITY_STREAM
/// </summary>
IntegrityStream = 0x8000,
/// <summary>
/// FILE_ATTRIBUTE_NORMAL
/// </summary>
Normal = 0x80,
/// <summary>
/// FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
/// </summary>
NotContentIndexed = 0x2000,
/// <summary>
/// FILE_ATTRIBUTE_NO_SCRUB_DATA
/// </summary>
NoScrubData = 0x20000,
/// <summary>
/// FILE_ATTRIBUTE_OFFLINE
/// </summary>
Offline = 0x1000,
/// <summary>
/// FILE_ATTRIBUTE_READONLY
/// </summary>
Readonly = 0x01,
/// <summary>
/// FILE_ATTRIBUTE_REPARSE_POINT
/// </summary>
ReparsePoint = 0x400,
/// <summary>
/// FILE_ATTRIBUTE_SPARSE_FILE
/// </summary>
SparseFile = 0x200,
/// <summary>
/// FILE_ATTRIBUTE_SYSTEM
/// </summary>
System = 0x04,
/// <summary>
/// FILE_ATTRIBUTE_TEMPORARY
/// </summary>
Temporary = 0x100,
/// <summary>
/// FILE_ATTRIBUTE_VIRTUAL
/// </summary>
Virtual = 0x10000,
/// <summary>
/// FILE_FLAG_BACKUP_SEMANTICS
/// </summary>
BackupSemantics = 0x02000000,
/// <summary>
/// FILE_FLAG_DELETE_ON_CLOSE
/// </summary>
DeleteOnClose = 0x04000000,
/// <summary>
/// FILE_FLAG_NO_BUFFERING
/// </summary>
NoBuffering = 0x20000000,
/// <summary>
/// FILE_FLAG_OPEN_NO_RECALL
/// </summary>
OpenNoRecall = 0x00100000,
/// <summary>
/// FILE_FLAG_OPEN_REPARSE_POINT
/// </summary>
OpenReparsePoint = 0x00200000,
/// <summary>
/// FILE_FLAG_OVERLAPPED
/// </summary>
Overlapped = 0x40000000,
/// <summary>
/// FILE_FLAG_POSIX_SEMANTICS
/// </summary>
PosixSemantics = 0x0100000,
/// <summary>
/// FILE_FLAG_RANDOM_ACCESS
/// </summary>
RandomAccess = 0x10000000,
/// <summary>
/// FILE_FLAG_SESSION_AWARE
/// </summary>
SessionAware = 0x00800000,
/// <summary>
/// FILE_FLAG_SEQUENTIAL_SCAN
/// </summary>
SequentialScan = 0x08000000,
/// <summary>
/// FILE_FLAG_WRITE_THROUGH
/// </summary>
WriteThrough = 0x80000000
}
[Flags]
internal enum FileAccess : uint
{
/// <summary>
/// FILE_READ_DATA
/// </summary>
ReadData = 0x0001,
/// <summary>
/// FILE_LIST_DIRECTORY
/// </summary>
ListDirectory = ReadData,
/// <summary>
/// FILE_WRITE_DATA
/// </summary>
WriteData = 0x0002,
/// <summary>
/// FILE_ADD_FILE
/// </summary>
AddFile = WriteData,
/// <summary>
/// FILE_APPEND_DATA
/// </summary>
AppendData = 0x0004,
/// <summary>
/// FILE_ADD_SUBDIRECTORY
/// </summary>
AddSubdirectory = AppendData,
/// <summary>
/// FILE_CREATE_PIPE_INSTANCE
/// </summary>
CreatePipeInstance = AppendData,
/// <summary>
/// FILE_READ_EA
/// </summary>
ReadEa = 0x0008,
/// <summary>
/// FILE_WRITE_EA
/// </summary>
WriteEa = 0x0010,
/// <summary>
/// FILE_EXECUTE
/// </summary>
Execute = 0x0020,
/// <summary>
/// FILE_TRAVERSE
/// </summary>
Traverse = Execute,
/// <summary>
/// FILE_DELETE_CHILD
/// </summary>
DeleteChild = 0x0040,
/// <summary>
/// FILE_READ_ATTRIBUTES
/// </summary>
ReadAttributes = 0x0080,
/// <summary>
/// FILE_WRITE_ATTRIBUTES
/// </summary>
WriteAttributes = 0x0100,
/// <summary>
/// GENERIC_READ
/// </summary>
GenericRead = 0x80000000,
/// <summary>
/// GENERIC_WRITE
/// </summary>
GenericWrite = 0x40000000,
/// <summary>
/// GENERIC_EXECUTE
/// </summary>
GenericExecute = 0x20000000,
/// <summary>
/// GENERIC_ALL
/// </summary>
GenericAll = 0x10000000
}
[Flags]
internal enum FileShare : uint
{
/// <summary>
/// FILE_SHARE_NONE
/// </summary>
None = 0x00,
/// <summary>
/// FILE_SHARE_READ
/// </summary>
Read = 0x01,
/// <summary>
/// FILE_SHARE_WRITE
/// </summary>
Write = 0x02,
/// <summary>
/// FILE_SHARE_DELETE
/// </summary>
Delete = 0x03
}
[Flags]
internal enum FileMode : uint
{
/// <summary>
/// NEW
/// </summary>
New = 0x01,
/// <summary>
/// CREATE_ALWAYS
/// </summary>
CreateAlways = 0x02,
/// <summary>
/// OPEN_EXISTING
/// </summary>
OpenExisting = 0x03,
/// <summary>
/// OPEN_ALWAYS
/// </summary>
OpenAlways = 0x04,
/// <summary>
/// TRUNCATE_EXISTING
/// </summary>
TruncateExisting = 0x05
}
/// <summary>
/// Direction of SCSI transfer
/// </summary>
internal enum ScsiIoctlDirection : byte
{
/// <summary>
/// From host to device
/// SCSI_IOCTL_DATA_OUT
/// </summary>
Out = 0,
/// <summary>
/// From device to host
/// SCSI_IOCTL_DATA_IN
/// </summary>
In = 1,
/// <summary>
/// Unspecified direction, or bidirectional, or no data
/// SCSI_IOCTL_DATA_UNSPECIFIED
/// </summary>
Unspecified = 2
}
internal enum WindowsIoctl : uint
{
IoctlAtaPassThrough = 0x4D02C,
IoctlAtaPassThroughDirect = 0x4D030,
/// <summary>
/// ScsiPassThrough
/// </summary>
IoctlScsiPassThrough = 0x4D004,
/// <summary>
/// ScsiPassThroughDirect
/// </summary>
IoctlScsiPassThroughDirect = 0x4D014,
/// <summary>
/// ScsiGetAddress
/// </summary>
IoctlScsiGetAddress = 0x41018,
IoctlStorageQueryProperty = 0x2D1400,
IoctlIdePassThrough = 0x4D028,
IoctlStorageGetDeviceNumber = 0x2D1080,
IoctlSffdiskQueryDeviceProtocol = 0x71E80,
IoctlSffdiskDeviceCommand = 0x79E84
}
[Flags]
internal enum AtaFlags : ushort
{
/// <summary>
/// ATA_FLAGS_DRDY_REQUIRED
/// </summary>
DrdyRequired = 0x01,
/// <summary>
/// ATA_FLAGS_DATA_IN
/// </summary>
DataIn = 0x02,
/// <summary>
/// ATA_FLAGS_DATA_OUT
/// </summary>
DataOut = 0x04,
/// <summary>
/// ATA_FLAGS_48BIT_COMMAND
/// </summary>
ExtendedCommand = 0x08,
/// <summary>
/// ATA_FLAGS_USE_DMA
/// </summary>
Dma = 0x10,
/// <summary>
/// ATA_FLAGS_NO_MULTIPLE
/// </summary>
NoMultiple = 0x20
}
internal enum StoragePropertyId
{
Device = 0,
Adapter = 1,
Id = 2,
UniqueId = 3,
WriteCache = 4,
Miniport = 5,
AccessAlignment = 6,
SeekPenalty = 7,
Trim = 8,
WriteAggregation = 9,
Telemetry = 10,
LbProvisioning = 11,
Power = 12,
Copyoffload = 13,
Resiliency = 14
}
internal enum StorageQueryType
{
Standard = 0,
Exists = 1,
Mask = 2,
Max = 3
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal enum StorageBusType
{
Unknown = 0,
SCSI = 1,
ATAPI = 2,
ATA = 3,
FireWire = 4,
SSA = 5,
Fibre = 6,
USB = 7,
RAID = 8,
iSCSI = 9,
SAS = 0xA,
SATA = 0xB,
SecureDigital = 0xC,
MultiMediaCard = 0xD,
Virtual = 0xE,
FileBackedVirtual = 0xF,
Spaces = 16,
SCM = 18,
UFS = 19,
Max = 20,
MaxReserved = 127,
NVMe = 0x11
}
[Flags]
internal enum DeviceGetClassFlags : uint
{
/// <summary>
/// DIGCF_DEFAULT
/// </summary>
Default = 0x01,
/// <summary>
/// DIGCF_PRESENT
/// </summary>
Present = 0x02,
/// <summary>
/// DIGCF_ALLCLASSES
/// </summary>
AllClasses = 0x04,
/// <summary>
/// DIGCF_PROFILE
/// </summary>
Profile = 0x08,
/// <summary>
/// DIGCF_DEVICEINTERFACE
/// </summary>
DeviceInterface = 0x10
}
internal enum SdCommandClass : uint
{
Standard,
AppCmd
}
internal enum SdTransferDirection : uint
{
Unspecified,
Read,
Write
}
internal enum SdTransferType : uint
{
Unspecified,
CmdOnly,
SingleBlock,
MultiBlock,
MultiBlockNoCmd12
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal enum SdResponseType : uint
{
Unspecified,
None,
R1,
R1b,
R2,
R3,
R4,
R5,
R5b,
R6
}
internal enum SffdiskDcmd : uint
{
GetVersion,
LockChannel,
UnlockChannel,
DeviceCommand
}
internal static class Consts
{
public static Guid GuidSffProtocolSd = new Guid("AD7536A8-D055-4C40-AA4D-96312DDB6B38");
public static Guid GuidSffProtocolMmc = new Guid("77274D3F-2365-4491-A030-8BB44AE60097");
public static Guid GuidDevinterfaceDisk =
new Guid(0x53F56307, 0xB6BF, 0x11D0, 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B);
}
}

View File

@@ -0,0 +1,141 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Extern.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Windows direct device access.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains the P/Invoke definitions of Windows syscalls used to directly
// interface devices.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2020 Natalia Portillo
// ****************************************************************************/
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace DiscImageChef.Devices.Windows
{
internal static class Extern
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern SafeFileHandle CreateFile([MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] FileAccess access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr
securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes, IntPtr templateFile);
[DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)]
internal static extern bool DeviceIoControlScsi(SafeFileHandle hDevice,
WindowsIoctl ioControlCode,
ref ScsiPassThroughDirectAndSenseBuffer inBuffer,
uint nInBufferSize,
ref ScsiPassThroughDirectAndSenseBuffer outBuffer,
uint nOutBufferSize,
ref uint pBytesReturned,
IntPtr overlapped);
[DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)]
internal static extern bool DeviceIoControlAta(SafeFileHandle hDevice,
WindowsIoctl ioControlCode,
ref AtaPassThroughExBuffer inBuffer,
uint nInBufferSize,
ref AtaPassThroughExBuffer outBuffer,
uint nOutBufferSize,
ref uint pBytesReturned, IntPtr overlapped);
[DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)]
internal static extern bool DeviceIoControlStorageQuery(SafeFileHandle hDevice,
WindowsIoctl ioControlCode,
ref StoragePropertyQuery inBuffer,
uint nInBufferSize,
IntPtr outBuffer,
uint nOutBufferSize,
ref uint pBytesReturned,
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, 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("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)]
internal static extern bool DeviceIoControl(SafeFileHandle hDevice,
WindowsIoctl ioControlCode, IntPtr inBuffer,
uint nInBufferSize,
ref SffdiskQueryDeviceProtocolData outBuffer,
uint nOutBufferSize,
out uint pBytesReturned,
IntPtr overlapped);
[DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)]
internal static extern bool DeviceIoControl(SafeFileHandle hDevice, WindowsIoctl ioControlCode,
byte[] inBuffer,
uint nInBufferSize, byte[] outBuffer,
uint nOutBufferSize,
out 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,
uint deviceInterfaceDetailDataSize,
ref uint 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);
}
}

View File

@@ -0,0 +1,208 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : ListDevices.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Windows direct device access.
//
// --[ Description ] ----------------------------------------------------------
//
// Gets a list of known physical devices.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2020 Natalia Portillo
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Text;
namespace DiscImageChef.Devices.Windows
{
internal static class ListDevices
{
/// <summary>
/// Converts a hex dump string to the ASCII string it represents
/// </summary>
/// <param name="hex">Hex dump</param>
/// <returns>Decoded string</returns>
private static string HexStringToString(string hex)
{
var result = new StringBuilder();
const string HEXTABLE = "0123456789abcdef";
for (var i = 0; i < hex.Length / 2; i++)
result.Append((char) (16 * HEXTABLE.IndexOf(hex[2 * i]) + HEXTABLE.IndexOf(hex[2 * i + 1])));
return result.ToString();
}
/// <summary>
/// Gets a list of all known storage devices on Windows
/// </summary>
/// <returns>List of devices</returns>
[SuppressMessage("ReSharper", "RedundantCatchClause")]
internal static DeviceInfo[] GetList()
{
var deviceIDs = new List<string>();
try
{
var mgmtObjSearcher =
new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
var objCol = mgmtObjSearcher.Get();
deviceIDs.AddRange(from ManagementObject drive in objCol select (string) drive["DeviceID"]);
mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_TapeDrive");
objCol = mgmtObjSearcher.Get();
deviceIDs.AddRange(from ManagementObject drive in objCol select (string) drive["DeviceID"]);
mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_CDROMDrive");
objCol = mgmtObjSearcher.Get();
deviceIDs.AddRange(from ManagementObject drive in objCol select (string) drive["Drive"]);
}
catch (Exception)
{
#if DEBUG
throw;
#else
return null;
#endif
}
var devList = new List<DeviceInfo>();
foreach (var devId in deviceIDs)
{
if (devId is null) continue;
var physId = devId;
// TODO: This can be done better
if (devId.Length == 2 && devId[1] == ':') physId = "\\\\?\\" + devId;
var fd = Extern.CreateFile(physId, 0, FileShare.Read | FileShare.Write, IntPtr.Zero,
FileMode.OpenExisting, 0, IntPtr.Zero);
if (fd.IsInvalid) continue;
var query = new StoragePropertyQuery
{
PropertyId = StoragePropertyId.Device,
QueryType = StorageQueryType.Standard,
AdditionalParameters = new byte[1]
};
//StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor();
//descriptor.RawDeviceProperties = new byte[16384];
var descriptorPtr = Marshal.AllocHGlobal(1000);
var descriptorB = new byte[1000];
uint returned = 0;
var error = 0;
var hasError = !Extern.DeviceIoControlStorageQuery(fd, WindowsIoctl.IoctlStorageQueryProperty,
ref query, (uint) Marshal.SizeOf(query),
descriptorPtr, 1000, ref returned, IntPtr.Zero);
if (hasError) error = Marshal.GetLastWin32Error();
Marshal.Copy(descriptorPtr, descriptorB, 0, 1000);
if (hasError && error != 0) continue;
var descriptor = new StorageDeviceDescriptor
{
Version = BitConverter.ToUInt32(descriptorB, 0),
Size = BitConverter.ToUInt32(descriptorB, 4),
DeviceType = descriptorB[8],
DeviceTypeModifier = descriptorB[9],
RemovableMedia = BitConverter.ToBoolean(descriptorB, 10),
CommandQueueing = BitConverter.ToBoolean(descriptorB, 11),
VendorIdOffset = BitConverter.ToInt32(descriptorB, 12),
ProductIdOffset = BitConverter.ToInt32(descriptorB, 16),
ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20),
SerialNumberOffset = BitConverter.ToInt32(descriptorB, 24),
BusType = (StorageBusType) BitConverter.ToUInt32(descriptorB, 28),
RawPropertiesLength = BitConverter.ToUInt32(descriptorB, 32)
};
var info = new DeviceInfo {Path = physId, Bus = descriptor.BusType.ToString()};
if (descriptor.VendorIdOffset > 0)
info.Vendor =
StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.VendorIdOffset);
if (descriptor.ProductIdOffset > 0)
info.Model =
StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.ProductIdOffset);
// TODO: Get serial number of SCSI and USB devices, probably also FireWire (untested)
if (descriptor.SerialNumberOffset > 0)
{
info.Serial =
StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.SerialNumberOffset);
// fix any serial numbers that are returned as hex-strings
if (Array.TrueForAll(info.Serial.ToCharArray(), c => "0123456789abcdef".IndexOf(c) >= 0) &&
info.Serial.Length == 40) info.Serial = HexStringToString(info.Serial).Trim();
}
if ((string.IsNullOrEmpty(info.Vendor) || info.Vendor == "ATA") && info.Model != null)
{
var pieces = info.Model.Split(' ');
if (pieces.Length > 1)
{
info.Vendor = pieces[0];
info.Model = info.Model.Substring(pieces[0].Length + 1);
}
}
switch (descriptor.BusType)
{
case StorageBusType.SCSI:
case StorageBusType.ATAPI:
case StorageBusType.ATA:
case StorageBusType.FireWire:
case StorageBusType.SSA:
case StorageBusType.Fibre:
case StorageBusType.USB:
case StorageBusType.iSCSI:
case StorageBusType.SAS:
case StorageBusType.SATA:
case StorageBusType.SecureDigital:
case StorageBusType.MultiMediaCard:
info.Supported = true;
break;
}
Marshal.FreeHGlobal(descriptorPtr);
devList.Add(info);
}
var devices = devList.ToArray();
return devices;
}
}
}

View File

@@ -0,0 +1,303 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Structs.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Windows direct device access.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains structures necessary for directly interfacing devices under
// Windows.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2020 Natalia Portillo
// ****************************************************************************/
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace DiscImageChef.Devices.Windows
{
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct ScsiPassThroughDirect
{
public ushort Length;
public byte ScsiStatus;
public byte PathId;
public byte TargetId;
public byte Lun;
public byte CdbLength;
public byte SenseInfoLength;
[MarshalAs(UnmanagedType.U1)] public ScsiIoctlDirection DataIn;
public uint DataTransferLength;
public uint TimeOutValue;
public IntPtr DataBuffer;
public uint SenseInfoOffset;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Cdb;
}
[StructLayout(LayoutKind.Sequential)]
internal struct ScsiPassThroughDirectAndSenseBuffer
{
public ScsiPassThroughDirect sptd;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] SenseBuf;
}
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct AtaPassThroughEx
{
/// <summary>
/// Length in bytes of this structure
/// </summary>
public ushort Length;
/// <summary>
/// Indicates transfer direction and kind of operation
/// </summary>
[MarshalAs(UnmanagedType.U2)] public AtaFlags AtaFlags;
/// <summary>
/// Indicates IDE port or bus, set by driver
/// </summary>
public byte PathId;
/// <summary>
/// Indicates target device on bus, set by driver
/// </summary>
public byte TargetId;
/// <summary>
/// Indicates logical unit number of device, set by driver
/// </summary>
public byte Lun;
/// <summary>
/// Reserved
/// </summary>
public byte ReservedAsUchar;
/// <summary>
/// Data transfer length in bytes
/// </summary>
public uint DataTransferLength;
/// <summary>
/// Timeout value in seconds
/// </summary>
public uint TimeOutValue;
/// <summary>
/// Reserved
/// </summary>
public uint ReservedAsUlong;
/// <summary>
/// Pointer to data buffer relative to start of this structure
/// </summary>
public IntPtr DataBufferOffset;
/// <summary>
/// Previous ATA registers, for LBA48
/// </summary>
public AtaTaskFile PreviousTaskFile;
/// <summary>
/// ATA registers
/// </summary>
public AtaTaskFile CurrentTaskFile;
}
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct AtaPassThroughExBuffer
{
public AtaPassThroughEx aptd;
public uint filler;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64 * 512)]
public byte[] dataBuffer;
}
[StructLayout(LayoutKind.Explicit)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct AtaTaskFile
{
// Fields for commands sent
[FieldOffset(0)] public byte Features;
[FieldOffset(6)] public byte Command;
// Fields on command return
[FieldOffset(0)] public byte Error;
[FieldOffset(6)] public byte Status;
// Common fields
[FieldOffset(1)] public byte SectorCount;
[FieldOffset(2)] public byte SectorNumber;
[FieldOffset(3)] public byte CylinderLow;
[FieldOffset(4)] public byte CylinderHigh;
[FieldOffset(5)] public byte DeviceHead;
[FieldOffset(7)] public byte Reserved;
}
[StructLayout(LayoutKind.Sequential)]
internal struct StoragePropertyQuery
{
[MarshalAs(UnmanagedType.U4)] public StoragePropertyId PropertyId;
[MarshalAs(UnmanagedType.U4)] public StorageQueryType QueryType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] AdditionalParameters;
}
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct StorageDescriptorHeader
{
public uint Version;
public uint Size;
}
[StructLayout(LayoutKind.Sequential)]
internal struct StorageDeviceDescriptor
{
public uint Version;
public uint Size;
public byte DeviceType;
public byte DeviceTypeModifier;
[MarshalAs(UnmanagedType.U1)] public bool RemovableMedia;
[MarshalAs(UnmanagedType.U1)] public bool CommandQueueing;
public int VendorIdOffset;
public int ProductIdOffset;
public int ProductRevisionOffset;
public int SerialNumberOffset;
public StorageBusType BusType;
public uint RawPropertiesLength;
public byte[] RawDeviceProperties;
}
[StructLayout(LayoutKind.Sequential)]
internal 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;
}
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct StorageDeviceNumber
{
public int deviceType;
public int deviceNumber;
public int partitionNumber;
}
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct DeviceInfoData
{
public int cbSize;
public Guid classGuid;
public uint devInst;
public IntPtr reserved;
}
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct DeviceInterfaceData
{
public int cbSize;
public Guid interfaceClassGuid;
public uint flags;
private readonly IntPtr reserved;
}
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct UsbSetupPacket
{
public byte bmRequest;
public byte bRequest;
public short wValue;
public short wIndex;
public short wLength;
}
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct UsbDescriptorRequest
{
public int ConnectionIndex;
public UsbSetupPacket SetupPacket;
//public byte[] Data;
}
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct SffdiskQueryDeviceProtocolData
{
public ushort size;
public ushort reserved;
public Guid protocolGuid;
}
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
internal struct SffdiskDeviceCommandData
{
public ushort size;
public ushort reserved;
public SffdiskDcmd command;
public ushort protocolArgumentSize;
public uint deviceDataBufferSize;
public uint information;
}
[StructLayout(LayoutKind.Sequential)]
internal struct SdCmdDescriptor
{
public byte commandCode;
public SdCommandClass cmdClass;
public SdTransferDirection transferDirection;
public SdTransferType transferType;
public SdResponseType responseType;
}
}

1049
Aaru.Devices/Windows/Usb.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,317 @@
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : UsbFunctions.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Windows direct device access.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains code to access USB device information under Windows.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General internal License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General internal License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2020 Natalia Portillo
// Copyright © 2007 Fort Hood TX, herethen, Public Domain
// ****************************************************************************/
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace DiscImageChef.Devices.Windows
{
//
// A place for "higher level" related functions
// You might not want to keep these in the USB class... your choice
//
// TODO: Even after cleaning, refactoring and xml-documenting, this code needs some love
/// <summary>
/// Implements functions for getting and accesing information from the USB bus
/// </summary>
internal static partial class Usb
{
private const int IOCTL_STORAGE_GET_DEVICE_NUMBER = 0x2D1080;
internal const string GuidDevinterfaceDisk = "53f56307-b6bf-11d0-94f2-00a0c91efb8b";
internal const string GuidDevinterfaceCdrom = "53f56308-b6bf-11d0-94f2-00a0c91efb8b";
internal const string GuidDevinterfaceFloppy = "53f56311-b6bf-11d0-94f2-00a0c91efb8b";
internal const string GuidDevinterfaceTape = "53f5630b-b6bf-11d0-94f2-00a0c91efb8b";
/// <summary>
/// Get a list of all connected devices
/// </summary>
/// <returns>List of usb devices</returns>
internal static List<UsbDevice> GetConnectedDevices()
{
var devList = new List<UsbDevice>();
foreach (var controller in GetHostControllers()) ListHub(controller.GetRootHub(), devList);
return devList;
}
/// <summary>
/// private routine for enumerating a hub
/// </summary>
/// <param name="hub">Hub</param>
/// <param name="devList">Device list</param>
private static void ListHub(UsbHub hub, ICollection<UsbDevice> devList)
{
foreach (var port in hub.GetPorts())
if (port.IsHub)
{
ListHub(port.GetHub(), devList);
}
else
{
if (port.IsDeviceConnected) devList.Add(port.GetDevice());
}
}
/// <summary>
/// Find a device based upon it's DriverKeyName
/// </summary>
/// <param name="driverKeyName">DriverKeyName</param>
/// <returns>USB device</returns>
internal static UsbDevice FindDeviceByDriverKeyName(string driverKeyName)
{
UsbDevice foundDevice = null;
foreach (var controller in GetHostControllers())
{
SearchHubDriverKeyName(controller.GetRootHub(), ref foundDevice, driverKeyName);
if (foundDevice != null) break;
}
return foundDevice;
}
/// <summary>
/// Finds a device connected to a specified hub by it's DriverKeyName
/// </summary>
/// <param name="hub">Hub</param>
/// <param name="foundDevice">UsbDevice</param>
/// <param name="driverKeyName">DriverKeyName</param>
private static void SearchHubDriverKeyName(UsbHub hub, ref UsbDevice foundDevice, string driverKeyName)
{
foreach (var port in hub.GetPorts())
if (port.IsHub)
{
SearchHubDriverKeyName(port.GetHub(), ref foundDevice, driverKeyName);
}
else
{
if (!port.IsDeviceConnected) continue;
var device = port.GetDevice();
if (device.DeviceDriverKey != driverKeyName) continue;
foundDevice = device;
break;
}
}
/// <summary>
/// Find a device based upon it's Instance ID
/// </summary>
/// <param name="instanceId">Device instance ID</param>
/// <returns>USB device</returns>
private static UsbDevice FindDeviceByInstanceId(string instanceId)
{
UsbDevice foundDevice = null;
foreach (var controller in GetHostControllers())
{
SearchHubInstanceId(controller.GetRootHub(), ref foundDevice, instanceId);
if (foundDevice != null) break;
}
return foundDevice;
}
/// <summary>
/// private routine for enumerating a hub
/// </summary>
/// <param name="hub">Hub</param>
/// <param name="foundDevice">USB device</param>
/// <param name="instanceId">Device instance ID</param>
private static void SearchHubInstanceId(UsbHub hub, ref UsbDevice foundDevice, string instanceId)
{
foreach (var port in hub.GetPorts())
if (port.IsHub)
{
SearchHubInstanceId(port.GetHub(), ref foundDevice, instanceId);
}
else
{
if (!port.IsDeviceConnected) continue;
var device = port.GetDevice();
if (device.InstanceId != instanceId) continue;
foundDevice = device;
break;
}
}
[DllImport("setupapi.dll")]
private static extern int CM_Get_Parent(out IntPtr pdnDevInst, IntPtr dnDevInst, int ulFlags);
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
private static extern int CM_Get_Device_ID(IntPtr dnDevInst, IntPtr buffer, int bufferLen, int ulFlags);
/// <summary>
/// Find a device based upon a Drive Letter
/// </summary>
/// <param name="driveLetter">Drive letter</param>
/// <param name="deviceGuid">Device GUID</param>
/// <returns>USB device</returns>
internal static UsbDevice FindDriveLetter(string driveLetter, string deviceGuid)
{
// We start by getting the unique DeviceNumber of the given
// DriveLetter. We'll use this later to find a matching
// DevicePath "symbolic name"
var devNum = GetDeviceNumber(@"\\.\" + driveLetter.TrimEnd('\\'));
return devNum < 0 ? null : FindDeviceNumber(devNum, deviceGuid);
}
/// <summary>
/// Find a device based upon a Drive Path
/// </summary>
/// <param name="drivePath">Drive path</param>
/// <param name="deviceGuid">Device GUID</param>
/// <returns>USB device</returns>
internal static UsbDevice FindDrivePath(string drivePath, string deviceGuid)
{
// We start by getting the unique DeviceNumber of the given
// DriveLetter. We'll use this later to find a matching
// DevicePath "symbolic name"
var devNum = GetDeviceNumber(drivePath);
return devNum < 0 ? null : FindDeviceNumber(devNum, deviceGuid);
}
/// <summary>
/// Find a device based upon a Device Number
/// </summary>
/// <param name="devNum">Device Number</param>
/// <param name="deviceGuid">Device GUID</param>
/// <returns>USB device</returns>
private static UsbDevice FindDeviceNumber(int devNum, string deviceGuid)
{
UsbDevice foundDevice = null;
var instanceId = "";
var diskGuid = new Guid(deviceGuid);
// We start at the "root" of the device tree and look for all
// devices that match the interface GUID of a disk
var h = SetupDiGetClassDevs(ref diskGuid, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (h != INVALID_HANDLE_VALUE)
{
bool success;
var i = 0;
do
{
// create a Device Interface Data structure
var dia = new SpDeviceInterfaceData();
dia.cbSize = Marshal.SizeOf(dia);
// start the enumeration
success = SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref diskGuid, i, ref dia);
if (success)
{
// build a DevInfo Data structure
var da = new SpDevinfoData();
da.cbSize = Marshal.SizeOf(da);
// build a Device Interface Detail Data structure
var didd =
new SpDeviceInterfaceDetailData {cbSize = 4 + Marshal.SystemDefaultCharSize}; // trust me :)
// now we can get some more detailed information
var nRequiredSize = 0;
const int N_BYTES = BUFFER_SIZE;
if (SetupDiGetDeviceInterfaceDetail(h, ref dia, ref didd, N_BYTES, ref nRequiredSize, ref da))
if (GetDeviceNumber(didd.DevicePath) == devNum)
{
// current InstanceID is at the "USBSTOR" level, so we
// need up "move up" one level to get to the "USB" level
CM_Get_Parent(out var ptrPrevious, da.DevInst, 0);
// Now we get the InstanceID of the USB level device
var ptrInstanceBuf = Marshal.AllocHGlobal(N_BYTES);
CM_Get_Device_ID(ptrPrevious, ptrInstanceBuf, N_BYTES, 0);
instanceId = Marshal.PtrToStringAuto(ptrInstanceBuf);
Marshal.FreeHGlobal(ptrInstanceBuf);
//System.Console.WriteLine("InstanceId: {0}", instanceId);
//break;
}
}
i++;
} while (success);
SetupDiDestroyDeviceInfoList(h);
}
// Did we find an InterfaceID of a USB device?
if (instanceId?.StartsWith("USB\\", StringComparison.Ordinal) == true)
foundDevice = FindDeviceByInstanceId(instanceId);
return foundDevice;
}
/// <summary>
/// return a unique device number for the given device path
/// </summary>
/// <param name="devicePath">Device path</param>
/// <returns>Device number</returns>
private static int GetDeviceNumber(string devicePath)
{
var ans = -1;
var h = CreateFile(devicePath.TrimEnd('\\'), 0, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (h == INVALID_HANDLE_VALUE) return ans;
var sdn = new StorageDeviceNumber();
var nBytes = Marshal.SizeOf(sdn);
var ptrSdn = Marshal.AllocHGlobal(nBytes);
if (DeviceIoControl(h, IOCTL_STORAGE_GET_DEVICE_NUMBER, IntPtr.Zero, 0, ptrSdn, nBytes, out _, IntPtr.Zero))
{
sdn = (StorageDeviceNumber) Marshal.PtrToStructure(ptrSdn, typeof(StorageDeviceNumber));
// just my way of combining the relevant parts of the
// STORAGE_DEVICE_NUMBER into a single number
ans = (sdn.DeviceType << 8) + sdn.DeviceNumber;
}
Marshal.FreeHGlobal(ptrSdn);
CloseHandle(h);
return ans;
}
[StructLayout(LayoutKind.Sequential)]
private struct StorageDeviceNumber
{
internal readonly int DeviceType;
internal readonly int DeviceNumber;
internal readonly int PartitionNumber;
}
}
}