Ejemplo n.º 1
0
        /// <summary>
        ///     Returns true if the specified handle is controlled by a SFFDISK (aka SDHCI) driver
        /// </summary>
        /// <param name="fd">Device handle</param>
        /// <returns><c>true</c> if SDHCI, false otherwise</returns>
        internal static bool IsSdhci(SafeFileHandle fd)
        {
            SffdiskQueryDeviceProtocolData queryData1 = new SffdiskQueryDeviceProtocolData();

            queryData1.size = (ushort)Marshal.SizeOf(queryData1);
            Extern.DeviceIoControl(fd, WindowsIoctl.IoctlSffdiskQueryDeviceProtocol, IntPtr.Zero, 0, ref queryData1,
                                   queryData1.size, out _, IntPtr.Zero);
            return(queryData1.protocolGuid.Equals(Consts.GuidSffProtocolSd));
        }
Ejemplo n.º 2
0
        /// <summary>
        ///     Gets the device number for a specified handle
        /// </summary>
        /// <param name="deviceHandle">Device handle</param>
        /// <returns>Device number</returns>
        static uint GetDeviceNumber(SafeFileHandle deviceHandle)
        {
            StorageDeviceNumber sdn = new StorageDeviceNumber {
                deviceNumber = -1
            };
            uint k = 0;

            if (!Extern.DeviceIoControlGetDeviceNumber(deviceHandle, WindowsIoctl.IoctlStorageGetDeviceNumber,
                                                       IntPtr.Zero, 0, ref sdn, (uint)Marshal.SizeOf(sdn), ref k,
                                                       IntPtr.Zero))
            {
                return(uint.MaxValue);
            }

            return((uint)sdn.deviceNumber);
        }
