File indexing completed on 2025-03-16 04:29:30
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"