Scan blank blocks in magneto-optical disks before dumping, and do not treat them as errors. Fixes #316

This commit is contained in:
2020-10-23 01:21:35 +01:00
parent ad2153f06d
commit 50edc53684
13 changed files with 329 additions and 39 deletions

View File

@@ -3,7 +3,8 @@
<component name="ContentModelStore">
<e p="$USER_HOME$/.cache/JetBrains/Rider2020.2/extResources" t="IncludeRecursive" />
<e p="$USER_HOME$/.cache/JetBrains/Rider2020.2/resharper-host/local/Transient/Rider/v202/SolutionCaches/_Aaru.232757112.00" t="ExcludeRecursive" />
<e p="$USER_HOME$/.config/git/ignore" t="IncludeRecursive" />
<e p="$APPLICATION_CONFIG_DIR$/consoles/db" t="IncludeRecursive" />
<e p="$APPLICATION_CONFIG_DIR$/extensions" t="IncludeRecursive" />
<e p="$APPLICATION_PLUGINS_DIR$/puppet/lib/stubs" t="IncludeRecursive" />
<e p="$USER_HOME$/.nuget/packages/microsoft.net.test.sdk/16.4.0/build/netcoreapp2.1" t="Include">
<e p="Microsoft.NET.Test.Sdk.Program.cs" t="Include" />
@@ -277,6 +278,7 @@
<e p="Data.cs" t="Include" />
<e p="Dump.cs" t="Include" />
<e p="Error.cs" t="Include" />
<e p="Optical.cs" t="Include" />
<e p="Trim.cs" t="Include" />
</e>
<e p="SecureDigital.cs" t="Include" />

View File

@@ -68,6 +68,7 @@
<Compile Include="Devices\Dumping\Sbc\Data.cs" />
<Compile Include="Devices\Dumping\Sbc\Error.cs" />
<Compile Include="Devices\Dumping\Sbc\Dump.cs" />
<Compile Include="Devices\Dumping\Sbc\Optical.cs" />
<Compile Include="Devices\Dumping\Sbc\Trim.cs" />
<Compile Include="Devices\Info\DeviceInfo.cs" />
<Compile Include="Devices\Info\Plextor.cs" />

View File

@@ -290,7 +290,7 @@ namespace Aaru.Core.Devices.Dumping
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, out _);
bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration, out _, out _);
if(!error)
{
@@ -383,7 +383,7 @@ namespace Aaru.Core.Devices.Dumping
PulseProgress?.Invoke($"Trimming sector {badSector}");
bool error =
ataReader.ReadBlock(out cmdBuf, badSector, out duration, out recoveredError);
ataReader.ReadBlock(out cmdBuf, badSector, out duration, out recoveredError, out _);
totalDuration += duration;
@@ -430,7 +430,7 @@ namespace Aaru.Core.Devices.Dumping
_persistent ? "recovering partial data, " : ""));
bool error =
ataReader.ReadBlock(out cmdBuf, badSector, out duration, out recoveredError);
ataReader.ReadBlock(out cmdBuf, badSector, out duration, out recoveredError, out _);
totalDuration += duration;

View File

@@ -48,6 +48,8 @@ namespace Aaru.Core.Devices.Dumping
byte[] buffer;
uint blocksToRead = maxBlocksToRead;
InitProgress?.Invoke();
for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead)
{
if(_aborted)
@@ -73,7 +75,7 @@ namespace Aaru.Core.Devices.Dumping
UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i,
(long)blocks);
sense = scsiReader.ReadBlocks(out buffer, i, blocksToRead, out double cmdDuration, out _);
sense = scsiReader.ReadBlocks(out buffer, i, blocksToRead, out double cmdDuration, out _, out _);
totalDuration += cmdDuration;
if(!sense &&
@@ -138,6 +140,8 @@ namespace Aaru.Core.Devices.Dumping
sectorSpeedStart = 0;
timeSpeedStart = DateTime.UtcNow;
}
EndProgress?.Invoke();
}
}
}