Ejemplo n.º 3
0
        /// <summary>
        ///     Opens the device for sending direct commands
        /// </summary>
        /// <param name="devicePath">Device path</param>
        public Device(string devicePath)
        {
            PlatformId  = DetectOS.GetRealPlatformID();
            Timeout     = 15;
            Error       = false;
            IsRemovable = false;

            switch (PlatformId)
            {
            case PlatformID.Win32NT:
            {
                FileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite,
                                               FileShare.Read | FileShare.Write, IntPtr.Zero,
                                               FileMode.OpenExisting,
                                               FileAttributes.Normal, IntPtr.Zero);

                if (((SafeFileHandle)FileHandle).IsInvalid)
                {
                    Error     = true;
                    LastError = Marshal.GetLastWin32Error();
                }

                break;
            }

            case PlatformID.Linux:
            {
                FileHandle =
                    Linux.Extern.open(devicePath,
                                      FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew);

                if ((int)FileHandle < 0)
                {
                    LastError = Marshal.GetLastWin32Error();

                    if (LastError == 13 || LastError == 30)    // EACCES or EROFS
                    {
                        FileHandle = Linux.Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking);
                        if ((int)FileHandle < 0)
                        {
                            Error     = true;
                            LastError = Marshal.GetLastWin32Error();
                        }
                    }
                    else
                    {
                        Error = true;
                    }

                    LastError = Marshal.GetLastWin32Error();
                }

                break;
            }

            case PlatformID.FreeBSD:
            {
                FileHandle = FreeBSD.Extern.cam_open_device(devicePath, FreeBSD.FileFlags.ReadWrite);

                if (((IntPtr)FileHandle).ToInt64() == 0)
                {
                    Error     = true;
                    LastError = Marshal.GetLastWin32Error();
                }

                CamDevice camDevice = (CamDevice)Marshal.PtrToStructure((IntPtr)FileHandle, typeof(CamDevice));

                if (StringHandlers.CToString(camDevice.SimName) == "ata")
                {
                    throw new
                          InvalidOperationException("Parallel ATA devices are not supported on FreeBSD due to upstream bug #224250.");
                }

                break;
            }

            default: throw new InvalidOperationException($"Platform {PlatformId} not yet supported.");
            }

            if (Error)
            {
                throw new SystemException($"Error {LastError} opening device.");
            }

            Type     = DeviceType.Unknown;
            ScsiType = PeripheralDeviceTypes.UnknownDevice;

            byte[] ataBuf;
            byte[] inqBuf = null;

            if (Error)
            {
                throw new SystemException($"Error {LastError} trying device.");
            }

            bool scsiSense = true;

            // Windows is answering SCSI INQUIRY for all device types so it needs to be detected first
            switch (PlatformId)
            {
            case PlatformID.Win32NT:
                StoragePropertyQuery query = new StoragePropertyQuery();
                query.PropertyId           = StoragePropertyId.Device;
                query.QueryType            = StorageQueryType.Standard;
                query.AdditionalParameters = new byte[1];

                IntPtr descriptorPtr = Marshal.AllocHGlobal(1000);
                byte[] descriptorB   = new byte[1000];

                uint returned = 0;
                int  error    = 0;

                bool hasError = !Extern.DeviceIoControlStorageQuery((SafeFileHandle)FileHandle,
                                                                    WindowsIoctl.IoctlStorageQueryProperty,
                                                                    ref query, (uint)Marshal.SizeOf(query),
                                                                    descriptorPtr, 1000, ref returned, IntPtr.Zero);

                if (hasError)
                {
                    error = Marshal.GetLastWin32Error();
                }

                Marshal.Copy(descriptorPtr, descriptorB, 0, 1000);

                if (!hasError && error == 0)
                {
                    StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor
                    {
                        Version               = BitConverter.ToUInt32(descriptorB, 0),
                        Size                  = BitConverter.ToUInt32(descriptorB, 4),
                        DeviceType            = descriptorB[8],
                        DeviceTypeModifier    = descriptorB[9],
                        RemovableMedia        = descriptorB[10] > 0,
                        CommandQueueing       = descriptorB[11] > 0,
                        VendorIdOffset        = BitConverter.ToInt32(descriptorB, 12),
                        ProductIdOffset       = BitConverter.ToInt32(descriptorB, 16),
                        ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20),
                        SerialNumberOffset    = BitConverter.ToInt32(descriptorB, 24),
                        BusType               = (StorageBusType)BitConverter.ToUInt32(descriptorB, 28),
                        RawPropertiesLength   = BitConverter.ToUInt32(descriptorB, 32)
                    };
                    descriptor.RawDeviceProperties = new byte[descriptor.RawPropertiesLength];
                    Array.Copy(descriptorB, 36, descriptor.RawDeviceProperties, 0, descriptor.RawPropertiesLength);

                    switch (descriptor.BusType)
                    {
                    case StorageBusType.SCSI:
                    case StorageBusType.SSA:
                    case StorageBusType.Fibre:
                    case StorageBusType.iSCSI:
                    case StorageBusType.SAS:
                        Type = DeviceType.SCSI;
                        break;

                    case StorageBusType.FireWire:
                        IsFireWire = true;
                        Type       = DeviceType.SCSI;
                        break;

                    case StorageBusType.USB:
                        IsUsb = true;
                        Type  = DeviceType.SCSI;
                        break;

                    case StorageBusType.ATAPI:
                        Type = DeviceType.ATAPI;
                        break;

                    case StorageBusType.ATA:
                    case StorageBusType.SATA:
                        Type = DeviceType.ATA;
                        break;

                    case StorageBusType.MultiMediaCard:
                        Type = DeviceType.MMC;
                        break;

                    case StorageBusType.SecureDigital:
                        Type = DeviceType.SecureDigital;
                        break;

                    case StorageBusType.NVMe:
                        Type = DeviceType.NVMe;
                        break;
                    }

                    switch (Type)
                    {
                    case DeviceType.SCSI:
                    case DeviceType.ATAPI:
                        scsiSense = ScsiInquiry(out inqBuf, out _);
                        break;

                    case DeviceType.ATA:
                        bool atapiSense = AtapiIdentify(out ataBuf, out _);

                        if (!atapiSense)
                        {
                            Type = DeviceType.ATAPI;
                            Identify.IdentifyDevice?ataid = Identify.Decode(ataBuf);

                            if (ataid.HasValue)
                            {
                                scsiSense = ScsiInquiry(out inqBuf, out _);
                            }
                        }
                        else
                        {
                            Manufacturer = "ATA";
                        }

                        break;
                    }
                }

                Marshal.FreeHGlobal(descriptorPtr);

                if (Windows.Command.IsSdhci((SafeFileHandle)FileHandle))
                {
                    byte[] sdBuffer = new byte[16];

                    LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCsd,
                                                               false, false,
                                                               MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 |
                                                               MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _,
                                                               out _, out bool sense);

                    if (!sense)
                    {
                        cachedCsd = new byte[16];
                        Array.Copy(sdBuffer, 0, cachedCsd, 0, 16);
                    }

                    sdBuffer = new byte[16];

                    LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCid,
                                                               false, false,
                                                               MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 |
                                                               MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _,
                                                               out _, out sense);

                    if (!sense)
                    {
                        cachedCid = new byte[16];
                        Array.Copy(sdBuffer, 0, cachedCid, 0, 16);
                    }

                    sdBuffer = new byte[8];

                    LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                               (MmcCommands)SecureDigitalCommands.SendScr, false,
                                                               true,
                                                               MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 |
                                                               MmcFlags.CommandAdtc, 0, 8, 1, ref sdBuffer, out _,
                                                               out _, out sense);

                    if (!sense)
                    {
                        cachedScr = new byte[8];
                        Array.Copy(sdBuffer, 0, cachedScr, 0, 8);
                    }

                    if (cachedScr != null)
                    {
                        sdBuffer = new byte[4];

                        LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                                   (MmcCommands)SecureDigitalCommands
                                                                   .SendOperatingCondition, false, true,
                                                                   MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 |
                                                                   MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer,
                                                                   out _, out _, out sense);

                        if (!sense)
                        {
                            cachedScr = new byte[4];
                            Array.Copy(sdBuffer, 0, cachedScr, 0, 4);
                        }
                    }
                    else
                    {
                        sdBuffer = new byte[4];

                        LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                                   MmcCommands.SendOpCond, false, true,
                                                                   MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 |
                                                                   MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer,
                                                                   out _, out _, out sense);

                        if (!sense)
                        {
                            cachedScr = new byte[4];
                            Array.Copy(sdBuffer, 0, cachedScr, 0, 4);
                        }
                    }
                }

                break;

            case PlatformID.Linux:
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    scsiSense = ScsiInquiry(out inqBuf, out _);
                }
                // MultiMediaCard and SecureDigital go here
                else if (devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (File.Exists("/sys/block/" + devPath + "/device/csd"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/csd", out cachedCsd);
                        if (len == 0)
                        {
                            cachedCsd = null;
                        }
                    }

                    if (File.Exists("/sys/block/" + devPath + "/device/cid"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/cid", out cachedCid);
                        if (len == 0)
                        {
                            cachedCid = null;
                        }
                    }

                    if (File.Exists("/sys/block/" + devPath + "/device/scr"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/scr", out cachedScr);
                        if (len == 0)
                        {
                            cachedScr = null;
                        }
                    }

                    if (File.Exists("/sys/block/" + devPath + "/device/ocr"))
                    {
                        int len =
                            ConvertFromHexAscii("/sys/block/" + devPath + "/device/ocr", out cachedOcr);
                        if (len == 0)
                        {
                            cachedOcr = null;
                        }
                    }
                }

                break;

            default:
                scsiSense = ScsiInquiry(out inqBuf, out _);
                break;
            }

            #region SecureDigital / MultiMediaCard
            if (cachedCid != null)
            {
                ScsiType    = PeripheralDeviceTypes.DirectAccess;
                IsRemovable = false;

                if (cachedScr != null)
                {
                    Type = DeviceType.SecureDigital;
                    CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(cachedCid);
                    Manufacturer = VendorString.Prettify(decoded.Manufacturer);
                    Model        = decoded.ProductName;
                    Revision     = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}";
                    Serial       = $"{decoded.ProductSerialNumber}";
                }
                else
                {
                    Type = DeviceType.MMC;
                    Decoders.MMC.CID decoded = Decoders.MMC.Decoders.DecodeCID(cachedCid);
                    Manufacturer = Decoders.MMC.VendorString.Prettify(decoded.Manufacturer);
                    Model        = decoded.ProductName;
                    Revision     = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}";
                    Serial       = $"{decoded.ProductSerialNumber}";
                }
            }
            #endregion SecureDigital / MultiMediaCard

            #region USB
            switch (PlatformId)
            {
            case PlatformID.Linux:
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (Directory.Exists("/sys/block/" + devPath))
                    {
                        string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath);
                        resolvedLink = "/sys" + resolvedLink.Substring(2);
                        if (!string.IsNullOrEmpty(resolvedLink))
                        {
                            while (resolvedLink.Contains("usb"))
                            {
                                resolvedLink = Path.GetDirectoryName(resolvedLink);
                                if (!File.Exists(resolvedLink + "/descriptors") ||
                                    !File.Exists(resolvedLink + "/idProduct") ||
                                    !File.Exists(resolvedLink + "/idVendor"))
                                {
                                    continue;
                                }

                                FileStream usbFs = new FileStream(resolvedLink + "/descriptors",
                                                                  System.IO.FileMode.Open,
                                                                  System.IO.FileAccess.Read);
                                byte[] usbBuf   = new byte[65536];
                                int    usbCount = usbFs.Read(usbBuf, 0, 65536);
                                UsbDescriptors = new byte[usbCount];
                                Array.Copy(usbBuf, 0, UsbDescriptors, 0, usbCount);
                                usbFs.Close();

                                StreamReader usbSr   = new StreamReader(resolvedLink + "/idProduct");
                                string       usbTemp = usbSr.ReadToEnd();
                                ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                                out usbProduct);
                                usbSr.Close();

                                usbSr   = new StreamReader(resolvedLink + "/idVendor");
                                usbTemp = usbSr.ReadToEnd();
                                ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                                out usbVendor);
                                usbSr.Close();

                                if (File.Exists(resolvedLink + "/manufacturer"))
                                {
                                    usbSr = new StreamReader(resolvedLink + "/manufacturer");
                                    UsbManufacturerString = usbSr.ReadToEnd().Trim();
                                    usbSr.Close();
                                }

                                if (File.Exists(resolvedLink + "/product"))
                                {
                                    usbSr            = new StreamReader(resolvedLink + "/product");
                                    UsbProductString = usbSr.ReadToEnd().Trim();
                                    usbSr.Close();
                                }

                                if (File.Exists(resolvedLink + "/serial"))
                                {
                                    usbSr           = new StreamReader(resolvedLink + "/serial");
                                    UsbSerialString = usbSr.ReadToEnd().Trim();
                                    usbSr.Close();
                                }

                                IsUsb = true;
                                break;
                            }
                        }
                    }
                }

                break;

            case PlatformID.Win32NT:
                Usb.UsbDevice usbDevice = null;

                // I have to search for USB disks, floppies and CD-ROMs as separate device types
                foreach (string devGuid in new[]
                {
                    Usb.GuidDevinterfaceFloppy, Usb.GuidDevinterfaceCdrom, Usb.GuidDevinterfaceDisk
                })
                {
                    usbDevice = Usb.FindDrivePath(devicePath, devGuid);
                    if (usbDevice != null)
                    {
                        break;
                    }
                }

                if (usbDevice != null)
                {
                    UsbDescriptors        = usbDevice.BinaryDescriptors;
                    usbVendor             = (ushort)usbDevice.DeviceDescriptor.idVendor;
                    usbProduct            = (ushort)usbDevice.DeviceDescriptor.idProduct;
                    UsbManufacturerString = usbDevice.Manufacturer;
                    UsbProductString      = usbDevice.Product;
                    UsbSerialString       =
                        usbDevice.SerialNumber;     // This is incorrect filled by Windows with SCSI/ATA serial number
                }

                break;

            default:
                IsUsb = false;
                break;
            }
            #endregion USB

            #region FireWire
            if (PlatformId == PlatformID.Linux)
            {
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (Directory.Exists("/sys/block/" + devPath))
                    {
                        string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath);
                        resolvedLink = "/sys" + resolvedLink.Substring(2);
                        if (!string.IsNullOrEmpty(resolvedLink))
                        {
                            while (resolvedLink.Contains("firewire"))
                            {
                                resolvedLink = Path.GetDirectoryName(resolvedLink);
                                if (!File.Exists(resolvedLink + "/model") || !File.Exists(resolvedLink + "/vendor") ||
                                    !File.Exists(resolvedLink + "/guid"))
                                {
                                    continue;
                                }

                                StreamReader fwSr   = new StreamReader(resolvedLink + "/model");
                                string       fwTemp = fwSr.ReadToEnd();
                                uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                              out firewireModel);
                                fwSr.Close();

                                fwSr   = new StreamReader(resolvedLink + "/vendor");
                                fwTemp = fwSr.ReadToEnd();
                                uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                              out firewireVendor);
                                fwSr.Close();

                                fwSr   = new StreamReader(resolvedLink + "/guid");
                                fwTemp = fwSr.ReadToEnd();
                                ulong.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                               out firewireGuid);
                                fwSr.Close();

                                if (File.Exists(resolvedLink + "/model_name"))
                                {
                                    fwSr = new StreamReader(resolvedLink + "/model_name");
                                    FireWireModelName = fwSr.ReadToEnd().Trim();
                                    fwSr.Close();
                                }

                                if (File.Exists(resolvedLink + "/vendor_name"))
                                {
                                    fwSr = new StreamReader(resolvedLink + "/vendor_name");
                                    FireWireVendorName = fwSr.ReadToEnd().Trim();
                                    fwSr.Close();
                                }

                                IsFireWire = true;
                                break;
                            }
                        }
                    }
                }
            }
            // TODO: Implement for other operating systems
            else
            {
                IsFireWire = false;
            }
            #endregion FireWire

            #region PCMCIA
            if (PlatformId == PlatformID.Linux)
            {
                if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                    devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                {
                    string devPath = devicePath.Substring(5);
                    if (Directory.Exists("/sys/block/" + devPath))
                    {
                        string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath);
                        resolvedLink = "/sys" + resolvedLink.Substring(2);
                        if (!string.IsNullOrEmpty(resolvedLink))
                        {
                            while (resolvedLink.Contains("/sys/devices"))
                            {
                                resolvedLink = Path.GetDirectoryName(resolvedLink);
                                if (!Directory.Exists(resolvedLink + "/pcmcia_socket"))
                                {
                                    continue;
                                }

                                string[] subdirs = Directory.GetDirectories(resolvedLink + "/pcmcia_socket",
                                                                            "pcmcia_socket*",
                                                                            SearchOption.TopDirectoryOnly);

                                if (subdirs.Length <= 0)
                                {
                                    continue;
                                }

                                string possibleDir = Path.Combine(resolvedLink, "pcmcia_socket", subdirs[0]);
                                if (!File.Exists(possibleDir + "/card_type") || !File.Exists(possibleDir + "/cis"))
                                {
                                    continue;
                                }

                                FileStream cisFs = new FileStream(possibleDir + "/cis", System.IO.FileMode.Open,
                                                                  System.IO.FileAccess.Read);
                                byte[] cisBuf   = new byte[65536];
                                int    cisCount = cisFs.Read(cisBuf, 0, 65536);
                                Cis = new byte[cisCount];
                                Array.Copy(cisBuf, 0, Cis, 0, cisCount);
                                cisFs.Close();

                                IsPcmcia = true;
                                break;
                            }
                        }
                    }
                }
            }
            // TODO: Implement for other operating systems
            else
            {
                IsPcmcia = false;
            }
            #endregion PCMCIA

            if (!scsiSense)
            {
                Inquiry.SCSIInquiry?inquiry = Inquiry.Decode(inqBuf);

                Type = DeviceType.SCSI;
                bool serialSense = ScsiInquiry(out inqBuf, out _, 0x80);
                if (!serialSense)
                {
                    Serial = EVPD.DecodePage80(inqBuf);
                }

                if (inquiry.HasValue)
                {
                    string tmp = StringHandlers.CToString(inquiry.Value.ProductRevisionLevel);
                    if (tmp != null)
                    {
                        Revision = tmp.Trim();
                    }
                    tmp = StringHandlers.CToString(inquiry.Value.ProductIdentification);
                    if (tmp != null)
                    {
                        Model = tmp.Trim();
                    }
                    tmp = StringHandlers.CToString(inquiry.Value.VendorIdentification);
                    if (tmp != null)
                    {
                        Manufacturer = tmp.Trim();
                    }
                    IsRemovable = inquiry.Value.RMB;

                    ScsiType = (PeripheralDeviceTypes)inquiry.Value.PeripheralDeviceType;
                }

                bool atapiSense = AtapiIdentify(out ataBuf, out _);

                if (!atapiSense)
                {
                    Type = DeviceType.ATAPI;
                    Identify.IdentifyDevice?ataId = Identify.Decode(ataBuf);

                    if (ataId.HasValue)
                    {
                        Serial = ataId.Value.SerialNumber;
                    }
                }

                LastError = 0;
                Error     = false;
            }

            if (scsiSense && (IsUsb || IsFireWire) || Manufacturer == "ATA")
            {
                bool ataSense = AtaIdentify(out ataBuf, out _);
                if (!ataSense)
                {
                    Type = DeviceType.ATA;
                    Identify.IdentifyDevice?ataid = Identify.Decode(ataBuf);

                    if (ataid.HasValue)
                    {
                        string[] separated = ataid.Value.Model.Split(' ');

                        if (separated.Length == 1)
                        {
                            Model = separated[0];
                        }
                        else
                        {
                            Manufacturer = separated[0];
                            Model        = separated[separated.Length - 1];
                        }

                        Revision = ataid.Value.FirmwareRevision;
                        Serial   = ataid.Value.SerialNumber;

                        ScsiType = PeripheralDeviceTypes.DirectAccess;

                        if ((ushort)ataid.Value.GeneralConfiguration != 0x848A)
                        {
                            IsRemovable |=
                                (ataid.Value.GeneralConfiguration & Identify.GeneralConfigurationBit.Removable) ==
                                Identify.GeneralConfigurationBit.Removable;
                        }
                        else
                        {
                            IsCompactFlash = true;
                        }
                    }
                }
            }

            if (Type == DeviceType.Unknown)
            {
                Manufacturer = null;
                Model        = null;
                Revision     = null;
                Serial       = null;
            }

            if (IsUsb)
            {
                if (string.IsNullOrEmpty(Manufacturer))
                {
                    Manufacturer = UsbManufacturerString;
                }
                if (string.IsNullOrEmpty(Model))
                {
                    Model = UsbProductString;
                }
                if (string.IsNullOrEmpty(Serial))
                {
                    Serial = UsbSerialString;
                }
                else
                {
                    foreach (char c in Serial.Where(char.IsControl))
                    {
                        Serial = UsbSerialString;
                    }
                }
            }

            if (IsFireWire)
            {
                if (string.IsNullOrEmpty(Manufacturer))
                {
                    Manufacturer = FireWireVendorName;
                }
                if (string.IsNullOrEmpty(Model))
                {
                    Model = FireWireModelName;
                }
                if (string.IsNullOrEmpty(Serial))
                {
                    Serial = $"{firewireGuid:X16}";
                }
                else
                {
                    foreach (char c in Serial.Where(char.IsControl))
                    {
                        Serial = $"{firewireGuid:X16}";
                    }
                }
            }

            // Some optical drives are not getting the correct serial, and IDENTIFY PACKET DEVICE is blocked without
            // administrator privileges
            if (ScsiType != PeripheralDeviceTypes.MultiMediaDevice)
            {
                return;
            }

            bool featureSense = GetConfiguration(out byte[] featureBuffer, out _, 0x0108, MmcGetConfigurationRt.Single,
                                                 Timeout, out _);

            if (featureSense)
            {
                return;
            }

            Features.SeparatedFeatures features = Features.Separate(featureBuffer);
            if (features.Descriptors?.Length != 1 || features.Descriptors[0].Code != 0x0108)
            {
                return;
            }

            Feature_0108?serialFeature = Features.Decode_0108(features.Descriptors[0].Data);

            if (serialFeature is null)
            {
                return;
            }

            Serial = serialFeature.Value.Serial;
        }
