Esempio n. 1
0
        public TestedMedia ReportAtaMedia()
        {
            var mediaTest = new TestedMedia();

            DicConsole.Write("Please write a description of the media type and press enter: ");
            mediaTest.MediumTypeName = System.Console.ReadLine();
            DicConsole.Write("Please write the media model and press enter: ");
            mediaTest.Model = System.Console.ReadLine();

            mediaTest.MediaIsRecognized = true;

            DicConsole.WriteLine("Querying ATA IDENTIFY...");
            _dev.AtaIdentify(out byte[] buffer, out _, _dev.Timeout, out _);

            mediaTest.IdentifyData   = ClearIdentify(buffer);
            mediaTest.IdentifyDevice = Identify.Decode(buffer);

            if (mediaTest.IdentifyDevice.HasValue)
            {
                Identify.IdentifyDevice ataId = mediaTest.IdentifyDevice.Value;

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

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

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

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

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

                    if (mediaTest.Blocks == 0)
                    {
                        mediaTest.Blocks =
                            (ulong)(ataId.CurrentCylinders * ataId.CurrentHeads * ataId.CurrentSectorsPerTrack);
                    }
                }

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

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

                if (ataId.NominalRotationRate != 0x0000 &&
                    ataId.NominalRotationRate != 0xFFFF)
                {
                    if (ataId.NominalRotationRate == 0x0001)
                    {
                        mediaTest.SolidStateDevice = true;
                    }
                    else
                    {
                        mediaTest.SolidStateDevice    = false;
                        mediaTest.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 = (uint)(logicalSectorSize * ((1 << ataId.PhysLogSectorSize) & 0xF));
                    }
                    else
                    {
                        physicalSectorSize = logicalSectorSize;
                    }
                }
                else
                {
                    logicalSectorSize  = 512;
                    physicalSectorSize = 512;
                }

                mediaTest.BlockSize = logicalSectorSize;

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

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

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

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

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

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

                ulong checkCorrectRead = BitConverter.ToUInt64(buffer, 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 _);

                mediaTest.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);

                mediaTest.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 _);

                mediaTest.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);

                mediaTest.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 _);

                mediaTest.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);

                mediaTest.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 _);

                mediaTest.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);

                mediaTest.ReadDmaRetryData = readBuf;

                DicConsole.WriteLine("Trying SEEK in CHS mode...");
                sense = _dev.Seek(out errorChs, 0, 0, 1, _dev.Timeout, out _);
                mediaTest.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 _);

                mediaTest.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,
                                          errorChs.Status, errorChs.Error, readBuf.Length);

                mediaTest.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 _);

                mediaTest.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,
                                          errorChs.Status, errorChs.Error, readBuf.Length);

                mediaTest.ReadRetryLbaData = readBuf;

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

                mediaTest.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,
                                          errorChs.Status, errorChs.Error, readBuf.Length);

                mediaTest.ReadDmaLbaData = readBuf;

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

                mediaTest.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,
                                          errorChs.Status, errorChs.Error, readBuf.Length);

                mediaTest.ReadDmaRetryLbaData = readBuf;

                DicConsole.WriteLine("Trying SEEK in LBA mode...");
                sense = _dev.Seek(out errorLba, 0, _dev.Timeout, out _);
                mediaTest.SupportsSeekLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.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 LBA48 mode...");
                sense = _dev.Read(out readBuf, out AtaErrorRegistersLba48 errorLba48, 0, 1, _dev.Timeout, out _);

                mediaTest.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,
                                          errorChs.Status, errorChs.Error, readBuf.Length);

                mediaTest.ReadLba48Data = readBuf;

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

                mediaTest.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,
                                          errorChs.Status, errorChs.Error, readBuf.Length);

                mediaTest.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 buffer, out _, _dev.Timeout, out _);

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

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

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

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

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

                mediaTest.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);

                mediaTest.ReadLongData = readBuf;

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

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

                mediaTest.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);

                mediaTest.ReadLongRetryData = readBuf;

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

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

                mediaTest.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,
                                          errorChs.Status, errorChs.Error, readBuf.Length);

                mediaTest.ReadLongLbaData = readBuf;

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

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

                mediaTest.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,
                                          errorChs.Status, errorChs.Error, readBuf.Length);

                mediaTest.ReadLongRetryLbaData = readBuf;
            }
            else
            {
                mediaTest.MediaIsRecognized = false;
            }

            return(mediaTest);
        }
Esempio n. 2
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;
            }
        }
