Ejemplo n.º 1
0
        internal Reader(Device dev, uint timeout, byte[] identification, bool raw = false)
        {
            this.dev     = dev;
            this.timeout = timeout;
            BlocksToRead = 64;
            CanReadRaw   = raw;

            switch (dev.Type)
            {
            case DeviceType.ATA:
                Identify.IdentifyDevice?ataIdNullable = Identify.Decode(identification);
                if (ataIdNullable.HasValue)
                {
                    ataId = ataIdNullable.Value;
                }
                break;

            case DeviceType.NVMe: throw new NotImplementedException("NVMe devices not yet supported.");
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        ///     Dumps an ATA device
        /// </summary>
        /// <param name="dev">Device</param>
        /// <param name="devicePath">Path to the device</param>
        /// <param name="outputPrefix">Prefix for output log files</param>
        /// <param name="outputPlugin">Plugin for output file</param>
        /// <param name="retryPasses">How many times to retry</param>
        /// <param name="force">Force to continue dump whenever possible</param>
        /// <param name="dumpRaw">Dump long sectors</param>
        /// <param name="persistent">Store whatever data the drive returned on error</param>
        /// <param name="stopOnError">Stop dump on first error</param>
        /// <param name="resume">Information for dump resuming</param>
        /// <param name="dumpLog">Dump logger</param>
        /// <param name="encoding">Encoding to use when analyzing dump</param>
        /// <param name="outputPath">Path to output file</param>
        /// <param name="formatOptions">Formats to pass to output file plugin</param>
        /// <exception cref="InvalidOperationException">If the resume file is invalid</exception>
        public static void Dump(Device dev, string devicePath,
                                IWritableImage outputPlugin, ushort retryPasses,
                                bool force, bool dumpRaw,
                                bool persistent, bool stopOnError, ref Resume resume,
                                ref DumpLog dumpLog, Encoding encoding,
                                string outputPrefix, string outputPath,
                                Dictionary <string, string> formatOptions, CICMMetadataType preSidecar,
                                uint skip,
                                bool nometadata, bool notrim)
        {
            bool aborted;

            if (dumpRaw)
            {
                DicConsole.ErrorWriteLine("Raw dumping not yet supported in ATA devices.");

                if (force)
                {
                    DicConsole.ErrorWriteLine("Continuing...");
                }
                else
                {
                    DicConsole.ErrorWriteLine("Aborting...");
                    return;
                }
            }

            bool         sense;
            const ushort ATA_PROFILE        = 0x0001;
            const uint   TIMEOUT            = 5;
            double       imageWriteDuration = 0;

            dumpLog.WriteLine("Requesting ATA IDENTIFY DEVICE.");
            sense = dev.AtaIdentify(out byte[] cmdBuf, out _);
            if (!sense && Identify.Decode(cmdBuf).HasValue)
            {
                Identify.IdentifyDevice?ataIdNullable = Identify.Decode(cmdBuf);
                if (ataIdNullable != null)
                {
                    Identify.IdentifyDevice ataId = ataIdNullable.Value;
                    byte[] ataIdentify            = cmdBuf;
                    cmdBuf = new byte[0];

                    DateTime start;
                    DateTime end;
                    double   totalDuration = 0;
                    double   currentSpeed  = 0;
                    double   maxSpeed      = double.MinValue;
                    double   minSpeed      = double.MaxValue;

                    aborted = false;
                    System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;

                    // Initializate reader
                    dumpLog.WriteLine("Initializing reader.");
                    Reader ataReader = new Reader(dev, TIMEOUT, ataIdentify);
                    // Fill reader blocks
                    ulong blocks = ataReader.GetDeviceBlocks();
                    // Check block sizes
                    if (ataReader.GetBlockSize())
                    {
                        dumpLog.WriteLine("ERROR: Cannot get block size: {0}.", ataReader.ErrorMessage);
                        DicConsole.ErrorWriteLine(ataReader.ErrorMessage);
                        return;
                    }

                    uint blockSize          = ataReader.LogicalBlockSize;
                    uint physicalsectorsize = ataReader.PhysicalBlockSize;
                    if (ataReader.FindReadCommand())
                    {
                        dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", ataReader.ErrorMessage);
                        DicConsole.ErrorWriteLine(ataReader.ErrorMessage);
                        return;
                    }

                    // Check how many blocks to read, if error show and return
                    if (ataReader.GetBlocksToRead())
                    {
                        dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", ataReader.ErrorMessage);
                        DicConsole.ErrorWriteLine(ataReader.ErrorMessage);
                        return;
                    }

                    uint   blocksToRead = ataReader.BlocksToRead;
                    ushort cylinders    = ataReader.Cylinders;
                    byte   heads        = ataReader.Heads;
                    byte   sectors      = ataReader.Sectors;

                    dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);
                    dumpLog.WriteLine("Device reports {0} cylinders {1} heads {2} sectors per track.", cylinders, heads,
                                      sectors);
                    dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
                    dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
                    dumpLog.WriteLine("Device reports {0} bytes per physical block.", physicalsectorsize);

                    bool removable = !dev.IsCompactFlash &&
                                     ataId.GeneralConfiguration.HasFlag(Identify.GeneralConfigurationBit.Removable);
                    DumpHardwareType currentTry = null;
                    ExtentsULong     extents    = null;
                    ResumeSupport.Process(ataReader.IsLba, removable, blocks, dev.Manufacturer, dev.Model, dev.Serial,
                                          dev.PlatformId, ref resume, ref currentTry, ref extents);
                    if (currentTry == null || extents == null)
                    {
                        throw new InvalidOperationException("Could not process resume file, not continuing...");
                    }

                    MhddLog mhddLog;
                    IbgLog  ibgLog;
                    double  duration;

                    bool ret = true;

                    if (dev.IsUsb && dev.UsbDescriptors != null &&
                        !outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors))
                    {
                        ret = false;
                        dumpLog.WriteLine("Output format does not support USB descriptors.");
                        DicConsole.ErrorWriteLine("Output format does not support USB descriptors.");
                    }

                    if (dev.IsPcmcia && dev.Cis != null &&
                        !outputPlugin.SupportedMediaTags.Contains(MediaTagType.PCMCIA_CIS))
                    {
                        ret = false;
                        dumpLog.WriteLine("Output format does not support PCMCIA CIS descriptors.");
                        DicConsole.ErrorWriteLine("Output format does not support PCMCIA CIS descriptors.");
                    }

                    if (!outputPlugin.SupportedMediaTags.Contains(MediaTagType.ATA_IDENTIFY))
                    {
                        ret = false;
                        dumpLog.WriteLine("Output format does not support ATA IDENTIFY.");
                        DicConsole.ErrorWriteLine("Output format does not support ATA IDENTIFY.");
                    }

                    if (!ret)
                    {
                        dumpLog.WriteLine("Several media tags not supported, {0}continuing...", force ? "" : "not ");
                        DicConsole.ErrorWriteLine("Several media tags not supported, {0}continuing...",
                                                  force ? "" : "not ");
                        if (!force)
                        {
                            return;
                        }
                    }

                    ret = outputPlugin.Create(outputPath,
                                              dev.IsCompactFlash ? MediaType.CompactFlash : MediaType.GENERIC_HDD,
                                              formatOptions, blocks, blockSize);

                    // Cannot create image
                    if (!ret)
                    {
                        dumpLog.WriteLine("Error creating output image, not continuing.");
                        dumpLog.WriteLine(outputPlugin.ErrorMessage);
                        DicConsole.ErrorWriteLine("Error creating output image, not continuing.");
                        DicConsole.ErrorWriteLine(outputPlugin.ErrorMessage);
                        return;
                    }

                    // Setting geometry
                    outputPlugin.SetGeometry(cylinders, heads, sectors);

                    if (ataReader.IsLba)
                    {
                        DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);
                        if (skip < blocksToRead)
                        {
                            skip = blocksToRead;
                        }

                        mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
                        ibgLog  = new IbgLog(outputPrefix + ".ibg", ATA_PROFILE);

                        if (resume.NextBlock > 0)
                        {
                            dumpLog.WriteLine("Resuming from block {0}.", resume.NextBlock);
                        }
                        bool newTrim = false;

                        start = DateTime.UtcNow;
                        for (ulong i = resume.NextBlock; i < blocks; i += blocksToRead)
                        {
                            if (aborted)
                            {
                                currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                dumpLog.WriteLine("Aborted!");
                                break;
                            }

                            if (blocks - i < blocksToRead)
                            {
                                blocksToRead = (byte)(blocks - i);
                            }

                            #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                            if (currentSpeed > maxSpeed && currentSpeed != 0)
                            {
                                maxSpeed = currentSpeed;
                            }
                            if (currentSpeed < minSpeed && currentSpeed != 0)
                            {
                                minSpeed = currentSpeed;
                            }
                            #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                            DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, blocks, currentSpeed);

                            bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration);

                            if (!error)
                            {
                                mhddLog.Write(i, duration);
                                ibgLog.Write(i, currentSpeed * 1024);
                                DateTime writeStart = DateTime.Now;
                                outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
                                imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                extents.Add(i, blocksToRead, true);
                            }
                            else
                            {
                                if (i + skip > blocks)
                                {
                                    skip = (uint)(blocks - i);
                                }

                                for (ulong b = i; b < i + skip; b++)
                                {
                                    resume.BadBlocks.Add(b);
                                }

                                mhddLog.Write(i, duration < 500 ? 65535 : duration);

                                ibgLog.Write(i, 0);
                                DateTime writeStart = DateTime.Now;
                                outputPlugin.WriteSectors(new byte[blockSize * skip], i, skip);
                                imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", skip, i);
                                i      += skip - blocksToRead;
                                newTrim = true;
                            }

                            double newSpeed =
                                (double)blockSize * blocksToRead / 1048576 / (duration / 1000);
                            if (!double.IsInfinity(newSpeed))
                            {
                                currentSpeed = newSpeed;
                            }
                            resume.NextBlock = i + blocksToRead;
                        }

                        end = DateTime.Now;
                        DicConsole.WriteLine();
                        mhddLog.Close();
                        ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                     blockSize * (double)(blocks + 1) / 1024 /
                                     (totalDuration / 1000), devicePath);
                        dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
                        dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                                          (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
                        dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
                                          (double)blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration);

                        #region Trimming
                        if (resume.BadBlocks.Count > 0 && !aborted && !notrim && newTrim)
                        {
                            start = DateTime.UtcNow;
                            dumpLog.WriteLine("Trimming bad sectors");

                            ulong[] tmpArray = resume.BadBlocks.ToArray();
                            foreach (ulong badSector in tmpArray)
                            {
                                if (aborted)
                                {
                                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                    dumpLog.WriteLine("Aborted!");
                                    break;
                                }

                                DicConsole.Write("\rTrimming sector {0}", badSector);

                                bool error = ataReader.ReadBlock(out cmdBuf, badSector, out duration);

                                totalDuration += duration;

                                if (error)
                                {
                                    continue;
                                }

                                resume.BadBlocks.Remove(badSector);
                                extents.Add(badSector);
                                outputPlugin.WriteSector(cmdBuf, badSector);
                            }

                            DicConsole.WriteLine();
                            end = DateTime.UtcNow;
                            dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
                        }
                        #endregion Trimming

                        #region Error handling
                        if (resume.BadBlocks.Count > 0 && !aborted && retryPasses > 0)
                        {
                            int  pass    = 1;
                            bool forward = true;

repeatRetryLba:
                            ulong[] tmpArray = resume.BadBlocks.ToArray();
                            foreach (ulong badSector in tmpArray)
                            {
                                if (aborted)
                                {
                                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                    dumpLog.WriteLine("Aborted!");
                                    break;
                                }

                                DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass,
                                                 forward ? "forward" : "reverse",
                                                 persistent ? "recovering partial data, " : "");

                                bool error = ataReader.ReadBlock(out cmdBuf, badSector, out duration);

                                totalDuration += duration;

                                if (!error)
                                {
                                    resume.BadBlocks.Remove(badSector);
                                    extents.Add(badSector);
                                    outputPlugin.WriteSector(cmdBuf, badSector);
                                    dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
                                }
                                else if (persistent)
                                {
                                    outputPlugin.WriteSector(cmdBuf, badSector);
                                }
                            }

                            if (pass < retryPasses && !aborted && resume.BadBlocks.Count > 0)
                            {
                                pass++;
                                forward = !forward;
                                resume.BadBlocks.Sort();
                                resume.BadBlocks.Reverse();
                                goto repeatRetryLba;
                            }

                            DicConsole.WriteLine();
                        }
                        #endregion Error handling LBA

                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    }
                    else
                    {
                        mhddLog = new MhddLog(outputPrefix + ".mhddlog.bin", dev, blocks, blockSize, blocksToRead);
                        ibgLog  = new IbgLog(outputPrefix + ".ibg", ATA_PROFILE);

                        ulong currentBlock = 0;
                        blocks = (ulong)(cylinders * heads * sectors);
                        start  = DateTime.UtcNow;
                        for (ushort cy = 0; cy < cylinders; cy++)
                        {
                            for (byte hd = 0; hd < heads; hd++)
                            {
                                for (byte sc = 1; sc < sectors; sc++)
                                {
                                    if (aborted)
                                    {
                                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                        dumpLog.WriteLine("Aborted!");
                                        break;
                                    }

                                    #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                                    if (currentSpeed > maxSpeed && currentSpeed != 0)
                                    {
                                        maxSpeed = currentSpeed;
                                    }
                                    if (currentSpeed < minSpeed && currentSpeed != 0)
                                    {
                                        minSpeed = currentSpeed;
                                    }
                                    #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                                    DicConsole.Write("\rReading cylinder {0} head {1} sector {2} ({3:F3} MiB/sec.)", cy,
                                                     hd, sc, currentSpeed);

                                    bool error = ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration);

                                    totalDuration += duration;

                                    if (!error)
                                    {
                                        mhddLog.Write(currentBlock, duration);
                                        ibgLog.Write(currentBlock, currentSpeed * 1024);
                                        DateTime writeStart = DateTime.Now;
                                        outputPlugin.WriteSector(cmdBuf,
                                                                 (ulong)((cy * heads + hd) * sectors + (sc - 1)));
                                        imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                        extents.Add(currentBlock);
                                        dumpLog.WriteLine("Error reading cylinder {0} head {1} sector {2}.", cy, hd,
                                                          sc);
                                    }
                                    else
                                    {
                                        resume.BadBlocks.Add(currentBlock);
                                        mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration);

                                        ibgLog.Write(currentBlock, 0);
                                        DateTime writeStart = DateTime.Now;
                                        outputPlugin.WriteSector(new byte[blockSize],
                                                                 (ulong)((cy * heads + hd) * sectors + (sc - 1)));
                                        imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                    }

                                    double newSpeed =
                                        blockSize / (double)1048576 / (duration / 1000);
                                    if (!double.IsInfinity(newSpeed))
                                    {
                                        currentSpeed = newSpeed;
                                    }

                                    currentBlock++;
                                }
                            }
                        }

                        end = DateTime.Now;
                        DicConsole.WriteLine();
                        mhddLog.Close();
                        ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                     blockSize * (double)(blocks + 1) / 1024 /
                                     (totalDuration / 1000), devicePath);
                        dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
                        dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                                          (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
                        dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
                                          (double)blockSize * (double)(blocks + 1) / 1024 /
                                          (imageWriteDuration / 1000));
                    }

                    foreach (ulong bad in resume.BadBlocks)
                    {
                        dumpLog.WriteLine("Sector {0} could not be read.", bad);
                    }
                    outputPlugin.SetDumpHardware(resume.Tries);
                    if (preSidecar != null)
                    {
                        outputPlugin.SetCicmMetadata(preSidecar);
                    }
                    dumpLog.WriteLine("Closing output file.");
                    DicConsole.WriteLine("Closing output file.");
                    DateTime closeStart = DateTime.Now;
                    outputPlugin.Close();
                    DateTime closeEnd = DateTime.Now;
                    dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

                    if (aborted)
                    {
                        dumpLog.WriteLine("Aborted!");
                        return;
                    }

                    double totalChkDuration = 0;
                    if (!nometadata)
                    {
                        dumpLog.WriteLine("Creating sidecar.");
                        FiltersList filters     = new FiltersList();
                        IFilter     filter      = filters.GetFilter(outputPath);
                        IMediaImage inputPlugin = ImageFormat.Detect(filter);
                        if (!inputPlugin.Open(filter))
                        {
                            throw new ArgumentException("Could not open created image.");
                        }

                        DateTime         chkStart = DateTime.UtcNow;
                        CICMMetadataType sidecar  = Sidecar.Create(inputPlugin, outputPath, filter.Id, encoding);
                        if (preSidecar != null)
                        {
                            preSidecar.BlockMedia = sidecar.BlockMedia;
                            sidecar = preSidecar;
                        }

                        if (dev.IsUsb)
                        {
                            dumpLog.WriteLine("Reading USB descriptors.");
                            ret = outputPlugin.WriteMediaTag(dev.UsbDescriptors, MediaTagType.USB_Descriptors);

                            if (ret)
                            {
                                sidecar.BlockMedia[0].USB = new USBType
                                {
                                    ProductID   = dev.UsbProductId,
                                    VendorID    = dev.UsbVendorId,
                                    Descriptors = new DumpType
                                    {
                                        Image     = outputPath,
                                        Size      = dev.UsbDescriptors.Length,
                                        Checksums = Checksum.GetChecksums(dev.UsbDescriptors).ToArray()
                                    }
                                }
                            }
                            ;
                        }

                        if (dev.IsPcmcia)
                        {
                            dumpLog.WriteLine("Reading PCMCIA CIS.");
                            ret = outputPlugin.WriteMediaTag(dev.Cis, MediaTagType.PCMCIA_CIS);

                            if (ret)
                            {
                                sidecar.BlockMedia[0].PCMCIA = new PCMCIAType
                                {
                                    CIS = new DumpType
                                    {
                                        Image     = outputPath,
                                        Size      = dev.Cis.Length,
                                        Checksums = Checksum.GetChecksums(dev.Cis).ToArray()
                                    }
                                }
                            }
                            ;

                            dumpLog.WriteLine("Decoding PCMCIA CIS.");
                            Tuple[] tuples = CIS.GetTuples(dev.Cis);
                            if (tuples != null)
                            {
                                foreach (Tuple tuple in tuples)
                                {
                                    switch (tuple.Code)
                                    {
                                    case TupleCodes.CISTPL_MANFID:
                                        ManufacturerIdentificationTuple manfid =
                                            CIS.DecodeManufacturerIdentificationTuple(tuple);

                                        if (manfid != null)
                                        {
                                            sidecar.BlockMedia[0].PCMCIA.ManufacturerCode =
                                                manfid.ManufacturerID;
                                            sidecar.BlockMedia[0].PCMCIA.CardCode = manfid.CardID;
                                            sidecar.BlockMedia[0].PCMCIA.ManufacturerCodeSpecified = true;
                                            sidecar.BlockMedia[0].PCMCIA.CardCodeSpecified         = true;
                                        }

                                        break;

                                    case TupleCodes.CISTPL_VERS_1:
                                        Level1VersionTuple vers = CIS.DecodeLevel1VersionTuple(tuple);

                                        if (vers != null)
                                        {
                                            sidecar.BlockMedia[0].PCMCIA.Manufacturer = vers.Manufacturer;
                                            sidecar.BlockMedia[0].PCMCIA.ProductName  = vers.Product;
                                            sidecar.BlockMedia[0].PCMCIA.Compliance   =
                                                $"{vers.MajorVersion}.{vers.MinorVersion}";
                                            sidecar.BlockMedia[0].PCMCIA.AdditionalInformation =
                                                vers.AdditionalInformation;
                                        }

                                        break;
                                    }
                                }
                            }
                        }

                        ret = outputPlugin.WriteMediaTag(ataIdentify, MediaTagType.ATA_IDENTIFY);

                        if (ret)
                        {
                            sidecar.BlockMedia[0].ATA = new ATAType
                            {
                                Identify = new DumpType
                                {
                                    Image     = outputPath,
                                    Size      = cmdBuf.Length,
                                    Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
                                }
                            }
                        }
                        ;

                        DateTime chkEnd = DateTime.UtcNow;

                        totalChkDuration = (chkEnd - chkStart).TotalMilliseconds;
                        dumpLog.WriteLine("Sidecar created in {0} seconds.", (chkEnd - chkStart).TotalSeconds);
                        dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                                          (double)blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000));

                        List <(ulong start, string type)> filesystems = new List <(ulong start, string type)>();
                        if (sidecar.BlockMedia[0].FileSystemInformation != null)
                        {
                            filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation
                                                 where partition.FileSystems != null
                                                 from fileSystem in partition.FileSystems
                                                 select((ulong)partition.StartSector, fileSystem.Type));
                        }

                        if (filesystems.Count > 0)
                        {
                            foreach (var filesystem in filesystems.Select(o => new { o.start, o.type }).Distinct())
                            {
                                dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type,
                                                  filesystem.start);
                            }
                        }

                        DicConsole.WriteLine();
                        string xmlDskTyp, xmlDskSubTyp;
                        if (dev.IsCompactFlash)
                        {
                            Metadata.MediaType.MediaTypeToString(MediaType.CompactFlash, out xmlDskTyp,
                                                                 out xmlDskSubTyp);
                        }
                        else if (dev.IsPcmcia)
                        {
                            Metadata.MediaType.MediaTypeToString(MediaType.PCCardTypeI, out xmlDskTyp,
                                                                 out xmlDskSubTyp);
                        }
                        else
                        {
                            Metadata.MediaType.MediaTypeToString(MediaType.GENERIC_HDD, out xmlDskTyp,
                                                                 out xmlDskSubTyp);
                        }
                        sidecar.BlockMedia[0].DiskType          = xmlDskTyp;
                        sidecar.BlockMedia[0].DiskSubType       = xmlDskSubTyp;
                        sidecar.BlockMedia[0].Interface         = "ATA";
                        sidecar.BlockMedia[0].LogicalBlocks     = (long)blocks;
                        sidecar.BlockMedia[0].PhysicalBlockSize = (int)physicalsectorsize;
                        sidecar.BlockMedia[0].LogicalBlockSize  = (int)blockSize;
                        sidecar.BlockMedia[0].Manufacturer      = dev.Manufacturer;
                        sidecar.BlockMedia[0].Model             = dev.Model;
                        sidecar.BlockMedia[0].Serial            = dev.Serial;
                        sidecar.BlockMedia[0].Size = (long)(blocks * blockSize);
                        if (cylinders > 0 && heads > 0 && sectors > 0)
                        {
                            sidecar.BlockMedia[0].Cylinders          = cylinders;
                            sidecar.BlockMedia[0].CylindersSpecified = true;
                            sidecar.BlockMedia[0].Heads                    = heads;
                            sidecar.BlockMedia[0].HeadsSpecified           = true;
                            sidecar.BlockMedia[0].SectorsPerTrack          = sectors;
                            sidecar.BlockMedia[0].SectorsPerTrackSpecified = true;
                        }

                        DicConsole.WriteLine("Writing metadata sidecar");

                        FileStream xmlFs = new FileStream(outputPrefix + ".cicm.xml", FileMode.Create);

                        XmlSerializer xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                        xmlSer.Serialize(xmlFs, sidecar);
                        xmlFs.Close();
                    }

                    DicConsole.WriteLine();

                    DicConsole
                    .WriteLine("Took a total of {0:F3} seconds ({1:F3} processing commands, {2:F3} checksumming, {3:F3} writing, {4:F3} closing).",
                               (end - start).TotalSeconds, totalDuration / 1000, totalChkDuration / 1000,
                               imageWriteDuration, (closeEnd - closeStart).TotalSeconds);
                    DicConsole.WriteLine("Avegare speed: {0:F3} MiB/sec.",
                                         (double)blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000));
                    DicConsole.WriteLine("Fastest speed burst: {0:F3} MiB/sec.", maxSpeed);
                    DicConsole.WriteLine("Slowest speed burst: {0:F3} MiB/sec.", minSpeed);
                    DicConsole.WriteLine("{0} sectors could not be read.", resume.BadBlocks.Count);
                    if (resume.BadBlocks.Count > 0)
                    {
                        resume.BadBlocks.Sort();
                    }
                    DicConsole.WriteLine();
                }

                if (dev.IsCompactFlash)
                {
                    Statistics.AddMedia(MediaType.CompactFlash, true);
                }
                else if (dev.IsPcmcia)
                {
                    Statistics.AddMedia(MediaType.PCCardTypeI, true);
                }
                else
                {
                    Statistics.AddMedia(MediaType.GENERIC_HDD, true);
                }
            }
            else
            {
                DicConsole.ErrorWriteLine("Unable to communicate with ATA device.");
            }
        }
    }
}
Ejemplo n.º 3
0
        /// <summary>
        ///     Fills a SCSI device report with parameters specific to an ATAPI device
        /// </summary>
        /// <param name="dev">Device</param>
        /// <param name="report">Device report</param>
        /// <param name="debug">If debug is enabled</param>
        internal static void Report(Device dev, ref DeviceReport report, bool debug)
        {
            if (report == null)
            {
                return;
            }

            const uint TIMEOUT = 5;

            DicConsole.WriteLine("Querying ATAPI IDENTIFY...");

            dev.AtapiIdentify(out byte[] buffer, out _, TIMEOUT, out _);

            if (!Identify.Decode(buffer).HasValue)
            {
                return;
            }

            Identify.IdentifyDevice?atapiIdNullable = Identify.Decode(buffer);
            if (atapiIdNullable != null)
            {
                Identify.IdentifyDevice atapiId = atapiIdNullable.Value;

                report.ATAPI = new ataType();

                if (!string.IsNullOrWhiteSpace(atapiId.AdditionalPID))
                {
                    report.ATAPI.AdditionalPID          = atapiId.AdditionalPID;
                    report.ATAPI.AdditionalPIDSpecified = true;
                }

                if (atapiId.APIOSupported != 0)
                {
                    report.ATAPI.APIOSupported          = atapiId.APIOSupported;
                    report.ATAPI.APIOSupportedSpecified = true;
                }

                if (atapiId.ATAPIByteCount != 0)
                {
                    report.ATAPI.ATAPIByteCount          = atapiId.ATAPIByteCount;
                    report.ATAPI.ATAPIByteCountSpecified = true;
                }

                if (atapiId.BufferType != 0)
                {
                    report.ATAPI.BufferType          = atapiId.BufferType;
                    report.ATAPI.BufferTypeSpecified = true;
                }

                if (atapiId.BufferSize != 0)
                {
                    report.ATAPI.BufferSize          = atapiId.BufferSize;
                    report.ATAPI.BufferSizeSpecified = true;
                }

                if (atapiId.Capabilities != 0)
                {
                    report.ATAPI.Capabilities          = atapiId.Capabilities;
                    report.ATAPI.CapabilitiesSpecified = true;
                }

                if (atapiId.Capabilities2 != 0)
                {
                    report.ATAPI.Capabilities2          = atapiId.Capabilities2;
                    report.ATAPI.Capabilities2Specified = true;
                }

                if (atapiId.Capabilities3 != 0)
                {
                    report.ATAPI.Capabilities3          = atapiId.Capabilities3;
                    report.ATAPI.Capabilities3Specified = true;
                }

                if (atapiId.CFAPowerMode != 0)
                {
                    report.ATAPI.CFAPowerMode          = atapiId.CFAPowerMode;
                    report.ATAPI.CFAPowerModeSpecified = true;
                }

                if (atapiId.CommandSet != 0)
                {
                    report.ATAPI.CommandSet          = atapiId.CommandSet;
                    report.ATAPI.CommandSetSpecified = true;
                }

                if (atapiId.CommandSet2 != 0)
                {
                    report.ATAPI.CommandSet2          = atapiId.CommandSet2;
                    report.ATAPI.CommandSet2Specified = true;
                }

                if (atapiId.CommandSet3 != 0)
                {
                    report.ATAPI.CommandSet3          = atapiId.CommandSet3;
                    report.ATAPI.CommandSet3Specified = true;
                }

                if (atapiId.CommandSet4 != 0)
                {
                    report.ATAPI.CommandSet4          = atapiId.CommandSet4;
                    report.ATAPI.CommandSet4Specified = true;
                }

                if (atapiId.CommandSet5 != 0)
                {
                    report.ATAPI.CommandSet5          = atapiId.CommandSet5;
                    report.ATAPI.CommandSet5Specified = true;
                }

                if (atapiId.CurrentAAM != 0)
                {
                    report.ATAPI.CurrentAAM          = atapiId.CurrentAAM;
                    report.ATAPI.CurrentAAMSpecified = true;
                }

                if (atapiId.CurrentAPM != 0)
                {
                    report.ATAPI.CurrentAPM          = atapiId.CurrentAPM;
                    report.ATAPI.CurrentAPMSpecified = true;
                }

                if (atapiId.DataSetMgmt != 0)
                {
                    report.ATAPI.DataSetMgmt          = atapiId.DataSetMgmt;
                    report.ATAPI.DataSetMgmtSpecified = true;
                }

                if (atapiId.DataSetMgmtSize != 0)
                {
                    report.ATAPI.DataSetMgmtSize          = atapiId.DataSetMgmtSize;
                    report.ATAPI.DataSetMgmtSizeSpecified = true;
                }

                if (atapiId.DeviceFormFactor != 0)
                {
                    report.ATAPI.DeviceFormFactor          = atapiId.DeviceFormFactor;
                    report.ATAPI.DeviceFormFactorSpecified = true;
                }

                if (atapiId.DMAActive != 0)
                {
                    report.ATAPI.DMAActive          = atapiId.DMAActive;
                    report.ATAPI.DMAActiveSpecified = true;
                }

                if (atapiId.DMASupported != 0)
                {
                    report.ATAPI.DMASupported          = atapiId.DMASupported;
                    report.ATAPI.DMASupportedSpecified = true;
                }

                if (atapiId.DMATransferTimingMode != 0)
                {
                    report.ATAPI.DMATransferTimingMode          = atapiId.DMATransferTimingMode;
                    report.ATAPI.DMATransferTimingModeSpecified = true;
                }

                if (atapiId.EnhancedSecurityEraseTime != 0)
                {
                    report.ATAPI.EnhancedSecurityEraseTime          = atapiId.EnhancedSecurityEraseTime;
                    report.ATAPI.EnhancedSecurityEraseTimeSpecified = true;
                }

                if (atapiId.EnabledCommandSet != 0)
                {
                    report.ATAPI.EnabledCommandSet          = atapiId.EnabledCommandSet;
                    report.ATAPI.EnabledCommandSetSpecified = true;
                }

                if (atapiId.EnabledCommandSet2 != 0)
                {
                    report.ATAPI.EnabledCommandSet2          = atapiId.EnabledCommandSet2;
                    report.ATAPI.EnabledCommandSet2Specified = true;
                }

                if (atapiId.EnabledCommandSet3 != 0)
                {
                    report.ATAPI.EnabledCommandSet3          = atapiId.EnabledCommandSet3;
                    report.ATAPI.EnabledCommandSet3Specified = true;
                }

                if (atapiId.EnabledCommandSet4 != 0)
                {
                    report.ATAPI.EnabledCommandSet4          = atapiId.EnabledCommandSet4;
                    report.ATAPI.EnabledCommandSet4Specified = true;
                }

                if (atapiId.EnabledSATAFeatures != 0)
                {
                    report.ATAPI.EnabledSATAFeatures          = atapiId.EnabledSATAFeatures;
                    report.ATAPI.EnabledSATAFeaturesSpecified = true;
                }

                if (atapiId.ExtendedUserSectors != 0)
                {
                    report.ATAPI.ExtendedUserSectors          = atapiId.ExtendedUserSectors;
                    report.ATAPI.ExtendedUserSectorsSpecified = true;
                }

                if (atapiId.FreeFallSensitivity != 0)
                {
                    report.ATAPI.FreeFallSensitivity          = atapiId.FreeFallSensitivity;
                    report.ATAPI.FreeFallSensitivitySpecified = true;
                }

                if (!string.IsNullOrWhiteSpace(atapiId.FirmwareRevision))
                {
                    report.ATAPI.FirmwareRevision          = atapiId.FirmwareRevision;
                    report.ATAPI.FirmwareRevisionSpecified = true;
                }

                if (atapiId.GeneralConfiguration != 0)
                {
                    report.ATAPI.GeneralConfiguration          = atapiId.GeneralConfiguration;
                    report.ATAPI.GeneralConfigurationSpecified = true;
                }

                if (atapiId.HardwareResetResult != 0)
                {
                    report.ATAPI.HardwareResetResult          = atapiId.HardwareResetResult;
                    report.ATAPI.HardwareResetResultSpecified = true;
                }

                if (atapiId.InterseekDelay != 0)
                {
                    report.ATAPI.InterseekDelay          = atapiId.InterseekDelay;
                    report.ATAPI.InterseekDelaySpecified = true;
                }

                if (atapiId.MajorVersion != 0)
                {
                    report.ATAPI.MajorVersion          = atapiId.MajorVersion;
                    report.ATAPI.MajorVersionSpecified = true;
                }

                if (atapiId.MasterPasswordRevisionCode != 0)
                {
                    report.ATAPI.MasterPasswordRevisionCode          = atapiId.MasterPasswordRevisionCode;
                    report.ATAPI.MasterPasswordRevisionCodeSpecified = true;
                }

                if (atapiId.MaxDownloadMicroMode3 != 0)
                {
                    report.ATAPI.MaxDownloadMicroMode3          = atapiId.MaxDownloadMicroMode3;
                    report.ATAPI.MaxDownloadMicroMode3Specified = true;
                }

                if (atapiId.MaxQueueDepth != 0)
                {
                    report.ATAPI.MaxQueueDepth          = atapiId.MaxQueueDepth;
                    report.ATAPI.MaxQueueDepthSpecified = true;
                }

                if (atapiId.MDMAActive != 0)
                {
                    report.ATAPI.MDMAActive          = atapiId.MDMAActive;
                    report.ATAPI.MDMAActiveSpecified = true;
                }

                if (atapiId.MDMASupported != 0)
                {
                    report.ATAPI.MDMASupported          = atapiId.MDMASupported;
                    report.ATAPI.MDMASupportedSpecified = true;
                }

                if (atapiId.MinDownloadMicroMode3 != 0)
                {
                    report.ATAPI.MinDownloadMicroMode3          = atapiId.MinDownloadMicroMode3;
                    report.ATAPI.MinDownloadMicroMode3Specified = true;
                }

                if (atapiId.MinMDMACycleTime != 0)
                {
                    report.ATAPI.MinMDMACycleTime          = atapiId.MinMDMACycleTime;
                    report.ATAPI.MinMDMACycleTimeSpecified = true;
                }

                if (atapiId.MinorVersion != 0)
                {
                    report.ATAPI.MinorVersion          = atapiId.MinorVersion;
                    report.ATAPI.MinorVersionSpecified = true;
                }

                if (atapiId.MinPIOCycleTimeNoFlow != 0)
                {
                    report.ATAPI.MinPIOCycleTimeNoFlow          = atapiId.MinPIOCycleTimeNoFlow;
                    report.ATAPI.MinPIOCycleTimeNoFlowSpecified = true;
                }

                if (atapiId.MinPIOCycleTimeFlow != 0)
                {
                    report.ATAPI.MinPIOCycleTimeFlow          = atapiId.MinPIOCycleTimeFlow;
                    report.ATAPI.MinPIOCycleTimeFlowSpecified = true;
                }

                if (!string.IsNullOrWhiteSpace(atapiId.Model))
                {
                    report.ATAPI.Model          = atapiId.Model;
                    report.ATAPI.ModelSpecified = true;
                }

                if (atapiId.MultipleMaxSectors != 0)
                {
                    report.ATAPI.MultipleMaxSectors          = atapiId.MultipleMaxSectors;
                    report.ATAPI.MultipleMaxSectorsSpecified = true;
                }

                if (atapiId.MultipleSectorNumber != 0)
                {
                    report.ATAPI.MultipleSectorNumber          = atapiId.MultipleSectorNumber;
                    report.ATAPI.MultipleSectorNumberSpecified = true;
                }

                if (atapiId.NVCacheCaps != 0)
                {
                    report.ATAPI.NVCacheCaps          = atapiId.NVCacheCaps;
                    report.ATAPI.NVCacheCapsSpecified = true;
                }

                if (atapiId.NVCacheSize != 0)
                {
                    report.ATAPI.NVCacheSize          = atapiId.NVCacheSize;
                    report.ATAPI.NVCacheSizeSpecified = true;
                }

                if (atapiId.NVCacheWriteSpeed != 0)
                {
                    report.ATAPI.NVCacheWriteSpeed          = atapiId.NVCacheWriteSpeed;
                    report.ATAPI.NVCacheWriteSpeedSpecified = true;
                }

                if (atapiId.NVEstimatedSpinUp != 0)
                {
                    report.ATAPI.NVEstimatedSpinUp          = atapiId.NVEstimatedSpinUp;
                    report.ATAPI.NVEstimatedSpinUpSpecified = true;
                }

                if (atapiId.PacketBusRelease != 0)
                {
                    report.ATAPI.PacketBusRelease          = atapiId.PacketBusRelease;
                    report.ATAPI.PacketBusReleaseSpecified = true;
                }

                if (atapiId.PIOTransferTimingMode != 0)
                {
                    report.ATAPI.PIOTransferTimingMode          = atapiId.PIOTransferTimingMode;
                    report.ATAPI.PIOTransferTimingModeSpecified = true;
                }

                if (atapiId.RecommendedAAM != 0)
                {
                    report.ATAPI.RecommendedAAM          = atapiId.RecommendedAAM;
                    report.ATAPI.RecommendedAAMSpecified = true;
                }

                if (atapiId.RecMDMACycleTime != 0)
                {
                    report.ATAPI.RecommendedMDMACycleTime          = atapiId.RecMDMACycleTime;
                    report.ATAPI.RecommendedMDMACycleTimeSpecified = true;
                }

                if (atapiId.RemovableStatusSet != 0)
                {
                    report.ATAPI.RemovableStatusSet          = atapiId.RemovableStatusSet;
                    report.ATAPI.RemovableStatusSetSpecified = true;
                }

                if (atapiId.SATACapabilities != 0)
                {
                    report.ATAPI.SATACapabilities          = atapiId.SATACapabilities;
                    report.ATAPI.SATACapabilitiesSpecified = true;
                }

                if (atapiId.SATACapabilities2 != 0)
                {
                    report.ATAPI.SATACapabilities2          = atapiId.SATACapabilities2;
                    report.ATAPI.SATACapabilities2Specified = true;
                }

                if (atapiId.SATAFeatures != 0)
                {
                    report.ATAPI.SATAFeatures          = atapiId.SATAFeatures;
                    report.ATAPI.SATAFeaturesSpecified = true;
                }

                if (atapiId.SCTCommandTransport != 0)
                {
                    report.ATAPI.SCTCommandTransport          = atapiId.SCTCommandTransport;
                    report.ATAPI.SCTCommandTransportSpecified = true;
                }

                if (atapiId.SectorsPerCard != 0)
                {
                    report.ATAPI.SectorsPerCard          = atapiId.SectorsPerCard;
                    report.ATAPI.SectorsPerCardSpecified = true;
                }

                if (atapiId.SecurityEraseTime != 0)
                {
                    report.ATAPI.SecurityEraseTime          = atapiId.SecurityEraseTime;
                    report.ATAPI.SecurityEraseTimeSpecified = true;
                }

                if (atapiId.SecurityStatus != 0)
                {
                    report.ATAPI.SecurityStatus          = atapiId.SecurityStatus;
                    report.ATAPI.SecurityStatusSpecified = true;
                }

                if (atapiId.ServiceBusyClear != 0)
                {
                    report.ATAPI.ServiceBusyClear          = atapiId.ServiceBusyClear;
                    report.ATAPI.ServiceBusyClearSpecified = true;
                }

                if (atapiId.SpecificConfiguration != 0)
                {
                    report.ATAPI.SpecificConfiguration          = atapiId.SpecificConfiguration;
                    report.ATAPI.SpecificConfigurationSpecified = true;
                }

                if (atapiId.StreamAccessLatency != 0)
                {
                    report.ATAPI.StreamAccessLatency          = atapiId.StreamAccessLatency;
                    report.ATAPI.StreamAccessLatencySpecified = true;
                }

                if (atapiId.StreamMinReqSize != 0)
                {
                    report.ATAPI.StreamMinReqSize          = atapiId.StreamMinReqSize;
                    report.ATAPI.StreamMinReqSizeSpecified = true;
                }

                if (atapiId.StreamPerformanceGranularity != 0)
                {
                    report.ATAPI.StreamPerformanceGranularity          = atapiId.StreamPerformanceGranularity;
                    report.ATAPI.StreamPerformanceGranularitySpecified = true;
                }

                if (atapiId.StreamTransferTimeDMA != 0)
                {
                    report.ATAPI.StreamTransferTimeDMA          = atapiId.StreamTransferTimeDMA;
                    report.ATAPI.StreamTransferTimeDMASpecified = true;
                }

                if (atapiId.StreamTransferTimePIO != 0)
                {
                    report.ATAPI.StreamTransferTimePIO          = atapiId.StreamTransferTimePIO;
                    report.ATAPI.StreamTransferTimePIOSpecified = true;
                }

                if (atapiId.TransportMajorVersion != 0)
                {
                    report.ATAPI.TransportMajorVersion          = atapiId.TransportMajorVersion;
                    report.ATAPI.TransportMajorVersionSpecified = true;
                }

                if (atapiId.TransportMinorVersion != 0)
                {
                    report.ATAPI.TransportMinorVersion          = atapiId.TransportMinorVersion;
                    report.ATAPI.TransportMinorVersionSpecified = true;
                }

                if (atapiId.TrustedComputing != 0)
                {
                    report.ATAPI.TrustedComputing          = atapiId.TrustedComputing;
                    report.ATAPI.TrustedComputingSpecified = true;
                }

                if (atapiId.UDMAActive != 0)
                {
                    report.ATAPI.UDMAActive          = atapiId.UDMAActive;
                    report.ATAPI.UDMAActiveSpecified = true;
                }

                if (atapiId.UDMASupported != 0)
                {
                    report.ATAPI.UDMASupported          = atapiId.UDMASupported;
                    report.ATAPI.UDMASupportedSpecified = true;
                }

                if (atapiId.WRVMode != 0)
                {
                    report.ATAPI.WRVMode          = atapiId.WRVMode;
                    report.ATAPI.WRVModeSpecified = true;
                }

                if (atapiId.WRVSectorCountMode3 != 0)
                {
                    report.ATAPI.WRVSectorCountMode3          = atapiId.WRVSectorCountMode3;
                    report.ATAPI.WRVSectorCountMode3Specified = true;
                }

                if (atapiId.WRVSectorCountMode2 != 0)
                {
                    report.ATAPI.WRVSectorCountMode2          = atapiId.WRVSectorCountMode2;
                    report.ATAPI.WRVSectorCountMode2Specified = true;
                }
            }

            if (debug)
            {
                report.ATAPI.Identify = buffer;
            }
        }
