Move OS specific command implementation to OS specific device class.

This commit is contained in:
2022-03-26 19:35:13 +00:00
parent 2fa340474a
commit 89a2c52911
21 changed files with 827 additions and 1048 deletions

View File

@@ -51,6 +51,7 @@ using Aaru.Decoders.SCSI;
using Aaru.Decoders.Xbox; using Aaru.Decoders.Xbox;
using Aaru.Devices; using Aaru.Devices;
using Schemas; using Schemas;
using Device = Aaru.Devices.Remote.Device;
using PlatformID = Aaru.CommonTypes.Interop.PlatformID; using PlatformID = Aaru.CommonTypes.Interop.PlatformID;
using TrackType = Aaru.CommonTypes.Enums.TrackType; using TrackType = Aaru.CommonTypes.Enums.TrackType;
using Version = Aaru.CommonTypes.Interop.Version; using Version = Aaru.CommonTypes.Interop.Version;
@@ -82,7 +83,7 @@ partial class Dump
if(DetectOS.GetRealPlatformID() != PlatformID.Win32NT) if(DetectOS.GetRealPlatformID() != PlatformID.Win32NT)
{ {
bool isAdmin = _dev.IsRemote ? _dev.IsRemoteAdmin : DetectOS.IsAdmin; bool isAdmin = _dev is Device remoteDev ? remoteDev.IsAdmin : DetectOS.IsAdmin;
if(!isAdmin) if(!isAdmin)
{ {

View File

@@ -114,17 +114,18 @@ public sealed class DumpLog
_logSw.WriteLine(); _logSw.WriteLine();
if(dev.IsRemote) if(dev is Aaru.Devices.Remote.Device remoteDev)
{ {
_logSw.WriteLine("################# Remote information #################"); _logSw.WriteLine("################# Remote information #################");
_logSw.WriteLine("Server: {0}", dev.RemoteApplication); _logSw.WriteLine("Server: {0}", remoteDev.RemoteApplication);
_logSw.WriteLine("Version: {0}", dev.RemoteVersion); _logSw.WriteLine("Version: {0}", remoteDev.RemoteVersion);
_logSw.WriteLine("Operating system: {0} {1}", dev.RemoteOperatingSystem, dev.RemoteOperatingSystemVersion); _logSw.WriteLine("Operating system: {0} {1}", remoteDev.RemoteOperatingSystem,
remoteDev.RemoteOperatingSystemVersion);
_logSw.WriteLine("Architecture: {0}", dev.RemoteArchitecture); _logSw.WriteLine("Architecture: {0}", remoteDev.RemoteArchitecture);
_logSw.WriteLine("Protocol version: {0}", dev.RemoteProtocolVersion); _logSw.WriteLine("Protocol version: {0}", remoteDev.RemoteProtocolVersion);
_logSw.WriteLine("Running as superuser: {0}", dev.IsRemoteAdmin ? "Yes" : "No"); _logSw.WriteLine("Running as superuser: {0}", remoteDev.IsAdmin ? "Yes" : "No");
_logSw.WriteLine("######################################################"); _logSw.WriteLine("######################################################");
} }

View File

@@ -65,6 +65,7 @@
<Compile Include="Linux\Structs.cs" /> <Compile Include="Linux\Structs.cs" />
<Compile Include="Linux\Enums.cs" /> <Compile Include="Linux\Enums.cs" />
<Compile Include="Enums.cs" /> <Compile Include="Enums.cs" />
<Compile Include="Remote\Command.cs" />
<Compile Include="Remote\Consts.cs" /> <Compile Include="Remote\Consts.cs" />
<Compile Include="Remote\Device.cs" /> <Compile Include="Remote\Device.cs" />
<Compile Include="Remote\Enums.cs" /> <Compile Include="Remote\Enums.cs" />
@@ -77,7 +78,6 @@
<Compile Include="Windows\Enums.cs" /> <Compile Include="Windows\Enums.cs" />
<Compile Include="Windows\Command.cs" /> <Compile Include="Windows\Command.cs" />
<Compile Include="Linux\Command.cs" /> <Compile Include="Linux\Command.cs" />
<Compile Include="Command.cs" />
<Compile Include="Device\Constructor.cs" /> <Compile Include="Device\Constructor.cs" />
<Compile Include="Device\Variables.cs" /> <Compile Include="Device\Variables.cs" />
<Compile Include="Device\Destructor.cs" /> <Compile Include="Device\Destructor.cs" />

View File

@@ -1,434 +0,0 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Command.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Direct device access.
//
// --[ Description ] ----------------------------------------------------------
//
// High level commands used to directly access devices.
//
// --[ 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-2022 Natalia Portillo
// ****************************************************************************/
// ReSharper disable UnusedMember.Global
namespace Aaru.Devices;
using System;
using Aaru.CommonTypes.Interop;
using Aaru.Decoders.ATA;
using Aaru.Devices.Windows;
using Microsoft.Win32.SafeHandles;
using PlatformID = Aaru.CommonTypes.Interop.PlatformID;
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>
/// <exception cref="InvalidOperationException">If the specified platform is not supported</exception>
internal static int SendScsiCommand(object fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout,
ScsiDirection direction, out double duration, out bool sense)
{
PlatformID ptId = DetectOS.GetRealPlatformID();
return SendScsiCommand(ptId, fd, cdb, ref buffer, out senseBuffer, timeout, direction, out duration, out sense);
}
/// <summary>Sends a SCSI command</summary>
/// <returns>0 if no error occurred, otherwise, errno</returns>
/// <param name="ptId">Platform ID for executing the command</param>
/// <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>
/// <exception cref="InvalidOperationException">If the specified platform is not supported</exception>
internal static int SendScsiCommand(PlatformID ptId, object fd, byte[] cdb, ref byte[] buffer,
out byte[] senseBuffer, uint timeout, ScsiDirection direction,
out double duration, out bool sense)
{
switch(ptId)
{
case PlatformID.Win32NT:
{
ScsiIoctlDirection dir;
switch(direction)
{
case ScsiDirection.In:
dir = ScsiIoctlDirection.In;
break;
case ScsiDirection.Out:
dir = ScsiIoctlDirection.Out;
break;
default:
dir = ScsiIoctlDirection.Unspecified;
break;
}
return Windows.Command.SendScsiCommand((SafeFileHandle)fd, cdb, ref buffer, out senseBuffer, timeout,
dir, out duration, out sense);
}
case PlatformID.Linux:
{
Linux.ScsiIoctlDirection dir;
switch(direction)
{
case ScsiDirection.In:
dir = Linux.ScsiIoctlDirection.In;
break;
case ScsiDirection.Out:
dir = Linux.ScsiIoctlDirection.Out;
break;
case ScsiDirection.Bidirectional:
dir = Linux.ScsiIoctlDirection.Unspecified;
break;
case ScsiDirection.None:
dir = Linux.ScsiIoctlDirection.None;
break;
default:
dir = Linux.ScsiIoctlDirection.Unknown;
break;
}
return Linux.Command.SendScsiCommand((int)fd, cdb, ref buffer, out senseBuffer, timeout, dir,
out duration, out sense);
}
default: throw new InvalidOperationException($"Platform {ptId} not yet supported.");
}
}
/// <summary>Sends an ATA command in CHS format</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 returned non-OK status</param>
/// <param name="registers">Registers to send to the device</param>
/// <param name="errorRegisters">Registers returned by the device</param>
/// <param name="protocol">ATA protocol to use</param>
/// <param name="transferRegister">What register contains the transfer length</param>
/// <param name="transferBlocks">Set to <c>true</c> if the transfer length is in block, otherwise it is in bytes</param>
/// <exception cref="InvalidOperationException">If the specified platform is not supported</exception>
internal static int SendAtaCommand(object fd, AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters,
AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
uint timeout, bool transferBlocks, out double duration, out bool sense)
{
PlatformID ptId = DetectOS.GetRealPlatformID();
return SendAtaCommand(ptId, fd, registers, out errorRegisters, protocol, transferRegister, ref buffer, timeout,
transferBlocks, out duration, out sense);
}
/// <summary>Sends an ATA command in CHS format</summary>
/// <returns>0 if no error occurred, otherwise, errno</returns>
/// <param name="ptId">Platform ID for executing the command</param>
/// <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 returned non-OK status</param>
/// <param name="registers">Registers to send to the device</param>
/// <param name="errorRegisters">Registers returned by the device</param>
/// <param name="protocol">ATA protocol to use</param>
/// <param name="transferRegister">What register contains the transfer length</param>
/// <param name="transferBlocks">Set to <c>true</c> if the transfer length is in block, otherwise it is in bytes</param>
/// <exception cref="InvalidOperationException">If the specified platform is not supported</exception>
internal static int SendAtaCommand(PlatformID ptId, object fd, AtaRegistersChs registers,
out AtaErrorRegistersChs errorRegisters, AtaProtocol protocol,
AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout,
bool transferBlocks, out double duration, out bool sense)
{
switch(ptId)
{
case PlatformID.Win32NT:
{
if(Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 1 &&
Environment.OSVersion.ServicePack is "Service Pack 1" or "" ||
Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0)
throw new InvalidOperationException("Windows XP or earlier is not supported.");
// Windows NT 4 or earlier, requires special ATA pass thru SCSI. But Aaru 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, protocol,
ref buffer, timeout, out duration, out sense);
}
case PlatformID.Linux:
{
return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, transferRegister,
ref buffer, timeout, transferBlocks, out duration, out sense);
}
default: throw new InvalidOperationException($"Platform {ptId} not yet supported.");
}
}
/// <summary>Sends an ATA command in CHS format</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 returned non-OK status</param>
/// <param name="registers">Registers to send to the device</param>
/// <param name="errorRegisters">Registers returned by the device</param>
/// <param name="protocol">ATA protocol to use</param>
/// <param name="transferRegister">What register contains the transfer length</param>
/// <param name="transferBlocks">Set to <c>true</c> if the transfer length is in block, otherwise it is in bytes</param>
/// <exception cref="InvalidOperationException">If the specified platform is not supported</exception>
internal static int SendAtaCommand(object fd, AtaRegistersLba28 registers,
out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol,
AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout,
bool transferBlocks, out double duration, out bool sense)
{
PlatformID ptId = DetectOS.GetRealPlatformID();
return SendAtaCommand(ptId, fd, registers, out errorRegisters, protocol, transferRegister, ref buffer, timeout,
transferBlocks, out duration, out sense);
}
/// <summary>Sends an ATA command in 28-bit LBA format</summary>
/// <returns>0 if no error occurred, otherwise, errno</returns>
/// <param name="ptId">Platform ID for executing the command</param>
/// <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 returned non-OK status</param>
/// <param name="registers">Registers to send to the device</param>
/// <param name="errorRegisters">Registers returned by the device</param>
/// <param name="protocol">ATA protocol to use</param>
/// <param name="transferRegister">What register contains the transfer length</param>
/// <param name="transferBlocks">Set to <c>true</c> if the transfer length is in block, otherwise it is in bytes</param>
/// <exception cref="InvalidOperationException">If the specified platform is not supported</exception>
internal static int SendAtaCommand(PlatformID ptId, object fd, AtaRegistersLba28 registers,
out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol,
AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout,
bool transferBlocks, out double duration, out bool sense)
{
switch(ptId)
{
case PlatformID.Win32NT:
{
if(Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 1 &&
Environment.OSVersion.ServicePack is "Service Pack 1" or "" ||
Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0)
throw new InvalidOperationException("Windows XP or earlier is not supported.");
// Windows NT 4 or earlier, requires special ATA pass thru SCSI. But Aaru 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, protocol,
ref buffer, timeout, out duration, out sense);
}
case PlatformID.Linux:
{
return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, transferRegister,
ref buffer, timeout, transferBlocks, out duration, out sense);
}
default: throw new InvalidOperationException($"Platform {ptId} not yet supported.");
}
}
/// <summary>Sends an ATA command in 48-bit LBA format</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 returned non-OK status</param>
/// <param name="registers">Registers to send to the device</param>
/// <param name="errorRegisters">Registers returned by the device</param>
/// <param name="protocol">ATA protocol to use</param>
/// <param name="transferRegister">What register contains the transfer length</param>
/// <param name="transferBlocks">Set to <c>true</c> if the transfer length is in block, otherwise it is in bytes</param>
/// <exception cref="InvalidOperationException">If the specified platform is not supported</exception>
internal static int SendAtaCommand(object fd, AtaRegistersLba48 registers,
out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol,
AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout,
bool transferBlocks, out double duration, out bool sense)
{
PlatformID ptId = DetectOS.GetRealPlatformID();
return SendAtaCommand(ptId, fd, registers, out errorRegisters, protocol, transferRegister, ref buffer, timeout,
transferBlocks, out duration, out sense);
}
/// <summary>Sends an ATA command in 48-bit format</summary>
/// <returns>0 if no error occurred, otherwise, errno</returns>
/// <param name="ptId">Platform ID for executing the command</param>
/// <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 returned non-OK status</param>
/// <param name="registers">Registers to send to the device</param>
/// <param name="errorRegisters">Registers returned by the device</param>
/// <param name="protocol">ATA protocol to use</param>
/// <param name="transferRegister">What register contains the transfer length</param>
/// <param name="transferBlocks">Set to <c>true</c> if the transfer length is in block, otherwise it is in bytes</param>
/// <exception cref="InvalidOperationException">If the specified platform is not supported</exception>
internal static int SendAtaCommand(PlatformID ptId, object fd, AtaRegistersLba48 registers,
out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol,
AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout,
bool transferBlocks, out double duration, out bool sense)
{
switch(ptId)
{
case 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, protocol,
ref buffer, timeout, out duration, out sense);
case PlatformID.Linux:
return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, transferRegister,
ref buffer, timeout, transferBlocks, out duration, out sense);
default: throw new InvalidOperationException($"Platform {ptId} not yet supported.");
}
}
/// <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>
/// <exception cref="InvalidOperationException">If the specified platform is not supported</exception>
internal static int SendMmcCommand(object 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)
{
PlatformID ptId = DetectOS.GetRealPlatformID();
return SendMmcCommand(ptId, (int)fd, command, write, isApplication, flags, argument, blockSize, blocks,
ref buffer, out response, out duration, out sense, timeout);
}
/// <summary>Sends a MMC/SD command</summary>
/// <returns>The result of the command.</returns>
/// <param name="ptId">Platform ID for executing the command</param>
/// <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>
/// <exception cref="InvalidOperationException">If the specified platform is not supported</exception>
internal static int SendMmcCommand(PlatformID ptId, object 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)
{
switch(ptId)
{
case PlatformID.Win32NT:
return Windows.Command.SendMmcCommand((SafeFileHandle)fd, command, write, isApplication, flags,
argument, blockSize, blocks, ref buffer, out response,
out duration, out sense, timeout);
case PlatformID.Linux:
return Linux.Command.SendMmcCommand((int)fd, command, write, isApplication, flags, argument, blockSize,
blocks, ref buffer, out response, out duration, out sense, timeout);
default: throw new InvalidOperationException($"Platform {ptId} not yet supported.");
}
}
internal static int SendMultipleMmcCommands(PlatformID ptId, object fd, Device.MmcSingleCommand[] commands,
out double duration, out bool sense, uint timeout = 0)
{
switch(ptId)
{
case PlatformID.Win32NT:
return Windows.Command.SendMultipleMmcCommands((SafeFileHandle)fd, commands, out duration, out sense,
timeout);
case PlatformID.Linux:
return Linux.Command.SendMultipleMmcCommands((int)fd, commands, out duration, out sense, timeout);
default: throw new InvalidOperationException($"Platform {ptId} not yet supported.");
}
}
internal static int ReOpen(PlatformID ptId, string devicePath, object fd, out object newFd)
{
switch(ptId)
{
case PlatformID.Win32NT: return Windows.Command.ReOpen(devicePath, (SafeFileHandle)fd, out newFd);
case PlatformID.Linux: return Linux.Command.ReOpen(devicePath, (int)fd, out newFd);
default: throw new InvalidOperationException($"Platform {ptId} not yet supported.");
}
}
internal static int BufferedOsRead(PlatformID ptId, object fd, out byte[] buffer, long offset, uint length,
out double duration)
{
switch(ptId)
{
case PlatformID.Win32NT:
return Windows.Command.BufferedOsRead((SafeFileHandle)fd, out buffer, offset, length, out duration);
case PlatformID.Linux:
return Linux.Command.BufferedOsRead((int)fd, out buffer, offset, length, out duration);
default: throw new InvalidOperationException($"Platform {ptId} not yet supported.");
}
}
}