Ejemplo n.º 4
0
        /// <summary>
        ///     Sends a SCSI command
        /// </summary>
        /// <returns>0 if no error occurred, otherwise, errno</returns>
        /// <param name="fd">File handle</param>
        /// <param name="cdb">SCSI CDB</param>
        /// <param name="buffer">Buffer for SCSI command response</param>
        /// <param name="senseBuffer">Buffer with the SCSI sense</param>
        /// <param name="timeout">Timeout in seconds</param>
        /// <param name="direction">SCSI command transfer direction</param>
        /// <param name="duration">Time it took to execute the command in milliseconds</param>
        /// <param name="sense">
        ///     <c>True</c> if SCSI error returned non-OK status and <paramref name="senseBuffer" /> contains SCSI
        ///     sense
        /// </param>
        internal static int SendScsiCommand(SafeFileHandle fd, byte[] cdb, ref byte[] buffer,
                                            out byte[]     senseBuffer,
                                            uint timeout, ScsiIoctlDirection direction, out double duration,
                                            out bool sense)
        {
            senseBuffer = null;
            duration    = 0;
            sense       = false;

            if (buffer == null)
            {
                return(-1);
            }

            ScsiPassThroughDirectAndSenseBuffer sptdSb = new ScsiPassThroughDirectAndSenseBuffer
            {
                SenseBuf = new byte[32],
                sptd     = new ScsiPassThroughDirect
                {
                    Cdb                = new byte[16],
                    CdbLength          = (byte)cdb.Length,
                    SenseInfoLength    = 32,
                    DataIn             = direction,
                    DataTransferLength = (uint)buffer.Length,
                    TimeOutValue       = timeout,
                    DataBuffer         = Marshal.AllocHGlobal(buffer.Length)
                }
            };

            sptdSb.sptd.Length          = (ushort)Marshal.SizeOf(sptdSb.sptd);
            sptdSb.sptd.SenseInfoOffset = (uint)Marshal.SizeOf(sptdSb.sptd);
            Array.Copy(cdb, sptdSb.sptd.Cdb, cdb.Length);

            uint k     = 0;
            int  error = 0;

            Marshal.Copy(buffer, 0, sptdSb.sptd.DataBuffer, buffer.Length);

            DateTime start    = DateTime.Now;
            bool     hasError = !Extern.DeviceIoControlScsi(fd, WindowsIoctl.IoctlScsiPassThroughDirect, ref sptdSb,
                                                            (uint)Marshal.SizeOf(sptdSb), ref sptdSb,
                                                            (uint)Marshal.SizeOf(sptdSb), ref k, IntPtr.Zero);
            DateTime end = DateTime.Now;

            if (hasError)
            {
                error = Marshal.GetLastWin32Error();
            }

            Marshal.Copy(sptdSb.sptd.DataBuffer, buffer, 0, buffer.Length);

            sense |= sptdSb.sptd.ScsiStatus != 0;

            senseBuffer = new byte[32];
            Array.Copy(sptdSb.SenseBuf, senseBuffer, 32);

            duration = (end - start).TotalMilliseconds;

            Marshal.FreeHGlobal(sptdSb.sptd.DataBuffer);

            return(error);
        }