Ejemplo n.º 4
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.º 5
0
        /// <summary>Creates a report of an ATA device</summary>
        public TestedMedia ReportAta(Identify.IdentifyDevice ataId)
        {
            var capabilities = new TestedMedia();

            if (ataId.UnformattedBPT != 0)
            {
                capabilities.UnformattedBPT = ataId.UnformattedBPT;
            }

            if (ataId.UnformattedBPS != 0)
            {
                capabilities.UnformattedBPS = ataId.UnformattedBPS;
            }

            if (ataId.Cylinders > 0 &&
                ataId.Heads > 0 &&
                ataId.SectorsPerTrack > 0)
            {
                capabilities.CHS = new Chs
                {
                    Cylinders = ataId.Cylinders, Heads = ataId.Heads, Sectors = ataId.SectorsPerTrack
                };

                capabilities.Blocks = (ulong)(ataId.Cylinders * ataId.Heads * ataId.SectorsPerTrack);
            }

            if (ataId.CurrentCylinders > 0 &&
                ataId.CurrentHeads > 0 &&
                ataId.CurrentSectorsPerTrack > 0)
            {
                capabilities.CurrentCHS = new Chs
                {
                    Cylinders = ataId.CurrentCylinders, Heads = ataId.CurrentHeads,
                    Sectors   = ataId.CurrentSectorsPerTrack
                };

                capabilities.Blocks =
                    (ulong)(ataId.CurrentCylinders * ataId.CurrentHeads * ataId.CurrentSectorsPerTrack);
            }

            if (ataId.Capabilities.HasFlag(Identify.CapabilitiesBit.LBASupport))
            {
                capabilities.LBASectors = ataId.LBASectors;
                capabilities.Blocks     = ataId.LBASectors;
            }

            if (ataId.CommandSet2.HasFlag(Identify.CommandSetBit2.LBA48))
            {
                capabilities.LBA48Sectors = ataId.LBA48Sectors;
                capabilities.Blocks       = ataId.LBA48Sectors;
            }

            if (ataId.NominalRotationRate != 0x0000 &&
                ataId.NominalRotationRate != 0xFFFF)
            {
                if (ataId.NominalRotationRate == 0x0001)
                {
                    capabilities.SolidStateDevice = true;
                }
                else
                {
                    capabilities.SolidStateDevice    = false;
                    capabilities.NominalRotationRate = ataId.NominalRotationRate;
                }
            }

            uint logicalSectorSize;
            uint physicalSectorSize;

            if ((ataId.PhysLogSectorSize & 0x8000) == 0x0000 &&
                (ataId.PhysLogSectorSize & 0x4000) == 0x4000)
            {
                if ((ataId.PhysLogSectorSize & 0x1000) == 0x1000)
                {
                    if (ataId.LogicalSectorWords <= 255 ||
                        ataId.LogicalAlignment == 0xFFFF)
                    {
                        logicalSectorSize = 512;
                    }
                    else
                    {
                        logicalSectorSize = ataId.LogicalSectorWords * 2;
                    }
                }
                else
                {
                    logicalSectorSize = 512;
                }

                if ((ataId.PhysLogSectorSize & 0x2000) == 0x2000)
                {
                    physicalSectorSize = logicalSectorSize * (uint)Math.Pow(2, ataId.PhysLogSectorSize & 0xF);
                }
                else
                {
                    physicalSectorSize = logicalSectorSize;
                }
            }
            else
            {
                logicalSectorSize  = 512;
                physicalSectorSize = 512;
            }

            capabilities.BlockSize = logicalSectorSize;

            if (physicalSectorSize != logicalSectorSize)
            {
                capabilities.PhysicalBlockSize = physicalSectorSize;

                if ((ataId.LogicalAlignment & 0x8000) == 0x0000 &&
                    (ataId.LogicalAlignment & 0x4000) == 0x4000)
                {
                    capabilities.LogicalAlignment = (ushort)(ataId.LogicalAlignment & 0x3FFF);
                }
            }

            if (ataId.EccBytes != 0x0000 &&
                ataId.EccBytes != 0xFFFF)
            {
                capabilities.LongBlockSize = logicalSectorSize + ataId.EccBytes;
            }

            if (ataId.UnformattedBPS > logicalSectorSize &&
                (!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || capabilities.LongBlockSize == 516))
            {
                capabilities.LongBlockSize = ataId.UnformattedBPS;
            }

            if (ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeSet) &&
                !ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeClear) &&
                ataId.EnabledCommandSet3.HasFlag(Identify.CommandSetBit3.MediaSerial))
            {
                capabilities.CanReadMediaSerial = true;

                if (!string.IsNullOrWhiteSpace(ataId.MediaManufacturer))
                {
                    capabilities.Manufacturer = ataId.MediaManufacturer;
                }
            }

            ulong checkCorrectRead = 0;
            bool  sense;

            DicConsole.WriteLine("Trying READ SECTOR(S) in CHS mode...");

            sense = _dev.Read(out byte[] readBuf, out AtaErrorRegistersChs errorChs, false, 0, 0, 1, 1, _dev.Timeout,
                              out _);

            capabilities.SupportsReadSectors = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
                                               readBuf.Length > 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorChs.Status, errorChs.Error, readBuf.Length);

            capabilities.ReadSectorsData = readBuf;

            DicConsole.WriteLine("Trying READ SECTOR(S) RETRY in CHS mode...");
            sense = _dev.Read(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _);

            capabilities.SupportsReadRetry =
                !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && readBuf.Length > 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorChs.Status, errorChs.Error, readBuf.Length);

            capabilities.ReadSectorsRetryData = readBuf;

            DicConsole.WriteLine("Trying READ DMA in CHS mode...");
            sense = _dev.ReadDma(out readBuf, out errorChs, false, 0, 0, 1, 1, _dev.Timeout, out _);

            capabilities.SupportsReadDma =
                !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && readBuf.Length > 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorChs.Status, errorChs.Error, readBuf.Length);

            capabilities.ReadDmaData = readBuf;

            DicConsole.WriteLine("Trying READ DMA RETRY in CHS mode...");
            sense = _dev.ReadDma(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _);

            capabilities.SupportsReadDmaRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
                                                readBuf.Length > 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorChs.Status, errorChs.Error, readBuf.Length);

            capabilities.ReadDmaRetryData = readBuf;

            DicConsole.WriteLine("Trying SEEK in CHS mode...");
            sense = _dev.Seek(out errorChs, 0, 0, 1, _dev.Timeout, out _);
            capabilities.SupportsSeek = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense,
                                      errorChs.Status, errorChs.Error);

            DicConsole.WriteLine("Trying READ SECTOR(S) in LBA mode...");
            sense = _dev.Read(out readBuf, out AtaErrorRegistersLba28 errorLba, false, 0, 1, _dev.Timeout, out _);

            capabilities.SupportsReadLba =
                !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && readBuf.Length > 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorLba.Status, errorLba.Error, readBuf.Length);

            capabilities.ReadLbaData = readBuf;

            DicConsole.WriteLine("Trying READ SECTOR(S) RETRY in LBA mode...");
            sense = _dev.Read(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _);

            capabilities.SupportsReadRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
                                                readBuf.Length > 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorLba.Status, errorLba.Error, readBuf.Length);

            capabilities.ReadRetryLbaData = readBuf;

            DicConsole.WriteLine("Trying READ DMA in LBA mode...");
            sense = _dev.ReadDma(out readBuf, out errorLba, false, 0, 1, _dev.Timeout, out _);

            capabilities.SupportsReadDmaLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
                                              readBuf.Length > 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorLba.Status, errorLba.Error, readBuf.Length);

            capabilities.ReadDmaLbaData = readBuf;

            DicConsole.WriteLine("Trying READ DMA RETRY in LBA mode...");
            sense = _dev.ReadDma(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _);

            capabilities.SupportsReadDmaRetryLba =
                !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && readBuf.Length > 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorLba.Status, errorLba.Error, readBuf.Length);

            capabilities.ReadDmaRetryLbaData = readBuf;

            DicConsole.WriteLine("Trying SEEK in LBA mode...");
            sense = _dev.Seek(out errorLba, 0, _dev.Timeout, out _);
            capabilities.SupportsSeekLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense,
                                      errorLba.Status, errorLba.Error);

            DicConsole.WriteLine("Trying READ SECTOR(S) in LBA48 mode...");
            sense = _dev.Read(out readBuf, out AtaErrorRegistersLba48 errorLba48, 0, 1, _dev.Timeout, out _);

            capabilities.SupportsReadLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 &&
                                             readBuf.Length > 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorLba48.Status, errorLba48.Error, readBuf.Length);

            capabilities.ReadLba48Data = readBuf;

            DicConsole.WriteLine("Trying READ DMA in LBA48 mode...");
            sense = _dev.ReadDma(out readBuf, out errorLba48, 0, 1, _dev.Timeout, out _);

            capabilities.SupportsReadDmaLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 &&
                                                readBuf.Length > 0;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorLba48.Status, errorLba48.Error, readBuf.Length);

            capabilities.ReadDmaLba48Data = readBuf;

            // Send SET FEATURES before sending READ LONG commands, retrieve IDENTIFY again and
            // check if ECC size changed. Sector is set to 1 because without it most drives just return
            // CORRECTABLE ERROR for this command.
            _dev.SetFeatures(out _, AtaFeatures.EnableReadLongVendorLength, 0, 0, 1, 0, _dev.Timeout, out _);

            _dev.AtaIdentify(out byte[] buffer, out _, _dev.Timeout, out _);

            if (Identify.Decode(buffer).HasValue)
            {
                ataId = Identify.Decode(buffer).Value;

                if (ataId.EccBytes != 0x0000 &&
                    ataId.EccBytes != 0xFFFF)
                {
                    capabilities.LongBlockSize = logicalSectorSize + ataId.EccBytes;
                }

                if (ataId.UnformattedBPS > logicalSectorSize &&
                    (!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || capabilities.LongBlockSize == 516))
                {
                    capabilities.LongBlockSize = ataId.UnformattedBPS;
                }
            }

            DicConsole.WriteLine("Trying READ LONG in CHS mode...");

            sense = _dev.ReadLong(out readBuf, out errorChs, false, 0, 0, 1, capabilities.LongBlockSize ?? 0,
                                  _dev.Timeout, out _);

            capabilities.SupportsReadLong = !sense && (errorChs.Status & 0x01) != 0x01 &&
                                            errorChs.Error == 0 &&
                                            readBuf.Length > 0 &&
                                            BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorChs.Status, errorChs.Error, readBuf.Length);

            capabilities.ReadLongData = readBuf;

            DicConsole.WriteLine("Trying READ LONG RETRY in CHS mode...");

            sense = _dev.ReadLong(out readBuf, out errorChs, true, 0, 0, 1, capabilities.LongBlockSize ?? 0,
                                  _dev.Timeout, out _);

            capabilities.SupportsReadLongRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 &&
                                                 readBuf.Length > 0 &&
                                                 BitConverter.ToUInt64(readBuf, 0) !=
                                                 checkCorrectRead;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorChs.Status, errorChs.Error, readBuf.Length);

            capabilities.ReadLongRetryData = readBuf;

            DicConsole.WriteLine("Trying READ LONG in LBA mode...");

            sense = _dev.ReadLong(out readBuf, out errorLba, false, 0, capabilities.LongBlockSize ?? 0, _dev.Timeout,
                                  out _);

            capabilities.SupportsReadLongLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
                                               readBuf.Length > 0 &&
                                               BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorLba.Status, errorLba.Error, readBuf.Length);

            capabilities.ReadLongLbaData = readBuf;

            DicConsole.WriteLine("Trying READ LONG RETRY in LBA mode...");

            sense = _dev.ReadLong(out readBuf, out errorLba, true, 0, capabilities.LongBlockSize ?? 0, _dev.Timeout,
                                  out _);

            capabilities.SupportsReadLongRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 &&
                                                    readBuf.Length > 0 &&
                                                    BitConverter.ToUInt64(readBuf, 0) !=
                                                    checkCorrectRead;

            DicConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}",
                                      sense, errorLba.Status, errorLba.Error, readBuf.Length);

            capabilities.ReadLongRetryLbaData = readBuf;

            return(capabilities);
        }