View File

@@ -32,7 +32,6 @@
namespace Aaru.Devices; namespace Aaru.Devices;
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Aaru.Decoders.ATA; using Aaru.Decoders.ATA;
@@ -51,19 +50,14 @@ public partial class Device
/// <c>True</c> if SCSI command returned non-OK status and <paramref name="senseBuffer" /> contains /// <c>True</c> if SCSI command returned non-OK status and <paramref name="senseBuffer" /> contains
/// SCSI sense /// SCSI sense
/// </param> /// </param>
public int SendScsiCommand(byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, public virtual int SendScsiCommand(byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout,
ScsiDirection direction, out double duration, out bool sense) ScsiDirection direction, out double duration, out bool sense)
{ {
// We need a timeout duration = 0;
if(timeout == 0) sense = true;
timeout = Timeout > 0 ? Timeout : 15; senseBuffer = null;
if(!(_remote is null)) return -1;
return _remote.SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, direction, out duration,
out sense);
return Command.SendScsiCommand(PlatformId, FileHandle, cdb, ref buffer, out senseBuffer, timeout, direction,
out duration, out sense);
} }
/// <summary>Sends an ATA/ATAPI command to this device using CHS addressing</summary> /// <summary>Sends an ATA/ATAPI command to this device using CHS addressing</summary>
@@ -80,20 +74,15 @@ public partial class Device
/// </param> /// </param>
/// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="duration">Time it took to execute the command in milliseconds</param>
/// <param name="sense"><c>True</c> if ATA/ATAPI command returned non-OK status</param> /// <param name="sense"><c>True</c> if ATA/ATAPI command returned non-OK status</param>
public int SendAtaCommand(AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters, AtaProtocol protocol, public virtual int SendAtaCommand(AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters,
AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
bool transferBlocks, out double duration, out bool sense) uint timeout, bool transferBlocks, out double duration, out bool sense)
{ {
// We need a timeout duration = 0;
if(timeout == 0) sense = true;
timeout = Timeout > 0 ? Timeout : 15; errorRegisters = default(AtaErrorRegistersChs);
if(!(_remote is null)) return -1;
return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer,
timeout, transferBlocks, out duration, out sense);
return Command.SendAtaCommand(PlatformId, FileHandle, registers, out errorRegisters, protocol, transferRegister,
ref buffer, timeout, transferBlocks, out duration, out sense);
} }
/// <summary>Sends an ATA/ATAPI command to this device using 28-bit LBA addressing</summary> /// <summary>Sends an ATA/ATAPI command to this device using 28-bit LBA addressing</summary>
@@ -110,20 +99,15 @@ public partial class Device
/// </param> /// </param>
/// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="duration">Time it took to execute the command in milliseconds</param>
/// <param name="sense"><c>True</c> if ATA/ATAPI command returned non-OK status</param> /// <param name="sense"><c>True</c> if ATA/ATAPI command returned non-OK status</param>
public int SendAtaCommand(AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters, public virtual int SendAtaCommand(AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters,
AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
uint timeout, bool transferBlocks, out double duration, out bool sense) uint timeout, bool transferBlocks, out double duration, out bool sense)
{ {
// We need a timeout errorRegisters = default(AtaErrorRegistersLba28);
if(timeout == 0) duration = 0;
timeout = Timeout > 0 ? Timeout : 15; sense = true;
if(!(_remote is null)) return -1;
return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer,
timeout, transferBlocks, out duration, out sense);
return Command.SendAtaCommand(PlatformId, FileHandle, registers, out errorRegisters, protocol, transferRegister,
ref buffer, timeout, transferBlocks, out duration, out sense);
} }
/// <summary>Sends an ATA/ATAPI command to this device using 48-bit LBA addressing</summary> /// <summary>Sends an ATA/ATAPI command to this device using 48-bit LBA addressing</summary>
@@ -140,20 +124,15 @@ public partial class Device
/// </param> /// </param>
/// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="duration">Time it took to execute the command in milliseconds</param>
/// <param name="sense"><c>True</c> if ATA/ATAPI command returned non-OK status</param> /// <param name="sense"><c>True</c> if ATA/ATAPI command returned non-OK status</param>
public int SendAtaCommand(AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters, public virtual int SendAtaCommand(AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters,
AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
uint timeout, bool transferBlocks, out double duration, out bool sense) uint timeout, bool transferBlocks, out double duration, out bool sense)
{ {
// We need a timeout errorRegisters = default(AtaErrorRegistersLba48);
if(timeout == 0) duration = 0;
timeout = Timeout > 0 ? Timeout : 15; sense = true;
if(!(_remote is null)) return -1;
return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer,
timeout, transferBlocks, out duration, out sense);
return Command.SendAtaCommand(PlatformId, FileHandle, registers, out errorRegisters, protocol, transferRegister,
ref buffer, timeout, transferBlocks, out duration, out sense);
} }
/// <summary>Sends a MMC/SD command to this device</summary> /// <summary>Sends a MMC/SD command to this device</summary>
@@ -170,74 +149,15 @@ public partial class Device
/// <param name="argument">Command argument</param> /// <param name="argument">Command argument</param>
/// <param name="response">Response registers</param> /// <param name="response">Response registers</param>
/// <param name="blockSize">Size of block in bytes</param> /// <param name="blockSize">Size of block in bytes</param>
public int SendMmcCommand(MmcCommands command, bool write, bool isApplication, MmcFlags flags, uint argument, public virtual int SendMmcCommand(MmcCommands command, bool write, bool isApplication, MmcFlags flags,
uint blockSize, uint blocks, ref byte[] buffer, out uint[] response, out double duration, uint argument, uint blockSize, uint blocks, ref byte[] buffer,
out bool sense, uint timeout = 15) out uint[] response, out double duration, out bool sense, uint timeout = 15)
{ {
// We need a timeout response = null;
if(timeout == 0) duration = 0;
timeout = Timeout > 0 ? Timeout : 15; sense = true;
switch(command) return -1;
{
case MmcCommands.SendCid when _cachedCid != null:
{
DateTime start = DateTime.Now;
buffer = new byte[_cachedCid.Length];
Array.Copy(_cachedCid, buffer, buffer.Length);
response = new uint[4];
sense = false;
DateTime end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case MmcCommands.SendCsd when _cachedCid != null:
{
DateTime start = DateTime.Now;
buffer = new byte[_cachedCsd.Length];
Array.Copy(_cachedCsd, buffer, buffer.Length);
response = new uint[4];
sense = false;
DateTime end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case (MmcCommands)SecureDigitalCommands.SendScr when _cachedScr != null:
{
DateTime start = DateTime.Now;
buffer = new byte[_cachedScr.Length];
Array.Copy(_cachedScr, buffer, buffer.Length);
response = new uint[4];
sense = false;
DateTime end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case (MmcCommands)SecureDigitalCommands.SendOperatingCondition when _cachedOcr != null:
case MmcCommands.SendOpCond when _cachedOcr != null:
{
DateTime start = DateTime.Now;
buffer = new byte[_cachedOcr.Length];
Array.Copy(_cachedOcr, buffer, buffer.Length);
response = new uint[4];
sense = false;
DateTime end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
}
return _remote is null
? Command.SendMmcCommand(PlatformId, FileHandle, command, write, isApplication, flags, argument,
blockSize, blocks, ref buffer, out response, out duration, out sense,
timeout) : _remote.SendMmcCommand(command, write, isApplication, flags,
argument, blockSize, blocks, ref buffer,
out response, out duration, out sense,
timeout);
} }
/// <summary>Encapsulates a single MMC command to send in a queue</summary> /// <summary>Encapsulates a single MMC command to send in a queue</summary>
@@ -273,59 +193,18 @@ public partial class Device
/// <param name="sense">Set to <c>true</c> if any of the commands returned an error status, <c>false</c> otherwise</param> /// <param name="sense">Set to <c>true</c> if any of the commands returned an error status, <c>false</c> otherwise</param>
/// <param name="timeout">Maximum allowed time to execute a single command</param> /// <param name="timeout">Maximum allowed time to execute a single command</param>
/// <returns>0 if no error occurred, otherwise, errno</returns> /// <returns>0 if no error occurred, otherwise, errno</returns>
public int SendMultipleMmcCommands(MmcSingleCommand[] commands, out double duration, out bool sense, public virtual int SendMultipleMmcCommands(MmcSingleCommand[] commands, out double duration, out bool sense,
uint timeout = 15) uint timeout = 15)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
if(_remote is null)
return Command.SendMultipleMmcCommands(PlatformId, FileHandle, commands, out duration, out sense, timeout);
if(_remote.ServerProtocolVersion >= 2)
return _remote.SendMultipleMmcCommands(commands, out duration, out sense, timeout);
var error = 0;
duration = 0; duration = 0;
sense = false;
foreach(MmcSingleCommand command in commands)
{
int singleError = _remote.SendMmcCommand(command.command, command.write, command.isApplication,
command.flags, command.argument, command.blockSize, command.blocks,
ref command.buffer, out command.response, out double cmdDuration,
out bool cmdSense, timeout);
if(error == 0 &&
singleError != 0)
error = singleError;
duration += cmdDuration;
if(cmdSense)
sense = true; sense = true;
}
return error; return -1;
} }
/// <summary>Closes then immediately reopens a device</summary> /// <summary>Closes then immediately reopens a device</summary>
/// <returns>Returned error number if any</returns> /// <returns>Returned error number if any</returns>
public bool ReOpen() public virtual bool ReOpen() => false;
{
if(!(_remote is null))
return _remote.ReOpen();
int ret = Command.ReOpen(PlatformId, _devicePath, FileHandle, out object newFileHandle);
FileHandle = newFileHandle;
Error = ret != 0;
LastError = ret;
return Error;
}
/// <summary>Reads data using operating system buffers.</summary> /// <summary>Reads data using operating system buffers.</summary>
/// <param name="buffer">Data buffer</param> /// <param name="buffer">Data buffer</param>
@@ -333,16 +212,11 @@ public partial class Device
/// <param name="length">Number of bytes to read</param> /// <param name="length">Number of bytes to read</param>
/// <param name="duration">Total time in milliseconds the reading took</param> /// <param name="duration">Total time in milliseconds the reading took</param>
/// <returns><c>true</c> if there was an error, <c>false</c> otherwise</returns> /// <returns><c>true</c> if there was an error, <c>false</c> otherwise</returns>
public bool BufferedOsRead(out byte[] buffer, long offset, uint length, out double duration) public virtual bool BufferedOsRead(out byte[] buffer, long offset, uint length, out double duration)
{ {
if(!(_remote is null)) buffer = null;
return _remote.BufferedOsRead(out buffer, offset, length, out duration); duration = 0;
int ret = Command.BufferedOsRead(PlatformId, FileHandle, out buffer, offset, length, out duration); return false;
Error = ret != 0;
LastError = ret;
return Error;
} }
} }