Ejemplo n.º 5
0
        /// <summary>
        ///     Sends a MMC/SD command
        /// </summary>
        /// <returns>The result of the command.</returns>
        /// <param name="fd">File handle</param>
        /// <param name="command">MMC/SD opcode</param>
        /// <param name="buffer">Buffer for MMC/SD command response</param>
        /// <param name="timeout">Timeout in seconds</param>
        /// <param name="duration">Time it took to execute the command in milliseconds</param>
        /// <param name="sense"><c>True</c> if MMC/SD returned non-OK status</param>
        /// <param name="write"><c>True</c> if data is sent from host to card</param>
        /// <param name="isApplication"><c>True</c> if command should be preceded with CMD55</param>
        /// <param name="flags">Flags indicating kind and place of response</param>
        /// <param name="blocks">How many blocks to transfer</param>
        /// <param name="argument">Command argument</param>
        /// <param name="response">Response registers</param>
        /// <param name="blockSize">Size of block in bytes</param>
        internal static int SendMmcCommand(SafeFileHandle fd, MmcCommands command, bool write,
                                           bool isApplication,
                                           MmcFlags flags, uint argument, uint blockSize,
                                           uint blocks,
                                           ref byte[]     buffer, out uint[] response, out double duration,
                                           out bool sense,
                                           uint timeout = 0)
        {
            SffdiskDeviceCommandData commandData       = new SffdiskDeviceCommandData();
            SdCmdDescriptor          commandDescriptor = new SdCmdDescriptor();

            commandData.size    = (ushort)Marshal.SizeOf(commandData);
            commandData.command = SffdiskDcmd.DeviceCommand;
            commandData.protocolArgumentSize    = (ushort)Marshal.SizeOf(commandDescriptor);
            commandData.deviceDataBufferSize    = blockSize * blocks;
            commandDescriptor.commandCode       = (byte)command;
            commandDescriptor.cmdClass          = isApplication ? SdCommandClass.AppCmd : SdCommandClass.Standard;
            commandDescriptor.transferDirection = write ? SdTransferDirection.Write : SdTransferDirection.Read;
            commandDescriptor.transferType      = flags.HasFlag(MmcFlags.CommandAdtc)
                                                 ? SdTransferType.SingleBlock
                                                 : SdTransferType.CmdOnly;
            commandDescriptor.responseType = 0;

            if (flags.HasFlag(MmcFlags.ResponseR1) || flags.HasFlag(MmcFlags.ResponseSpiR1))
            {
                commandDescriptor.responseType = SdResponseType.R1;
            }
            if (flags.HasFlag(MmcFlags.ResponseR1B) || flags.HasFlag(MmcFlags.ResponseSpiR1B))
            {
                commandDescriptor.responseType = SdResponseType.R1b;
            }
            if (flags.HasFlag(MmcFlags.ResponseR2) || flags.HasFlag(MmcFlags.ResponseSpiR2))
            {
                commandDescriptor.responseType = SdResponseType.R2;
            }
            if (flags.HasFlag(MmcFlags.ResponseR3) || flags.HasFlag(MmcFlags.ResponseSpiR3))
            {
                commandDescriptor.responseType = SdResponseType.R3;
            }
            if (flags.HasFlag(MmcFlags.ResponseR4) || flags.HasFlag(MmcFlags.ResponseSpiR4))
            {
                commandDescriptor.responseType = SdResponseType.R4;
            }
            if (flags.HasFlag(MmcFlags.ResponseR5) || flags.HasFlag(MmcFlags.ResponseSpiR5))
            {
                commandDescriptor.responseType = SdResponseType.R5;
            }
            if (flags.HasFlag(MmcFlags.ResponseR6))
            {
                commandDescriptor.responseType = SdResponseType.R6;
            }

            byte[] commandB = new byte[commandData.size + commandData.protocolArgumentSize +
                                       commandData.deviceDataBufferSize];
            IntPtr hBuf = Marshal.AllocHGlobal(commandB.Length);

            Marshal.StructureToPtr(commandData, hBuf, true);
            IntPtr descriptorOffset = IntPtr.Add(hBuf, commandData.size);

            Marshal.StructureToPtr(commandDescriptor, descriptorOffset, true);
            Marshal.Copy(hBuf, commandB, 0, commandB.Length);
            Marshal.FreeHGlobal(hBuf);

            int      error = 0;
            DateTime start = DateTime.Now;

            sense = !Extern.DeviceIoControl(fd, WindowsIoctl.IoctlSffdiskDeviceCommand, commandB, (uint)commandB.Length,
                                            commandB, (uint)commandB.Length, out _, IntPtr.Zero);
            DateTime end = DateTime.Now;

            if (sense)
            {
                error = Marshal.GetLastWin32Error();
            }

            buffer = new byte[blockSize * blocks];
            Buffer.BlockCopy(commandB, commandB.Length - buffer.Length, buffer, 0, buffer.Length);

            response = new uint[4];
            duration = (end - start).TotalMilliseconds;

            return(error);
        }