View File

@@ -81,6 +81,7 @@ namespace Aaru.Core.Devices.Dumping
byte[] readBuffer;
Modes.DecodedMode? decMode = null;
bool ret;
ExtentsULong blankExtents = null;
if(opticalDisc)
switch(dskType)
@@ -591,15 +592,21 @@ namespace Aaru.Core.Devices.Dumping
_dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _);
}
bool newTrim = false;
InitProgress?.Invoke();
if(_resume?.BlankExtents != null)
blankExtents = ExtentsConverter.FromMetadata(_resume.BlankExtents);
ReadSbcData(blocks, blocksToRead, blockSize, currentTry, extents, ref currentSpeed, ref minSpeed,
ref maxSpeed, ref totalDuration, scsiReader, mhddLog, ibgLog, ref imageWriteDuration,
ref newTrim);
bool newTrim = false;
if(_dev.ScsiType == PeripheralDeviceTypes.OpticalDevice)
ReadOpticalData(blocks, blocksToRead, blockSize, currentTry, extents, ref currentSpeed, ref minSpeed,
ref maxSpeed, ref totalDuration, scsiReader, mhddLog, ibgLog, ref imageWriteDuration,
ref newTrim, ref blankExtents);
else
ReadSbcData(blocks, blocksToRead, blockSize, currentTry, extents, ref currentSpeed, ref minSpeed,
ref maxSpeed, ref totalDuration, scsiReader, mhddLog, ibgLog, ref imageWriteDuration,
ref newTrim);
end = DateTime.UtcNow;
EndProgress?.Invoke();
mhddLog.Close();
ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024,
@@ -633,7 +640,7 @@ namespace Aaru.Core.Devices.Dumping
InitProgress?.Invoke();
TrimSbcData(scsiReader, extents, currentTry);
TrimSbcData(scsiReader, extents, currentTry, blankExtents);
EndProgress?.Invoke();
end = DateTime.UtcNow;
@@ -646,7 +653,7 @@ namespace Aaru.Core.Devices.Dumping
if(_resume.BadBlocks.Count > 0 &&
!_aborted &&
_retryPasses > 0)
RetrySbcData(scsiReader, currentTry, extents, ref totalDuration);
RetrySbcData(scsiReader, currentTry, extents, ref totalDuration, blankExtents);
#endregion Error handling
if(!_aborted)

View File

@@ -41,18 +41,19 @@ namespace Aaru.Core.Devices.Dumping
partial class Dump
{
void RetrySbcData(Reader scsiReader, DumpHardwareType currentTry, ExtentsULong extents,
ref double totalDuration)
ref double totalDuration, ExtentsULong blankExtents)
{
int pass = 1;
bool forward = true;
bool runningPersistent = false;
bool sense;
byte[] buffer;
bool recoveredError;
int pass = 1;
bool forward = true;
bool runningPersistent = false;
bool sense;
byte[] buffer;
bool recoveredError;
Modes.ModePage? currentModePage = null;
byte[] md6;
byte[] md10;
bool blankCheck;
bool newBlank = false;
if(_persistent)
{
@@ -231,9 +232,23 @@ namespace Aaru.Core.Devices.Dumping
forward ? "forward" : "reverse",
runningPersistent ? "recovering partial data, " : ""));
sense = scsiReader.ReadBlock(out buffer, badSector, out double cmdDuration, out recoveredError);
sense = scsiReader.ReadBlock(out buffer, badSector, out double cmdDuration, out recoveredError,
out blankCheck);
totalDuration += cmdDuration;
if(blankCheck)
{
_resume.BadBlocks.Remove(badSector);
blankExtents.Add(badSector, badSector);
newBlank = true;
UpdateStatus?.Invoke($"Found blank block {badSector} in pass {pass}.");
_dumpLog.WriteLine("Found blank block {0} in pass {1}.", badSector, pass);
continue;
}
if((!sense && !_dev.Error) || recoveredError)
{
_resume.BadBlocks.Remove(badSector);
@@ -284,6 +299,9 @@ namespace Aaru.Core.Devices.Dumping
_dev.ModeSelect10(md10, out _, true, false, _dev.Timeout, out _);
}
if(newBlank)
_resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents);
EndProgress?.Invoke();
}
}

