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