2020-10-17 23:30:15 +01:00
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : Data.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
2022-02-18 10:02:53 +00:00
// Copyright © 2011-2022 Natalia Portillo
// Copyright © 2020-2022 Rebecca Wallander
2020-10-17 23:30:15 +01:00
// ****************************************************************************/
2022-03-07 07:36:44 +00:00
using DVDDecryption = Aaru . Decryption . DVD . Dump ;
// ReSharper disable JoinDeclarationAndInitializer
// ReSharper disable InlineOutVariableDeclaration
// ReSharper disable TooWideLocalVariableScope
namespace Aaru.Core.Devices.Dumping ;
2020-10-17 23:30:15 +01:00
using System ;
2020-11-03 01:40:10 +00:00
using System.Linq ;
2021-01-14 00:03:37 +01:00
using Aaru.CommonTypes.Enums ;
2020-10-17 23:30:15 +01:00
using Aaru.CommonTypes.Extents ;
2021-12-28 00:14:33 +00:00
using Aaru.CommonTypes.Interfaces ;
2020-10-17 23:30:15 +01:00
using Aaru.Core.Logging ;
2021-01-14 00:03:37 +01:00
using Aaru.Decoders.DVD ;
using Aaru.Decryption ;
using Aaru.Decryption.DVD ;
2022-03-07 07:36:44 +00:00
using Aaru.Settings ;
2020-10-17 23:30:15 +01:00
using Schemas ;
2022-03-06 13:29:38 +00:00
partial class Dump
2020-10-17 23:30:15 +01:00
{
2022-03-06 13:29:38 +00:00
/// <summary>Dumps data when dumping from a SCSI Block Commands compliant device</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="dvdDecrypt">DVD CSS decryption module</param>
/// <param name="discKey">The DVD disc key</param>
void ReadSbcData ( 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 DVDDecryption dvdDecrypt , byte [ ] discKey )
2020-10-17 23:30:15 +01:00
{
2022-03-06 13:29:38 +00:00
ulong sectorSpeedStart = 0 ;
bool sense ;
DateTime timeSpeedStart = DateTime . UtcNow ;
byte [ ] buffer ;
uint blocksToRead = maxBlocksToRead ;
var outputFormat = _outputPlugin as IWritableImage ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
InitProgress ? . Invoke ( ) ;
2020-10-23 01:21:35 +01:00
2022-03-06 13:29:38 +00:00
for ( ulong i = _resume . NextBlock ; i < blocks ; i + = blocksToRead )
{
if ( _aborted )
2020-10-17 23:30:15 +01:00
{
2022-03-06 13:29:38 +00:00
currentTry . Extents = ExtentsConverter . ToMetadata ( extents ) ;
UpdateStatus ? . Invoke ( "Aborted!" ) ;
_dumpLog . WriteLine ( "Aborted!" ) ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
break ;
}
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
if ( blocks - i < blocksToRead )
blocksToRead = ( uint ) ( blocks - i ) ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
if ( currentSpeed > maxSpeed & &
currentSpeed > 0 )
maxSpeed = currentSpeed ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
if ( currentSpeed < minSpeed & &
currentSpeed > 0 )
minSpeed = currentSpeed ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
UpdateProgress ? . Invoke ( $"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)" , ( long ) i ,
( long ) blocks ) ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
sense = scsiReader . ReadBlocks ( out buffer , i , blocksToRead , out double cmdDuration , out _ , out _ ) ;
totalDuration + = cmdDuration ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
if ( ! sense & &
! _dev . Error )
{
2022-03-07 07:36:44 +00:00
if ( Settings . Current . EnableDecryption & &
discKey ! = null & &
2022-03-06 13:29:38 +00:00
_titleKeys )
2020-10-17 23:30:15 +01:00
{
2022-03-06 13:29:38 +00:00
for ( ulong j = 0 ; j < blocksToRead ; j + + )
2021-01-14 00:03:37 +01:00
{
2022-03-06 13:29:38 +00:00
if ( _aborted )
break ;
2021-01-14 00:03:37 +01:00
2022-03-06 13:29:38 +00:00
if ( ! _resume . MissingTitleKeys . Contains ( i + j ) )
2021-01-19 15:19:01 +01:00
2022-03-06 13:29:38 +00:00
// Key is already dumped.
continue ;
2021-01-14 00:03:37 +01:00
2022-03-06 13:29:38 +00:00
byte [ ] tmpBuf ;
2021-01-14 00:03:37 +01:00
2022-03-06 13:29:38 +00:00
bool tmpSense = dvdDecrypt . ReadTitleKey ( out tmpBuf , out _ , DvdCssKeyClass . DvdCssCppmOrCprm ,
i + j , _dev . Timeout , out _ ) ;
if ( ! tmpSense )
{
CSS_CPRM . TitleKey ? titleKey = CSS . DecodeTitleKey ( tmpBuf , dvdDecrypt . BusKey ) ;
2021-01-14 00:03:37 +01:00
2022-03-06 13:29:38 +00:00
if ( titleKey . HasValue )
outputFormat . WriteSectorTag ( new [ ]
{
titleKey . Value . CMI
} , i + j , SectorTagType . DvdCmi ) ;
else
continue ;
// If the CMI bit is 1, the sector is using copy protection, else it is not
if ( ( titleKey . Value . CMI & 0x80 ) > > 7 = = 0 )
2021-01-19 15:19:01 +01:00
{
2022-03-06 13:29:38 +00:00
// The CMI indicates this sector is not encrypted.
outputFormat . WriteSectorTag ( new byte [ ]
2021-01-14 00:03:37 +01:00
{
2022-03-06 13:29:38 +00:00
0 , 0 , 0 , 0 , 0
} , i + j , SectorTagType . DvdTitleKey ) ;
2021-01-19 15:19:01 +01:00
2022-03-06 13:29:38 +00:00
outputFormat . WriteSectorTag ( new byte [ ]
{
0 , 0 , 0 , 0 , 0
} , i + j , SectorTagType . DvdTitleKeyDecrypted ) ;
2021-01-14 00:03:37 +01:00
2022-03-06 13:29:38 +00:00
_resume . MissingTitleKeys . Remove ( i + j ) ;
2021-01-14 00:03:37 +01:00
2022-03-06 13:29:38 +00:00
continue ;
}
2021-01-14 00:03:37 +01:00
2022-03-06 13:29:38 +00:00
// According to libdvdcss, if the key is all zeroes, the sector is actually
// not encrypted even if the CMI says it is.
if ( titleKey . Value . Key . All ( k = > k = = 0 ) )
{
outputFormat . WriteSectorTag ( new byte [ ]
2021-01-19 15:19:01 +01:00
{
2022-03-06 13:29:38 +00:00
0 , 0 , 0 , 0 , 0
} , i + j , SectorTagType . DvdTitleKey ) ;
2021-01-14 00:03:37 +01:00
2022-03-06 13:29:38 +00:00
outputFormat . WriteSectorTag ( new byte [ ]
{
0 , 0 , 0 , 0 , 0
} , i + j , SectorTagType . DvdTitleKeyDecrypted ) ;
2021-01-14 00:03:37 +01:00
2022-03-06 13:29:38 +00:00
_resume . MissingTitleKeys . Remove ( i + j ) ;
2021-01-19 15:19:01 +01:00
2022-03-06 13:29:38 +00:00
continue ;
}
2021-01-19 15:19:01 +01:00
2022-03-06 13:29:38 +00:00
outputFormat . WriteSectorTag ( titleKey . Value . Key , i + j , SectorTagType . DvdTitleKey ) ;
_resume . MissingTitleKeys . Remove ( i + j ) ;
2021-01-19 15:19:01 +01:00
2022-03-06 13:29:38 +00:00
CSS . DecryptTitleKey ( 0 , discKey , titleKey . Value . Key , out tmpBuf ) ;
outputFormat . WriteSectorTag ( tmpBuf , i + j , SectorTagType . DvdTitleKeyDecrypted ) ;
2021-01-14 00:03:37 +01:00
}
2022-03-06 13:29:38 +00:00
}
2021-01-19 15:19:01 +01:00
2022-03-06 13:29:38 +00:00
if ( ! _storeEncrypted )
2021-01-19 15:19:01 +01:00
2022-03-06 13:29:38 +00:00
// Todo: Flag in the outputFormat that a sector has been decrypted
{
ErrorNumber errno =
outputFormat . ReadSectorsTag ( i , blocksToRead , SectorTagType . DvdCmi , out byte [ ] cmi ) ;
if ( errno ! = ErrorNumber . NoError )
ErrorMessage ? . Invoke ( $"Error retrieving CMI for sector {i}" ) ;
else
2021-09-20 20:52:18 +01:00
{
2022-03-07 07:36:44 +00:00
errno = outputFormat . ReadSectorsTag ( i , blocksToRead , SectorTagType . DvdTitleKeyDecrypted ,
2022-03-06 13:29:38 +00:00
out byte [ ] titleKey ) ;
2021-09-20 20:52:18 +01:00
if ( errno ! = ErrorNumber . NoError )
2022-03-06 13:29:38 +00:00
ErrorMessage ? . Invoke ( $"Error retrieving title key for sector {i}" ) ;
2021-09-20 20:52:18 +01:00
else
2022-03-06 13:29:38 +00:00
buffer = CSS . DecryptSector ( buffer , cmi , titleKey , blocksToRead , blockSize ) ;
2021-09-20 20:52:18 +01:00
}
2021-01-14 00:03:37 +01:00
}
2020-10-17 23:30:15 +01:00
}
2022-03-06 13:29:38 +00:00
mhddLog . Write ( i , cmdDuration ) ;
ibgLog . Write ( i , currentSpeed * 1024 ) ;
DateTime writeStart = DateTime . Now ;
outputFormat . WriteSectors ( buffer , i , blocksToRead ) ;
imageWriteDuration + = ( DateTime . Now - writeStart ) . TotalSeconds ;
extents . Add ( i , blocksToRead , true ) ;
}
else
{
if ( _dev . Manufacturer . ToLowerInvariant ( ) = = "insite" )
2020-10-17 23:30:15 +01:00
{
2022-03-06 13:29:38 +00:00
_resume . BadBlocks . Add ( i ) ;
_resume . BadBlocks = _resume . BadBlocks . Distinct ( ) . ToList ( ) ;
_resume . NextBlock + + ;
_aborted = true ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
_dumpLog ? .
WriteLine ( "INSITE floptical drives get crazy on the SCSI bus when an error is found, stopping so you can reboot the computer or reset the scsi bus appropriately." ) ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
UpdateStatus ? .
Invoke ( "INSITE floptical drives get crazy on the SCSI bus when an error is found, stopping so you can reboot the computer or reset the scsi bus appropriately" ) ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
continue ;
}
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
// TODO: Reset device after X errors
if ( _stopOnError )
return ; // TODO: Return more cleanly
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
if ( i + _skip > blocks )
_skip = ( uint ) ( blocks - i ) ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
// Write empty data
DateTime writeStart = DateTime . Now ;
outputFormat . WriteSectors ( new byte [ blockSize * _skip ] , i , _skip ) ;
imageWriteDuration + = ( DateTime . Now - writeStart ) . TotalSeconds ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
for ( ulong b = i ; b < i + _skip ; b + + )
_resume . BadBlocks . Add ( b ) ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
mhddLog . Write ( i , cmdDuration < 500 ? 65535 : cmdDuration ) ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
ibgLog . Write ( i , 0 ) ;
_dumpLog . WriteLine ( "Skipping {0} blocks from errored block {1}." , _skip , i ) ;
i + = _skip - blocksToRead ;
newTrim = true ;
}
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
sectorSpeedStart + = blocksToRead ;
_resume . NextBlock = i + blocksToRead ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
double elapsed = ( DateTime . UtcNow - timeSpeedStart ) . TotalSeconds ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
if ( elapsed < = 0 )
continue ;
2020-10-17 23:30:15 +01:00
2022-03-06 13:29:38 +00:00
currentSpeed = sectorSpeedStart * blockSize / ( 1048576 * elapsed ) ;
sectorSpeedStart = 0 ;
timeSpeedStart = DateTime . UtcNow ;
}
2020-10-23 01:21:35 +01:00
2022-03-06 13:29:38 +00:00
_resume . BadBlocks = _resume . BadBlocks . Distinct ( ) . ToList ( ) ;
2020-11-03 01:40:10 +00:00
2022-03-06 13:29:38 +00:00
EndProgress ? . Invoke ( ) ;
2020-10-17 23:30:15 +01:00
}
}