mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
260 lines
9.0 KiB
C#
260 lines
9.0 KiB
C#
// /***************************************************************************
|
|
// Aaru Data Preservation Suite
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Filename : Constructor.cs
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
//
|
|
// Component : Direct device access.
|
|
//
|
|
// --[ Description ] ----------------------------------------------------------
|
|
//
|
|
// Prepares a device for direct access.
|
|
//
|
|
// --[ 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-2025 Natalia Portillo
|
|
// ****************************************************************************/
|
|
|
|
using System;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using Aaru.CommonTypes.Enums;
|
|
using Aaru.CommonTypes.Structs.Devices.ATA;
|
|
using Aaru.CommonTypes.Structs.Devices.SCSI;
|
|
using Aaru.Decoders.SCSI;
|
|
using Aaru.Decoders.SCSI.MMC;
|
|
using Aaru.Helpers;
|
|
using Sentry;
|
|
using Inquiry = Aaru.CommonTypes.Structs.Devices.SCSI.Inquiry;
|
|
|
|
namespace Aaru.Devices;
|
|
|
|
/// <summary>Implements a device or media containing drive</summary>
|
|
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
|
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
|
[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
|
|
public partial class Device : IDisposable
|
|
{
|
|
// Pointer to send CDB to device
|
|
protected unsafe void* CdbPtr;
|
|
protected unsafe void* SensePtr;
|
|
|
|
protected Device()
|
|
{
|
|
unsafe
|
|
{
|
|
CdbPtr = NativeMemory.AlignedAlloc(16, 64);
|
|
SensePtr = NativeMemory.AlignedAlloc(64, 64);
|
|
NativeMemory.Clear(CdbPtr, 16);
|
|
NativeMemory.Clear(SensePtr, 64);
|
|
}
|
|
}
|
|
|
|
// Span to make pointer usable as data. Fixed size CDB is maximum 16 bytes. Variable size CDB is another problem.
|
|
public unsafe Span<byte> CdbBuffer => new(CdbPtr, 16);
|
|
public unsafe Span<byte> SenseBuffer => new(SensePtr, 64);
|
|
|
|
#region IDisposable Members
|
|
|
|
/// <inheritdoc />
|
|
public unsafe void Dispose()
|
|
{
|
|
void* p = CdbPtr;
|
|
CdbPtr = null;
|
|
if(p != null) NativeMemory.AlignedFree(p);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>Opens the device for sending direct commands</summary>
|
|
/// <param name="devicePath">Device path</param>
|
|
/// <param name="errno">Sets the error if a device cannot be opened</param>
|
|
/// <returns>Device</returns>
|
|
public static Device Create(string devicePath, out ErrorNumber errno)
|
|
{
|
|
Device dev = null;
|
|
Uri aaruUri;
|
|
errno = ErrorNumber.NoError;
|
|
|
|
try
|
|
{
|
|
aaruUri = new Uri(devicePath);
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
SentrySdk.CaptureException(ex);
|
|
|
|
aaruUri = null;
|
|
}
|
|
|
|
if(aaruUri?.Scheme is "dic" or "aaru")
|
|
dev = Remote.Device.Create(aaruUri, out errno);
|
|
else if(OperatingSystem.IsLinux())
|
|
dev = Linux.Device.Create(devicePath, out errno);
|
|
else if(OperatingSystem.IsWindows())
|
|
dev = Windows.Device.Create(devicePath, out errno);
|
|
else
|
|
errno = ErrorNumber.NotSupported;
|
|
|
|
if(dev is null) return null;
|
|
|
|
if(dev.Type is DeviceType.SCSI or DeviceType.ATAPI)
|
|
{
|
|
dev.ScsiInquiry(out byte[] inqBuf, out _);
|
|
|
|
Inquiry? inquiry = Inquiry.Decode(inqBuf);
|
|
|
|
dev.Type = DeviceType.SCSI;
|
|
bool serialSense = dev.ScsiInquiry(out inqBuf, out _, 0x80);
|
|
|
|
if(!serialSense) dev.Serial = EVPD.DecodePage80(inqBuf);
|
|
|
|
if(inquiry.HasValue)
|
|
{
|
|
string tmp = StringHandlers.CToString(inquiry.Value.ProductRevisionLevel);
|
|
|
|
if(tmp != null) dev.FirmwareRevision = tmp.Trim();
|
|
|
|
tmp = StringHandlers.CToString(inquiry.Value.ProductIdentification);
|
|
|
|
if(tmp != null) dev.Model = tmp.Trim();
|
|
|
|
tmp = StringHandlers.CToString(inquiry.Value.VendorIdentification);
|
|
|
|
if(tmp != null) dev.Manufacturer = tmp.Trim();
|
|
|
|
dev.IsRemovable = inquiry.Value.RMB;
|
|
|
|
dev.ScsiType = (PeripheralDeviceTypes)inquiry.Value.PeripheralDeviceType;
|
|
}
|
|
|
|
bool atapiSense = dev.AtapiIdentify(out byte[] ataBuf, out _);
|
|
|
|
if(!atapiSense)
|
|
{
|
|
dev.Type = DeviceType.ATAPI;
|
|
Identify.IdentifyDevice? ataId = Identify.Decode(ataBuf);
|
|
|
|
if(ataId.HasValue) dev.Serial = ataId.Value.SerialNumber;
|
|
}
|
|
|
|
dev.LastError = 0;
|
|
dev.Error = false;
|
|
}
|
|
|
|
if(dev.Type != DeviceType.SCSI && dev.Type != DeviceType.ATAPI && !(dev.IsUsb || dev.IsFireWire) ||
|
|
dev.Manufacturer == "ATA")
|
|
{
|
|
bool ataSense = dev.AtaIdentify(out byte[] ataBuf, out _);
|
|
|
|
if(!ataSense)
|
|
{
|
|
dev.Type = DeviceType.ATA;
|
|
Identify.IdentifyDevice? ataid = Identify.Decode(ataBuf);
|
|
|
|
if(ataid.HasValue)
|
|
{
|
|
string[] separated = ataid.Value.Model.Split(' ');
|
|
|
|
if(separated.Length == 1)
|
|
dev.Model = separated[0];
|
|
else
|
|
{
|
|
dev.Manufacturer = separated[0];
|
|
dev.Model = separated[^1];
|
|
}
|
|
|
|
dev.FirmwareRevision = ataid.Value.FirmwareRevision;
|
|
dev.Serial = ataid.Value.SerialNumber;
|
|
|
|
dev.ScsiType = PeripheralDeviceTypes.DirectAccess;
|
|
|
|
if((ushort)ataid.Value.GeneralConfiguration != 0x848A)
|
|
{
|
|
dev.IsRemovable |=
|
|
(ataid.Value.GeneralConfiguration & Identify.GeneralConfigurationBit.Removable) ==
|
|
Identify.GeneralConfigurationBit.Removable;
|
|
}
|
|
else
|
|
dev.IsCompactFlash = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(dev.Type == DeviceType.Unknown)
|
|
{
|
|
dev.Manufacturer = null;
|
|
dev.Model = null;
|
|
dev.FirmwareRevision = null;
|
|
dev.Serial = null;
|
|
}
|
|
|
|
if(dev.IsUsb)
|
|
{
|
|
if(string.IsNullOrEmpty(dev.Manufacturer)) dev.Manufacturer = dev.UsbManufacturerString;
|
|
|
|
if(string.IsNullOrEmpty(dev.Model)) dev.Model = dev.UsbProductString;
|
|
|
|
if(string.IsNullOrEmpty(dev.Serial))
|
|
dev.Serial = dev.UsbSerialString;
|
|
else
|
|
foreach(char c in dev.Serial.Where(c => !char.IsControl(c)))
|
|
dev.Serial = $"{dev.Serial}{c:X2}";
|
|
}
|
|
|
|
if(dev.IsFireWire)
|
|
{
|
|
if(string.IsNullOrEmpty(dev.Manufacturer)) dev.Manufacturer = dev.FireWireVendorName;
|
|
|
|
if(string.IsNullOrEmpty(dev.Model)) dev.Model = dev.FireWireModelName;
|
|
|
|
if(string.IsNullOrEmpty(dev.Serial))
|
|
dev.Serial = $"{dev.FirewireGuid:X16}";
|
|
else
|
|
foreach(char c in dev.Serial.Where(c => !char.IsControl(c)))
|
|
dev.Serial = $"{dev.Serial}{c:X2}";
|
|
}
|
|
|
|
// Some optical drives are not getting the correct serial, and IDENTIFY PACKET DEVICE is blocked without
|
|
// administrator privileges
|
|
if(dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice) return dev;
|
|
|
|
bool featureSense = dev.GetConfiguration(out byte[] featureBuffer,
|
|
out _,
|
|
0x0108,
|
|
MmcGetConfigurationRt.Single,
|
|
dev.Timeout,
|
|
out _);
|
|
|
|
if(featureSense) return dev;
|
|
|
|
Features.SeparatedFeatures features = Features.Separate(featureBuffer);
|
|
|
|
if(features.Descriptors?.Length != 1 || features.Descriptors[0].Code != 0x0108) return dev;
|
|
|
|
Feature_0108? serialFeature = Features.Decode_0108(features.Descriptors[0].Data);
|
|
|
|
if(serialFeature is null) return dev;
|
|
|
|
dev.Serial = serialFeature.Value.Serial;
|
|
|
|
return dev;
|
|
}
|
|
} |