Files
Aaru/Aaru.Devices/Linux/Device.cs

417 lines
15 KiB
C#
Raw Normal View History

2022-03-26 18:31:04 +00:00
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Device.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Linux direct device access.
//
// --[ Description ] ----------------------------------------------------------
//
// Prepares a Linux 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/>.
//
// ----------------------------------------------------------------------------
2022-12-03 16:07:10 +00:00
// Copyright © 2011-2023 Natalia Portillo
2022-03-26 18:31:04 +00:00
// ****************************************************************************/
using System;
using System.Globalization;
using System.IO;
using System.Runtime.Versioning;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interop;
using Aaru.CommonTypes.Structs.Devices.SCSI;
using Aaru.Decoders.SecureDigital;
using Aaru.Helpers;
using Marshal = System.Runtime.InteropServices.Marshal;
2022-03-26 18:31:04 +00:00
using PlatformID = Aaru.CommonTypes.Interop.PlatformID;
using VendorString = Aaru.Decoders.MMC.VendorString;
namespace Aaru.Devices.Linux;
2022-03-26 18:31:04 +00:00
/// <inheritdoc />
[SupportedOSPlatform("linux")]
partial class Device : Devices.Device
2022-03-26 18:31:04 +00:00
{
/// <summary>Gets the file handle representing this device</summary>
/// <value>The file handle</value>
int _fileDescriptor;
2022-03-26 18:31:04 +00:00
Device() {}
internal new static Device Create(string devicePath, out ErrorNumber errno)
2022-03-26 18:31:04 +00:00
{
errno = ErrorNumber.NoError;
2022-03-26 18:31:04 +00:00
var dev = new Device
{
PlatformId = DetectOS.GetRealPlatformID(),
Timeout = 15,
Error = false,
IsRemovable = false,
_fileDescriptor = Extern.open(devicePath, FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew)
2022-03-26 18:31:04 +00:00
};
if(dev._fileDescriptor < 0)
2022-03-26 18:31:04 +00:00
{
dev.LastError = Marshal.GetLastWin32Error();
if(dev.LastError is 13 or 30) // EACCES or EROFS
{
dev._fileDescriptor = Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking);
2022-03-26 18:31:04 +00:00
if(dev._fileDescriptor < 0)
2022-03-26 18:31:04 +00:00
{
dev.Error = true;
dev.LastError = Marshal.GetLastWin32Error();
}
}
else
dev.Error = true;
dev.LastError = Marshal.GetLastWin32Error();
}
if(dev.Error)
{
errno = (ErrorNumber)dev.LastError;
return null;
}
2022-03-26 18:31:04 +00:00
// Seems ioctl(2) does not allow the atomicity needed
if(dev.PlatformId == PlatformID.Linux)
2022-11-13 20:38:15 +00:00
ReadMultipleBlockCannotSetBlockCount = true;
2022-03-26 18:31:04 +00:00
dev.Type = DeviceType.Unknown;
dev.ScsiType = PeripheralDeviceTypes.UnknownDevice;
if(dev.Error)
{
errno = (ErrorNumber)dev.LastError;
return null;
}
2022-03-26 18:31:04 +00:00
string devPath;
2022-03-26 18:31:04 +00:00
if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/st", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/sg", StringComparison.Ordinal))
if(!dev.ScsiInquiry(out byte[] _, out _))
2022-03-26 18:31:04 +00:00
dev.Type = DeviceType.SCSI;
// MultiMediaCard and SecureDigital go here
else if(devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal))
{
devPath = devicePath[5..];
2022-03-26 18:31:04 +00:00
if(File.Exists("/sys/block/" + devPath + "/device/csd"))
{
int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/csd", out dev._cachedCsd);
if(len == 0)
dev._cachedCsd = null;
}
if(File.Exists("/sys/block/" + devPath + "/device/cid"))
{
int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/cid", out dev._cachedCid);
if(len == 0)
dev._cachedCid = null;
}
if(File.Exists("/sys/block/" + devPath + "/device/scr"))
{
int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/scr", out dev._cachedScr);
if(len == 0)
dev._cachedScr = null;
}
if(File.Exists("/sys/block/" + devPath + "/device/ocr"))
{
int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/ocr", out dev._cachedOcr);
if(len == 0)
dev._cachedOcr = null;
}
}
#region SecureDigital / MultiMediaCard
if(dev._cachedCid != null)
{
dev.ScsiType = PeripheralDeviceTypes.DirectAccess;
dev.IsRemovable = false;
if(dev._cachedScr != null)
{
dev.Type = DeviceType.SecureDigital;
CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(dev._cachedCid);
2022-03-26 18:31:04 +00:00
dev.Manufacturer = VendorString.Prettify(decoded.Manufacturer);
dev.Model = decoded.ProductName;
dev.FirmwareRevision =
$"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}";
dev.Serial = $"{decoded.ProductSerialNumber}";
}
else
{
dev.Type = DeviceType.MMC;
Decoders.MMC.CID decoded = Decoders.MMC.Decoders.DecodeCID(dev._cachedCid);
2022-03-26 18:31:04 +00:00
dev.Manufacturer = VendorString.Prettify(decoded.Manufacturer);
dev.Model = decoded.ProductName;
dev.FirmwareRevision =
$"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}";
dev.Serial = $"{decoded.ProductSerialNumber}";
}
return dev;
}
#endregion SecureDigital / MultiMediaCard
#region USB
string resolvedLink;
2022-03-26 18:31:04 +00:00
if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
{
devPath = devicePath[5..];
2022-03-26 18:31:04 +00:00
if(Directory.Exists("/sys/block/" + devPath))
{
resolvedLink = ReadLink("/sys/block/" + devPath);
2022-03-26 18:31:04 +00:00
if(!string.IsNullOrEmpty(resolvedLink))
{
resolvedLink = "/sys" + resolvedLink[2..];
2022-03-26 18:31:04 +00:00
while(resolvedLink?.Contains("usb") == true)
2022-03-26 18:31:04 +00:00
{
resolvedLink = Path.GetDirectoryName(resolvedLink);
if(!File.Exists(resolvedLink + "/descriptors") ||
!File.Exists(resolvedLink + "/idProduct") ||
!File.Exists(resolvedLink + "/idVendor"))
continue;
var usbFs = new FileStream(resolvedLink + "/descriptors", FileMode.Open, FileAccess.Read);
byte[] usbBuf = new byte[65536];
int usbCount = usbFs.EnsureRead(usbBuf, 0, 65536);
2022-03-26 18:31:04 +00:00
dev.UsbDescriptors = new byte[usbCount];
Array.Copy(usbBuf, 0, dev.UsbDescriptors, 0, usbCount);
usbFs.Close();
var usbSr = new StreamReader(resolvedLink + "/idProduct");
string usbTemp = usbSr.ReadToEnd();
ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
out dev._usbProduct);
usbSr.Close();
usbSr = new StreamReader(resolvedLink + "/idVendor");
usbTemp = usbSr.ReadToEnd();
ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
out dev._usbVendor);
usbSr.Close();
if(File.Exists(resolvedLink + "/manufacturer"))
{
usbSr = new StreamReader(resolvedLink + "/manufacturer");
dev.UsbManufacturerString = usbSr.ReadToEnd().Trim();
usbSr.Close();
}
if(File.Exists(resolvedLink + "/product"))
{
usbSr = new StreamReader(resolvedLink + "/product");
dev.UsbProductString = usbSr.ReadToEnd().Trim();
usbSr.Close();
}
if(File.Exists(resolvedLink + "/serial"))
{
usbSr = new StreamReader(resolvedLink + "/serial");
dev.UsbSerialString = usbSr.ReadToEnd().Trim();
usbSr.Close();
}
dev.IsUsb = true;
break;
}
}
}
}
#endregion USB
#region FireWire
if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
2022-03-26 18:31:04 +00:00
{
devPath = devicePath[5..];
if(Directory.Exists("/sys/block/" + devPath))
2022-03-26 18:31:04 +00:00
{
resolvedLink = ReadLink("/sys/block/" + devPath);
resolvedLink = "/sys" + resolvedLink[2..];
2022-03-26 18:31:04 +00:00
if(!string.IsNullOrEmpty(resolvedLink))
while(resolvedLink?.Contains("firewire") == true)
2022-03-26 18:31:04 +00:00
{
resolvedLink = Path.GetDirectoryName(resolvedLink);
2022-03-26 18:31:04 +00:00
if(!File.Exists(resolvedLink + "/model") ||
!File.Exists(resolvedLink + "/vendor") ||
!File.Exists(resolvedLink + "/guid"))
continue;
2022-03-26 18:31:04 +00:00
var fwSr = new StreamReader(resolvedLink + "/model");
string fwTemp = fwSr.ReadToEnd();
2022-03-26 18:31:04 +00:00
uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
out dev._firewireModel);
2022-03-26 18:31:04 +00:00
fwSr.Close();
2022-03-26 18:31:04 +00:00
fwSr = new StreamReader(resolvedLink + "/vendor");
fwTemp = fwSr.ReadToEnd();
2022-03-26 18:31:04 +00:00
uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
out dev._firewireVendor);
2022-03-26 18:31:04 +00:00
fwSr.Close();
2022-03-26 18:31:04 +00:00
fwSr = new StreamReader(resolvedLink + "/guid");
fwTemp = fwSr.ReadToEnd();
2022-03-26 18:31:04 +00:00
ulong.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
out dev._firewireGuid);
2022-03-26 18:31:04 +00:00
fwSr.Close();
2022-03-26 18:31:04 +00:00
if(File.Exists(resolvedLink + "/model_name"))
{
fwSr = new StreamReader(resolvedLink + "/model_name");
dev.FireWireModelName = fwSr.ReadToEnd().Trim();
fwSr.Close();
}
2022-03-26 18:31:04 +00:00
if(File.Exists(resolvedLink + "/vendor_name"))
{
fwSr = new StreamReader(resolvedLink + "/vendor_name");
dev.FireWireVendorName = fwSr.ReadToEnd().Trim();
fwSr.Close();
}
2022-03-26 18:31:04 +00:00
dev.IsFireWire = true;
2022-03-26 18:31:04 +00:00
break;
2022-03-26 18:31:04 +00:00
}
}
}
#endregion FireWire
#region PCMCIA
if(!devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) &&
!devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) &&
!devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
return dev;
2022-03-26 18:31:04 +00:00
devPath = devicePath[5..];
2022-03-26 18:31:04 +00:00
if(!Directory.Exists("/sys/block/" + devPath))
return dev;
2022-03-26 18:31:04 +00:00
resolvedLink = ReadLink("/sys/block/" + devPath);
resolvedLink = "/sys" + resolvedLink[2..];
2022-03-26 18:31:04 +00:00
if(string.IsNullOrEmpty(resolvedLink))
return dev;
2022-03-26 18:31:04 +00:00
while(resolvedLink.Contains("/sys/devices"))
{
resolvedLink = Path.GetDirectoryName(resolvedLink);
2022-03-26 18:31:04 +00:00
if(!Directory.Exists(resolvedLink + "/pcmcia_socket"))
continue;
2022-03-26 18:31:04 +00:00
string[] subdirs = Directory.GetDirectories(resolvedLink + "/pcmcia_socket", "pcmcia_socket*",
SearchOption.TopDirectoryOnly);
2022-03-26 18:31:04 +00:00
if(subdirs.Length <= 0)
continue;
2022-03-26 18:31:04 +00:00
string possibleDir = Path.Combine(resolvedLink, "pcmcia_socket", subdirs[0]);
2022-03-26 18:31:04 +00:00
if(!File.Exists(possibleDir + "/card_type") ||
!File.Exists(possibleDir + "/cis"))
continue;
2022-03-26 18:31:04 +00:00
var cisFs = new FileStream(possibleDir + "/cis", FileMode.Open, FileAccess.Read);
byte[] cisBuf = new byte[65536];
int cisCount = cisFs.EnsureRead(cisBuf, 0, 65536);
dev.Cis = new byte[cisCount];
Array.Copy(cisBuf, 0, dev.Cis, 0, cisCount);
cisFs.Close();
dev.IsPcmcia = true;
break;
2022-03-26 18:31:04 +00:00
}
#endregion PCMCIA
return dev;
}
static int ConvertFromFileHexAscii(string file, out byte[] outBuf)
{
var sr = new StreamReader(file);
string ins = sr.ReadToEnd().Trim();
int count = Helpers.Marshal.ConvertFromHexAscii(ins, out outBuf);
sr.Close();
return count;
}
/// <inheritdoc />
public override void Close()
{
if(_fileDescriptor == 0)
2022-03-26 18:31:04 +00:00
return;
Extern.close(_fileDescriptor);
2022-03-26 18:31:04 +00:00
_fileDescriptor = 0;
2022-03-26 18:31:04 +00:00
}
}