View File

@@ -0,0 +1,227 @@
using System;
using Aaru.CommonTypes.Extents;
using Aaru.Console;
using Aaru.Core.Logging;
using Schemas;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace Aaru.Core.Devices.Dumping
{
partial class Dump
{
void ReadOpticalData(in ulong blocks, in uint maxBlocksToRead, in uint blockSize, DumpHardwareType currentTry,
ExtentsULong extents, ref double currentSpeed, ref double minSpeed, ref double maxSpeed,
ref double totalDuration, Reader scsiReader, MhddLog mhddLog, IbgLog ibgLog,
ref double imageWriteDuration, ref bool newTrim, ref ExtentsULong blankExtents)
{
const uint maxBlocks = 256;
var writtenExtents = new ExtentsULong();
bool written;
uint c = maxBlocks;
bool conditionMet;
bool changingCounter;
bool changingWritten;
uint blocksToRead = maxBlocksToRead;
bool sense;
byte[] buffer;
ulong sectorSpeedStart = 0;
DateTime timeSpeedStart = DateTime.UtcNow;
InitProgress?.Invoke();
if(blankExtents is null)
{
blankExtents = new ExtentsULong();
written = _dev.MediumScan(out buffer, true, false, false, false, false, 0, 1, 1, out _, out _,
uint.MaxValue, out _);
// TODO: Find a place where MEDIUM SCAN works properly
if(buffer?.Length > 0)
AaruConsole.
WriteLine("Please open a bug report in github with the manufacturer and model of this device, as well as your operating system name and version and this message: This environment correctly supports MEDIUM SCAN command.");
changingCounter = false;
changingWritten = false;
for(uint b = 0; b < blocks; b += c)
{
if(_aborted)
{
_resume.BlankExtents = null;
UpdateStatus?.Invoke("Aborted!");
_dumpLog.WriteLine("Aborted!");
break;
}
if(changingWritten)
{
changingWritten = false;
written = !written;
c = maxBlocks;
}
if(changingCounter)
{
b -= c;
changingCounter = false;
}
if(b + c >= blocks)
c = (uint)(blocks - b);
UpdateProgress?.
Invoke($"Scanning for {c} {(written ? "written" : "blank")} blocks starting in block {b}", b,
(long)blocks);
conditionMet = _dev.MediumScan(out _, written, false, false, false, false, b, c, c, out _, out _,
uint.MaxValue, out _);
if(conditionMet)
{
if(written)
writtenExtents.Add(b, c, true);
else
blankExtents.Add(b, c, true);
if(c < maxBlocks)
changingWritten = true;
}
else
{
if(c > 64)
c /= 2;
else
c--;
changingCounter = true;
if(c != 0)
continue;
written = !written;
c = maxBlocks;
}
}
if(_resume != null)
_resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents);
EndProgress?.Invoke();
}
else
{
writtenExtents.Add(0, blocks - 1);
foreach(Tuple<ulong, ulong> blank in blankExtents.ToArray())
for(ulong b = blank.Item1; b <= blank.Item2; b++)
writtenExtents.Remove(b);
}
if(writtenExtents.Count == 0)
{
UpdateStatus?.Invoke("Cannot dump empty media!");
_dumpLog.WriteLine("Cannot dump empty media!");
return;
}
InitProgress?.Invoke();
Tuple<ulong, ulong>[] extentsToDump = writtenExtents.ToArray();
foreach(Tuple<ulong, ulong> extent in extentsToDump)
{
if(extent.Item2 < _resume.NextBlock)
continue; // Skip this extent
ulong nextBlock = extent.Item1;
if(extent.Item1 < _resume.NextBlock)
nextBlock = (uint)_resume.NextBlock;
for(ulong i = nextBlock; i <= extent.Item2; i += blocksToRead)
{
if(_aborted)
{
currentTry.Extents = ExtentsConverter.ToMetadata(extents);
UpdateStatus?.Invoke("Aborted!");
_dumpLog.WriteLine("Aborted!");
break;
}
if((extent.Item2 + 1) - i < blocksToRead)
blocksToRead = (uint)((extent.Item2 + 1) - 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);
sense = scsiReader.ReadBlocks(out buffer, i, blocksToRead, out double cmdDuration, out _, out _);
totalDuration += cmdDuration;
if(!sense &&
!_dev.Error)
{
mhddLog.Write(i, cmdDuration);
ibgLog.Write(i, currentSpeed * 1024);
DateTime writeStart = DateTime.Now;
_outputPlugin.WriteSectors(buffer, i, blocksToRead);
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
extents.Add(i, blocksToRead, true);
}
else
{
// TODO: Reset device after X errors
if(_stopOnError)
return; // TODO: Return more cleanly
if(i + _skip > extent.Item2 + 1)
_skip = (uint)((extent.Item2 + 1) - i);
// Write empty data
DateTime writeStart = DateTime.Now;
_outputPlugin.WriteSectors(new byte[blockSize * _skip], i, _skip);
imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds;
for(ulong b = i; b < i + _skip; b++)
_resume.BadBlocks.Add(b);
mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration);
ibgLog.Write(i, 0);
_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;
}
}
EndProgress?.Invoke();
}
}
}

