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