Ejemplo n.º 6
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.StartsWith("aaru://"))
            {
                if (devicePath.StartsWith("dic://"))
                {
                    devicePath = devicePath.Substring(6);
                }
                else
                {
                    devicePath = devicePath.Substring(7);
                }

                string[] pieces = devicePath.Split('/');
                string   host   = pieces[0];
                devicePath = devicePath.Substring(host.Length);

                _remote = new Remote.Remote(host);

                Error     = !_remote.Open(devicePath, out int 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
                              DeviceException("Parallel ATA devices are not supported on FreeBSD due to upstream bug #224250.");
                    }

                    break;
                }

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

            if (Error)
            {
                throw new DeviceException(LastError);
            }

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

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

            if (Error)
            {
                throw new DeviceException(LastError);
            }

            bool 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];

                    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)
                    {
                        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:
                            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);
                        }

                        sdBuffer = new byte[4];

                        LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle,
                                                                   cachedScr != null
                                                                           ? (MmcCommands)SecureDigitalCommands.
                                                                   SendOperatingCondition
                                                                           : 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) ||
                        devicePath.StartsWith("/dev/sg", 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;
                }
            }
            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;
                    CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(cachedCid);
                    Manufacturer = VendorString.Prettify(decoded.Manufacturer);
                    Model        = decoded.ProductName;

                    FirmwareRevision =
                        $"{(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;

                    FirmwareRevision =
                        $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}";

                    Serial = $"{decoded.ProductSerialNumber}";
                }

                return;
            }
            #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))
                    {
                        string devPath = devicePath.Substring(5);

                        if (Directory.Exists("/sys/block/" + devPath))
                        {
                            string 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);

                                    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();

                                    var    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,
                        Usb.GuidDevinterfaceTape
                    })
                    {
                        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 byte[] remoteUsbDescriptors, out ushort remoteUsbVendor,
                                       out ushort remoteUsbProduct, out string remoteUsbManufacturer,
                                       out string remoteUsbProductString, out string 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 string remoteFireWireVendorName, out string remoteFireWireModelName))
                {
                    IsFireWire         = true;
                    FireWireVendorName = remoteFireWireVendorName;
                    FireWireModelName  = remoteFireWireModelName;
                }
            }
