File indexing completed on 2024-06-16 07:42:04

0001 /*
0002     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 #include "k3bdatatrackreader.h"
0006 
0007 #include "k3blibdvdcss.h"
0008 #include "k3bdevice.h"
0009 #include "k3bdeviceglobals.h"
0010 #include "k3btrack.h"
0011 #include "k3bthread.h"
0012 #include "k3bcore.h"
0013 #include "k3b_i18n.h"
0014 
0015 #include <QDebug>
0016 #include <QFile>
0017 
0018 #include <unistd.h>
0019 
0020 
0021 
0022 // FIXME: determine max DMA buffer size
0023 static int s_bufferSizeSectors = 10;
0024 
0025 
0026 class K3b::DataTrackReader::Private
0027 {
0028 public:
0029     Private();
0030     ~Private();
0031 
0032     bool ignoreReadErrors;
0033     bool noCorrection;
0034     int retries;
0035     K3b::Device::Device* device;
0036     K3b::Msf firstSector;
0037     K3b::Msf lastSector;
0038     K3b::Msf nextReadSector;
0039     QIODevice* ioDevice;
0040     QString imagePath;
0041     ReadSectorSize sectorSize;
0042     bool useLibdvdcss;
0043     K3b::LibDvdCss* libcss;
0044 
0045     int oldErrorRecoveryMode;
0046 
0047     int errorSectorCount;
0048 
0049     ReadSectorSize usedSectorSize;
0050 };
0051 
0052 
0053 K3b::DataTrackReader::Private::Private()
0054     : ignoreReadErrors(false),
0055       noCorrection(false),
0056       retries(10),
0057       device(0),
0058       ioDevice(0),
0059       libcss(0)
0060 {
0061 }
0062 
0063 
0064 K3b::DataTrackReader::Private::~Private()
0065 {
0066     delete libcss;
0067 }
0068 
0069 
0070 
0071 
0072 K3b::DataTrackReader::DataTrackReader( K3b::JobHandler* jh, QObject* parent )
0073     : K3b::ThreadJob( jh, parent ),
0074       d( new Private() )
0075 {
0076 }
0077 
0078 
0079 K3b::DataTrackReader::~DataTrackReader()
0080 {
0081     delete d;
0082 }
0083 
0084 
0085 void K3b::DataTrackReader::setDevice( K3b::Device::Device* dev )
0086 {
0087     d->device = dev;
0088 }
0089 
0090 
0091 void K3b::DataTrackReader::setSectorRange( const K3b::Msf& start, const K3b::Msf& end )
0092 {
0093     d->firstSector = start;
0094     d->lastSector = end;
0095 }
0096 
0097 
0098 void K3b::DataTrackReader::setRetries( int r )
0099 {
0100     d->retries = r;
0101 }
0102 
0103 
0104 void K3b::DataTrackReader::setIgnoreErrors( bool b )
0105 {
0106     d->ignoreReadErrors = b;
0107 }
0108 
0109 
0110 void K3b::DataTrackReader::setNoCorrection( bool b )
0111 {
0112     d->noCorrection = b;
0113 }
0114 
0115 
0116 void K3b::DataTrackReader::writeTo( QIODevice* ioDev )
0117 {
0118     d->ioDevice = ioDev;
0119 }
0120 
0121 
0122 void K3b::DataTrackReader::setImagePath( const QString& p )
0123 {
0124     d->imagePath = p;
0125     d->ioDevice = 0;
0126 }
0127 
0128 
0129 void K3b::DataTrackReader::setSectorSize( ReadSectorSize size )
0130 {
0131     d->sectorSize = size;
0132 }
0133 
0134 
0135 bool K3b::DataTrackReader::run()
0136 {
0137     if( !d->device->open() ) {
0138         emit infoMessage( i18n("Could not open device %1",d->device->blockDeviceName()), K3b::Job::MessageError );
0139         return false;
0140     }
0141 
0142     // 1. determine sector size by checking the first sectors mode
0143     //    if impossible or MODE2 (mode2 formless) finish(false)
0144 
0145     d->useLibdvdcss = false;
0146     d->usedSectorSize = d->sectorSize;
0147 
0148     Device::MediaType mediaType = d->device->mediaType();
0149 
0150     if( K3b::Device::isDvdMedia( mediaType ) ) {
0151         d->usedSectorSize = MODE1;
0152 
0153         //
0154         // In case of an encrypted VideoDVD we read with libdvdcss which takes care of decrypting the vobs
0155         //
0156         if( d->device->copyrightProtectionSystemType() == K3b::Device::COPYRIGHT_PROTECTION_CSS ) {
0157 
0158             // close the device for libdvdcss
0159             d->device->close();
0160 
0161             qDebug() << "(K3b::DataTrackReader::WorkThread) found encrypted dvd. using libdvdcss.";
0162 
0163             // open the libdvdcss stuff
0164             if( !d->libcss )
0165                 d->libcss = K3b::LibDvdCss::create();
0166             if( !d->libcss ) {
0167                 emit infoMessage( i18n("Unable to open libdvdcss."), K3b::Job::MessageError );
0168                 return false;
0169             }
0170 
0171             if( !d->libcss->open(d->device) ) {
0172                 emit infoMessage( i18n("Could not open device %1",d->device->blockDeviceName()), K3b::Job::MessageError );
0173                 return false;
0174             }
0175 
0176             emit infoMessage( i18n("Retrieving all CSS keys. This might take a while."), K3b::Job::MessageInfo );
0177             if( !d->libcss->crackAllKeys() ) {
0178                 d->libcss->close();
0179                 emit infoMessage( i18n("Failed to retrieve all CSS keys."), K3b::Job::MessageError );
0180                 emit infoMessage( i18n("Video DVD decryption failed."), K3b::Job::MessageError );
0181                 return false;
0182             }
0183 
0184             d->useLibdvdcss = true;
0185         }
0186     }
0187     else if ( K3b::Device::isBdMedia( mediaType ) ) {
0188         d->usedSectorSize = MODE1;
0189     }
0190     else {
0191         if( d->usedSectorSize == AUTO ) {
0192             switch( d->device->getDataMode( d->firstSector ) ) {
0193             case K3b::Device::Track::MODE1:
0194             case K3b::Device::Track::DVD:
0195                 d->usedSectorSize = MODE1;
0196                 break;
0197             case K3b::Device::Track::XA_FORM1:
0198                 d->usedSectorSize = MODE2FORM1;
0199                 break;
0200             case K3b::Device::Track::XA_FORM2:
0201                 d->usedSectorSize = MODE2FORM2;
0202                 break;
0203             case K3b::Device::Track::MODE2:
0204                 emit infoMessage( i18n("No support for reading formless Mode2 sectors."), K3b::Job::MessageError );
0205             default:
0206                 emit infoMessage( i18n("Unsupported sector type."), K3b::Job::MessageError );
0207                 d->device->close();
0208                 return false;
0209             }
0210         }
0211     }
0212 
0213     emit infoMessage( i18n("Reading with sector size %1.",d->usedSectorSize), K3b::Job::MessageInfo );
0214     emit debuggingOutput( "K3b::DataTrackReader",
0215                           QString("reading sectors %1 to %2 with sector size %3. Length: %4 sectors, %5 bytes.")
0216                           .arg( d->firstSector.lba() )
0217                           .arg( d->lastSector.lba() )
0218                           .arg( d->usedSectorSize )
0219                           .arg( d->lastSector.lba() - d->firstSector.lba() + 1 )
0220                           .arg( quint64(d->usedSectorSize) * (quint64)(d->lastSector.lba() - d->firstSector.lba() + 1) ) );
0221 
0222     QFile file;
0223     if( !d->ioDevice ) {
0224         file.setFileName( d->imagePath );
0225         if( !file.open( QIODevice::WriteOnly ) ) {
0226             d->device->close();
0227             if( d->useLibdvdcss )
0228                 d->libcss->close();
0229             emit infoMessage( i18n("Unable to open '%1' for writing.",d->imagePath), K3b::Job::MessageError );
0230             return false;
0231         }
0232     }
0233 
0234     k3bcore->blockDevice( d->device );
0235     d->device->block( true );
0236 
0237     //
0238     // set the error recovery mode to 0x21 or 0x20 depending on d->ignoreReadErrors
0239     // TODO: should we also set RC=1 in d->ignoreReadErrors mode (0x11 because TB is ignored)
0240     //
0241     setErrorRecovery( d->device, d->noCorrection ? 0x21 : 0x20 );
0242 
0243     //
0244     // Let the drive determine the optimal reading speed
0245     //
0246     d->device->setSpeed( 0xffff, 0xffff );
0247 
0248 #ifdef Q_OS_NETBSD
0249     s_bufferSizeSectors = 31;
0250 #else
0251     s_bufferSizeSectors = 128;
0252 #endif
0253     unsigned char* buffer = new unsigned char[d->usedSectorSize*s_bufferSizeSectors];
0254     while( s_bufferSizeSectors > 0 && read( buffer, d->firstSector.lba(), s_bufferSizeSectors ) < 0 ) {
0255         qDebug() << "(K3b::DataTrackReader) determine max read sectors: "
0256                  << s_bufferSizeSectors << " too high." << Qt::endl;
0257         s_bufferSizeSectors /= 2;
0258     }
0259     qDebug() << "(K3b::DataTrackReader) determine max read sectors: "
0260              << s_bufferSizeSectors << " is max." << Qt::endl;
0261 
0262     //    s_bufferSizeSectors = K3b::Device::determineMaxReadingBufferSize( d->device, d->firstSector );
0263     if( s_bufferSizeSectors <= 0 ) {
0264         emit infoMessage( i18n("Error while reading sector %1.",d->firstSector.lba()), K3b::Job::MessageError );
0265         d->device->block( false );
0266         k3bcore->unblockDevice( d->device );
0267         return false;
0268     }
0269 
0270     qDebug() << "(K3b::DataTrackReader) using buffer size of " << s_bufferSizeSectors << " blocks.";
0271     emit debuggingOutput( "K3b::DataTrackReader", QString("using buffer size of %1 blocks.").arg( s_bufferSizeSectors ) );
0272 
0273     // 2. get it on
0274     K3b::Msf currentSector = d->firstSector;
0275     K3b::Msf totalReadSectors;
0276     d->nextReadSector = 0;
0277     d->errorSectorCount = 0;
0278     bool writeError = false;
0279     bool readError = false;
0280     int lastPercent = 0;
0281     unsigned long lastReadMb = 0;
0282     int bufferLen = s_bufferSizeSectors*d->usedSectorSize;
0283     while( !canceled() && currentSector <= d->lastSector ) {
0284 
0285         int maxReadSectors = qMin( bufferLen/d->usedSectorSize, d->lastSector.lba()-currentSector.lba()+1 );
0286 
0287         int readSectors = read( buffer,
0288                                 currentSector.lba(),
0289                                 maxReadSectors );
0290         if( readSectors < 0 ) {
0291             if( !retryRead( buffer,
0292                             currentSector.lba(),
0293                             maxReadSectors ) ) {
0294                 readError = true;
0295                 break;
0296             }
0297             else
0298                 readSectors = maxReadSectors;
0299         }
0300 
0301         totalReadSectors += readSectors;
0302 
0303         int readBytes = readSectors * d->usedSectorSize;
0304 
0305         if( d->ioDevice ) {
0306             if( d->ioDevice->write( reinterpret_cast<char*>(buffer ), readBytes ) != readBytes ) {
0307                 qDebug() << "(K3b::DataTrackReader::WorkThread) error while writing to dev " << d->ioDevice
0308                          << " current sector: " << (currentSector.lba()-d->firstSector.lba()) << Qt::endl;
0309                 emit debuggingOutput( "K3b::DataTrackReader",
0310                                       QString("Error while writing to IO device. Current sector is %2.")
0311                                       .arg(currentSector.lba()-d->firstSector.lba()) );
0312                 writeError = true;
0313                 break;
0314             }
0315         }
0316         else {
0317             if( file.write( reinterpret_cast<char*>(buffer), readBytes ) != readBytes ) {
0318                 qDebug() << "(K3b::DataTrackReader::WorkThread) error while writing to file " << d->imagePath
0319                          << " current sector: " << (currentSector.lba()-d->firstSector.lba()) << Qt::endl;
0320                 emit debuggingOutput( "K3b::DataTrackReader",
0321                                       QString("Error while writing to file %1. Current sector is %2.")
0322                                       .arg(d->imagePath).arg(currentSector.lba()-d->firstSector.lba()) );
0323                 writeError = true;
0324                 break;
0325             }
0326         }
0327 
0328         currentSector += readSectors;
0329 
0330         int currentPercent = 100 * (currentSector.lba() - d->firstSector.lba() + 1 ) /
0331                              (d->lastSector.lba() - d->firstSector.lba() + 1 );
0332 
0333         if( currentPercent > lastPercent ) {
0334             lastPercent = currentPercent;
0335             emit percent( currentPercent );
0336         }
0337 
0338         unsigned long readMb = (currentSector.lba() - d->firstSector.lba() + 1) / 512;
0339         if( readMb > lastReadMb ) {
0340             lastReadMb = readMb;
0341             emit processedSize( readMb, ( d->lastSector.lba() - d->firstSector.lba() + 1 ) / 512 );
0342         }
0343     }
0344 
0345     if( d->errorSectorCount > 0 )
0346         emit infoMessage( i18np("Ignored %1 erroneous sector.", "Ignored a total of %1 erroneous sectors.", d->errorSectorCount ),
0347                           K3b::Job::MessageError );
0348 
0349     // reset the error recovery mode
0350     setErrorRecovery( d->device, d->oldErrorRecoveryMode );
0351 
0352     d->device->block( false );
0353     k3bcore->unblockDevice( d->device );
0354 
0355     // cleanup
0356     if( d->useLibdvdcss )
0357         d->libcss->close();
0358     d->device->close();
0359     delete [] buffer;
0360 
0361     emit debuggingOutput( "K3b::DataTrackReader",
0362                           QString("Read a total of %1 sectors (%2 bytes)")
0363                           .arg(totalReadSectors.lba())
0364                           .arg((quint64)totalReadSectors.lba()*(quint64)d->usedSectorSize) );
0365 
0366     return( !canceled() && !writeError && !readError );
0367 }
0368 
0369 
0370 int K3b::DataTrackReader::read( unsigned char* buffer, unsigned long sector, unsigned int len )
0371 {
0372     //
0373     // Encrypted DVD reading with libdvdcss
0374     //
0375     if( d->useLibdvdcss ) {
0376         return d->libcss->readWrapped( reinterpret_cast<void*>(buffer), sector, len );
0377     }
0378 
0379     //
0380     // Standard reading
0381     //
0382     else {
0383         bool success = false;
0384         //      setErrorRecovery( d->device, d->ignoreReadErrors ? 0x21 : 0x20 );
0385         if( d->usedSectorSize == 2048 )
0386             success = d->device->read10( buffer, len*2048, sector, len );
0387         else
0388             success = d->device->readCd( buffer,
0389                                         len*d->usedSectorSize,
0390                                         0,     // all sector types
0391                                         false, // no dap
0392                                         sector,
0393                                         len,
0394                                         false, // no sync
0395                                         false, // no header
0396                                         d->usedSectorSize != MODE1,  // subheader
0397                                         true,  // user data
0398                                         false, // no edc/ecc
0399                                         0,     // no c2 error info... FIXME: should we check this??
0400                                         0      // no subchannel data
0401                 );
0402 
0403         if( success )
0404             return len;
0405         else
0406             return -1;
0407     }
0408 }
0409 
0410 
0411 // here we read every single sector for itself to find the troubling ones
0412 bool K3b::DataTrackReader::retryRead( unsigned char* buffer, unsigned long startSector, unsigned int len )
0413 {
0414     emit debuggingOutput( "K3b::DataTrackReader", QString( "Problem while reading. Retrying from sector %1.").arg(startSector) );
0415     emit infoMessage( i18n("Problem while reading. Retrying from sector %1.",startSector), K3b::Job::MessageWarning );
0416 
0417     int sectorsRead = -1;
0418     bool success = true;
0419     for( unsigned long sector = startSector; sector < startSector+len; ++sector ) {
0420         int retry = d->retries;
0421         while( !canceled() && retry && (sectorsRead = read( &buffer[( sector - startSector ) * d->usedSectorSize], sector, 1 )) < 0 )
0422             --retry;
0423 
0424         success = ( sectorsRead > 0 );
0425 
0426         if( canceled() )
0427             return false;
0428 
0429         if( !success ) {
0430             if( d->ignoreReadErrors ) {
0431                 emit infoMessage( i18n("Ignoring read error in sector %1.",sector), K3b::Job::MessageError );
0432                 emit debuggingOutput( "K3b::DataTrackReader", QString( "Ignoring read error in sector %1.").arg(sector) );
0433 
0434                 ++d->errorSectorCount;
0435                 //    ::memset( &buffer[i], 0, 1 );
0436                 success = true;
0437             }
0438             else {
0439                 emit infoMessage( i18n("Error while reading sector %1.",sector), K3b::Job::MessageError );
0440                 emit debuggingOutput( "K3b::DataTrackReader", QString( "Read error in sector %1.").arg(sector) );
0441                 break;
0442             }
0443         }
0444     }
0445 
0446     return success;
0447 }
0448 
0449 
0450 bool K3b::DataTrackReader::setErrorRecovery( K3b::Device::Device* dev, int code )
0451 {
0452     Device::UByteArray data;
0453     if( !dev->modeSense( data, 0x01 ) )
0454         return false;
0455 
0456     // in MMC1 the page has 8 bytes (12 in MMC4 but we only need the first 3 anyway)
0457     if( data.size() < 8+8 ) {
0458         qDebug() << "(K3b::DataTrackReader) modepage 0x01 data too small: " << data.size();
0459         return false;
0460     }
0461 
0462     d->oldErrorRecoveryMode = data[8+2];
0463     data[8+2] = code;
0464 
0465     if( d->oldErrorRecoveryMode != code )
0466         qDebug() << "(K3b::DataTrackReader) changing data recovery mode from " << d->oldErrorRecoveryMode << " to " << code;
0467 
0468     bool success = dev->modeSelect( data, true, false );
0469 
0470     return success;
0471 }
0472 
0473 #include "moc_k3bdatatrackreader.cpp"