View File

@@ -55,10 +55,6 @@ public partial class Device
/// <value>The Platform ID</value> /// <value>The Platform ID</value>
public PlatformID PlatformId { get; private protected set; } public PlatformID PlatformId { get; private protected set; }
/// <summary>Gets the file handle representing this device</summary>
/// <value>The file handle</value>
public object FileHandle { get; private protected set; }
/// <summary>Gets or sets the standard timeout for commands sent to this device</summary> /// <summary>Gets or sets the standard timeout for commands sent to this device</summary>
/// <value>The timeout in seconds</value> /// <value>The timeout in seconds</value>
public uint Timeout { get; set; } public uint Timeout { get; set; }
@@ -162,33 +158,5 @@ public partial class Device
/// <summary>Contains the PCMCIA CIS if applicable</summary> /// <summary>Contains the PCMCIA CIS if applicable</summary>
public byte[] Cis { get; private protected set; } public byte[] Cis { get; private protected set; }
private protected Remote.Remote _remote;
bool? _isRemoteAdmin;
private protected string _devicePath; private protected string _devicePath;
/// <summary>Returns if remote is running under administrative (aka root) privileges</summary>
public bool IsRemoteAdmin
{
get
{
_isRemoteAdmin ??= _remote.IsRoot;
return _isRemoteAdmin == true;
}
}
/// <summary>Current device is remote</summary>
public bool IsRemote => _remote != null;
/// <summary>Remote application</summary>
public string RemoteApplication => _remote?.ServerApplication;
/// <summary>Remote application server</summary>
public string RemoteVersion => _remote?.ServerVersion;
/// <summary>Remote operating system name</summary>
public string RemoteOperatingSystem => _remote?.ServerOperatingSystem;
/// <summary>Remote operating system version</summary>
public string RemoteOperatingSystemVersion => _remote?.ServerOperatingSystemVersion;
/// <summary>Remote architecture</summary>
public string RemoteArchitecture => _remote?.ServerArchitecture;
/// <summary>Remote protocol version</summary>
public int RemoteProtocolVersion => _remote?.ServerProtocolVersion ?? 0;
} }

View File