Ejemplo n.º 7
0
        /// <summary>
        ///     Gets a list of all known storage devices on FreeBSD
        /// </summary>
        /// <returns>List of devices</returns>
        internal static DeviceInfo[] GetList()
        {
            string[]          passDevices = Directory.GetFiles("/dev/", "pass*", SearchOption.TopDirectoryOnly);
            List <DeviceInfo> listDevices = new List <DeviceInfo>();

            foreach (string passDevice in passDevices)
            {
                DeviceInfo deviceInfo = new DeviceInfo();
                IntPtr     dev        = cam_open_device(passDevice, FileFlags.ReadWrite);
                CamDevice  camDevice  = (CamDevice)Marshal.PtrToStructure(dev, typeof(CamDevice));

                IntPtr ccbPtr = cam_getccb(dev);

                if (ccbPtr.ToInt64() == 0)
                {
                    continue;
                }

                CcbGetdev cgd = (CcbGetdev)Marshal.PtrToStructure(ccbPtr, typeof(CcbGetdev));

                cgd.ccb_h.func_code = XptOpcode.XptGdevType;

                Marshal.StructureToPtr(cgd, ccbPtr, false);

                int error = cam_send_ccb(dev, ccbPtr);

                if (error < 0)
                {
                    cam_freeccb(ccbPtr);
                    continue;
                }

                cgd = (CcbGetdev)Marshal.PtrToStructure(ccbPtr, typeof(CcbGetdev));

                cam_freeccb(ccbPtr);
                cam_close_device(dev);

                string simName = StringHandlers.CToString(camDevice.SimName);
                deviceInfo.Path = passDevice;
                byte[] serialNumber = new byte[camDevice.SerialNumLen];
                Array.Copy(camDevice.SerialNum, 0, serialNumber, 0, serialNumber.Length);
                deviceInfo.Serial = StringHandlers.CToString(serialNumber);

                switch (cgd.protocol)
                {
                case CamProto.ProtoAta:
                case CamProto.ProtoAtapi:
                case CamProto.ProtoSatapm:
                {
                    // Little-endian FreeBSD gives it resorted
                    // Big-endian FreeBSD, no idea
                    byte[] atadTneid = new byte[512];
                    for (int aIndex = 0; aIndex < 512; aIndex += 2)
                    {
                        atadTneid[aIndex]     = cgd.ident_data[aIndex + 1];
                        atadTneid[aIndex + 1] = cgd.ident_data[aIndex];
                    }

                    Identify.IdentifyDevice?idt = Identify.Decode(atadTneid);
                    if (idt.HasValue)
                    {
                        string[] separated = idt.Value.Model.Split(' ');

                        if (separated.Length == 1)
                        {
                            deviceInfo.Vendor = "ATA";
                            deviceInfo.Model  = separated[0];
                        }
                        else
                        {
                            deviceInfo.Vendor = separated[0];
                            deviceInfo.Model  = separated[separated.Length - 1];
                        }

                        deviceInfo.Serial    = idt.Value.SerialNumber;
                        deviceInfo.Bus       = simName == "ahcich" ? "SATA" : "ATA";
                        deviceInfo.Supported = simName != "ata";
                    }

                    if (cgd.protocol == CamProto.ProtoAtapi)
                    {
                        goto case CamProto.ProtoScsi;
                    }
                    break;
                }

                case CamProto.ProtoScsi:
                {
                    Inquiry.SCSIInquiry?inq = Inquiry.Decode(cgd.inq_data);
                    if (inq.HasValue)
                    {
                        deviceInfo.Vendor    = StringHandlers.CToString(inq.Value.VendorIdentification).Trim();
                        deviceInfo.Model     = StringHandlers.CToString(inq.Value.ProductIdentification).Trim();
                        deviceInfo.Bus       = simName == "ata" || simName == "ahcich" ? "ATAPI" : "SCSI";
                        deviceInfo.Supported = simName != "ata";
                    }

                    break;
                }

                case CamProto.ProtoNvme:
                    deviceInfo.Bus       = "NVMe";
                    deviceInfo.Supported = false;
                    break;

                case CamProto.ProtoMmcsd:
                    deviceInfo.Model     = "Unknown card";
                    deviceInfo.Bus       = "MMC/SD";
                    deviceInfo.Supported = false;
                    break;
                }

                listDevices.Add(deviceInfo);
            }

            return(listDevices.Count > 0 ? listDevices.OrderBy(t => t.Path).ToArray() : null);
        }
