🐛Fix sending error recovery MODE page to devices before trying damaged sectors. Fixes #169

This commit is contained in:
2018-04-02 23:08:26 +01:00
parent 8d8735fdcb
commit 2a9f5ff828
2 changed files with 174 additions and 180 deletions

View File

@@ -82,16 +82,16 @@ namespace DiscImageChef.Core.Devices.Dumping
/// <exception cref="NotImplementedException">If trying to dump scrambled sectors</exception> /// <exception cref="NotImplementedException">If trying to dump scrambled sectors</exception>
/// <exception cref="InvalidOperationException">If the resume file is invalid</exception> /// <exception cref="InvalidOperationException">If the resume file is invalid</exception>
/// <exception cref="ArgumentOutOfRangeException">If the track type is unknown (never)</exception> /// <exception cref="ArgumentOutOfRangeException">If the track type is unknown (never)</exception>
internal static void Dump(Device dev, string devicePath, IWritableImage outputPlugin, ushort retryPasses, internal static void Dump(Device dev, string devicePath,
bool force, bool dumpRaw, bool persistent, bool stopOnError, IWritableImage outputPlugin, ushort retryPasses,
ref MediaType dskType, bool force, bool dumpRaw,
ref bool persistent, bool stopOnError, ref MediaType dskType,
Resume resume, ref DumpLog dumpLog, bool dumpLeadIn, ref Resume resume, ref DumpLog dumpLog,
Encoding encoding, bool dumpLeadIn, Encoding encoding,
string string outputPrefix, string outputPath,
outputPrefix, string outputPath, Dictionary<string, string> formatOptions, Dictionary<string, string> formatOptions,
CICMMetadataType CICMMetadataType preSidecar, uint skip,
preSidecar, uint skip, bool nometadata) bool nometadata)
{ {
uint subSize; uint subSize;
DateTime start; DateTime start;
@@ -335,8 +335,7 @@ namespace DiscImageChef.Core.Devices.Dumping
toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray(); toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray();
foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4)) foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4))
if(trk.POINT >= 0x01 && if(trk.POINT >= 0x01 && trk.POINT <= 0x63)
trk.POINT <= 0x63)
{ {
trackList.Add(new Track trackList.Add(new Track
{ {
@@ -732,8 +731,7 @@ namespace DiscImageChef.Core.Devices.Dumping
subSize = 0; subSize = 0;
blockSize = SECTOR_SIZE + subSize; blockSize = SECTOR_SIZE + subSize;
for(int t = 0; t < tracks.Length; t++) tracks[t].TrackSubchannelType = TrackSubchannelType.None; for(int t = 0; t < tracks.Length; t++) tracks[t].TrackSubchannelType = TrackSubchannelType.None;
ret = ret = outputPlugin.SetTracks(tracks.ToList());
outputPlugin.SetTracks(tracks.ToList());
if(!ret) if(!ret)
{ {
dumpLog.WriteLine("Error sending tracks to output image, not continuing."); dumpLog.WriteLine("Error sending tracks to output image, not continuing.");
@@ -888,10 +886,9 @@ namespace DiscImageChef.Core.Devices.Dumping
end = DateTime.UtcNow; end = DateTime.UtcNow;
mhddLog.Close(); mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000),
1024 / (totalDuration / 1000), devicePath); devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.", dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
(end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
@@ -906,6 +903,44 @@ namespace DiscImageChef.Core.Devices.Dumping
bool forward = true; bool forward = true;
bool runningPersistent = false; bool runningPersistent = false;
Modes.ModePage? currentModePage = null;
byte[] md6;
byte[] md10;
if(persistent)
{
Modes.ModePage_01_MMC pgMmc =
new Modes.ModePage_01_MMC {PS = false, ReadRetryCount = 255, Parameter = 0x20};
Modes.DecodedMode md = new Modes.DecodedMode
{
Header = new Modes.ModeHeader(),
Pages = new[]
{
new Modes.ModePage
{
Page = 0x01,
Subpage = 0x00,
PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
}
}
};
md6 = Modes.EncodeMode6(md, dev.ScsiType);
md10 = Modes.EncodeMode10(md, dev.ScsiType);
dumpLog.WriteLine("Sending MODE SELECT to drive.");
sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out _);
if(sense) sense = dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _);
if(sense)
{
DicConsole
.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
DicConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf));
dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
}
else runningPersistent = true;
}
cdRepeatRetry: cdRepeatRetry:
ulong[] tmpArray = resume.BadBlocks.ToArray(); ulong[] tmpArray = resume.BadBlocks.ToArray();
foreach(ulong badSector in tmpArray) foreach(ulong badSector in tmpArray)
@@ -960,42 +995,7 @@ namespace DiscImageChef.Core.Devices.Dumping
goto cdRepeatRetry; goto cdRepeatRetry;
} }
Modes.ModePage? currentModePage = null; if(runningPersistent && currentModePage.HasValue)
byte[] md6;
byte[] md10;
if(!runningPersistent && persistent)
{
Modes.ModePage_01_MMC pgMmc =
new Modes.ModePage_01_MMC {PS = false, ReadRetryCount = 255, Parameter = 0x20};
Modes.DecodedMode md = new Modes.DecodedMode
{
Header = new Modes.ModeHeader(),
Pages = new[]
{
new Modes.ModePage
{
Page = 0x01,
Subpage = 0x00,
PageResponse = Modes.EncodeModePage_01_MMC(pgMmc)
}
}
};
md6 = Modes.EncodeMode6(md, dev.ScsiType);
md10 = Modes.EncodeMode10(md, dev.ScsiType);
dumpLog.WriteLine("Sending MODE SELECT to drive.");
sense = dev.ModeSelect(md6, out senseBuf, true, false, dev.Timeout, out _);
if(sense) sense = dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _);
runningPersistent = true;
if(!sense && !dev.Error)
{
pass--;
goto cdRepeatRetry;
}
}
else if(runningPersistent && persistent && currentModePage.HasValue)
{ {
Modes.DecodedMode md = new Modes.DecodedMode Modes.DecodedMode md = new Modes.DecodedMode
{ {

View File

@@ -83,12 +83,11 @@ namespace DiscImageChef.Core.Devices.Dumping
bool persistent, bool stopOnError, bool persistent, bool stopOnError,
Dictionary<MediaTagType, byte[]> mediaTags, ref MediaType dskType, Dictionary<MediaTagType, byte[]> mediaTags, ref MediaType dskType,
bool opticalDisc, bool opticalDisc,
ref Resume resume, ref Resume resume, ref DumpLog dumpLog,
ref DumpLog dumpLog, Encoding encoding, string outputPrefix, Encoding encoding, string outputPrefix,
string outputPath, string outputPath, Dictionary<string, string> formatOptions,
Dictionary<string, string> formatOptions, CICMMetadataType preSidecar, uint skip,
CICMMetadataType preSidecar, bool nometadata)
uint skip, bool nometadata)
{ {
bool sense; bool sense;
ulong blocks; ulong blocks;
@@ -152,12 +151,9 @@ namespace DiscImageChef.Core.Devices.Dumping
{ {
mediaTags = new Dictionary<MediaTagType, byte[]>(); mediaTags = new Dictionary<MediaTagType, byte[]>();
if(dev.IsUsb && dev.UsbDescriptors != null) if(dev.IsUsb && dev.UsbDescriptors != null) mediaTags.Add(MediaTagType.USB_Descriptors, null);
mediaTags.Add(MediaTagType.USB_Descriptors, null); if(dev.Type == DeviceType.ATAPI) mediaTags.Add(MediaTagType.ATAPI_IDENTIFY, null);
if(dev.Type == DeviceType.ATAPI) if(dev.IsPcmcia && dev.Cis != null) mediaTags.Add(MediaTagType.PCMCIA_CIS, null);
mediaTags.Add(MediaTagType.ATAPI_IDENTIFY, null);
if(dev.IsPcmcia && dev.Cis != null)
mediaTags.Add(MediaTagType.PCMCIA_CIS, null);
sense = dev.ScsiInquiry(out byte[] cmdBuf, out _); sense = dev.ScsiInquiry(out byte[] cmdBuf, out _);
mediaTags.Add(MediaTagType.SCSI_INQUIRY, cmdBuf); mediaTags.Add(MediaTagType.SCSI_INQUIRY, cmdBuf);
@@ -413,10 +409,9 @@ namespace DiscImageChef.Core.Devices.Dumping
DicConsole.WriteLine(); DicConsole.WriteLine();
mhddLog.Close(); mhddLog.Close();
ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, ibgLog.Close(dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
blockSize * (double)(blocks + 1) / blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000),
1024 / (totalDuration / 1000), devicePath); devicePath);
dumpLog.WriteLine("Dump finished in {0} seconds.", dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds);
(end - start).TotalSeconds);
dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.",
(double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); (double)blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000));
dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.",
@@ -429,49 +424,11 @@ namespace DiscImageChef.Core.Devices.Dumping
bool forward = true; bool forward = true;
bool runningPersistent = false; bool runningPersistent = false;
repeatRetry:
ulong[] tmpArray = resume.BadBlocks.ToArray();
foreach(ulong badSector in tmpArray)
{
if(aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
dumpLog.WriteLine("Aborted!");
break;
}
DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass + 1,
forward ? "forward" : "reverse",
runningPersistent ? "recovering partial data, " : "");
sense = scsiReader.ReadBlock(out readBuffer, badSector, out double cmdDuration);
totalDuration += cmdDuration;
if(!sense && !dev.Error)
{
resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
outputPlugin.WriteSector(readBuffer, badSector);
dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
}
else if(runningPersistent)
outputPlugin.WriteSector(readBuffer, badSector);
}
if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0)
{
pass++;
forward = !forward;
resume.BadBlocks.Sort();
resume.BadBlocks.Reverse();
goto repeatRetry;
}
Modes.ModePage? currentModePage = null; Modes.ModePage? currentModePage = null;
byte[] md6; byte[] md6;
byte[] md10; byte[] md10;
if(!runningPersistent && persistent) if(persistent)
{ {
if(dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) if(dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice)
{ {
@@ -526,17 +483,57 @@ namespace DiscImageChef.Core.Devices.Dumping
} }
dumpLog.WriteLine("Sending MODE SELECT to drive."); dumpLog.WriteLine("Sending MODE SELECT to drive.");
sense = dev.ModeSelect(md6, out _, true, false, dev.Timeout, out _); sense = dev.ModeSelect(md6, out byte[] senseBuf, true, false, dev.Timeout, out _);
if(sense) sense = dev.ModeSelect10(md10, out _, true, false, dev.Timeout, out _); if(sense) sense = dev.ModeSelect10(md10, out senseBuf, true, false, dev.Timeout, out _);
if(sense)
{
DicConsole
.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
DicConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf));
dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive.");
}
else runningPersistent = true;
}
repeatRetry:
ulong[] tmpArray = resume.BadBlocks.ToArray();
foreach(ulong badSector in tmpArray)
{
if(aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
dumpLog.WriteLine("Aborted!");
break;
}
DicConsole.Write("\rRetrying sector {0}, pass {1}, {3}{2}", badSector, pass + 1,
forward ? "forward" : "reverse",
runningPersistent ? "recovering partial data, " : "");
sense = scsiReader.ReadBlock(out readBuffer, badSector, out double cmdDuration);
totalDuration += cmdDuration;
runningPersistent = true;
if(!sense && !dev.Error) if(!sense && !dev.Error)
{ {
pass--; resume.BadBlocks.Remove(badSector);
extents.Add(badSector);
outputPlugin.WriteSector(readBuffer, badSector);
dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass);
}
else if(runningPersistent) outputPlugin.WriteSector(readBuffer, badSector);
}
if(pass < retryPasses && !aborted && resume.BadBlocks.Count > 0)
{
pass++;
forward = !forward;
resume.BadBlocks.Sort();
resume.BadBlocks.Reverse();
goto repeatRetry; goto repeatRetry;
} }
}
else if(runningPersistent && persistent && currentModePage.HasValue) if(runningPersistent && currentModePage.HasValue)
{ {
Modes.DecodedMode md = new Modes.DecodedMode Modes.DecodedMode md = new Modes.DecodedMode
{ {
@@ -872,12 +869,9 @@ namespace DiscImageChef.Core.Devices.Dumping
if(!dev.IsRemovable || dev.IsUsb) if(!dev.IsRemovable || dev.IsUsb)
if(dev.Type == DeviceType.ATAPI) if(dev.Type == DeviceType.ATAPI)
sidecar.BlockMedia[0].Interface = "ATAPI"; sidecar.BlockMedia[0].Interface = "ATAPI";
else if(dev.IsUsb) else if(dev.IsUsb) sidecar.BlockMedia[0].Interface = "USB";
sidecar.BlockMedia[0].Interface = "USB"; else if(dev.IsFireWire) sidecar.BlockMedia[0].Interface = "FireWire";
else if(dev.IsFireWire) else sidecar.BlockMedia[0].Interface = "SCSI";
sidecar.BlockMedia[0].Interface = "FireWire";
else
sidecar.BlockMedia[0].Interface = "SCSI";
sidecar.BlockMedia[0].LogicalBlocks = (long)blocks; sidecar.BlockMedia[0].LogicalBlocks = (long)blocks;
sidecar.BlockMedia[0].PhysicalBlockSize = (int)physicalBlockSize; sidecar.BlockMedia[0].PhysicalBlockSize = (int)physicalBlockSize;
sidecar.BlockMedia[0].LogicalBlockSize = (int)logicalBlockSize; sidecar.BlockMedia[0].LogicalBlockSize = (int)logicalBlockSize;