@@ -39,24 +39,16 @@ using System.Text;
using Aaru.CommonTypes.Interop; using Aaru.CommonTypes.Interop;
using Aaru.Decoders.ATA; using Aaru.Decoders.ATA;
static class Command partial class Device
{ {
/// <summary>Sends a SCSI command</summary> /// <inheritdoc />
/// <returns>0 if no error occurred, otherwise, errno</returns> public override int SendScsiCommand(byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout,
/// <param name="fd">File handle</param> ScsiDirection direction, out double duration, out bool sense)
/// <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(int fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout,
ScsiIoctlDirection direction, out double duration, out bool sense)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
senseBuffer = null; senseBuffer = null;
duration = 0; duration = 0;
sense = false; sense = false;
@@ -64,6 +56,32 @@ static class Command
if(buffer == null) if(buffer == null)
return -1; return -1;
ScsiIoctlDirection dir;
switch(direction)
{
case ScsiDirection.In:
dir = ScsiIoctlDirection.In;
break;
case ScsiDirection.Out:
dir = ScsiIoctlDirection.Out;
break;
case ScsiDirection.Bidirectional:
dir = ScsiIoctlDirection.Unspecified;
break;
case ScsiDirection.None:
dir = ScsiIoctlDirection.None;
break;
default:
dir = ScsiIoctlDirection.Unknown;
break;
}
var ioHdr = new SgIoHdrT(); var ioHdr = new SgIoHdrT();
senseBuffer = new byte[64]; senseBuffer = new byte[64];
@@ -71,7 +89,7 @@ static class Command
ioHdr.interface_id = 'S'; ioHdr.interface_id = 'S';
ioHdr.cmd_len = (byte)cdb.Length; ioHdr.cmd_len = (byte)cdb.Length;
ioHdr.mx_sb_len = (byte)senseBuffer.Length; ioHdr.mx_sb_len = (byte)senseBuffer.Length;
ioHdr.dxfer_direction = direction; ioHdr.dxfer_direction = dir;
ioHdr.dxfer_len = (uint)buffer.Length; ioHdr.dxfer_len = (uint)buffer.Length;
ioHdr.dxferp = Marshal.AllocHGlobal(buffer.Length); ioHdr.dxferp = Marshal.AllocHGlobal(buffer.Length);
ioHdr.cmdp = Marshal.AllocHGlobal(cdb.Length); ioHdr.cmdp = Marshal.AllocHGlobal(cdb.Length);
@@ -84,7 +102,7 @@ static class Command
Marshal.Copy(senseBuffer, 0, ioHdr.sbp, senseBuffer.Length); Marshal.Copy(senseBuffer, 0, ioHdr.sbp, senseBuffer.Length);
DateTime start = DateTime.UtcNow; DateTime start = DateTime.UtcNow;
int error = Extern.ioctlSg(fd, LinuxIoctl.SgIo, ref ioHdr); int error = Extern.ioctlSg(_fileDescriptor, LinuxIoctl.SgIo, ref ioHdr);
DateTime end = DateTime.UtcNow; DateTime end = DateTime.UtcNow;
if(error < 0) if(error < 0)
@@ -108,7 +126,7 @@ static class Command
/// <summary>Converts ATA protocol to SG_IO direction</summary> /// <summary>Converts ATA protocol to SG_IO direction</summary>
/// <param name="protocol">ATA protocol</param> /// <param name="protocol">ATA protocol</param>
/// <returns>SG_IO direction</returns> /// <returns>SG_IO direction</returns>
static ScsiIoctlDirection AtaProtocolToScsiDirection(AtaProtocol protocol) static ScsiDirection AtaProtocolToScsiDirection(AtaProtocol protocol)
{ {
switch(protocol) switch(protocol)
{ {
@@ -117,31 +135,24 @@ static class Command
case AtaProtocol.HardReset: case AtaProtocol.HardReset:
case AtaProtocol.NonData: case AtaProtocol.NonData:
case AtaProtocol.SoftReset: case AtaProtocol.SoftReset:
case AtaProtocol.ReturnResponse: return ScsiIoctlDirection.None; case AtaProtocol.ReturnResponse: return ScsiDirection.None;
case AtaProtocol.PioIn: case AtaProtocol.PioIn:
case AtaProtocol.UDmaIn: return ScsiIoctlDirection.In; case AtaProtocol.UDmaIn: return ScsiDirection.In;
case AtaProtocol.PioOut: case AtaProtocol.PioOut:
case AtaProtocol.UDmaOut: return ScsiIoctlDirection.Out; case AtaProtocol.UDmaOut: return ScsiDirection.Out;
default: return ScsiIoctlDirection.Unspecified; default: return ScsiDirection.Unspecified;
} }
} }
/// <summary>Sends an ATA command in CHS mode</summary> /// <inheritdoc />
/// <returns>0 if no error occurred, otherwise, errno</returns> public override int SendAtaCommand(AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters,
/// <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>
/// <param name="transferRegister">Which register contains the transfer count</param>
/// <param name="transferBlocks">Set to <c>true</c> if the transfer count is in blocks, otherwise it is in bytes</param>
internal static int SendAtaCommand(int fd, AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters,
AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
uint timeout, bool transferBlocks, out double duration, out bool sense) uint timeout, bool transferBlocks, out double duration, out bool sense)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
duration = 0; duration = 0;
sense = false; sense = false;
errorRegisters = new AtaErrorRegistersChs(); errorRegisters = new AtaErrorRegistersChs();
@@ -185,7 +196,7 @@ static class Command
cdb[13] = registers.DeviceHead; cdb[13] = registers.DeviceHead;
cdb[14] = registers.Command; cdb[14] = registers.Command;
int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, int error = SendScsiCommand(cdb, ref buffer, out byte[] senseBuffer, timeout,
AtaProtocolToScsiDirection(protocol), out duration, out sense); AtaProtocolToScsiDirection(protocol), out duration, out sense);
if(senseBuffer.Length < 22 || if(senseBuffer.Length < 22 ||
@@ -206,22 +217,15 @@ static class Command
return error; return error;
} }
/// <summary>Sends an ATA command in 28-bit LBA mode</summary> /// <inheritdoc />
/// <returns>0 if no error occurred, otherwise, errno</returns> public override int SendAtaCommand(AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters,
/// <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>
/// <param name="transferRegister">Which register contains the transfer count</param>
/// <param name="transferBlocks">Set to <c>true</c> if the transfer count is in blocks, otherwise it is in bytes</param>
internal static int SendAtaCommand(int fd, AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters,
AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
uint timeout, bool transferBlocks, out double duration, out bool sense) uint timeout, bool transferBlocks, out double duration, out bool sense)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
duration = 0; duration = 0;
sense = false; sense = false;
errorRegisters = new AtaErrorRegistersLba28(); errorRegisters = new AtaErrorRegistersLba28();
@@ -265,7 +269,7 @@ static class Command
cdb[13] = registers.DeviceHead; cdb[13] = registers.DeviceHead;
cdb[14] = registers.Command; cdb[14] = registers.Command;
int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, int error = SendScsiCommand(cdb, ref buffer, out byte[] senseBuffer, timeout,
AtaProtocolToScsiDirection(protocol), out duration, out sense); AtaProtocolToScsiDirection(protocol), out duration, out sense);
if(senseBuffer.Length < 22 || if(senseBuffer.Length < 22 ||
@@ -286,22 +290,15 @@ static class Command
return error; return error;
} }
/// <summary>Sends an ATA command in 48-bit LBA mode</summary> /// <inheritdoc />
/// <returns>0 if no error occurred, otherwise, errno</returns> public override int SendAtaCommand(AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters,
/// <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>
/// <param name="transferRegister">Which register contains the transfer count</param>
/// <param name="transferBlocks">Set to <c>true</c> if the transfer count is in blocks, otherwise it is in bytes</param>
internal static int SendAtaCommand(int fd, AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters,
AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
uint timeout, bool transferBlocks, out double duration, out bool sense) uint timeout, bool transferBlocks, out double duration, out bool sense)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
duration = 0; duration = 0;
sense = false; sense = false;
errorRegisters = new AtaErrorRegistersLba48(); errorRegisters = new AtaErrorRegistersLba48();
@@ -351,7 +348,7 @@ static class Command
cdb[13] = registers.DeviceHead; cdb[13] = registers.DeviceHead;
cdb[14] = registers.Command; cdb[14] = registers.Command;
int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, int error = SendScsiCommand(cdb, ref buffer, out byte[] senseBuffer, timeout,
AtaProtocolToScsiDirection(protocol), out duration, out sense); AtaProtocolToScsiDirection(protocol), out duration, out sense);
if(senseBuffer.Length < 22 || if(senseBuffer.Length < 22 ||
@@ -377,25 +374,71 @@ static class Command
return error; return error;
} }
/// <summary>Sends a MMC/SD command</summary> /// <inheritdoc />
/// <returns>The result of the command.</returns> public override int SendMmcCommand(MmcCommands command, bool write, bool isApplication, MmcFlags flags,
/// <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(int fd, MmcCommands command, bool write, bool isApplication, MmcFlags flags,
uint argument, uint blockSize, uint blocks, ref byte[] buffer, uint argument, uint blockSize, uint blocks, ref byte[] buffer,
out uint[] response, out double duration, out bool sense, uint timeout = 0) out uint[] response, out double duration, out bool sense, uint timeout = 15)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
DateTime start;
DateTime end;
switch(command)
{
case MmcCommands.SendCid when _cachedCid != null:
{
start = DateTime.Now;
buffer = new byte[_cachedCid.Length];
Array.Copy(_cachedCid, buffer, buffer.Length);
response = new uint[4];
sense = false;
end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case MmcCommands.SendCsd when _cachedCid != null:
{
start = DateTime.Now;
buffer = new byte[_cachedCsd.Length];
Array.Copy(_cachedCsd, buffer, buffer.Length);
response = new uint[4];
sense = false;
end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case (MmcCommands)SecureDigitalCommands.SendScr when _cachedScr != null:
{
start = DateTime.Now;
buffer = new byte[_cachedScr.Length];
Array.Copy(_cachedScr, buffer, buffer.Length);
response = new uint[4];
sense = false;
end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case (MmcCommands)SecureDigitalCommands.SendOperatingCondition when _cachedOcr != null:
case MmcCommands.SendOpCond when _cachedOcr != null:
{
start = DateTime.Now;
buffer = new byte[_cachedOcr.Length];
Array.Copy(_cachedOcr, buffer, buffer.Length);
response = new uint[4];
sense = false;
end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
}
response = null; response = null;
duration = 0; duration = 0;
sense = false; sense = false;
@@ -425,9 +468,9 @@ static class Command
Marshal.Copy(buffer, 0, bufPtr, buffer.Length); Marshal.Copy(buffer, 0, bufPtr, buffer.Length);
DateTime start = DateTime.UtcNow; start = DateTime.UtcNow;
int error = Extern.ioctlMmc(fd, LinuxIoctl.MmcIocCmd, ref ioCmd); int error = Extern.ioctlMmc(_fileDescriptor, LinuxIoctl.MmcIocCmd, ref ioCmd);
DateTime end = DateTime.UtcNow; end = DateTime.UtcNow;
sense |= error < 0; sense |= error < 0;
@@ -444,9 +487,14 @@ static class Command
return error; return error;
} }
internal static int SendMultipleMmcCommands(int fd, Device.MmcSingleCommand[] commands, out double duration, /// <inheritdoc />
out bool sense, uint timeout = 0) public override int SendMultipleMmcCommands(MmcSingleCommand[] commands, out double duration, out bool sense,
uint timeout = 15)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
duration = 0; duration = 0;
sense = false; sense = false;
int off; int off;
@@ -506,7 +554,7 @@ static class Command
// Send command // Send command
DateTime start = DateTime.UtcNow; DateTime start = DateTime.UtcNow;
int error = Extern.ioctlMmcMulti(fd, LinuxIoctl.MmcIocMultiCmd, ioMultiCmdPtr); int error = Extern.ioctlMmcMulti(_fileDescriptor, LinuxIoctl.MmcIocMultiCmd, ioMultiCmdPtr);
DateTime end = DateTime.UtcNow; DateTime end = DateTime.UtcNow;
sense |= error < 0; sense |= error < 0;
@@ -549,35 +597,61 @@ static class Command
return error; return error;
} }
internal static int ReOpen(string devicePath, int fd, out object newFd) /// <inheritdoc />
{ public override bool ReOpen()
newFd = -1;
int ret = Extern.close(fd); {
int ret = Extern.close(_fileDescriptor);
if(ret < 0) if(ret < 0)
return Marshal.GetLastWin32Error(); {
LastError = Marshal.GetLastWin32Error();
Error = true;
newFd = Extern.open(devicePath, FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew); return true;
}
if((int)newFd >= 0) int newFd = Extern.open(_devicePath, FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew);
return 0;
if(newFd >= 0)
{
Error = false;
_fileDescriptor = newFd;
return false;
}
int error = Marshal.GetLastWin32Error(); int error = Marshal.GetLastWin32Error();
if(error != 13 && if(error != 13 &&
error != 30) error != 30)
return Marshal.GetLastWin32Error(); {
LastError = Marshal.GetLastWin32Error();
Error = true;
newFd = Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking); return true;
}
return (int)newFd < 0 ? Marshal.GetLastWin32Error() : 0; newFd = Extern.open(_devicePath, FileFlags.Readonly | FileFlags.NonBlocking);
if(newFd < 0)
{
LastError = Marshal.GetLastWin32Error();
Error = true;
return true;
}
Error = false;
_fileDescriptor = newFd;
return false;
} }
/// <summary>Reads the contents of a symbolic link</summary> /// <summary>Reads the contents of a symbolic link</summary>
/// <param name="path">Path to the symbolic link</param> /// <param name="path">Path to the symbolic link</param>
/// <returns>Contents of the symbolic link</returns> /// <returns>Contents of the symbolic link</returns>
internal static string ReadLink(string path) static string ReadLink(string path)
{ {
IntPtr buf = Marshal.AllocHGlobal(4096); IntPtr buf = Marshal.AllocHGlobal(4096);
int resultSize; int resultSize;
@@ -608,13 +682,15 @@ static class Command
return Encoding.ASCII.GetString(resultString); return Encoding.ASCII.GetString(resultString);
} }
internal static int BufferedOsRead(int fd, out byte[] buffer, long offset, uint length, out double duration) /// <inheritdoc />
public override bool BufferedOsRead(out byte[] buffer, long offset, uint length, out double duration)
{ {
buffer = new byte[length]; buffer = new byte[length];
DateTime start = DateTime.Now; DateTime start = DateTime.Now;
long sense = Extern.lseek(fd, offset, SeekWhence.Begin); long sense = Extern.lseek(_fileDescriptor, offset, SeekWhence.Begin);
DateTime end = DateTime.Now; DateTime end = DateTime.Now;
@@ -622,10 +698,14 @@ static class Command
{ {
duration = (end - start).TotalMilliseconds; duration = (end - start).TotalMilliseconds;
return Marshal.GetLastWin32Error(); Error = true;
LastError = Marshal.GetLastWin32Error();
return true;
} }
sense = DetectOS.Is64Bit ? Extern.read64(fd, buffer, length) : Extern.read(fd, buffer, (int)length); sense = DetectOS.Is64Bit ? Extern.read64(_fileDescriptor, buffer, length)
: Extern.read(_fileDescriptor, buffer, (int)length);
end = DateTime.Now; end = DateTime.Now;
duration = (end - start).TotalMilliseconds; duration = (end - start).TotalMilliseconds;
@@ -637,6 +717,9 @@ static class Command
else if(errno == 0) else if(errno == 0)
errno = -22; errno = -22;
return errno; LastError = errno;
Error = errno == 0;
return errno != 0;
} }
} }