Ejemplo n.º 8
0
        /// <summary>Scans the media from an ATA device</summary>
        /// <returns>Scanning results</returns>
        ScanResults Ata()
        {
            var  results = new ScanResults();
            bool sense;

            results.Blocks = 0;
            const ushort ATA_PROFILE = 0x0001;
            const uint   TIMEOUT     = 5;

            sense = dev.AtaIdentify(out byte[] cmdBuf, out _);

            if (!sense &&
                Identify.Decode(cmdBuf).HasValue)
            {
                // Initializate reader
                var ataReader = new Reader(dev, TIMEOUT, cmdBuf);

                // Fill reader blocks
                results.Blocks = ataReader.GetDeviceBlocks();

                if (ataReader.FindReadCommand())
                {
                    StoppingErrorMessage?.Invoke(ataReader.ErrorMessage);

                    return(results);
                }

                // Check block sizes
                if (ataReader.GetBlockSize())
                {
                    StoppingErrorMessage?.Invoke(ataReader.ErrorMessage);

                    return(results);
                }

                uint blockSize = ataReader.LogicalBlockSize;

                // Check how many blocks to read, if error show and return
                if (ataReader.GetBlocksToRead())
                {
                    StoppingErrorMessage?.Invoke(ataReader.ErrorMessage);

                    return(results);
                }

                uint   blocksToRead = ataReader.BlocksToRead;
                ushort cylinders    = ataReader.Cylinders;
                byte   heads        = ataReader.Heads;
                byte   sectors      = ataReader.Sectors;

                results.A       = 0; // <3ms
                results.B       = 0; // >=3ms, <10ms
                results.C       = 0; // >=10ms, <50ms
                results.D       = 0; // >=50ms, <150ms
                results.E       = 0; // >=150ms, <500ms
                results.F       = 0; // >=500ms
                results.Errored = 0;
                DateTime start;
                DateTime end;
                results.ProcessingTime = 0;
                double currentSpeed = 0;
                results.MaxSpeed          = double.MinValue;
                results.MinSpeed          = double.MaxValue;
                results.UnreadableSectors = new List <ulong>();
                results.SeekMax           = double.MinValue;
                results.SeekMin           = double.MaxValue;
                results.SeekTotal         = 0;
                const int SEEK_TIMES = 1000;

                double seekCur;

                var rnd = new Random();

                MhddLog mhddLog;
                IbgLog  ibgLog;
                double  duration;

                if (ataReader.IsLba)
                {
                    UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time.");

                    InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, ATA_PROFILE);
                    mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                    ibgLog  = new IbgLog(ibgLogPath, ATA_PROFILE);

                    start = DateTime.UtcNow;
                    DateTime timeSpeedStart   = DateTime.UtcNow;
                    ulong    sectorSpeedStart = 0;
                    InitProgress?.Invoke();

                    for (ulong i = 0; i < results.Blocks; i += blocksToRead)
                    {
                        if (aborted)
                        {
                            break;
                        }

                        if (results.Blocks - i < blocksToRead)
                        {
                            blocksToRead = (byte)(results.Blocks - i);
                        }

                        #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                        if (currentSpeed > results.MaxSpeed &&
                            currentSpeed != 0)
                        {
                            results.MaxSpeed = currentSpeed;
                        }

                        if (currentSpeed < results.MinSpeed &&
                            currentSpeed != 0)
                        {
                            results.MinSpeed = currentSpeed;
                        }
                        #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                        UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)",
                                               (long)i, (long)results.Blocks);

                        bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration);

                        if (!error)
                        {
                            if (duration >= 500)
                            {
                                results.F += blocksToRead;
                            }
                            else if (duration >= 150)
                            {
                                results.E += blocksToRead;
                            }
                            else if (duration >= 50)
                            {
                                results.D += blocksToRead;
                            }
                            else if (duration >= 10)
                            {
                                results.C += blocksToRead;
                            }
                            else if (duration >= 3)
                            {
                                results.B += blocksToRead;
                            }
                            else
                            {
                                results.A += blocksToRead;
                            }

                            ScanTime?.Invoke(i, duration);
                            mhddLog.Write(i, duration);
                            ibgLog.Write(i, currentSpeed * 1024);
                        }
                        else
                        {
                            ScanUnreadable?.Invoke(i);
                            results.Errored += blocksToRead;

                            for (ulong b = i; b < i + blocksToRead; b++)
                            {
                                results.UnreadableSectors.Add(b);
                            }

                            mhddLog.Write(i, duration < 500 ? 65535 : duration);

                            ibgLog.Write(i, 0);
                        }

                        sectorSpeedStart += blocksToRead;

                        double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;

                        if (elapsed < 1)
                        {
                            continue;
                        }

                        currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                        ScanSpeed?.Invoke(i, currentSpeed * 1024);
                        sectorSpeedStart = 0;
                        timeSpeedStart   = DateTime.UtcNow;
                    }

                    end = DateTime.UtcNow;
                    EndProgress?.Invoke();
                    mhddLog.Close();

                    ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                 (blockSize * (double)(results.Blocks + 1)) / 1024 /
                                 (results.ProcessingTime / 1000),
                                 devicePath);

                    InitProgress?.Invoke();

                    if (ataReader.CanSeekLba)
                    {
                        for (int i = 0; i < SEEK_TIMES; i++)
                        {
                            if (aborted)
                            {
                                break;
                            }

                            uint seekPos = (uint)rnd.Next((int)results.Blocks);

                            PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t");

                            ataReader.Seek(seekPos, out seekCur);

                            #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                            if (seekCur > results.SeekMax &&
                                seekCur != 0)
                            {
                                results.SeekMax = seekCur;
                            }

                            if (seekCur < results.SeekMin &&
                                seekCur != 0)
                            {
                                results.SeekMin = seekCur;
                            }
                            #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                            results.SeekTotal += seekCur;
                            GC.Collect();
                        }
                    }

                    EndProgress?.Invoke();
                }
                else
                {
                    InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, ATA_PROFILE);
                    mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                    ibgLog  = new IbgLog(ibgLogPath, ATA_PROFILE);

                    ulong currentBlock = 0;
                    results.Blocks = (ulong)(cylinders * heads * sectors);
                    start          = DateTime.UtcNow;
                    DateTime timeSpeedStart   = DateTime.UtcNow;
                    ulong    sectorSpeedStart = 0;
                    InitProgress?.Invoke();

                    for (ushort cy = 0; cy < cylinders; cy++)
                    {
                        for (byte hd = 0; hd < heads; hd++)
                        {
                            for (byte sc = 1; sc < sectors; sc++)
                            {
                                if (aborted)
                                {
                                    break;
                                }

                                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                                if (currentSpeed > results.MaxSpeed &&
                                    currentSpeed != 0)
                                {
                                    results.MaxSpeed = currentSpeed;
                                }

                                if (currentSpeed < results.MinSpeed &&
                                    currentSpeed != 0)
                                {
                                    results.MinSpeed = currentSpeed;
                                }
                                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                                PulseProgress?.
                                Invoke($"Reading cylinder {cy} head {hd} sector {sc} ({currentSpeed:F3} MiB/sec.)");

                                bool error = ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration);

                                if (!error)
                                {
                                    if (duration >= 500)
                                    {
                                        results.F += blocksToRead;
                                    }
                                    else if (duration >= 150)
                                    {
                                        results.E += blocksToRead;
                                    }
                                    else if (duration >= 50)
                                    {
                                        results.D += blocksToRead;
                                    }
                                    else if (duration >= 10)
                                    {
                                        results.C += blocksToRead;
                                    }
                                    else if (duration >= 3)
                                    {
                                        results.B += blocksToRead;
                                    }
                                    else
                                    {
                                        results.A += blocksToRead;
                                    }

                                    ScanTime?.Invoke(currentBlock, duration);
                                    mhddLog.Write(currentBlock, duration);
                                    ibgLog.Write(currentBlock, currentSpeed * 1024);
                                }
                                else
                                {
                                    ScanUnreadable?.Invoke(currentBlock);
                                    results.Errored += blocksToRead;
                                    results.UnreadableSectors.Add(currentBlock);
                                    mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration);

                                    ibgLog.Write(currentBlock, 0);
                                }

                                sectorSpeedStart++;
                                currentBlock++;

                                double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;

                                if (elapsed < 1)
                                {
                                    continue;
                                }

                                currentSpeed = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                                ScanSpeed?.Invoke(currentBlock, currentSpeed * 1024);
                                sectorSpeedStart = 0;
                                timeSpeedStart   = DateTime.UtcNow;
                            }
                        }
                    }

                    end = DateTime.UtcNow;
                    EndProgress?.Invoke();
                    mhddLog.Close();

                    ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                 (blockSize * (double)(results.Blocks + 1)) / 1024 /
                                 (results.ProcessingTime / 1000),
                                 devicePath);

                    InitProgress?.Invoke();

                    if (ataReader.CanSeek)
                    {
                        for (int i = 0; i < SEEK_TIMES; i++)
                        {
                            if (aborted)
                            {
                                break;
                            }

                            ushort seekCy = (ushort)rnd.Next(cylinders);
                            byte   seekHd = (byte)rnd.Next(heads);
                            byte   seekSc = (byte)rnd.Next(sectors);

                            PulseProgress?.
                            Invoke($"\rSeeking to cylinder {seekCy}, head {seekHd}, sector {seekSc}...\t\t");

                            ataReader.SeekChs(seekCy, seekHd, seekSc, out seekCur);

                            #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                            if (seekCur > results.SeekMax &&
                                seekCur != 0)
                            {
                                results.SeekMax = seekCur;
                            }

                            if (seekCur < results.SeekMin &&
                                seekCur != 0)
                            {
                                results.SeekMin = seekCur;
                            }
                            #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                            results.SeekTotal += seekCur;
                            GC.Collect();
                        }
                    }

                    EndProgress?.Invoke();
                }

                results.ProcessingTime /= 1000;
                results.TotalTime       = (end - start).TotalSeconds;
                results.AvgSpeed        = (blockSize * (double)(results.Blocks + 1)) / 1048576 / results.ProcessingTime;
                results.SeekTimes       = SEEK_TIMES;

                return(results);
            }

            StoppingErrorMessage?.Invoke("Unable to communicate with ATA device.");

            return(results);
        }