View File

@@ -35,12 +35,15 @@ namespace Aaru.Core.Devices.Dumping
{
partial class Dump
{
void TrimSbcData(Reader scsiReader, ExtentsULong extents, DumpHardwareType currentTry)
void TrimSbcData(Reader scsiReader, ExtentsULong extents, DumpHardwareType currentTry,
ExtentsULong blankExtents)
{
ulong[] tmpArray = _resume.BadBlocks.ToArray();
bool sense;
bool recoveredError;
bool blankCheck;
byte[] buffer;
bool newBlank = false;
foreach(ulong badSector in tmpArray)
{
@@ -55,7 +58,19 @@ namespace Aaru.Core.Devices.Dumping
PulseProgress?.Invoke($"Trimming sector {badSector}");
sense = scsiReader.ReadBlock(out buffer, badSector, out double _, out recoveredError);
sense = scsiReader.ReadBlock(out buffer, badSector, out double _, out recoveredError, out blankCheck);
if(blankCheck)
{
blankExtents.Add(badSector, badSector);
newBlank = true;
_resume.BadBlocks.Remove(badSector);
UpdateStatus?.Invoke($"Found blank block {badSector}.");
_dumpLog.WriteLine("Found blank block {0} in pass {1}.", badSector);
continue;
}
if((sense || _dev.Error) &&
!recoveredError)
@@ -65,6 +80,9 @@ namespace Aaru.Core.Devices.Dumping
extents.Add(badSector);
_outputPlugin.WriteSector(buffer, badSector);
}
if(newBlank)
_resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents);
}
}
}

View File

