예제 #1
0
        /// <summary>Dumps an ATA device</summary>
        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 ataProfile         = 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 AtaErrorRegistersChs errorChs);

            if (sense)
            {
                _errorLog?.WriteLine("ATA IDENTIFY DEVICE", _dev.Error, _dev.LastError, errorChs);
            }
            else if (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;

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

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

                    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", ataProfile);

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

                            if (currentSpeed > maxSpeed &&
                                currentSpeed > 0)
                            {
                                maxSpeed = currentSpeed;
                            }

                            if (currentSpeed < minSpeed &&
                                currentSpeed > 0)
                            {
                                minSpeed = currentSpeed;
                            }

                            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($"Trimming finished in {(end - start).TotalSeconds} seconds.");
                            _dumpLog.WriteLine("Trimming 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", ataProfile);

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

                                    if (currentSpeed > maxSpeed &&
                                        currentSpeed > 0)
                                    {
                                        maxSpeed = currentSpeed;
                                    }

                                    if (currentSpeed < minSpeed &&
                                        currentSpeed > 0)
                                    {
                                        minSpeed = currentSpeed;
                                    }

                                    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 manufacturerId =
                                            CIS.DecodeManufacturerIdentificationTuple(tuple);

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

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

                                        break;

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

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

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

                                            sidecar.BlockMedia[0].PCMCIA.AdditionalInformation =
                                                version.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.");
            }
        }
    }
}
예제 #2
0
파일: ATA.cs 프로젝트: morefun0302/Aaru
        /// <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;

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

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

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

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

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

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

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

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

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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;

            AaruConsole.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);
        }