Ejemplo n.º 9
0
        /// <summary>
        ///     Scans the media from an ATA device
        /// </summary>
        /// <param name="mhddLogPath">Path to a MHDD log file</param>
        /// <param name="ibgLogPath">Path to a IMGBurn log file</param>
        /// <param name="devicePath">Device path</param>
        /// <param name="dev">Device</param>
        /// <returns>Scanning results</returns>
        public static ScanResults Scan(string mhddLogPath, string ibgLogPath, string devicePath, Device dev)
        {
            ScanResults results = new ScanResults();
            bool        aborted;
            bool        sense;

            results.Blocks = 0;
            const ushort ATA_PROFILE = 0x0001;
            const uint   TIMEOUT     = 5;

            sense = dev.AtaIdentify(out byte[] cmdBuf, out _);
            if (!sense && Identify.Decode(cmdBuf).HasValue)
            {
                // Initializate reader
                Reader ataReader = new Reader(dev, TIMEOUT, cmdBuf);
                // Fill reader blocks
                results.Blocks = ataReader.GetDeviceBlocks();
                if (ataReader.FindReadCommand())
                {
                    DicConsole.ErrorWriteLine(ataReader.ErrorMessage);
                    return(results);
                }

                // Check block sizes
                if (ataReader.GetBlockSize())
                {
                    DicConsole.ErrorWriteLine(ataReader.ErrorMessage);
                    return(results);
                }

                uint blockSize = ataReader.LogicalBlockSize;
                // Check how many blocks to read, if error show and return
                if (ataReader.GetBlocksToRead())
                {
                    DicConsole.ErrorWriteLine(ataReader.ErrorMessage);
                    return(results);
                }

                uint   blocksToRead = ataReader.BlocksToRead;
                ushort cylinders    = ataReader.Cylinders;
                byte   heads        = ataReader.Heads;
                byte   sectors      = ataReader.Sectors;

                results.A       = 0; // <3ms
                results.B       = 0; // >=3ms, <10ms
                results.C       = 0; // >=10ms, <50ms
                results.D       = 0; // >=50ms, <150ms
                results.E       = 0; // >=150ms, <500ms
                results.F       = 0; // >=500ms
                results.Errored = 0;
                DateTime start;
                DateTime end;
                results.ProcessingTime = 0;
                double currentSpeed = 0;
                results.MaxSpeed          = double.MinValue;
                results.MinSpeed          = double.MaxValue;
                results.UnreadableSectors = new List <ulong>();
                results.SeekMax           = double.MinValue;
                results.SeekMin           = double.MaxValue;
                results.SeekTotal         = 0;
                const int SEEK_TIMES = 1000;

                double seekCur;

                Random rnd = new Random();

                aborted = false;
                System.Console.CancelKeyPress += (sender, e) => e.Cancel = aborted = true;

                MhddLog mhddLog;
                IbgLog  ibgLog;
                double  duration;
                if (ataReader.IsLba)
                {
                    DicConsole.WriteLine("Reading {0} sectors at a time.", blocksToRead);

                    mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                    ibgLog  = new IbgLog(ibgLogPath, ATA_PROFILE);

                    start = DateTime.UtcNow;
                    for (ulong i = 0; i < results.Blocks; i += blocksToRead)
                    {
                        if (aborted)
                        {
                            break;
                        }

                        if (results.Blocks - i < blocksToRead)
                        {
                            blocksToRead = (byte)(results.Blocks - i);
                        }

                        #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                        if (currentSpeed > results.MaxSpeed && currentSpeed != 0)
                        {
                            results.MaxSpeed = currentSpeed;
                        }
                        if (currentSpeed < results.MinSpeed && currentSpeed != 0)
                        {
                            results.MinSpeed = currentSpeed;
                        }
                        #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                        DicConsole.Write("\rReading sector {0} of {1} ({2:F3} MiB/sec.)", i, results.Blocks,
                                         currentSpeed);

                        bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration);

                        if (!error)
                        {
                            if (duration >= 500)
                            {
                                results.F += blocksToRead;
                            }
                            else if (duration >= 150)
                            {
                                results.E += blocksToRead;
                            }
                            else if (duration >= 50)
                            {
                                results.D += blocksToRead;
                            }
                            else if (duration >= 10)
                            {
                                results.C += blocksToRead;
                            }
                            else if (duration >= 3)
                            {
                                results.B += blocksToRead;
                            }
                            else
                            {
                                results.A += blocksToRead;
                            }

                            mhddLog.Write(i, duration);
                            ibgLog.Write(i, currentSpeed * 1024);
                        }
                        else
                        {
                            results.Errored += blocksToRead;
                            for (ulong b = i; b < i + blocksToRead; b++)
                            {
                                results.UnreadableSectors.Add(b);
                            }

                            mhddLog.Write(i, duration < 500 ? 65535 : duration);

                            ibgLog.Write(i, 0);
                        }

                        double newSpeed =
                            (double)blockSize * blocksToRead / 1048576 / (duration / 1000);
                        if (!double.IsInfinity(newSpeed))
                        {
                            currentSpeed = newSpeed;
                        }
                    }

                    end = DateTime.UtcNow;
                    DicConsole.WriteLine();
                    mhddLog.Close();
                    ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                 blockSize * (double)(results.Blocks + 1) / 1024 /
                                 (results.ProcessingTime / 1000),
                                 devicePath);

                    if (ataReader.CanSeekLba)
                    {
                        for (int i = 0; i < SEEK_TIMES; i++)
                        {
                            if (aborted)
                            {
                                break;
                            }

                            uint seekPos = (uint)rnd.Next((int)results.Blocks);

                            DicConsole.Write("\rSeeking to sector {0}...\t\t", seekPos);

                            ataReader.Seek(seekPos, out seekCur);

                            #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                            if (seekCur > results.SeekMax && seekCur != 0)
                            {
                                results.SeekMax = seekCur;
                            }
                            if (seekCur < results.SeekMin && seekCur != 0)
                            {
                                results.SeekMin = seekCur;
                            }
                            #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                            results.SeekTotal += seekCur;
                            GC.Collect();
                        }
                    }
                }
                else
                {
                    mhddLog = new MhddLog(mhddLogPath, dev, results.Blocks, blockSize, blocksToRead);
                    ibgLog  = new IbgLog(ibgLogPath, ATA_PROFILE);

                    ulong currentBlock = 0;
                    results.Blocks = (ulong)(cylinders * heads * sectors);
                    start          = DateTime.UtcNow;
                    for (ushort cy = 0; cy < cylinders; cy++)
                    {
                        for (byte hd = 0; hd < heads; hd++)
                        {
                            for (byte sc = 1; sc < sectors; sc++)
                            {
                                if (aborted)
                                {
                                    break;
                                }

                                #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                                if (currentSpeed > results.MaxSpeed && currentSpeed != 0)
                                {
                                    results.MaxSpeed = currentSpeed;
                                }
                                if (currentSpeed < results.MinSpeed && currentSpeed != 0)
                                {
                                    results.MinSpeed = currentSpeed;
                                }
                                #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                                DicConsole.Write("\rReading cylinder {0} head {1} sector {2} ({3:F3} MiB/sec.)", cy, hd,
                                                 sc, currentSpeed);

                                bool error = ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration);

                                if (!error)
                                {
                                    if (duration >= 500)
                                    {
                                        results.F += blocksToRead;
                                    }
                                    else if (duration >= 150)
                                    {
                                        results.E += blocksToRead;
                                    }
                                    else if (duration >= 50)
                                    {
                                        results.D += blocksToRead;
                                    }
                                    else if (duration >= 10)
                                    {
                                        results.C += blocksToRead;
                                    }
                                    else if (duration >= 3)
                                    {
                                        results.B += blocksToRead;
                                    }
                                    else
                                    {
                                        results.A += blocksToRead;
                                    }

                                    mhddLog.Write(currentBlock, duration);
                                    ibgLog.Write(currentBlock, currentSpeed * 1024);
                                }
                                else
                                {
                                    results.Errored += blocksToRead;
                                    results.UnreadableSectors.Add(currentBlock);
                                    mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration);

                                    ibgLog.Write(currentBlock, 0);
                                }

                                double newSpeed =
                                    blockSize / (double)1048576 / (duration / 1000);
                                if (!double.IsInfinity(newSpeed))
                                {
                                    currentSpeed = newSpeed;
                                }

                                currentBlock++;
                            }
                        }
                    }

                    end = DateTime.UtcNow;
                    DicConsole.WriteLine();
                    mhddLog.Close();
                    ibgLog.Close(dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                 blockSize * (double)(results.Blocks + 1) / 1024 /
                                 (results.ProcessingTime / 1000),
                                 devicePath);

                    if (ataReader.CanSeek)
                    {
                        for (int i = 0; i < SEEK_TIMES; i++)
                        {
                            if (aborted)
                            {
                                break;
                            }

                            ushort seekCy = (ushort)rnd.Next(cylinders);
                            byte   seekHd = (byte)rnd.Next(heads);
                            byte   seekSc = (byte)rnd.Next(sectors);

                            DicConsole.Write("\rSeeking to cylinder {0}, head {1}, sector {2}...\t\t", seekCy, seekHd,
                                             seekSc);

                            ataReader.SeekChs(seekCy, seekHd, seekSc, out seekCur);

                            #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                            if (seekCur > results.SeekMax && seekCur != 0)
                            {
                                results.SeekMax = seekCur;
                            }
                            if (seekCur < results.SeekMin && seekCur != 0)
                            {
                                results.SeekMin = seekCur;
                            }
                            #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                            results.SeekTotal += seekCur;
                            GC.Collect();
                        }
                    }
                }

                DicConsole.WriteLine();

                results.ProcessingTime /= 1000;
                results.TotalTime       = (end - start).TotalSeconds;
                results.AvgSpeed        = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime;
                results.SeekTimes       = SEEK_TIMES;

                return(results);
            }

            DicConsole.ErrorWriteLine("Unable to communicate with ATA device.");
            return(results);
        }
