2020-10-23 01:21:35 +01:00
using System ;
2020-11-03 01:40:10 +00:00
using System.Linq ;
2020-10-23 01:21:35 +01:00
using Aaru.CommonTypes.Extents ;
using Aaru.Console ;
using Aaru.Core.Logging ;
2020-10-24 15:07:17 +01:00
using Aaru.Decoders.SCSI ;
using Aaru.Helpers ;
2020-10-23 01:21:35 +01:00
using Schemas ;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace Aaru.Core.Devices.Dumping
{
partial class Dump
{
2020-11-11 04:19:18 +00:00
/// <summary>
/// Dumps data when dumping from a SCSI Block Commands compliant device, optical variant (magneto-optical and
/// successors)
/// </summary>
/// <param name="blocks">Media blocks</param>
/// <param name="maxBlocksToRead">Maximum number of blocks to read in a single command</param>
/// <param name="blockSize">Block size in bytes</param>
/// <param name="currentTry">Resume information</param>
/// <param name="extents">Correctly dump extents</param>
/// <param name="currentSpeed">Current speed</param>
/// <param name="minSpeed">Minimum speed</param>
/// <param name="maxSpeed">Maximum speed</param>
/// <param name="totalDuration">Total time spent in commands</param>
/// <param name="scsiReader">SCSI reader</param>
/// <param name="mhddLog">MHDD log</param>
/// <param name="ibgLog">ImgBurn log</param>
/// <param name="imageWriteDuration">Total time spent writing to image</param>
/// <param name="newTrim">Set if we need to start a trim</param>
/// <param name="blankExtents">Blank extents</param>
2020-10-23 01:21:35 +01:00
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 ;
2020-10-24 15:07:17 +01:00
bool canMediumScan = true ;
2020-10-23 01:21:35 +01:00
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 _ ) ;
2021-03-26 00:37:54 +00:00
DecodedSense ? decodedSense = Sense . Decode ( buffer ) ;
2020-10-24 15:07:17 +01:00
if ( _dev . LastError ! = 0 | |
decodedSense ? . SenseKey = = SenseKeys . IllegalRequest )
{
UpdateStatus ? .
Invoke ( "The current environment doesn't support the medium scan command, dump will take much longer than normal." ) ;
canMediumScan = false ;
writtenExtents . Add ( 0 , blocks - 1 ) ;
}
2020-10-23 01:21:35 +01:00
// TODO: Find a place where MEDIUM SCAN works properly
2020-10-24 15:07:17 +01:00
else if ( buffer ? . Length > 0 & &
! ArrayHelpers . ArrayIsNullOrEmpty ( buffer ) )
2020-10-23 01:21:35 +01:00
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 )
{
2020-10-24 15:07:17 +01:00
if ( ! canMediumScan )
break ;
2020-10-23 01:21:35 +01:00
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 ;
}
}
2020-10-24 15:07:17 +01:00
if ( _resume ! = null & & canMediumScan )
2020-10-23 01:21:35 +01:00
_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 ;
}
2021-03-26 00:37:54 +00:00
if ( extent . Item2 + 1 - i < blocksToRead )
blocksToRead = ( uint ) ( extent . Item2 + 1 - i ) ;
2020-10-23 01:21:35 +01:00
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 )
2021-03-26 00:37:54 +00:00
_skip = ( uint ) ( extent . Item2 + 1 - i ) ;
2020-10-23 01:21:35 +01:00
// 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 ;
2021-03-26 00:37:54 +00:00
currentSpeed = sectorSpeedStart * blockSize / ( 1048576 * elapsed ) ;
2020-10-23 01:21:35 +01:00
sectorSpeedStart = 0 ;
timeSpeedStart = DateTime . UtcNow ;
}
}
2020-11-03 01:40:10 +00:00
_resume . BadBlocks = _resume . BadBlocks . Distinct ( ) . ToList ( ) ;
2020-10-23 01:21:35 +01:00
EndProgress ? . Invoke ( ) ;
}
}
}