Esempio n. 3
0
        public bool Close()
        {
            if (!IsWriting)
            {
                ErrorMessage = "Image is not opened for writing";

                return(false);
            }

            if (imageInfo.Cylinders == 0)
            {
                imageInfo.Cylinders       = (uint)(imageInfo.Sectors / 16 / 63);
                imageInfo.Heads           = 16;
                imageInfo.SectorsPerTrack = 63;

                while (imageInfo.Cylinders == 0)
                {
                    imageInfo.Heads--;

                    if (imageInfo.Heads == 0)
                    {
                        imageInfo.SectorsPerTrack--;
                        imageInfo.Heads = 16;
                    }

                    imageInfo.Cylinders = (uint)(imageInfo.Sectors / imageInfo.Heads / imageInfo.SectorsPerTrack);

                    if (imageInfo.Cylinders == 0 &&
                        imageInfo.Heads == 0 &&
                        imageInfo.SectorsPerTrack == 0)
                    {
                        break;
                    }
                }
            }

            var header = new RsIdeHeader
            {
                magic    = signature, identify = new byte[106], dataOff = (ushort)Marshal.SizeOf <RsIdeHeader>(),
                revision = 1, reserved = new byte[11]
            };

            if (imageInfo.SectorSize == 256)
            {
                header.flags = RsIdeFlags.HalfSectors;
            }

            if (identify == null)
            {
                var ataId = new Identify.IdentifyDevice
                {
                    GeneralConfiguration =
                        CommonTypes.Structs.Devices.ATA.Identify.GeneralConfigurationBit.UltraFastIDE |
                        CommonTypes.Structs.Devices.ATA.Identify.GeneralConfigurationBit.Fixed |
                        CommonTypes.Structs.Devices.ATA.Identify.GeneralConfigurationBit.NotMFM |
                        CommonTypes.Structs.Devices.ATA.Identify.GeneralConfigurationBit.SoftSector,
                    Cylinders       = (ushort)imageInfo.Cylinders, Heads = (ushort)imageInfo.Heads,
                    SectorsPerTrack = (ushort)imageInfo.SectorsPerTrack, VendorWord47 = 0x80,
                    Capabilities    = CommonTypes.Structs.Devices.ATA.Identify.CapabilitiesBit.DMASupport |
                                      CommonTypes.Structs.Devices.ATA.Identify.CapabilitiesBit.IORDY |
                                      CommonTypes.Structs.Devices.ATA.Identify.CapabilitiesBit.LBASupport,
                    ExtendedIdentify =
                        CommonTypes.Structs.Devices.ATA.Identify.ExtendedIdentifyBit.Words54to58Valid,
                    CurrentCylinders       = (ushort)imageInfo.Cylinders, CurrentHeads = (ushort)imageInfo.Heads,
                    CurrentSectorsPerTrack = (ushort)imageInfo.SectorsPerTrack,
                    CurrentSectors         = (uint)imageInfo.Sectors, LBASectors = (uint)imageInfo.Sectors,
                    DMASupported           = CommonTypes.Structs.Devices.ATA.Identify.TransferMode.Mode0,
                    DMAActive = CommonTypes.Structs.Devices.ATA.Identify.TransferMode.Mode0
                };

                if (string.IsNullOrEmpty(imageInfo.DriveManufacturer))
                {
                    imageInfo.DriveManufacturer = "Aaru";
                }

                if (string.IsNullOrEmpty(imageInfo.DriveModel))
                {
                    imageInfo.DriveModel = "";
                }

                if (string.IsNullOrEmpty(imageInfo.DriveFirmwareRevision))
                {
                    Version.GetVersion();
                }

                if (string.IsNullOrEmpty(imageInfo.DriveSerialNumber))
                {
                    imageInfo.DriveSerialNumber = $"{new Random().NextDouble():16X}";
                }

                byte[] ataIdBytes = new byte[Marshal.SizeOf <Identify.IdentifyDevice>()];
                IntPtr ptr        = System.Runtime.InteropServices.Marshal.AllocHGlobal(512);
                System.Runtime.InteropServices.Marshal.StructureToPtr(ataId, ptr, true);

                System.Runtime.InteropServices.Marshal.Copy(ptr, ataIdBytes, 0,
                                                            Marshal.SizeOf <Identify.IdentifyDevice>());

                System.Runtime.InteropServices.Marshal.FreeHGlobal(ptr);

                Array.Copy(ScrambleAtaString(imageInfo.DriveManufacturer + " " + imageInfo.DriveModel, 40), 0,
                           ataIdBytes, 27 * 2, 40);

                Array.Copy(ScrambleAtaString(imageInfo.DriveFirmwareRevision, 8), 0, ataIdBytes, 23 * 2, 8);
                Array.Copy(ScrambleAtaString(imageInfo.DriveSerialNumber, 20), 0, ataIdBytes, 10 * 2, 20);
                Array.Copy(ataIdBytes, 0, header.identify, 0, 106);
            }
            else
            {
                Array.Copy(identify, 0, header.identify, 0, 106);
            }

            byte[] hdr    = new byte[Marshal.SizeOf <RsIdeHeader>()];
            IntPtr hdrPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(Marshal.SizeOf <RsIdeHeader>());

            System.Runtime.InteropServices.Marshal.StructureToPtr(header, hdrPtr, true);
            System.Runtime.InteropServices.Marshal.Copy(hdrPtr, hdr, 0, hdr.Length);
            System.Runtime.InteropServices.Marshal.FreeHGlobal(hdrPtr);

            writingStream.Seek(0, SeekOrigin.Begin);
            writingStream.Write(hdr, 0, hdr.Length);

            writingStream.Flush();
            writingStream.Close();

            IsWriting    = false;
            ErrorMessage = "";

            return(true);
        }
Esempio n. 4
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;

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

                    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;
                        }
                    }

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

                        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);
                        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 &&
                            !notrim &&
                            newTrim)
                        {
                            start = DateTime.UtcNow;
                            UpdateStatus?.Invoke("Trimming bad sectors");
                            dumpLog.WriteLine("Trimming bad 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();
                                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);
                        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);

                    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 (!nometadata)
                    {
                        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;
                                    }
                                }
                            }
                        }

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

                        if (dev.IsCompactFlash)
                        {
                            xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.CompactFlash);
                        }
                        else if (dev.IsPcmcia)
                        {
                            xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.PCCardTypeI);
                        }
                        else
                        {
                            xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.GENERIC_HDD);
                        }

                        sidecar.BlockMedia[0].DiskType          = xmlType.type;
                        sidecar.BlockMedia[0].DiskSubType       = xmlType.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;
                        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.");

                    UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec.");
                    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("");
                }

                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
            {
                StoppingErrorMessage?.Invoke("Unable to communicate with ATA device.");
            }
        }
    }
}