Ejemplo n.º 6
0
        /// <summary>
        ///     Sends an ATA command in 48-bit LBA mode
        /// </summary>
        /// <returns>0 if no error occurred, otherwise, errno</returns>
        /// <param name="fd">File handle</param>
        /// <param name="buffer">Buffer for SCSI command response</param>
        /// <param name="timeout">Timeout in seconds</param>
        /// <param name="duration">Time it took to execute the command in milliseconds</param>
        /// <param name="sense"><c>True</c> if ATA error returned non-OK status</param>
        /// <param name="registers">Registers to send to drive</param>
        /// <param name="errorRegisters">Registers returned by drive</param>
        /// <param name="protocol">ATA protocol to use</param>
        internal static int SendAtaCommand(SafeFileHandle fd, AtaRegistersLba48 registers,
                                           out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol,
                                           ref byte[]                 buffer, uint timeout,
                                           out double duration, out bool sense)
        {
            duration       = 0;
            sense          = false;
            errorRegisters = new AtaErrorRegistersLba48();

            if (buffer == null)
            {
                return(-1);
            }

            uint offsetForBuffer = (uint)(Marshal.SizeOf(typeof(AtaPassThroughDirect)) + Marshal.SizeOf(typeof(uint)));

            AtaPassThroughDirectWithBuffer aptdBuf = new AtaPassThroughDirectWithBuffer
            {
                aptd = new AtaPassThroughDirect
                {
                    TimeOutValue       = timeout,
                    DataBuffer         = (IntPtr)offsetForBuffer,
                    Length             = (ushort)Marshal.SizeOf(typeof(AtaPassThroughDirect)),
                    DataTransferLength = (uint)buffer.Length,
                    PreviousTaskFile   =
                        new AtaTaskFile
                    {
                        CylinderHigh = (byte)((registers.LbaHigh & 0xFF00) >> 8),
                        CylinderLow  = (byte)((registers.LbaMid & 0xFF00) >> 8),
                        Features     = (byte)((registers.Feature & 0xFF00) >> 8),
                        SectorCount  = (byte)((registers.SectorCount & 0xFF00) >> 8),
                        SectorNumber = (byte)((registers.LbaLow & 0xFF00) >> 8)
                    },
                    CurrentTaskFile = new AtaTaskFile
                    {
                        Command      = registers.Command,
                        CylinderHigh = (byte)(registers.LbaHigh & 0xFF),
                        CylinderLow  = (byte)(registers.LbaMid & 0xFF),
                        DeviceHead   = registers.DeviceHead,
                        Features     = (byte)(registers.Feature & 0xFF),
                        SectorCount  = (byte)(registers.SectorCount & 0xFF),
                        SectorNumber = (byte)(registers.LbaLow & 0xFF)
                    }
                },
                dataBuffer = new byte[64 * 512]
            };

            switch (protocol)
            {
            case AtaProtocol.PioIn:
            case AtaProtocol.UDmaIn:
            case AtaProtocol.Dma:
                aptdBuf.aptd.AtaFlags = AtaFlags.DataIn;
                break;

            case AtaProtocol.PioOut:
            case AtaProtocol.UDmaOut:
                aptdBuf.aptd.AtaFlags = AtaFlags.DataOut;
                break;
            }

            switch (protocol)
            {
            case AtaProtocol.Dma:
            case AtaProtocol.DmaQueued:
            case AtaProtocol.FpDma:
            case AtaProtocol.UDmaIn:
            case AtaProtocol.UDmaOut:
                aptdBuf.aptd.AtaFlags |= AtaFlags.Dma;
                break;
            }

            // Unknown if needed
            aptdBuf.aptd.AtaFlags |= AtaFlags.DrdyRequired;

            uint k     = 0;
            int  error = 0;

            Array.Copy(buffer, 0, aptdBuf.dataBuffer, 0, buffer.Length);

            DateTime start = DateTime.Now;

            sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThrough, ref aptdBuf,
                                               (uint)Marshal.SizeOf(aptdBuf), ref aptdBuf,
                                               (uint)Marshal.SizeOf(aptdBuf), ref k, IntPtr.Zero);
            DateTime end = DateTime.Now;

            if (sense)
            {
                error = Marshal.GetLastWin32Error();
            }

            Array.Copy(aptdBuf.dataBuffer, 0, buffer, 0, buffer.Length);

            duration = (end - start).TotalMilliseconds;

            errorRegisters.SectorCount = (ushort)((aptdBuf.aptd.PreviousTaskFile.SectorCount << 8) +
                                                  aptdBuf.aptd.CurrentTaskFile.SectorCount);
            errorRegisters.LbaLow = (ushort)((aptdBuf.aptd.PreviousTaskFile.SectorNumber << 8) +
                                             aptdBuf.aptd.CurrentTaskFile.SectorNumber);
            errorRegisters.LbaMid = (ushort)((aptdBuf.aptd.PreviousTaskFile.CylinderLow << 8) +
                                             aptdBuf.aptd.CurrentTaskFile.CylinderLow);
            errorRegisters.LbaHigh = (ushort)((aptdBuf.aptd.PreviousTaskFile.CylinderHigh << 8) +
                                              aptdBuf.aptd.CurrentTaskFile.CylinderHigh);
            errorRegisters.DeviceHead = aptdBuf.aptd.CurrentTaskFile.DeviceHead;
            errorRegisters.Error      = aptdBuf.aptd.CurrentTaskFile.Error;
            errorRegisters.Status     = aptdBuf.aptd.CurrentTaskFile.Status;

            sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0;

            return(error);
        }