@@ -132,24 +132,31 @@ namespace Aaru.Core.Devices
}
}
internal bool ReadBlock(out byte[] buffer, ulong block, out double duration, out bool recoveredError) =>
ReadBlocks(out buffer, block, 1, out duration, out recoveredError);
internal bool ReadBlock(out byte[] buffer, ulong block, out double duration, out bool recoveredError,
out bool blankCheck) =>
ReadBlocks(out buffer, block, 1, out duration, out recoveredError, out blankCheck);
internal bool ReadBlocks(out byte[] buffer, ulong block, out double duration, out bool recoveredError) =>
ReadBlocks(out buffer, block, BlocksToRead, out duration, out recoveredError);
internal bool ReadBlocks(out byte[] buffer, ulong block, out double duration, out bool recoveredError,
out bool blankCheck) => ReadBlocks(out buffer, block, BlocksToRead, out duration,
out recoveredError, out blankCheck);
internal bool ReadBlocks(out byte[] buffer, ulong block, uint count, out double duration,
out bool recoveredError)
out bool recoveredError, out bool blankCheck)
{
switch(_dev.Type)
{
case DeviceType.ATA: return AtaReadBlocks(out buffer, block, count, out duration, out recoveredError);
case DeviceType.ATA:
blankCheck = false;
return AtaReadBlocks(out buffer, block, count, out duration, out recoveredError);
case DeviceType.ATAPI:
case DeviceType.SCSI: return ScsiReadBlocks(out buffer, block, count, out duration, out recoveredError);
case DeviceType.SCSI:
return ScsiReadBlocks(out buffer, block, count, out duration, out recoveredError, out blankCheck);
default:
buffer = null;
duration = 0d;
recoveredError = false;
blankCheck = false;
return true;
}

View File

@@ -571,13 +571,15 @@ namespace Aaru.Core.Devices
return true;
}
bool ScsiReadBlocks(out byte[] buffer, ulong block, uint count, out double duration, out bool recoveredError)
bool ScsiReadBlocks(out byte[] buffer, ulong block, uint count, out double duration, out bool recoveredError,
out bool blankCheck)
{
bool sense;
byte[] senseBuf;
buffer = null;
duration = 0;
recoveredError = false;
blankCheck = false;
if(CanReadRaw)
if(_readLong16)
@@ -628,6 +630,9 @@ namespace Aaru.Core.Devices
recoveredError = Sense.DecodeFixed(senseBuf)?.SenseKey == SenseKeys.RecoveredError ||
Sense.DecodeDescriptor(senseBuf)?.SenseKey == SenseKeys.RecoveredError;
blankCheck = Sense.DecodeFixed(senseBuf)?.SenseKey == SenseKeys.BlankCheck ||
Sense.DecodeDescriptor(senseBuf)?.SenseKey == SenseKeys.BlankCheck;
AaruConsole.DebugWriteLine("SCSI Reader", "READ error:\n{0}", Sense.PrettifySense(senseBuf));
return sense;

View File

@@ -152,7 +152,7 @@ namespace Aaru.Core.Devices.Scanning
UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)",
(long)i, (long)results.Blocks);
bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration, out _);
bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration, out _, out _);
if(!error)
{

View File

@@ -52,10 +52,11 @@ namespace Aaru.Core.Devices.Scanning
MhddLog mhddLog;
IbgLog ibgLog;
byte[] senseBuf;
bool sense = false;
bool sense = false;
uint blockSize = 0;
ushort currentProfile = 0x0001;
results.Blocks = 0;
uint blockSize = 0;
ushort currentProfile = 0x0001;
if(_dev.IsRemovable)
{
@@ -481,7 +482,7 @@ namespace Aaru.Core.Devices.Scanning
UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)",
(long)i, (long)results.Blocks);
sense = scsiReader.ReadBlocks(out _, i, blocksToRead, out double cmdDuration, out _);
sense = scsiReader.ReadBlocks(out _, i, blocksToRead, out double cmdDuration, out _, out _);
results.ProcessingTime += cmdDuration;
if(!sense &&
@@ -563,7 +564,7 @@ namespace Aaru.Core.Devices.Scanning
if(scsiReader.CanSeek)
scsiReader.Seek(seekPos, out seekCur);
else
scsiReader.ReadBlock(out _, seekPos, out seekCur, out _);
scsiReader.ReadBlock(out _, seekPos, out seekCur, out _, out _);
if(seekCur > results.SeekMax &&
seekCur > 0)