View File

@@ -46,31 +46,34 @@ using VendorString = Aaru.Decoders.MMC.VendorString;
/// <inheritdoc /> /// <inheritdoc />
[SupportedOSPlatform("linux")] [SupportedOSPlatform("linux")]
public class Device : Devices.Device partial class Device : Devices.Device
{ {
/// <summary>Gets the file handle representing this device</summary>
/// <value>The file handle</value>
int _fileDescriptor;
Device() {} Device() {}
public new static Device Create(string devicePath) internal new static Device Create(string devicePath)
{ {
var dev = new Device var dev = new Device
{ {
PlatformId = DetectOS.GetRealPlatformID(), PlatformId = DetectOS.GetRealPlatformID(),
Timeout = 15, Timeout = 15,
Error = false, Error = false,
IsRemovable = false IsRemovable = false,
_fileDescriptor = Extern.open(devicePath, FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew)
}; };
dev.FileHandle = Extern.open(devicePath, FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew); if(dev._fileDescriptor < 0)
if((int)dev.FileHandle < 0)
{ {
dev.LastError = Marshal.GetLastWin32Error(); dev.LastError = Marshal.GetLastWin32Error();
if(dev.LastError is 13 or 30) // EACCES or EROFS if(dev.LastError is 13 or 30) // EACCES or EROFS
{ {
dev.FileHandle = Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking); dev._fileDescriptor = Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking);
if((int)dev.FileHandle < 0) if(dev._fileDescriptor < 0)
{ {
dev.Error = true; dev.Error = true;
dev.LastError = Marshal.GetLastWin32Error(); dev.LastError = Marshal.GetLastWin32Error();
@@ -92,23 +95,22 @@ public class Device : Devices.Device
dev.Type = DeviceType.Unknown; dev.Type = DeviceType.Unknown;
dev.ScsiType = PeripheralDeviceTypes.UnknownDevice; dev.ScsiType = PeripheralDeviceTypes.UnknownDevice;
byte[] ataBuf;
byte[] inqBuf = null;
if(dev.Error) if(dev.Error)
throw new DeviceException(dev.LastError); throw new DeviceException(dev.LastError);
string devPath;
if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/st", StringComparison.Ordinal) || devicePath.StartsWith("/dev/st", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/sg", StringComparison.Ordinal)) devicePath.StartsWith("/dev/sg", StringComparison.Ordinal))
if(!dev.ScsiInquiry(out inqBuf, out _)) if(!dev.ScsiInquiry(out byte[] _, out _))
dev.Type = DeviceType.SCSI; dev.Type = DeviceType.SCSI;
// MultiMediaCard and SecureDigital go here // MultiMediaCard and SecureDigital go here
else if(devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal)) else if(devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal))
{ {
string devPath = devicePath.Substring(5); devPath = devicePath[5..];
if(File.Exists("/sys/block/" + devPath + "/device/csd")) if(File.Exists("/sys/block/" + devPath + "/device/csd"))
{ {
@@ -179,21 +181,23 @@ public class Device : Devices.Device
#endregion SecureDigital / MultiMediaCard #endregion SecureDigital / MultiMediaCard
#region USB #region USB
string resolvedLink;
if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
{ {
string devPath = devicePath.Substring(5); devPath = devicePath[5..];
if(Directory.Exists("/sys/block/" + devPath)) if(Directory.Exists("/sys/block/" + devPath))
{ {
string resolvedLink = Command.ReadLink("/sys/block/" + devPath); resolvedLink = ReadLink("/sys/block/" + devPath);
if(!string.IsNullOrEmpty(resolvedLink)) if(!string.IsNullOrEmpty(resolvedLink))
{ {
resolvedLink = "/sys" + resolvedLink.Substring(2); resolvedLink = "/sys" + resolvedLink[2..];
while(resolvedLink.Contains("usb")) while(resolvedLink?.Contains("usb") == true)
{ {
resolvedLink = Path.GetDirectoryName(resolvedLink); resolvedLink = Path.GetDirectoryName(resolvedLink);
@@ -257,22 +261,19 @@ public class Device : Devices.Device
#endregion USB #endregion USB
#region FireWire #region FireWire
{
if(true)
{
if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
{ {
string devPath = devicePath.Substring(5); devPath = devicePath[5..];
if(Directory.Exists("/sys/block/" + devPath)) if(Directory.Exists("/sys/block/" + devPath))
{ {
string resolvedLink = Command.ReadLink("/sys/block/" + devPath); resolvedLink = ReadLink("/sys/block/" + devPath);
resolvedLink = "/sys" + resolvedLink.Substring(2); resolvedLink = "/sys" + resolvedLink[2..];
if(!string.IsNullOrEmpty(resolvedLink)) if(!string.IsNullOrEmpty(resolvedLink))
while(resolvedLink.Contains("firewire")) while(resolvedLink?.Contains("firewire") == true)
{ {
resolvedLink = Path.GetDirectoryName(resolvedLink); resolvedLink = Path.GetDirectoryName(resolvedLink);
@@ -325,27 +326,25 @@ public class Device : Devices.Device
} }
} }
} }
}
// TODO: Implement for other operating systems
else
dev.IsFireWire = false;
}
#endregion FireWire #endregion FireWire
#region PCMCIA #region PCMCIA
if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || if(!devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) &&
devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || !devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) &&
devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) !devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
{ return dev;
string devPath = devicePath.Substring(5);
if(Directory.Exists("/sys/block/" + devPath)) devPath = devicePath[5..];
{
string resolvedLink = Command.ReadLink("/sys/block/" + devPath); if(!Directory.Exists("/sys/block/" + devPath))
resolvedLink = "/sys" + resolvedLink.Substring(2); return dev;
resolvedLink = ReadLink("/sys/block/" + devPath);
resolvedLink = "/sys" + resolvedLink[2..];
if(string.IsNullOrEmpty(resolvedLink))
return dev;
if(!string.IsNullOrEmpty(resolvedLink))
while(resolvedLink.Contains("/sys/devices")) while(resolvedLink.Contains("/sys/devices"))
{ {
resolvedLink = Path.GetDirectoryName(resolvedLink); resolvedLink = Path.GetDirectoryName(resolvedLink);
@@ -377,8 +376,6 @@ public class Device : Devices.Device
break; break;
} }
}
}
#endregion PCMCIA #endregion PCMCIA
return dev; return dev;
@@ -399,11 +396,11 @@ public class Device : Devices.Device
/// <inheritdoc /> /// <inheritdoc />
public override void Close() public override void Close()
{ {
if(FileHandle == null) if(_fileDescriptor == 0)
return; return;
Extern.close((int)FileHandle); Extern.close(_fileDescriptor);
FileHandle = null; _fileDescriptor = 0;
} }
} }

View File

@@ -0,0 +1,199 @@
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Commands.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Direct device access.
//
// --[ Description ] ----------------------------------------------------------
//
// Sends commands to 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-2022 Natalia Portillo
// ****************************************************************************/
namespace Aaru.Devices.Remote;
using System;
using System.Diagnostics.CodeAnalysis;
using Aaru.Decoders.ATA;
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public partial class Device
{
/// <inheritdoc />
public override int SendScsiCommand(byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout,
ScsiDirection direction, out double duration, out bool sense)
{
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
return _remote.SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, direction, out duration, out sense);
}
/// <inheritdoc />
public override int SendAtaCommand(AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters,
AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
uint timeout, bool transferBlocks, out double duration, out bool sense)
{
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer, timeout,
transferBlocks, out duration, out sense);
}
/// <inheritdoc />
public override int SendAtaCommand(AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters,
AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
uint timeout, bool transferBlocks, out double duration, out bool sense)
{
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer, timeout,
transferBlocks, out duration, out sense);
}
/// <inheritdoc />
public override int SendAtaCommand(AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters,
AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
uint timeout, bool transferBlocks, out double duration, out bool sense)
{
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer, timeout,
transferBlocks, out duration, out sense);
}
/// <inheritdoc />
public override int SendMmcCommand(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 = 15)
{
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
switch(command)
{
case MmcCommands.SendCid when _cachedCid != null:
{
DateTime start = DateTime.Now;
buffer = new byte[_cachedCid.Length];
Array.Copy(_cachedCid, buffer, buffer.Length);
response = new uint[4];
sense = false;
DateTime end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case MmcCommands.SendCsd when _cachedCid != null:
{
DateTime start = DateTime.Now;
buffer = new byte[_cachedCsd.Length];
Array.Copy(_cachedCsd, buffer, buffer.Length);
response = new uint[4];
sense = false;
DateTime end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case (MmcCommands)SecureDigitalCommands.SendScr when _cachedScr != null:
{
DateTime start = DateTime.Now;
buffer = new byte[_cachedScr.Length];
Array.Copy(_cachedScr, buffer, buffer.Length);
response = new uint[4];
sense = false;
DateTime end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case (MmcCommands)SecureDigitalCommands.SendOperatingCondition when _cachedOcr != null:
case MmcCommands.SendOpCond when _cachedOcr != null:
{
DateTime start = DateTime.Now;
buffer = new byte[_cachedOcr.Length];
Array.Copy(_cachedOcr, buffer, buffer.Length);
response = new uint[4];
sense = false;
DateTime end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
}
return _remote.SendMmcCommand(command, write, isApplication, flags, argument, blockSize, blocks, ref buffer,
out response, out duration, out sense, timeout);
}
/// <inheritdoc />
public override int SendMultipleMmcCommands(MmcSingleCommand[] commands, out double duration, out bool sense,
uint timeout = 15)
{
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
if(_remote.ServerProtocolVersion >= 2)
return _remote.SendMultipleMmcCommands(commands, out duration, out sense, timeout);
var error = 0;
duration = 0;
sense = false;
foreach(MmcSingleCommand command in commands)
{
int singleError = _remote.SendMmcCommand(command.command, command.write, command.isApplication,
command.flags, command.argument, command.blockSize, command.blocks,
ref command.buffer, out command.response, out double cmdDuration,
out bool cmdSense, timeout);
if(error == 0 &&
singleError != 0)
error = singleError;
duration += cmdDuration;
if(cmdSense)
sense = true;
}
return error;
}
/// <inheritdoc />
public override bool ReOpen() => _remote.ReOpen();
/// <inheritdoc />
public override bool BufferedOsRead(out byte[] buffer, long offset, uint length, out double duration) =>
_remote.BufferedOsRead(out buffer, offset, length, out duration);
}

View File

@@ -39,14 +39,43 @@ using Aaru.CommonTypes.Structs.Devices.SCSI;
using Aaru.Decoders.SecureDigital; using Aaru.Decoders.SecureDigital;
/// <inheritdoc /> /// <inheritdoc />
public sealed class Device : Devices.Device public sealed partial class Device : Devices.Device
{ {
Remote _remote;
/// <summary>Returns if remote is running under administrative (aka root) privileges</summary>
public bool IsAdmin
{
get
{
_isRemoteAdmin ??= _remote.IsRoot;
return _isRemoteAdmin == true;
}
}
/// <summary>Current device is remote</summary>
public bool IsRemote => _remote != null;
/// <summary>Remote application</summary>
public string RemoteApplication => _remote?.ServerApplication;
/// <summary>Remote application server</summary>
public string RemoteVersion => _remote?.ServerVersion;
/// <summary>Remote operating system name</summary>
public string RemoteOperatingSystem => _remote?.ServerOperatingSystem;
/// <summary>Remote operating system version</summary>
public string RemoteOperatingSystemVersion => _remote?.ServerOperatingSystemVersion;
/// <summary>Remote architecture</summary>
public string RemoteArchitecture => _remote?.ServerArchitecture;
/// <summary>Remote protocol version</summary>
public int RemoteProtocolVersion => _remote?.ServerProtocolVersion ?? 0;
bool? _isRemoteAdmin;
Device() {} Device() {}
/// <summary>Opens the device for sending direct commands</summary> /// <summary>Opens the device for sending direct commands</summary>
/// <param name="aaruUri">AaruRemote URI</param> /// <param name="aaruUri">AaruRemote URI</param>
/// <returns>Device</returns> /// <returns>Device</returns>
public static Device Create(Uri aaruUri) internal static Device Create(Uri aaruUri)
{ {
var dev = new Device var dev = new Device
{ {
@@ -83,9 +112,6 @@ public sealed class Device : Devices.Device
dev.Type = DeviceType.Unknown; dev.Type = DeviceType.Unknown;
dev.ScsiType = PeripheralDeviceTypes.UnknownDevice; dev.ScsiType = PeripheralDeviceTypes.UnknownDevice;
byte[] ataBuf;
byte[] inqBuf = null;
if(dev.Error) if(dev.Error)
throw new DeviceException(dev.LastError); throw new DeviceException(dev.LastError);
@@ -93,11 +119,6 @@ public sealed class Device : Devices.Device
switch(dev.Type) switch(dev.Type)
{ {
case DeviceType.ATAPI:
case DeviceType.SCSI:
bool scsiSense = dev.ScsiInquiry(out inqBuf, out _);
break;
case DeviceType.SecureDigital: case DeviceType.SecureDigital:
case DeviceType.MMC: case DeviceType.MMC:
if(!dev._remote.GetSdhciRegisters(out dev._cachedCsd, out dev._cachedCid, out dev._cachedOcr, if(!dev._remote.GetSdhciRegisters(out dev._cachedCsd, out dev._cachedCid, out dev._cachedOcr,

View File

@@ -40,24 +40,16 @@ using Aaru.Decoders.ATA;
using Microsoft.Win32.SafeHandles; using Microsoft.Win32.SafeHandles;
[SuppressMessage("ReSharper", "UnusedParameter.Global")] [SuppressMessage("ReSharper", "UnusedParameter.Global")]
static class Command partial class Device
{ {
/// <summary>Sends a SCSI command</summary> /// <inheritdoc />
/// <returns>0 if no error occurred, otherwise, errno</returns> public override int SendScsiCommand(byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout,
/// <param name="fd">File handle</param> ScsiDirection direction, out double duration, out bool sense)
/// <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)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
senseBuffer = null; senseBuffer = null;
duration = 0; duration = 0;
sense = false; sense = false;
@@ -65,6 +57,24 @@ static class Command
if(buffer == null) if(buffer == null)
return -1; return -1;
ScsiIoctlDirection dir;
switch(direction)
{
case ScsiDirection.In:
dir = ScsiIoctlDirection.In;
break;
case ScsiDirection.Out:
dir = ScsiIoctlDirection.Out;
break;
default:
dir = ScsiIoctlDirection.Unspecified;
break;
}
var sptdSb = new ScsiPassThroughDirectAndSenseBuffer var sptdSb = new ScsiPassThroughDirectAndSenseBuffer
{ {
SenseBuf = new byte[32], SenseBuf = new byte[32],
@@ -73,7 +83,7 @@ static class Command
Cdb = new byte[16], Cdb = new byte[16],
CdbLength = (byte)cdb.Length, CdbLength = (byte)cdb.Length,
SenseInfoLength = 32, SenseInfoLength = 32,
DataIn = direction, DataIn = dir,
DataTransferLength = (uint)buffer.Length, DataTransferLength = (uint)buffer.Length,
TimeOutValue = timeout, TimeOutValue = timeout,
DataBuffer = Marshal.AllocHGlobal(buffer.Length) DataBuffer = Marshal.AllocHGlobal(buffer.Length)
@@ -91,7 +101,7 @@ static class Command
DateTime start = DateTime.Now; DateTime start = DateTime.Now;
bool hasError = !Extern.DeviceIoControlScsi(fd, WindowsIoctl.IoctlScsiPassThroughDirect, ref sptdSb, bool hasError = !Extern.DeviceIoControlScsi(_fileHandle, WindowsIoctl.IoctlScsiPassThroughDirect, ref sptdSb,
(uint)Marshal.SizeOf(sptdSb), ref sptdSb, (uint)Marshal.SizeOf(sptdSb), ref sptdSb,
(uint)Marshal.SizeOf(sptdSb), ref k, IntPtr.Zero); (uint)Marshal.SizeOf(sptdSb), ref k, IntPtr.Zero);
@@ -114,20 +124,15 @@ static class Command
return error; return error;
} }
/// <summary>Sends an ATA command in CHS mode</summary> /// <inheritdoc />
/// <returns>0 if no error occurred, otherwise, errno</returns> public override int SendAtaCommand(AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters,
/// <param name="fd">File handle</param> AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
/// <param name="buffer">Buffer for SCSI command response</param> uint timeout, bool transferBlocks, out double duration, out bool sense)
/// <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)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
duration = 0; duration = 0;
sense = false; sense = false;
errorRegisters = new AtaErrorRegistersChs(); errorRegisters = new AtaErrorRegistersChs();
@@ -191,7 +196,7 @@ static class Command
DateTime start = DateTime.Now; DateTime start = DateTime.Now;
sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd, sense = !Extern.DeviceIoControlAta(_fileHandle, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd,
(uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k,
IntPtr.Zero); IntPtr.Zero);
@@ -219,20 +224,15 @@ static class Command
return error; return error;
} }
/// <summary>Sends an ATA command in 28-bit LBA mode</summary> /// <inheritdoc />
/// <returns>0 if no error occurred, otherwise, errno</returns> public override int SendAtaCommand(AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters,
/// <param name="fd">File handle</param> AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
/// <param name="buffer">Buffer for SCSI command response</param> uint timeout, bool transferBlocks, out double duration, out bool sense)
/// <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)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
duration = 0; duration = 0;
sense = false; sense = false;
errorRegisters = new AtaErrorRegistersLba28(); errorRegisters = new AtaErrorRegistersLba28();
@@ -296,7 +296,7 @@ static class Command
DateTime start = DateTime.Now; DateTime start = DateTime.Now;
sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd, sense = !Extern.DeviceIoControlAta(_fileHandle, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd,
(uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k,
IntPtr.Zero); IntPtr.Zero);
@@ -324,20 +324,15 @@ static class Command
return error; return error;
} }
/// <summary>Sends an ATA command in 48-bit LBA mode</summary> /// <inheritdoc />
/// <returns>0 if no error occurred, otherwise, errno</returns> public override int SendAtaCommand(AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters,
/// <param name="fd">File handle</param> AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer,
/// <param name="buffer">Buffer for SCSI command response</param> uint timeout, bool transferBlocks, out double duration, out bool sense)
/// <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)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
duration = 0; duration = 0;
sense = false; sense = false;
errorRegisters = new AtaErrorRegistersLba48(); errorRegisters = new AtaErrorRegistersLba48();
@@ -410,7 +405,7 @@ static class Command
DateTime start = DateTime.Now; DateTime start = DateTime.Now;
sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd, sense = !Extern.DeviceIoControlAta(_fileHandle, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd,
(uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k,
IntPtr.Zero); IntPtr.Zero);
@@ -458,25 +453,67 @@ static class Command
queryData1.protocolGuid.Equals(Consts.GuidSffProtocolMmc); queryData1.protocolGuid.Equals(Consts.GuidSffProtocolMmc);
} }
/// <summary>Sends a MMC/SD command</summary> /// <inheritdoc />
/// <returns>The result of the command.</returns> public override int SendMmcCommand(MmcCommands command, bool write, bool isApplication, MmcFlags flags,
/// <param name="fd">File handle</param> uint argument, uint blockSize, uint blocks, ref byte[] buffer,
/// <param name="command">MMC/SD opcode</param> out uint[] response, out double duration, out bool sense, uint timeout = 15)
/// <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)
{ {
DateTime start;
DateTime end;
switch(command)
{
case MmcCommands.SendCid when _cachedCid != null:
{
start = DateTime.Now;
buffer = new byte[_cachedCid.Length];
Array.Copy(_cachedCid, buffer, buffer.Length);
response = new uint[4];
sense = false;
end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case MmcCommands.SendCsd when _cachedCid != null:
{
start = DateTime.Now;
buffer = new byte[_cachedCsd.Length];
Array.Copy(_cachedCsd, buffer, buffer.Length);
response = new uint[4];
sense = false;
end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case (MmcCommands)SecureDigitalCommands.SendScr when _cachedScr != null:
{
start = DateTime.Now;
buffer = new byte[_cachedScr.Length];
Array.Copy(_cachedScr, buffer, buffer.Length);
response = new uint[4];
sense = false;
end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
case (MmcCommands)SecureDigitalCommands.SendOperatingCondition when _cachedOcr != null:
case MmcCommands.SendOpCond when _cachedOcr != null:
{
start = DateTime.Now;
buffer = new byte[_cachedOcr.Length];
Array.Copy(_cachedOcr, buffer, buffer.Length);
response = new uint[4];
sense = false;
end = DateTime.Now;
duration = (end - start).TotalMilliseconds;
return 0;
}
}
var commandData = new SffdiskDeviceCommandData(); var commandData = new SffdiskDeviceCommandData();
var commandDescriptor = new SdCmdDescriptor(); var commandDescriptor = new SdCmdDescriptor();
commandData.size = (ushort)Marshal.SizeOf(commandData); commandData.size = (ushort)Marshal.SizeOf(commandData);
@@ -532,12 +569,12 @@ static class Command
Marshal.FreeHGlobal(hBuf); Marshal.FreeHGlobal(hBuf);
var error = 0; var error = 0;
DateTime start = DateTime.Now; start = DateTime.Now;
sense = !Extern.DeviceIoControl(fd, WindowsIoctl.IoctlSffdiskDeviceCommand, commandB, (uint)commandB.Length, sense = !Extern.DeviceIoControl(_fileHandle, WindowsIoctl.IoctlSffdiskDeviceCommand, commandB,
commandB, (uint)commandB.Length, out _, IntPtr.Zero); (uint)commandB.Length, commandB, (uint)commandB.Length, out _, IntPtr.Zero);
DateTime end = DateTime.Now; end = DateTime.Now;
if(sense) if(sense)
error = Marshal.GetLastWin32Error(); error = Marshal.GetLastWin32Error();
@@ -551,9 +588,14 @@ static class Command
return error; return error;
} }
internal static int SendMultipleMmcCommands(SafeFileHandle fd, Device.MmcSingleCommand[] commands, /// <inheritdoc />
out double duration, out bool sense, uint timeout = 0) public override int SendMultipleMmcCommands(MmcSingleCommand[] commands, out double duration, out bool sense,
uint timeout = 15)
{ {
// We need a timeout
if(timeout == 0)
timeout = Timeout > 0 ? Timeout : 15;
var error = 0; var error = 0;
duration = 0; duration = 0;
sense = false; sense = false;
@@ -562,13 +604,13 @@ static class Command
commands[0].command == MmcCommands.SetBlocklen && commands[0].command == MmcCommands.SetBlocklen &&
commands[1].command == MmcCommands.ReadMultipleBlock && commands[1].command == MmcCommands.ReadMultipleBlock &&
commands[2].command == MmcCommands.StopTransmission) commands[2].command == MmcCommands.StopTransmission)
return SendMmcCommand(fd, commands[1].command, commands[1].write, commands[1].isApplication, return SendMmcCommand(commands[1].command, commands[1].write, commands[1].isApplication, commands[1].flags,
commands[1].flags, commands[1].argument, commands[1].blockSize, commands[1].blocks, commands[1].argument, commands[1].blockSize, commands[1].blocks,
ref commands[1].buffer, out commands[1].response, out duration, out sense, timeout); ref commands[1].buffer, out commands[1].response, out duration, out sense, timeout);
foreach(Device.MmcSingleCommand command in commands) foreach(MmcSingleCommand command in commands)
{ {
int singleError = SendMmcCommand(fd, command.command, command.write, command.isApplication, command.flags, int singleError = SendMmcCommand(command.command, command.write, command.isApplication, command.flags,
command.argument, command.blockSize, command.blocks, ref command.buffer, command.argument, command.blockSize, command.blocks, ref command.buffer,
out command.response, out double cmdDuration, out bool cmdSense, timeout); out command.response, out double cmdDuration, out bool cmdSense, timeout);
@@ -585,25 +627,36 @@ static class Command
return error; return error;
} }
internal static int ReOpen(string devicePath, SafeFileHandle fd, out object newFd) /// <inheritdoc />
public override bool ReOpen()
{ {
Extern.CloseHandle(fd); Extern.CloseHandle(_fileHandle);
newFd = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite, SafeFileHandle newFd = Extern.CreateFile(_devicePath, FileAccess.GenericRead | FileAccess.GenericWrite,
FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting,
FileAttributes.Normal, IntPtr.Zero); FileAttributes.Normal, IntPtr.Zero);
return ((SafeFileHandle)newFd).IsInvalid ? Marshal.GetLastWin32Error() : 0; if(newFd.IsInvalid)
{
LastError = Marshal.GetLastWin32Error();
Error = true;
return true;
} }
internal static int BufferedOsRead(SafeFileHandle fd, out byte[] buffer, long offset, uint length, _fileHandle = newFd;
out double duration)
return false;
}
/// <inheritdoc />
public override bool BufferedOsRead(out byte[] buffer, long offset, uint length, out double duration)
{ {
buffer = new byte[length]; buffer = new byte[length];
DateTime start = DateTime.Now; DateTime start = DateTime.Now;
bool sense = !Extern.SetFilePointerEx(fd, offset, out _, MoveMethod.Begin); bool sense = !Extern.SetFilePointerEx(_fileHandle, offset, out _, MoveMethod.Begin);
DateTime end = DateTime.Now; DateTime end = DateTime.Now;
@@ -611,14 +664,27 @@ static class Command
{ {
duration = (end - start).TotalMilliseconds; duration = (end - start).TotalMilliseconds;
return Marshal.GetLastWin32Error(); LastError = Marshal.GetLastWin32Error();
Error = true;
return true;
} }
sense = !Extern.ReadFile(fd, buffer, length, out _, IntPtr.Zero); sense = !Extern.ReadFile(_fileHandle, buffer, length, out _, IntPtr.Zero);
end = DateTime.Now; end = DateTime.Now;
duration = (end - start).TotalMilliseconds; duration = (end - start).TotalMilliseconds;
return sense ? Marshal.GetLastWin32Error() : 0; if(sense)
{
Error = true;
LastError = Marshal.GetLastWin32Error();
return true;
}
Error = false;
return false;
} }
} }

View File

@@ -43,25 +43,28 @@ using Microsoft.Win32.SafeHandles;
/// <inheritdoc /> /// <inheritdoc />
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
public class Device : Devices.Device partial class Device : Devices.Device
{ {
/// <summary>Gets the file handle representing this device</summary>
/// <value>The file handle</value>
SafeFileHandle _fileHandle;
Device() {} Device() {}
public new static Device Create(string devicePath) internal new static Device Create(string devicePath)
{ {
var dev = new Device var dev = new Device
{ {
PlatformId = DetectOS.GetRealPlatformID(), PlatformId = DetectOS.GetRealPlatformID(),
Timeout = 15, Timeout = 15,
Error = false, Error = false,
IsRemovable = false IsRemovable = false,
_fileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite,
FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting,
FileAttributes.Normal, IntPtr.Zero)
}; };
dev.FileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite, if(dev._fileHandle.IsInvalid)
FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting,
FileAttributes.Normal, IntPtr.Zero);
if(((SafeFileHandle)dev.FileHandle).IsInvalid)
{ {
dev.Error = true; dev.Error = true;
dev.LastError = Marshal.GetLastWin32Error(); dev.LastError = Marshal.GetLastWin32Error();
@@ -73,9 +76,6 @@ public class Device : Devices.Device
dev.Type = DeviceType.Unknown; dev.Type = DeviceType.Unknown;
dev.ScsiType = PeripheralDeviceTypes.UnknownDevice; dev.ScsiType = PeripheralDeviceTypes.UnknownDevice;
byte[] ataBuf;
byte[] inqBuf = null;
if(dev.Error) if(dev.Error)
throw new DeviceException(dev.LastError); throw new DeviceException(dev.LastError);
@@ -91,9 +91,8 @@ public class Device : Devices.Device
uint returned = 0; uint returned = 0;
var error = 0; var error = 0;
bool hasError = !Extern.DeviceIoControlStorageQuery((SafeFileHandle)dev.FileHandle, bool hasError = !Extern.DeviceIoControlStorageQuery(dev._fileHandle, WindowsIoctl.IoctlStorageQueryProperty,
WindowsIoctl.IoctlStorageQueryProperty, ref query, ref query, (uint)Marshal.SizeOf(query), descriptorPtr, 1000,
(uint)Marshal.SizeOf(query), descriptorPtr, 1000,
ref returned, IntPtr.Zero); ref returned, IntPtr.Zero);
if(hasError) if(hasError)
@@ -170,7 +169,7 @@ public class Device : Devices.Device
switch(dev.Type) switch(dev.Type)
{ {
case DeviceType.ATA: case DeviceType.ATA:
bool atapiSense = dev.AtapiIdentify(out ataBuf, out _); bool atapiSense = dev.AtapiIdentify(out byte[] _, out _);
if(!atapiSense) if(!atapiSense)
dev.Type = DeviceType.ATAPI; dev.Type = DeviceType.ATAPI;
@@ -183,13 +182,13 @@ public class Device : Devices.Device
Marshal.FreeHGlobal(descriptorPtr); Marshal.FreeHGlobal(descriptorPtr);
if(Command.IsSdhci((SafeFileHandle)dev.FileHandle)) if(IsSdhci(dev._fileHandle))
{ {
var sdBuffer = new byte[16]; var sdBuffer = new byte[16];
dev.LastError = Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, MmcCommands.SendCsd, false, false, dev.LastError = dev.SendMmcCommand(MmcCommands.SendCsd, false, false,
MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, 16,
16, 1, ref sdBuffer, out _, out _, out bool sense); 1, ref sdBuffer, out _, out _, out bool sense);
if(!sense) if(!sense)
{ {
@@ -199,9 +198,9 @@ public class Device : Devices.Device
sdBuffer = new byte[16]; sdBuffer = new byte[16];
dev.LastError = Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, MmcCommands.SendCid, false, false, dev.LastError = dev.SendMmcCommand(MmcCommands.SendCid, false, false,
MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, 16,
16, 1, ref sdBuffer, out _, out _, out sense); 1, ref sdBuffer, out _, out _, out sense);
if(!sense) if(!sense)
{ {
@@ -211,10 +210,9 @@ public class Device : Devices.Device
sdBuffer = new byte[8]; sdBuffer = new byte[8];
dev.LastError = Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, dev.LastError = dev.SendMmcCommand((MmcCommands)SecureDigitalCommands.SendScr, false, true,
(MmcCommands)SecureDigitalCommands.SendScr, false, true, MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, 0,
MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, 8, 1, ref sdBuffer, out _, out _, out sense);
0, 8, 1, ref sdBuffer, out _, out _, out sense);
if(!sense) if(!sense)
{ {
@@ -224,12 +222,10 @@ public class Device : Devices.Device
sdBuffer = new byte[4]; sdBuffer = new byte[4];
dev.LastError = Command.SendMmcCommand((SafeFileHandle)dev.FileHandle, dev.LastError =
dev._cachedScr != null dev.SendMmcCommand(dev._cachedScr != null ? (MmcCommands)SecureDigitalCommands.SendOperatingCondition : MmcCommands.SendOpCond,
? (MmcCommands)SecureDigitalCommands.SendOperatingCondition false, true, MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | MmcFlags.CommandBcr, 0,
: MmcCommands.SendOpCond, false, true, 4, 1, ref sdBuffer, out _, out _, out sense);
MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | MmcFlags.CommandBcr,
0, 4, 1, ref sdBuffer, out _, out _, out sense);
if(!sense) if(!sense)
{ {
@@ -318,11 +314,11 @@ public class Device : Devices.Device
/// <inheritdoc /> /// <inheritdoc />
public override void Close() public override void Close()
{ {
if(FileHandle == null) if(_fileHandle == null)
return; return;
(FileHandle as SafeFileHandle)?.Close(); _fileHandle?.Close();
FileHandle = null; _fileHandle = null;
} }
} }

View File

@@ -250,10 +250,11 @@ public sealed class MainWindowViewModel : ViewModelBase
{ {
var dev = Device.Create(deviceModel.Path); var dev = Device.Create(deviceModel.Path);
if(dev.IsRemote) if(dev is Devices.Remote.Device remoteDev)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, Statistics.AddRemote(remoteDev.RemoteApplication, remoteDev.RemoteVersion,
dev.RemoteOperatingSystem, dev.RemoteOperatingSystemVersion, remoteDev.RemoteOperatingSystem,
dev.RemoteArchitecture); remoteDev.RemoteOperatingSystemVersion,
remoteDev.RemoteArchitecture);
if(dev.Error) if(dev.Error)
{ {
@@ -789,9 +790,10 @@ public sealed class MainWindowViewModel : ViewModelBase
{ {
var dev = Device.Create(device.Path); var dev = Device.Create(device.Path);
if(dev.IsRemote) if(dev is Devices.Remote.Device remoteDev)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem, Statistics.AddRemote(remoteDev.RemoteApplication, remoteDev.RemoteVersion,
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture); remoteDev.RemoteOperatingSystem, remoteDev.RemoteOperatingSystemVersion,
remoteDev.RemoteArchitecture);
switch(dev.Type) switch(dev.Type)
{ {

View File

@@ -728,9 +728,10 @@ public sealed class MediaDumpViewModel : ViewModelBase
{ {
_dev = Device.Create(_devicePath); _dev = Device.Create(_devicePath);
if(_dev.IsRemote) if(_dev is Devices.Remote.Device remoteDev)
Statistics.AddRemote(_dev.RemoteApplication, _dev.RemoteVersion, _dev.RemoteOperatingSystem, Statistics.AddRemote(remoteDev.RemoteApplication, remoteDev.RemoteVersion,
_dev.RemoteOperatingSystemVersion, _dev.RemoteArchitecture); remoteDev.RemoteOperatingSystem, remoteDev.RemoteOperatingSystemVersion,
remoteDev.RemoteArchitecture);
if(_dev.Error) if(_dev.Error)
{ {

View File

@@ -330,9 +330,9 @@ public sealed class MediaScanViewModel : ViewModelBase
var dev = Device.Create(_devicePath); var dev = Device.Create(_devicePath);
if(dev.IsRemote) if(dev is Devices.Remote.Device remoteDev)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem, Statistics.AddRemote(remoteDev.RemoteApplication, remoteDev.RemoteVersion, remoteDev.RemoteOperatingSystem,
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture); remoteDev.RemoteOperatingSystemVersion, remoteDev.RemoteArchitecture);
if(dev.Error) if(dev.Error)
{ {

View File

@@ -44,7 +44,6 @@ static partial class MainClass
while(true) while(true)
{ {
AaruConsole.WriteLine("dev.PlatformID = {0}", dev.PlatformId); AaruConsole.WriteLine("dev.PlatformID = {0}", dev.PlatformId);
AaruConsole.WriteLine("dev.FileHandle = {0}", dev.FileHandle);
AaruConsole.WriteLine("dev.Timeout = {0}", dev.Timeout); AaruConsole.WriteLine("dev.Timeout = {0}", dev.Timeout);
AaruConsole.WriteLine("dev.Error = {0}", dev.Error); AaruConsole.WriteLine("dev.Error = {0}", dev.Error);
AaruConsole.WriteLine("dev.LastError = {0}", dev.LastError); AaruConsole.WriteLine("dev.LastError = {0}", dev.LastError);

View File

@@ -130,9 +130,10 @@ sealed class DeviceReportCommand : Command
{ {
dev = Device.Create(devicePath); dev = Device.Create(devicePath);
if(dev.IsRemote) if(dev is Devices.Remote.Device remoteDev)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem, Statistics.AddRemote(remoteDev.RemoteApplication, remoteDev.RemoteVersion,
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture); remoteDev.RemoteOperatingSystem, remoteDev.RemoteOperatingSystemVersion,
remoteDev.RemoteArchitecture);
if(dev.Error) if(dev.Error)
{ {
@@ -150,7 +151,7 @@ sealed class DeviceReportCommand : Command
Statistics.AddDevice(dev); Statistics.AddDevice(dev);
bool isAdmin = dev.IsRemote ? dev.IsRemoteAdmin : DetectOS.IsAdmin; bool isAdmin = dev is Devices.Remote.Device remoteDev2 ? remoteDev2.IsAdmin : DetectOS.IsAdmin;
if(!isAdmin) if(!isAdmin)
{ {

View File

@@ -134,9 +134,10 @@ sealed class DeviceInfoCommand : Command
{ {
dev = Device.Create(devicePath); dev = Device.Create(devicePath);
if(dev.IsRemote) if(dev is Devices.Remote.Device remoteDev)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem, Statistics.AddRemote(remoteDev.RemoteApplication, remoteDev.RemoteVersion,
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture); remoteDev.RemoteOperatingSystem, remoteDev.RemoteOperatingSystemVersion,
remoteDev.RemoteArchitecture);
if(dev.Error) if(dev.Error)
{ {

View File

@@ -546,9 +546,10 @@ sealed class DumpMediaCommand : Command
dev = Device.Create(devicePath); dev = Device.Create(devicePath);
}); });
if(dev.IsRemote) if(dev is Devices.Remote.Device remoteDev)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem, Statistics.AddRemote(remoteDev.RemoteApplication, remoteDev.RemoteVersion,
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture); remoteDev.RemoteOperatingSystem, remoteDev.RemoteOperatingSystemVersion,
remoteDev.RemoteArchitecture);
if(dev.Error) if(dev.Error)
{ {

View File

@@ -139,9 +139,10 @@ sealed class MediaInfoCommand : Command
dev = Device.Create(devicePath); dev = Device.Create(devicePath);
}); });
if(dev.IsRemote) if(dev is Devices.Remote.Device remoteDev)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem, Statistics.AddRemote(remoteDev.RemoteApplication, remoteDev.RemoteVersion,
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture); remoteDev.RemoteOperatingSystem, remoteDev.RemoteOperatingSystemVersion,
remoteDev.RemoteArchitecture);
if(dev.Error) if(dev.Error)
{ {

View File

@@ -140,9 +140,10 @@ sealed class MediaScanCommand : Command
dev = Device.Create(devicePath); dev = Device.Create(devicePath);
}); });
if(dev.IsRemote) if(dev is Devices.Remote.Device remoteDev)
Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem, Statistics.AddRemote(remoteDev.RemoteApplication, remoteDev.RemoteVersion,
dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture); remoteDev.RemoteOperatingSystem, remoteDev.RemoteOperatingSystemVersion,
remoteDev.RemoteArchitecture);
if(dev.Error) if(dev.Error)
{ {