Ejemplo n.º 10
0
        internal static DeviceInfo[] GetList()
        {
            string[]          passDevices = Directory.GetFiles("/dev/", "pass*", SearchOption.TopDirectoryOnly);
            List <DeviceInfo> listDevices = new List <DeviceInfo>();

            foreach (string passDevice in passDevices)
            {
                var    deviceInfo = new DeviceInfo();
                IntPtr dev        = cam_open_device(passDevice, FileFlags.ReadWrite);
                var    camDevice  = (CamDevice)Marshal.PtrToStructure(dev, typeof(CamDevice));

                IntPtr ccbPtr = cam_getccb(dev);

                if (ccbPtr.ToInt64() == 0)
                {
                    continue;
                }

                var cgd = (CcbGetdev)Marshal.PtrToStructure(ccbPtr, typeof(CcbGetdev));

                cgd.ccb_h.func_code = XptOpcode.XptGdevType;

                Marshal.StructureToPtr(cgd, ccbPtr, false);

                int error = cam_send_ccb(dev, ccbPtr);

                if (error < 0)
                {
                    cam_freeccb(ccbPtr);

                    continue;
                }

                cgd = (CcbGetdev)Marshal.PtrToStructure(ccbPtr, typeof(CcbGetdev));

                cam_freeccb(ccbPtr);
                cam_close_device(dev);

                string simName = StringHandlers.CToString(camDevice.SimName);
                deviceInfo.Path = passDevice;
                byte[] serialNumber = new byte[camDevice.SerialNumLen];
                Array.Copy(camDevice.SerialNum, 0, serialNumber, 0, serialNumber.Length);
                deviceInfo.Serial = StringHandlers.CToString(serialNumber);

                switch (cgd.protocol)
                {
                case CamProto.ProtoAta:
                case CamProto.ProtoAtapi:
                case CamProto.ProtoSatapm:
                {
                    // Little-endian FreeBSD gives it resorted
                    // Big-endian FreeBSD, no idea
                    byte[] atadTneid = new byte[512];

                    for (int aIndex = 0; aIndex < 512; aIndex += 2)
                    {
                        atadTneid[aIndex]     = cgd.ident_data[aIndex + 1];
                        atadTneid[aIndex + 1] = cgd.ident_data[aIndex];
                    }

                    Identify.IdentifyDevice?idt = Identify.Decode(atadTneid);

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

                        if (separated.Length == 1)
                        {
                            deviceInfo.Vendor = "ATA";
                            deviceInfo.Model  = separated[0];
                        }
                        else
                        {
                            deviceInfo.Vendor = separated[0];
                            deviceInfo.Model  = separated[^ 1];
Ejemplo n.º 11
0
        /// <summary>Dumps an ATA device</summary>
        public void Ata()
        {
            if (_dumpRaw)
            {
                if (_force)
                {
                    ErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, continuing...");
                }
                else
                {
                    StoppingErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, aborting...");

                    return;
                }
            }

            const ushort ATA_PROFILE        = 0x0001;
            const uint   TIMEOUT            = 5;
            double       imageWriteDuration = 0;
            MediaType    mediaType          = MediaType.Unknown;

            UpdateStatus?.Invoke("Requesting ATA IDENTIFY DEVICE.");
            _dumpLog.WriteLine("Requesting ATA IDENTIFY DEVICE.");
            bool sense = _dev.AtaIdentify(out byte[] cmdBuf, out _);

            if (!sense &&
                Identify.Decode(cmdBuf).HasValue)
            {
                Identify.IdentifyDevice?ataIdNullable = Identify.Decode(cmdBuf);

                if (ataIdNullable != null)
                {
                    Identify.IdentifyDevice ataId = ataIdNullable.Value;
                    byte[] ataIdentify            = cmdBuf;
                    cmdBuf = new byte[0];

                    DateTime start;
                    DateTime end;
                    double   totalDuration = 0;
                    double   currentSpeed  = 0;
                    double   maxSpeed      = double.MinValue;
                    double   minSpeed      = double.MaxValue;

                    // Initializate reader
                    UpdateStatus?.Invoke("Initializing reader.");
                    _dumpLog.WriteLine("Initializing reader.");
                    var ataReader = new Reader(_dev, TIMEOUT, ataIdentify);

                    // Fill reader blocks
                    ulong blocks = ataReader.GetDeviceBlocks();

                    // Check block sizes
                    if (ataReader.GetBlockSize())
                    {
                        _dumpLog.WriteLine("ERROR: Cannot get block size: {0}.", ataReader.ErrorMessage);
                        ErrorMessage(ataReader.ErrorMessage);

                        return;
                    }

                    uint blockSize          = ataReader.LogicalBlockSize;
                    uint physicalsectorsize = ataReader.PhysicalBlockSize;

                    if (ataReader.FindReadCommand())
                    {
                        _dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", ataReader.ErrorMessage);
                        ErrorMessage(ataReader.ErrorMessage);

                        return;
                    }

                    // Check how many blocks to read, if error show and return
                    if (ataReader.GetBlocksToRead(_maximumReadable))
                    {
                        _dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", ataReader.ErrorMessage);
                        ErrorMessage(ataReader.ErrorMessage);

                        return;
                    }

                    uint   blocksToRead = ataReader.BlocksToRead;
                    ushort cylinders    = ataReader.Cylinders;
                    byte   heads        = ataReader.Heads;
                    byte   sectors      = ataReader.Sectors;

                    UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes).");

                    UpdateStatus?.
                    Invoke($"Device reports {cylinders} cylinders {heads} heads {sectors} sectors per track.");

                    UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time.");
                    UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block.");
                    UpdateStatus?.Invoke($"Device reports {physicalsectorsize} bytes per physical block.");
                    _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize);

                    _dumpLog.WriteLine("Device reports {0} cylinders {1} heads {2} sectors per track.", cylinders,
                                       heads, sectors);

                    _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead);
                    _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize);
                    _dumpLog.WriteLine("Device reports {0} bytes per physical block.", physicalsectorsize);

                    bool removable = !_dev.IsCompactFlash &&
                                     ataId.GeneralConfiguration.HasFlag(Identify.GeneralConfigurationBit.Removable);

                    DumpHardwareType currentTry = null;
                    ExtentsULong     extents    = null;

                    ResumeSupport.Process(ataReader.IsLba, removable, blocks, _dev.Manufacturer, _dev.Model,
                                          _dev.Serial, _dev.PlatformId, ref _resume, ref currentTry, ref extents,
                                          _dev.FirmwareRevision, _private);

                    if (currentTry == null ||
                        extents == null)
                    {
                        StoppingErrorMessage?.Invoke("Could not process resume file, not continuing...");

                        return;
                    }

                    MhddLog mhddLog;
                    IbgLog  ibgLog;
                    double  duration;

                    bool ret = true;

                    if (_dev.IsUsb &&
                        _dev.UsbDescriptors != null &&
                        !_outputPlugin.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors))
                    {
                        ret = false;
                        _dumpLog.WriteLine("Output format does not support USB descriptors.");
                        ErrorMessage("Output format does not support USB descriptors.");
                    }

                    if (_dev.IsPcmcia &&
                        _dev.Cis != null &&
                        !_outputPlugin.SupportedMediaTags.Contains(MediaTagType.PCMCIA_CIS))
                    {
                        ret = false;
                        _dumpLog.WriteLine("Output format does not support PCMCIA CIS descriptors.");
                        ErrorMessage("Output format does not support PCMCIA CIS descriptors.");
                    }

                    if (!_outputPlugin.SupportedMediaTags.Contains(MediaTagType.ATA_IDENTIFY))
                    {
                        ret = false;
                        _dumpLog.WriteLine("Output format does not support ATA IDENTIFY.");
                        ErrorMessage("Output format does not support ATA IDENTIFY.");
                    }

                    if (!ret)
                    {
                        _dumpLog.WriteLine("Several media tags not supported, {0}continuing...", _force ? "" : "not ");

                        if (_force)
                        {
                            ErrorMessage("Several media tags not supported, continuing...");
                        }
                        else
                        {
                            StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing...");

                            return;
                        }
                    }

                    mediaType = MediaTypeFromDevice.GetFromAta(_dev.Manufacturer, _dev.Model, _dev.IsRemovable,
                                                               _dev.IsCompactFlash, _dev.IsPcmcia, blocks, blockSize);

                    ret = _outputPlugin.Create(_outputPath, mediaType, _formatOptions, blocks, blockSize);

                    // Cannot create image
                    if (!ret)
                    {
                        _dumpLog.WriteLine("Error creating output image, not continuing.");
                        _dumpLog.WriteLine(_outputPlugin.ErrorMessage);

                        StoppingErrorMessage?.Invoke("Error creating output image, not continuing." +
                                                     Environment.NewLine + _outputPlugin.ErrorMessage);

                        return;
                    }

                    // Setting geometry
                    _outputPlugin.SetGeometry(cylinders, heads, sectors);

                    if (ataReader.IsLba)
                    {
                        UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time.");

                        if (_skip < blocksToRead)
                        {
                            _skip = blocksToRead;
                        }

                        mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead,
                                              _private);

                        ibgLog = new IbgLog(_outputPrefix + ".ibg", ATA_PROFILE);

                        if (_resume.NextBlock > 0)
                        {
                            UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}.");
                            _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock);
                        }

                        bool newTrim = false;

                        start = DateTime.UtcNow;
                        DateTime timeSpeedStart   = DateTime.UtcNow;
                        ulong    sectorSpeedStart = 0;
                        InitProgress?.Invoke();

                        for (ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
                        {
                            if (_aborted)
                            {
                                currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                UpdateStatus?.Invoke("Aborted!");
                                _dumpLog.WriteLine("Aborted!");

                                break;
                            }

                            if (blocks - i < blocksToRead)
                            {
                                blocksToRead = (byte)(blocks - i);
                            }

                            #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                            if (currentSpeed > maxSpeed &&
                                currentSpeed != 0)
                            {
                                maxSpeed = currentSpeed;
                            }

                            if (currentSpeed < minSpeed &&
                                currentSpeed != 0)
                            {
                                minSpeed = currentSpeed;
                            }
                            #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                            UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)",
                                                   (long)i, (long)blocks);

                            bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration);

                            if (!error)
                            {
                                mhddLog.Write(i, duration);
                                ibgLog.Write(i, currentSpeed * 1024);
                                DateTime writeStart = DateTime.Now;
                                _outputPlugin.WriteSectors(cmdBuf, i, blocksToRead);
                                imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                extents.Add(i, blocksToRead, true);
                            }
                            else
                            {
                                if (i + _skip > blocks)
                                {
                                    _skip = (uint)(blocks - i);
                                }

                                for (ulong b = i; b < i + _skip; b++)
                                {
                                    _resume.BadBlocks.Add(b);
                                }

                                mhddLog.Write(i, duration < 500 ? 65535 : duration);

                                ibgLog.Write(i, 0);
                                DateTime writeStart = DateTime.Now;
                                _outputPlugin.WriteSectors(new byte[blockSize * _skip], i, _skip);
                                imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i);
                                i      += _skip - blocksToRead;
                                newTrim = true;
                            }

                            sectorSpeedStart += blocksToRead;
                            _resume.NextBlock = i + blocksToRead;

                            double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;

                            if (elapsed < 1)
                            {
                                continue;
                            }

                            currentSpeed     = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                            sectorSpeedStart = 0;
                            timeSpeedStart   = DateTime.UtcNow;
                        }

                        end = DateTime.Now;
                        EndProgress?.Invoke();
                        mhddLog.Close();

                        ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                     (blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000), _devicePath);

                        UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");

                        UpdateStatus?.
                        Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");

                        UpdateStatus?.
                        Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration:F3} KiB/sec.");

                        _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);

                        _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                                           ((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));

                        _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
                                           ((double)blockSize * (double)(blocks + 1)) / 1024 / imageWriteDuration);

                        #region Trimming
                        if (_resume.BadBlocks.Count > 0 &&
                            !_aborted &&
                            _trim &&
                            newTrim)
                        {
                            start = DateTime.UtcNow;
                            UpdateStatus?.Invoke("Trimming skipped sectors");
                            _dumpLog.WriteLine("Trimming skipped sectors");

                            ulong[] tmpArray = _resume.BadBlocks.ToArray();
                            InitProgress?.Invoke();

                            foreach (ulong badSector in tmpArray)
                            {
                                if (_aborted)
                                {
                                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                    UpdateStatus?.Invoke("Aborted!");
                                    _dumpLog.WriteLine("Aborted!");

                                    break;
                                }

                                PulseProgress?.Invoke($"Trimming sector {badSector}");

                                bool error = ataReader.ReadBlock(out cmdBuf, badSector, out duration);

                                totalDuration += duration;

                                if (error)
                                {
                                    continue;
                                }

                                _resume.BadBlocks.Remove(badSector);
                                extents.Add(badSector);
                                _outputPlugin.WriteSector(cmdBuf, badSector);
                            }

                            EndProgress?.Invoke();
                            end = DateTime.UtcNow;
                            UpdateStatus?.Invoke($"Trimmming finished in {(end - start).TotalSeconds} seconds.");
                            _dumpLog.WriteLine("Trimmming finished in {0} seconds.", (end - start).TotalSeconds);
                        }
                        #endregion Trimming

                        #region Error handling
                        if (_resume.BadBlocks.Count > 0 &&
                            !_aborted &&
                            _retryPasses > 0)
                        {
                            int  pass    = 1;
                            bool forward = true;

                            InitProgress?.Invoke();
repeatRetryLba:
                            ulong[] tmpArray = _resume.BadBlocks.ToArray();

                            foreach (ulong badSector in tmpArray)
                            {
                                if (_aborted)
                                {
                                    currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                    UpdateStatus?.Invoke("Aborted!");
                                    _dumpLog.WriteLine("Aborted!");

                                    break;
                                }

                                PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector,
                                                                    pass, forward ? "forward" : "reverse",
                                                                    _persistent ? "recovering partial data, " : ""));

                                bool error = ataReader.ReadBlock(out cmdBuf, badSector, out duration);

                                totalDuration += duration;

                                if (!error)
                                {
                                    _resume.BadBlocks.Remove(badSector);
                                    extents.Add(badSector);
                                    _outputPlugin.WriteSector(cmdBuf, badSector);
                                    UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}.");
                                    _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
                                }
                                else if (_persistent)
                                {
                                    _outputPlugin.WriteSector(cmdBuf, badSector);
                                }
                            }

                            if (pass < _retryPasses &&
                                !_aborted &&
                                _resume.BadBlocks.Count > 0)
                            {
                                pass++;
                                forward = !forward;
                                _resume.BadBlocks.Sort();

                                if (!forward)
                                {
                                    _resume.BadBlocks.Reverse();
                                }

                                goto repeatRetryLba;
                            }

                            EndProgress?.Invoke();
                        }
                        #endregion Error handling LBA

                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                    }
                    else
                    {
                        mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead,
                                              _private);

                        ibgLog = new IbgLog(_outputPrefix + ".ibg", ATA_PROFILE);

                        ulong currentBlock = 0;
                        blocks = (ulong)(cylinders * heads * sectors);
                        start  = DateTime.UtcNow;
                        DateTime timeSpeedStart   = DateTime.UtcNow;
                        ulong    sectorSpeedStart = 0;
                        InitProgress?.Invoke();

                        for (ushort cy = 0; cy < cylinders; cy++)
                        {
                            for (byte hd = 0; hd < heads; hd++)
                            {
                                for (byte sc = 1; sc < sectors; sc++)
                                {
                                    if (_aborted)
                                    {
                                        currentTry.Extents = ExtentsConverter.ToMetadata(extents);
                                        UpdateStatus?.Invoke("Aborted!");
                                        _dumpLog.WriteLine("Aborted!");

                                        break;
                                    }

                                    #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
                                    if (currentSpeed > maxSpeed &&
                                        currentSpeed != 0)
                                    {
                                        maxSpeed = currentSpeed;
                                    }

                                    if (currentSpeed < minSpeed &&
                                        currentSpeed != 0)
                                    {
                                        minSpeed = currentSpeed;
                                    }
                                    #pragma warning restore RECS0018 // Comparison of floating point numbers with equality operator

                                    PulseProgress?.
                                    Invoke($"Reading cylinder {cy} head {hd} sector {sc} ({currentSpeed:F3} MiB/sec.)");

                                    bool error = ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration);

                                    totalDuration += duration;

                                    if (!error)
                                    {
                                        mhddLog.Write(currentBlock, duration);
                                        ibgLog.Write(currentBlock, currentSpeed * 1024);
                                        DateTime writeStart = DateTime.Now;

                                        _outputPlugin.WriteSector(cmdBuf,
                                                                  (ulong)((((cy * heads) + hd) * sectors) + (sc - 1)));

                                        imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                        extents.Add(currentBlock);

                                        _dumpLog.WriteLine("Error reading cylinder {0} head {1} sector {2}.", cy, hd,
                                                           sc);
                                    }
                                    else
                                    {
                                        _resume.BadBlocks.Add(currentBlock);
                                        mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration);

                                        ibgLog.Write(currentBlock, 0);
                                        DateTime writeStart = DateTime.Now;

                                        _outputPlugin.WriteSector(new byte[blockSize],
                                                                  (ulong)((((cy * heads) + hd) * sectors) + (sc - 1)));

                                        imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
                                    }

                                    sectorSpeedStart++;
                                    currentBlock++;

                                    double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds;

                                    if (elapsed < 1)
                                    {
                                        continue;
                                    }

                                    currentSpeed     = (sectorSpeedStart * blockSize) / (1048576 * elapsed);
                                    sectorSpeedStart = 0;
                                    timeSpeedStart   = DateTime.UtcNow;
                                }
                            }
                        }

                        end = DateTime.Now;
                        EndProgress?.Invoke();
                        mhddLog.Close();

                        ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
                                     (blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000), _devicePath);

                        UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds.");

                        UpdateStatus?.
                        Invoke($"Average dump speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000):F3} KiB/sec.");

                        UpdateStatus?.
                        Invoke($"Average write speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (imageWriteDuration / 1000):F3} KiB/sec.");

                        _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);

                        _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
                                           ((double)blockSize * (double)(blocks + 1)) / 1024 / (totalDuration / 1000));

                        _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
                                           ((double)blockSize * (double)(blocks + 1)) / 1024 /
                                           (imageWriteDuration / 1000));
                    }

                    foreach (ulong bad in _resume.BadBlocks)
                    {
                        _dumpLog.WriteLine("Sector {0} could not be read.", bad);
                    }

                    _outputPlugin.SetDumpHardware(_resume.Tries);

                    // TODO: Non-removable
                    var metadata = new CommonTypes.Structs.ImageInfo
                    {
                        Application = "Aaru", ApplicationVersion = Version.GetVersion()
                    };

                    if (!_outputPlugin.SetMetadata(metadata))
                    {
                        ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine +
                                             _outputPlugin.ErrorMessage);
                    }

                    if (_preSidecar != null)
                    {
                        _outputPlugin.SetCicmMetadata(_preSidecar);
                    }

                    _dumpLog.WriteLine("Closing output file.");
                    UpdateStatus?.Invoke("Closing output file.");
                    DateTime closeStart = DateTime.Now;
                    _outputPlugin.Close();
                    DateTime closeEnd = DateTime.Now;
                    UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds.");
                    _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds);

                    if (_aborted)
                    {
                        _dumpLog.WriteLine("Aborted!");
                        UpdateStatus?.Invoke("Aborted!");

                        return;
                    }

                    double totalChkDuration = 0;

                    if (_metadata)
                    {
                        _dumpLog.WriteLine("Creating sidecar.");
                        UpdateStatus?.Invoke("Creating sidecar.");
                        var         filters     = new FiltersList();
                        IFilter     filter      = filters.GetFilter(_outputPath);
                        IMediaImage inputPlugin = ImageFormat.Detect(filter);

                        if (!inputPlugin.Open(filter))
                        {
                            StoppingErrorMessage?.Invoke("Could not open created image.");

                            return;
                        }

                        DateTime chkStart = DateTime.UtcNow;

                        _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding);

                        _sidecarClass.InitProgressEvent    += InitProgress;
                        _sidecarClass.UpdateProgressEvent  += UpdateProgress;
                        _sidecarClass.EndProgressEvent     += EndProgress;
                        _sidecarClass.InitProgressEvent2   += InitProgress2;
                        _sidecarClass.UpdateProgressEvent2 += UpdateProgress2;
                        _sidecarClass.EndProgressEvent2    += EndProgress2;
                        _sidecarClass.UpdateStatusEvent    += UpdateStatus;
                        CICMMetadataType sidecar = _sidecarClass.Create();

                        if (_preSidecar != null)
                        {
                            _preSidecar.BlockMedia = sidecar.BlockMedia;
                            sidecar = _preSidecar;
                        }

                        if (_dev.IsUsb &&
                            _dev.UsbDescriptors != null)
                        {
                            _dumpLog.WriteLine("Reading USB descriptors.");
                            UpdateStatus?.Invoke("Reading USB descriptors.");
                            ret = _outputPlugin.WriteMediaTag(_dev.UsbDescriptors, MediaTagType.USB_Descriptors);

                            if (ret)
                            {
                                sidecar.BlockMedia[0].USB = new USBType
                                {
                                    ProductID = _dev.UsbProductId, VendorID = _dev.UsbVendorId, Descriptors =
                                        new DumpType
                                    {
                                        Image     = _outputPath, Size = (ulong)_dev.UsbDescriptors.Length,
                                        Checksums = Checksum.GetChecksums(_dev.UsbDescriptors).ToArray()
                                    }
                                }
                            }
                            ;
                        }

                        if (_dev.IsPcmcia &&
                            _dev.Cis != null)
                        {
                            _dumpLog.WriteLine("Reading PCMCIA CIS.");
                            UpdateStatus?.Invoke("Reading PCMCIA CIS.");
                            ret = _outputPlugin.WriteMediaTag(_dev.Cis, MediaTagType.PCMCIA_CIS);

                            if (ret)
                            {
                                sidecar.BlockMedia[0].PCMCIA = new PCMCIAType
                                {
                                    CIS = new DumpType
                                    {
                                        Image     = _outputPath, Size = (ulong)_dev.Cis.Length,
                                        Checksums = Checksum.GetChecksums(_dev.Cis).ToArray()
                                    }
                                }
                            }
                            ;

                            _dumpLog.WriteLine("Decoding PCMCIA CIS.");
                            UpdateStatus?.Invoke("Decoding PCMCIA CIS.");
                            Tuple[] tuples = CIS.GetTuples(_dev.Cis);

                            if (tuples != null)
                            {
                                foreach (Tuple tuple in tuples)
                                {
                                    switch (tuple.Code)
                                    {
                                    case TupleCodes.CISTPL_MANFID:
                                        ManufacturerIdentificationTuple manfid =
                                            CIS.DecodeManufacturerIdentificationTuple(tuple);

                                        if (manfid != null)
                                        {
                                            sidecar.BlockMedia[0].PCMCIA.ManufacturerCode = manfid.ManufacturerID;

                                            sidecar.BlockMedia[0].PCMCIA.CardCode = manfid.CardID;
                                            sidecar.BlockMedia[0].PCMCIA.ManufacturerCodeSpecified = true;
                                            sidecar.BlockMedia[0].PCMCIA.CardCodeSpecified         = true;
                                        }

                                        break;

                                    case TupleCodes.CISTPL_VERS_1:
                                        Level1VersionTuple vers = CIS.DecodeLevel1VersionTuple(tuple);

                                        if (vers != null)
                                        {
                                            sidecar.BlockMedia[0].PCMCIA.Manufacturer = vers.Manufacturer;
                                            sidecar.BlockMedia[0].PCMCIA.ProductName  = vers.Product;

                                            sidecar.BlockMedia[0].PCMCIA.Compliance =
                                                $"{vers.MajorVersion}.{vers.MinorVersion}";

                                            sidecar.BlockMedia[0].PCMCIA.AdditionalInformation =
                                                vers.AdditionalInformation;
                                        }

                                        break;
                                    }
                                }
                            }
                        }

                        if (!_private)
                        {
                            DeviceReport.ClearIdentify(ataIdentify);
                        }

                        ret = _outputPlugin.WriteMediaTag(ataIdentify, MediaTagType.ATA_IDENTIFY);

                        if (ret)
                        {
                            sidecar.BlockMedia[0].ATA = new ATAType
                            {
                                Identify = new DumpType
                                {
                                    Image     = _outputPath, Size = (ulong)cmdBuf.Length,
                                    Checksums = Checksum.GetChecksums(cmdBuf).ToArray()
                                }
                            }
                        }
                        ;

                        DateTime chkEnd = DateTime.UtcNow;

                        totalChkDuration = (chkEnd - chkStart).TotalMilliseconds;
                        UpdateStatus?.Invoke($"Sidecar created in {(chkEnd - chkStart).TotalSeconds} seconds.");

                        UpdateStatus?.
                        Invoke($"Average checksum speed {((double)blockSize * (double)(blocks + 1)) / 1024 / (totalChkDuration / 1000):F3} KiB/sec.");

                        _dumpLog.WriteLine("Sidecar created in {0} seconds.", (chkEnd - chkStart).TotalSeconds);

                        _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.",
                                           ((double)blockSize * (double)(blocks + 1)) / 1024 /
                                           (totalChkDuration / 1000));

                        List <(ulong start, string type)> filesystems = new List <(ulong start, string type)>();

                        if (sidecar.BlockMedia[0].FileSystemInformation != null)
                        {
                            filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation
                                                 where partition.FileSystems != null
                                                 from fileSystem in partition.FileSystems
                                                 select(partition.StartSector, fileSystem.Type));
                        }

                        if (filesystems.Count > 0)
                        {
                            foreach (var filesystem in filesystems.Select(o => new
                            {
                                o.start, o.type
                            }).Distinct())
                            {
                                UpdateStatus?.
                                Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}");

                                _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type,
                                                   filesystem.start);
                            }
                        }

                        (string type, string subType) = CommonTypes.Metadata.MediaType.MediaTypeToString(mediaType);

                        sidecar.BlockMedia[0].DiskType          = type;
                        sidecar.BlockMedia[0].DiskSubType       = subType;
                        sidecar.BlockMedia[0].Interface         = "ATA";
                        sidecar.BlockMedia[0].LogicalBlocks     = blocks;
                        sidecar.BlockMedia[0].PhysicalBlockSize = physicalsectorsize;
                        sidecar.BlockMedia[0].LogicalBlockSize  = blockSize;
                        sidecar.BlockMedia[0].Manufacturer      = _dev.Manufacturer;
                        sidecar.BlockMedia[0].Model             = _dev.Model;

                        if (!_private)
                        {
                            sidecar.BlockMedia[0].Serial = _dev.Serial;
                        }

                        sidecar.BlockMedia[0].Size = blocks * blockSize;

                        if (cylinders > 0 &&
                            heads > 0 &&
                            sectors > 0)
                        {
                            sidecar.BlockMedia[0].Cylinders          = cylinders;
                            sidecar.BlockMedia[0].CylindersSpecified = true;
                            sidecar.BlockMedia[0].Heads                    = heads;
                            sidecar.BlockMedia[0].HeadsSpecified           = true;
                            sidecar.BlockMedia[0].SectorsPerTrack          = sectors;
                            sidecar.BlockMedia[0].SectorsPerTrackSpecified = true;
                        }

                        UpdateStatus?.Invoke("Writing metadata sidecar");

                        var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create);

                        var xmlSer = new XmlSerializer(typeof(CICMMetadataType));
                        xmlSer.Serialize(xmlFs, sidecar);
                        xmlFs.Close();
                    }

                    UpdateStatus?.Invoke("");

                    UpdateStatus?.
                    Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing).");

                    UpdateStatus?.
                    Invoke($"Average speed: {((double)blockSize * (double)(blocks + 1)) / 1048576 / (totalDuration / 1000):F3} MiB/sec.");

                    if (maxSpeed > 0)
                    {
                        UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
                    }

                    if (minSpeed > 0 &&
                        minSpeed < double.MaxValue)
                    {
                        UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec.");
                    }

                    UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read.");

                    if (_resume.BadBlocks.Count > 0)
                    {
                        _resume.BadBlocks.Sort();
                    }

                    UpdateStatus?.Invoke("");
                }

                Statistics.AddMedia(mediaType, true);
            }
            else
            {
                StoppingErrorMessage?.Invoke("Unable to communicate with ATA device.");
            }
        }
    }
}