Ejemplo n.º 7
0
        /// <summary>
        ///     Opens the device for sending direct commands
        /// </summary>
        /// <param name="devicePath">Device path</param>
        public Device(string devicePath)
        {
            PlatformId  = DetectOS.GetRealPlatformID();
            Timeout     = 15;
            Error       = false;
            IsRemovable = false;

            if (devicePath.StartsWith("dic://"))
            {
                devicePath = devicePath.Substring(6);
                var pieces = devicePath.Split('/');
                var host   = pieces[0];
                devicePath = devicePath.Substring(host.Length);

                remote = new Remote.Remote(host);

                Error     = !remote.Open(devicePath, out var errno);
                LastError = errno;
            }
            else
            {
                switch (PlatformId)
                {
                case PlatformID.Win32NT:
                {
                    FileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite,
                                                   FileShare.Read | FileShare.Write, IntPtr.Zero,
                                                   FileMode.OpenExisting,
                                                   FileAttributes.Normal, IntPtr.Zero);

                    if (((SafeFileHandle)FileHandle).IsInvalid)
                    {
                        Error     = true;
                        LastError = Marshal.GetLastWin32Error();
                    }

                    break;
                }

                case PlatformID.Linux:
                {
                    FileHandle =
                        Linux.Extern.open(devicePath,
                                          FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew);

                    if ((int)FileHandle < 0)
                    {
                        LastError = Marshal.GetLastWin32Error();

                        if (LastError == 13 || LastError == 30)     // EACCES or EROFS
                        {
                            FileHandle = Linux.Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking);
                            if ((int)FileHandle < 0)
                            {
                                Error     = true;
                                LastError = Marshal.GetLastWin32Error();
                            }
                        }
                        else
                        {
                            Error = true;
                        }

                        LastError = Marshal.GetLastWin32Error();
                    }

                    break;
                }

                case PlatformID.FreeBSD:
                {
                    FileHandle = FreeBSD.Extern.cam_open_device(devicePath, FreeBSD.FileFlags.ReadWrite);

                    if (((IntPtr)FileHandle).ToInt64() == 0)
                    {
                        Error     = true;
                        LastError = Marshal.GetLastWin32Error();
                    }

                    var camDevice = (CamDevice)Marshal.PtrToStructure((IntPtr)FileHandle, typeof(CamDevice));

                    if (StringHandlers.CToString(camDevice.SimName) == "ata")
                    {
                        throw new
                              InvalidOperationException(
                                  "Parallel ATA devices are not supported on FreeBSD due to upstream bug #224250.");
                    }

                    break;
                }

                default: throw new InvalidOperationException($"Platform {PlatformId} not yet supported.");
                }
            }

            if (Error)
            {
                throw new SystemException($"Error {LastError} opening device.");
            }

            Type     = DeviceType.Unknown;
            ScsiType = PeripheralDeviceTypes.UnknownDevice;

            byte[] ataBuf;
            byte[] inqBuf = null;

            if (Error)
            {
                throw new SystemException($"Error {LastError} trying device.");
            }

            var scsiSense = true;

            if (remote is null)
            {
                // Windows is answering SCSI INQUIRY for all device types so it needs to be detected first
                switch (PlatformId)
                {
                case PlatformID.Win32NT:
                    var query = new StoragePropertyQuery();
                    query.PropertyId           = StoragePropertyId.Device;
                    query.QueryType            = StorageQueryType.Standard;
                    query.AdditionalParameters = new byte[1];

                    var descriptorPtr = Marshal.AllocHGlobal(1000);
                    var descriptorB   = new byte[1000];

                    uint returned = 0;
                    var  error    = 0;

                    var hasError = !Extern.DeviceIoControlStorageQuery((SafeFileHandle)FileHandle,
                                                                       WindowsIoctl.IoctlStorageQueryProperty,
                                                                       ref query, (uint)Marshal.SizeOf(query),
                                                                       descriptorPtr, 1000, ref returned, IntPtr.Zero);

                    if (hasError)
                    {
                        error = Marshal.GetLastWin32Error();
                    }

                    Marshal.Copy(descriptorPtr, descriptorB, 0, 1000);

                    if (!hasError && error == 0)
                    {
                        var descriptor = new StorageDeviceDescriptor
                        {
                            Version               = BitConverter.ToUInt32(descriptorB, 0),
                            Size                  = BitConverter.ToUInt32(descriptorB, 4),
                            DeviceType            = descriptorB[8],
                            DeviceTypeModifier    = descriptorB[9],
                            RemovableMedia        = descriptorB[10] > 0,
                            CommandQueueing       = descriptorB[11] > 0,
                            VendorIdOffset        = BitConverter.ToInt32(descriptorB, 12),
                            ProductIdOffset       = BitConverter.ToInt32(descriptorB, 16),
                            ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20),
                            SerialNumberOffset    = BitConverter.ToInt32(descriptorB, 24),
                            BusType               = (StorageBusType)BitConverter.ToUInt32(descriptorB, 28),
                            RawPropertiesLength   = BitConverter.ToUInt32(descriptorB, 32)
                        };
                        descriptor.RawDeviceProperties = new byte[descriptor.RawPropertiesLength];
                        Array.Copy(descriptorB, 36, descriptor.RawDeviceProperties, 0,
                                   descriptor.RawPropertiesLength);

                        switch (descriptor.BusType)
                        {
                        case StorageBusType.SCSI:
                        case StorageBusType.SSA:
                        case StorageBusType.Fibre:
                        case StorageBusType.iSCSI:
                        case StorageBusType.SAS:
                            Type = DeviceType.SCSI;
                            break;

                        case StorageBusType.FireWire:
                            IsFireWire = true;
                            Type       = DeviceType.SCSI;
                            break;

                        case StorageBusType.USB:
                            IsUsb = true;
                            Type  = DeviceType.SCSI;
                            break;

                        case StorageBusType.ATAPI:
                            Type = DeviceType.ATAPI;
                            break;

                        case StorageBusType.ATA:
                        case StorageBusType.SATA:
                            Type = DeviceType.ATA;
                            break;

                        case StorageBusType.MultiMediaCard:
                            Type = DeviceType.MMC;
                            break;

                        case StorageBusType.SecureDigital:
                            Type = DeviceType.SecureDigital;
                            break;

                        case StorageBusType.NVMe:
                            Type = DeviceType.NVMe;
                            break;
                        }

                        switch (Type)
                        {
                        case DeviceType.SCSI:
                        case DeviceType.ATAPI:
                            scsiSense = ScsiInquiry(out inqBuf, out _);
                            break;

                        case DeviceType.ATA:
                            var atapiSense = AtapiIdentify(out ataBuf, out _);

                            if (!atapiSense)
                            {
                                Type = DeviceType.ATAPI;
                                var ataid = Identify.Decode(ataBuf);

                                if (ataid.HasValue)
                                {
                                    scsiSense = ScsiInquiry(out inqBuf, out _);
                                }
                            }
                            else
                            {
                                Manufacturer = "ATA";
                            }

                            break;
                        }
                    }

                    Marshal.FreeHGlobal(descriptorPtr);

                    if (Windows.Command.IsSdhci((SafeFileHandle)FileHandle))
                    {
                        var sdBuffer = new byte[16];

                        LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCsd,
                                                                   false, false,
                                                                   MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 |
                                                                   MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _,
                                                                   out _, out var sense);

                        if (!sense)
                        {
                            cachedCsd = new byte[16];
                            Array.Copy(sdBuffer, 0, cachedCsd, 0, 16);
                        }

                        sdBuffer = new byte[16];

                        LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCid,
                                                                   false, false,
                                                                   MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 |
                                                                   MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, out _,
                                                                   out _, out sense);

                        if (!sense)
                        {
                            cachedCid = new byte[16];
                            Array.Copy(sdBuffer, 0, cachedCid, 0, 16);
                        }

                        sdBuffer = new byte[8];

                        LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                                   (MmcCommands)SecureDigitalCommands.SendScr, false,
                                                                   true,
                                                                   MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 |
                                                                   MmcFlags.CommandAdtc, 0, 8, 1, ref sdBuffer, out _,
                                                                   out _, out sense);

                        if (!sense)
                        {
                            cachedScr = new byte[8];
                            Array.Copy(sdBuffer, 0, cachedScr, 0, 8);
                        }

                        if (cachedScr != null)
                        {
                            sdBuffer = new byte[4];

                            LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                                       (MmcCommands)SecureDigitalCommands
                                                                       .SendOperatingCondition, false, true,
                                                                       MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 |
                                                                       MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer,
                                                                       out _, out _, out sense);

                            if (!sense)
                            {
                                cachedScr = new byte[4];
                                Array.Copy(sdBuffer, 0, cachedScr, 0, 4);
                            }
                        }
                        else
                        {
                            sdBuffer = new byte[4];

                            LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                                       MmcCommands.SendOpCond, false, true,
                                                                       MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 |
                                                                       MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer,
                                                                       out _, out _, out sense);

                            if (!sense)
                            {
                                cachedScr = new byte[4];
                                Array.Copy(sdBuffer, 0, cachedScr, 0, 4);
                            }
                        }
                    }

                    break;

                case PlatformID.Linux:
                    if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                        devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                        devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                    {
                        scsiSense = ScsiInquiry(out inqBuf, out _);
                    }
                    // MultiMediaCard and SecureDigital go here
                    else if (devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal))
                    {
                        var devPath = devicePath.Substring(5);
                        if (File.Exists("/sys/block/" + devPath + "/device/csd"))
                        {
                            var len =
                                ConvertFromHexAscii("/sys/block/" + devPath + "/device/csd", out cachedCsd);
                            if (len == 0)
                            {
                                cachedCsd = null;
                            }
                        }

                        if (File.Exists("/sys/block/" + devPath + "/device/cid"))
                        {
                            var len =
                                ConvertFromHexAscii("/sys/block/" + devPath + "/device/cid", out cachedCid);
                            if (len == 0)
                            {
                                cachedCid = null;
                            }
                        }

                        if (File.Exists("/sys/block/" + devPath + "/device/scr"))
                        {
                            var len =
                                ConvertFromHexAscii("/sys/block/" + devPath + "/device/scr", out cachedScr);
                            if (len == 0)
                            {
                                cachedScr = null;
                            }
                        }

                        if (File.Exists("/sys/block/" + devPath + "/device/ocr"))
                        {
                            var len =
                                ConvertFromHexAscii("/sys/block/" + devPath + "/device/ocr", out cachedOcr);
                            if (len == 0)
                            {
                                cachedOcr = null;
                            }
                        }
                    }

                    break;

                default:
                    scsiSense = ScsiInquiry(out inqBuf, out _);
                    break;
                }
            }
            else
            {
                Type = remote.GetDeviceType();

                switch (Type)
                {
                case DeviceType.ATAPI:
                case DeviceType.SCSI:
                    scsiSense = ScsiInquiry(out inqBuf, out _);
                    break;

                case DeviceType.SecureDigital:
                case DeviceType.MMC:
                    if (!remote.GetSdhciRegisters(out cachedCsd, out cachedCid, out cachedOcr, out cachedScr))
                    {
                        Type     = DeviceType.SCSI;
                        ScsiType = PeripheralDeviceTypes.DirectAccess;
                    }

                    break;
                }
            }

            #region SecureDigital / MultiMediaCard

            if (cachedCid != null)
            {
                ScsiType    = PeripheralDeviceTypes.DirectAccess;
                IsRemovable = false;

                if (cachedScr != null)
                {
                    Type = DeviceType.SecureDigital;
                    var decoded = Decoders.SecureDigital.Decoders.DecodeCID(cachedCid);
                    Manufacturer = VendorString.Prettify(decoded.Manufacturer);
                    Model        = decoded.ProductName;
                    Revision     = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}";
                    Serial       = $"{decoded.ProductSerialNumber}";
                }
                else
                {
                    Type = DeviceType.MMC;
                    var decoded = Decoders.MMC.Decoders.DecodeCID(cachedCid);
                    Manufacturer = Decoders.MMC.VendorString.Prettify(decoded.Manufacturer);
                    Model        = decoded.ProductName;
                    Revision     = $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}";
                    Serial       = $"{decoded.ProductSerialNumber}";
                }
            }

            #endregion SecureDigital / MultiMediaCard

            #region USB

            if (remote is null)
            {
                switch (PlatformId)
                {
                case PlatformID.Linux:
                    if (devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) ||
                        devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) ||
                        devicePath.StartsWith("/dev/st", StringComparison.Ordinal))
                    {
                        var devPath = devicePath.Substring(5);
                        if (Directory.Exists("/sys/block/" + devPath))
                        {
                            var resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath);
                            if (!string.IsNullOrEmpty(resolvedLink))
                            {
                                resolvedLink = "/sys" + resolvedLink.Substring(2);

                                while (resolvedLink.Contains("usb"))
                                {
                                    resolvedLink = Path.GetDirectoryName(resolvedLink);
                                    if (!File.Exists(resolvedLink + "/descriptors") ||
                                        !File.Exists(resolvedLink + "/idProduct") ||
                                        !File.Exists(resolvedLink + "/idVendor"))
                                    {
                                        continue;
                                    }

                                    var usbFs = new FileStream(resolvedLink + "/descriptors",
                                                               System.IO.FileMode.Open,
                                                               System.IO.FileAccess.Read);
                                    var usbBuf   = new byte[65536];
                                    var usbCount = usbFs.Read(usbBuf, 0, 65536);
                                    UsbDescriptors = new byte[usbCount];
                                    Array.Copy(usbBuf, 0, UsbDescriptors, 0, usbCount);
                                    usbFs.Close();

                                    var usbSr   = new StreamReader(resolvedLink + "/idProduct");
                                    var usbTemp = usbSr.ReadToEnd();
                                    ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                                    out usbProduct);
                                    usbSr.Close();

                                    usbSr   = new StreamReader(resolvedLink + "/idVendor");
                                    usbTemp = usbSr.ReadToEnd();
                                    ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
                                                    out usbVendor);
                                    usbSr.Close();

                                    if (File.Exists(resolvedLink + "/manufacturer"))
                                    {
                                        usbSr = new StreamReader(resolvedLink + "/manufacturer");
                                        UsbManufacturerString = usbSr.ReadToEnd().Trim();
                                        usbSr.Close();
                                    }

                                    if (File.Exists(resolvedLink + "/product"))
                                    {
                                        usbSr            = new StreamReader(resolvedLink + "/product");
                                        UsbProductString = usbSr.ReadToEnd().Trim();
                                        usbSr.Close();
                                    }

                                    if (File.Exists(resolvedLink + "/serial"))
                                    {
                                        usbSr           = new StreamReader(resolvedLink + "/serial");
                                        UsbSerialString = usbSr.ReadToEnd().Trim();
                                        usbSr.Close();
                                    }

                                    IsUsb = true;
                                    break;
                                }
                            }
                        }
                    }

                    break;

                case PlatformID.Win32NT:
                    Usb.UsbDevice usbDevice = null;

                    // I have to search for USB disks, floppies and CD-ROMs as separate device types
                    foreach (var devGuid in new[]
                    {
                        Usb.GuidDevinterfaceFloppy, Usb.GuidDevinterfaceCdrom, Usb.GuidDevinterfaceDisk
                    })
                    {
                        usbDevice = Usb.FindDrivePath(devicePath, devGuid);
                        if (usbDevice != null)
                        {
                            break;
                        }
                    }

                    if (usbDevice != null)
                    {
                        UsbDescriptors        = usbDevice.BinaryDescriptors;
                        usbVendor             = (ushort)usbDevice.DeviceDescriptor.idVendor;
                        usbProduct            = (ushort)usbDevice.DeviceDescriptor.idProduct;
                        UsbManufacturerString = usbDevice.Manufacturer;
                        UsbProductString      = usbDevice.Product;
                        UsbSerialString       =
                            usbDevice
                            .SerialNumber;         // This is incorrect filled by Windows with SCSI/ATA serial number
                    }

                    break;

                default:
                    IsUsb = false;
                    break;
                }
            }
            else
            {
                if (remote.GetUsbData(out var remoteUsbDescriptors, out var remoteUsbVendor,
                                      out var remoteUsbProduct, out var remoteUsbManufacturer, out var remoteUsbProductString,
                                      out var remoteUsbSerial))
                {
                    IsUsb                 = true;
                    UsbDescriptors        = remoteUsbDescriptors;
                    usbVendor             = remoteUsbVendor;
                    usbProduct            = remoteUsbProduct;
                    UsbManufacturerString = remoteUsbManufacturer;
                    UsbProductString      = remoteUsbProductString;
                    UsbSerialString       = remoteUsbSerial;
                }
            }

            #endregion USB

            #region FireWire

            if (!(remote is null))
            {
                if (remote.GetFireWireData(out firewireVendor, out firewireModel,
                                           out firewireGuid, out var remoteFireWireVendorName, out var remoteFireWireModelName))
                {
                    IsFireWire         = true;
                    FireWireVendorName = remoteFireWireVendorName;
                    FireWireModelName  = remoteFireWireModelName;
                }
            }
Ejemplo n.º 8
0
        internal static DeviceInfo[] GetList()
        {
            List <string> deviceIDs = new List <string>();

            try
            {
                ManagementObjectSearcher mgmtObjSearcher =
                    new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
                ManagementObjectCollection objCol = mgmtObjSearcher.Get();

                deviceIDs.AddRange(from ManagementObject drive in objCol select(string) drive["DeviceID"]);

                mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_TapeDrive");
                objCol          = mgmtObjSearcher.Get();

                deviceIDs.AddRange(from ManagementObject drive in objCol select(string) drive["DeviceID"]);

                mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_CDROMDrive");
                objCol          = mgmtObjSearcher.Get();

                deviceIDs.AddRange(from ManagementObject drive in objCol select(string) drive["Drive"]);
            }
            catch (Exception)
            {
#if DEBUG
                throw;
#else
                return(null);
#endif
            }

            List <DeviceInfo> devList = new List <DeviceInfo>();

            foreach (string devId in deviceIDs)
            {
                string physId = devId;
                // TODO: This can be done better
                if (devId.Length == 2 && devId[1] == ':')
                {
                    physId = "\\\\?\\" + devId;
                }
                SafeFileHandle fd = Extern.CreateFile(physId, 0, FileShare.Read | FileShare.Write, IntPtr.Zero,
                                                      FileMode.OpenExisting, 0, IntPtr.Zero);
                if (fd.IsInvalid)
                {
                    continue;
                }

                StoragePropertyQuery query = new StoragePropertyQuery
                {
                    PropertyId           = StoragePropertyId.Device,
                    QueryType            = StorageQueryType.Standard,
                    AdditionalParameters = new byte[1]
                };

                //StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor();
                //descriptor.RawDeviceProperties = new byte[16384];

                IntPtr descriptorPtr = Marshal.AllocHGlobal(1000);
                byte[] descriptorB   = new byte[1000];

                uint returned = 0;
                int  error    = 0;

                bool hasError = !Extern.DeviceIoControlStorageQuery(fd, WindowsIoctl.IoctlStorageQueryProperty,
                                                                    ref query, (uint)Marshal.SizeOf(query),
                                                                    descriptorPtr, 1000, ref returned, IntPtr.Zero);

                if (hasError)
                {
                    error = Marshal.GetLastWin32Error();
                }

                Marshal.Copy(descriptorPtr, descriptorB, 0, 1000);

                if (hasError && error != 0)
                {
                    continue;
                }

                StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor
                {
                    Version               = BitConverter.ToUInt32(descriptorB, 0),
                    Size                  = BitConverter.ToUInt32(descriptorB, 4),
                    DeviceType            = descriptorB[8],
                    DeviceTypeModifier    = descriptorB[9],
                    RemovableMedia        = BitConverter.ToBoolean(descriptorB, 10),
                    CommandQueueing       = BitConverter.ToBoolean(descriptorB, 11),
                    VendorIdOffset        = BitConverter.ToInt32(descriptorB, 12),
                    ProductIdOffset       = BitConverter.ToInt32(descriptorB, 16),
                    ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20),
                    SerialNumberOffset    = BitConverter.ToInt32(descriptorB, 24),
                    BusType               = (StorageBusType)BitConverter.ToUInt32(descriptorB, 28),
                    RawPropertiesLength   = BitConverter.ToUInt32(descriptorB, 32)
                };

                DeviceInfo info = new DeviceInfo {
                    Path = physId, Bus = descriptor.BusType.ToString()
                };

                if (descriptor.VendorIdOffset > 0)
                {
                    info.Vendor =
                        StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.VendorIdOffset);
                }
                if (descriptor.ProductIdOffset > 0)
                {
                    info.Model =
                        StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.ProductIdOffset);
                }
                // TODO: Get serial number of SCSI and USB devices, probably also FireWire (untested)
                if (descriptor.SerialNumberOffset > 0)
                {
                    info.Serial =
                        StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.SerialNumberOffset);

                    // fix any serial numbers that are returned as hex-strings
                    if (Array.TrueForAll(info.Serial.ToCharArray(), c => "0123456789abcdef".IndexOf(c) >= 0) &&
                        info.Serial.Length == 40)
                    {
                        info.Serial = HexStringToString(info.Serial).Trim();
                    }
                }

                if (string.IsNullOrEmpty(info.Vendor) || info.Vendor == "ATA")
                {
                    string[] pieces = info.Model.Split(' ');
                    if (pieces.Length > 1)
                    {
                        info.Vendor = pieces[0];
                        info.Model  = info.Model.Substring(pieces[0].Length + 1);
                    }
                }

                switch (descriptor.BusType)
                {
                case StorageBusType.SCSI:
                case StorageBusType.ATAPI:
                case StorageBusType.ATA:
                case StorageBusType.FireWire:
                case StorageBusType.SSA:
                case StorageBusType.Fibre:
                case StorageBusType.USB:
                case StorageBusType.iSCSI:
                case StorageBusType.SAS:
                case StorageBusType.SATA:
                    info.Supported = true;
                    break;
                }

                Marshal.FreeHGlobal(descriptorPtr);
                devList.Add(info);
            }

            DeviceInfo[] devices = devList.ToArray();

            return(devices);
        }
Ejemplo n.º 9
0
        /// <summary>
        ///     Gets the internal device path for a specified handle
        /// </summary>
        /// <param name="fd">Device handle</param>
        /// <returns>Device path</returns>
        internal static string GetDevicePath(SafeFileHandle fd)
        {
            uint devNumber = GetDeviceNumber(fd);

            if (devNumber == uint.MaxValue)
            {
                return(null);
            }

            SafeFileHandle hDevInfo = Extern.SetupDiGetClassDevs(ref Consts.GuidDevinterfaceDisk, IntPtr.Zero,
                                                                 IntPtr.Zero,
                                                                 DeviceGetClassFlags.Present |
                                                                 DeviceGetClassFlags.DeviceInterface);

            if (hDevInfo.IsInvalid)
            {
                return(null);
            }

            uint index = 0;
            DeviceInterfaceData spdid = new DeviceInterfaceData();

            spdid.cbSize = Marshal.SizeOf(spdid);

            while (true)
            {
                byte[] buffer = new byte[2048];

                if (!Extern.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref Consts.GuidDevinterfaceDisk, index,
                                                        ref spdid))
                {
                    break;
                }

                uint size = 0;

                Extern.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref spdid, IntPtr.Zero, 0, ref size, IntPtr.Zero);

                if (size > 0 && size < buffer.Length)
                {
                    buffer[0] = (byte)(IntPtr.Size == 8
                                           ? IntPtr.Size
                                           : IntPtr.Size + Marshal.SystemDefaultCharSize); // Funny...

                    IntPtr pspdidd = Marshal.AllocHGlobal(buffer.Length);
                    Marshal.Copy(buffer, 0, pspdidd, buffer.Length);

                    bool result =
                        Extern.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref spdid, pspdidd, size, ref size,
                                                               IntPtr.Zero);

                    buffer = new byte[size];
                    Marshal.Copy(pspdidd, buffer, 0, buffer.Length);
                    Marshal.FreeHGlobal(pspdidd);

                    if (result)
                    {
                        string         devicePath = Encoding.Unicode.GetString(buffer, 4, (int)size - 4);
                        SafeFileHandle hDrive     = Extern.CreateFile(devicePath, 0, FileShare.Read | FileShare.Write,
                                                                      IntPtr.Zero, FileMode.OpenExisting, 0, IntPtr.Zero);

                        if (!hDrive.IsInvalid)
                        {
                            uint newDeviceNumber = GetDeviceNumber(hDrive);

                            if (newDeviceNumber == devNumber)
                            {
                                Extern.CloseHandle(hDrive);
                                return(devicePath);
                            }
                        }

                        Extern.CloseHandle(hDrive);
                    }
                }

                index++;
            }

            Extern.SetupDiDestroyDeviceInfoList(hDevInfo);
            return(null);
        }
Ejemplo n.º 10
0
        /// <summary>
        ///     Sends an ATA command in 28-bit LBA mode using undocumented Windows XP ioctl
        /// </summary>
        /// <returns>0 if no error occurred, otherwise, errno</returns>
        /// <param name="fd">File handle</param>
        /// <param name="buffer">Buffer for SCSI command response</param>
        /// <param name="timeout">Timeout in seconds</param>
        /// <param name="duration">Time it took to execute the command in milliseconds</param>
        /// <param name="sense"><c>True</c> if ATA error returned non-OK status</param>
        /// <param name="registers">Registers to send to drive</param>
        /// <param name="errorRegisters">Registers returned by drive</param>
        /// <param name="protocol">ATA protocol to use</param>
        internal static int SendIdeCommand(SafeFileHandle fd, AtaRegistersLba28 registers,
                                           out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol,
                                           ref byte[] buffer, uint timeout, out double duration, out bool sense)
        {
            duration       = 0;
            sense          = false;
            errorRegisters = new AtaErrorRegistersLba28();

            if (buffer == null)
            {
                return(-1);
            }

            IdePassThroughDirect iptd = new IdePassThroughDirect
            {
                CurrentTaskFile = new AtaTaskFile
                {
                    Command      = registers.Command,
                    CylinderHigh = registers.LbaHigh,
                    CylinderLow  = registers.LbaMid,
                    DeviceHead   = registers.DeviceHead,
                    Features     = registers.Feature,
                    SectorCount  = registers.SectorCount,
                    SectorNumber = registers.LbaLow
                },
                DataBufferSize = 512,
                DataBuffer     = new byte[512]
            };

            uint k     = 0;
            int  error = 0;

            Array.Copy(buffer, 0, iptd.DataBuffer, 0, buffer.Length);

            DateTime start = DateTime.Now;

            sense = !Extern.DeviceIoControlIde(fd, WindowsIoctl.IoctlIdePassThrough, ref iptd,
                                               (uint)Marshal.SizeOf(iptd), ref iptd, (uint)Marshal.SizeOf(iptd), ref k,
                                               IntPtr.Zero);
            DateTime end = DateTime.Now;

            if (sense)
            {
                error = Marshal.GetLastWin32Error();
            }

            buffer = new byte[k - 12];
            Array.Copy(iptd.DataBuffer, 0, buffer, 0, buffer.Length);

            duration = (end - start).TotalMilliseconds;

            errorRegisters.LbaHigh     = iptd.CurrentTaskFile.CylinderHigh;
            errorRegisters.LbaMid      = iptd.CurrentTaskFile.CylinderLow;
            errorRegisters.DeviceHead  = iptd.CurrentTaskFile.DeviceHead;
            errorRegisters.Error       = iptd.CurrentTaskFile.Error;
            errorRegisters.LbaLow      = iptd.CurrentTaskFile.SectorNumber;
            errorRegisters.SectorCount = iptd.CurrentTaskFile.SectorCount;
            errorRegisters.Status      = iptd.CurrentTaskFile.Status;

            sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0;

            return(error);
        }