File indexing completed on 2025-03-16 04:29:30
0001 /* 0002 SPDX-FileCopyrightText: 2011 Michal Malek <michalm@jabster.pl> 0003 SPDX-FileCopyrightText: 1998-2010 Sebastian Trueg <trueg@k3b.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "k3bdvdcopyjob.h" 0009 #include "k3blibdvdcss.h" 0010 0011 #include "k3breadcdreader.h" 0012 #include "k3bdatatrackreader.h" 0013 #include "k3bexternalbinmanager.h" 0014 #include "k3bdevice.h" 0015 #include "k3bdeviceglobals.h" 0016 #include "k3bdevicehandler.h" 0017 #include "k3bdiskinfo.h" 0018 #include "k3bglobals.h" 0019 #include "k3bcore.h" 0020 #include "k3bgrowisofswriter.h" 0021 #include "k3bcdrecordwriter.h" 0022 #include "k3bversion.h" 0023 #include "k3biso9660.h" 0024 #include "k3bfilesplitter.h" 0025 #include "k3bchecksumpipe.h" 0026 #include "k3bverificationjob.h" 0027 #include "k3bglobalsettings.h" 0028 #include "k3b_i18n.h" 0029 0030 #include <KIO/Global> 0031 0032 #include <QDebug> 0033 #include <QFile> 0034 #include <QFileInfo> 0035 0036 0037 class K3b::DvdCopyJob::Private 0038 { 0039 public: 0040 Private() 0041 : doneCopies(0), 0042 running(false), 0043 canceled(false), 0044 writerJob(0), 0045 readcdReader(0), 0046 dataTrackReader(0), 0047 verificationJob(0), 0048 usedWritingMode(K3b::WritingModeAuto), 0049 verifyData(false) { 0050 outPipe.readFrom( &imageFile, true ); 0051 } 0052 0053 K3b::WritingApp usedWritingApp; 0054 0055 int doneCopies; 0056 0057 bool running; 0058 bool readerRunning; 0059 bool writerRunning; 0060 bool canceled; 0061 0062 K3b::AbstractWriter* writerJob; 0063 K3b::ReadcdReader* readcdReader; 0064 K3b::DataTrackReader* dataTrackReader; 0065 K3b::VerificationJob* verificationJob; 0066 0067 K3b::Device::DiskInfo sourceDiskInfo; 0068 0069 K3b::Msf lastSector; 0070 0071 K3b::WritingMode usedWritingMode; 0072 0073 K3b::FileSplitter imageFile; 0074 K3b::ChecksumPipe inPipe; 0075 K3b::ActivePipe outPipe; 0076 0077 bool verifyData; 0078 }; 0079 0080 0081 K3b::DvdCopyJob::DvdCopyJob( K3b::JobHandler* hdl, QObject* parent ) 0082 : K3b::BurnJob( hdl, parent ), 0083 m_writerDevice(0), 0084 m_readerDevice(0), 0085 m_onTheFly(false), 0086 m_removeImageFiles(false), 0087 m_simulate(false), 0088 m_speed(1), 0089 m_copies(1), 0090 m_onlyCreateImage(false), 0091 m_ignoreReadErrors(false), 0092 m_readRetries(128), 0093 m_writingMode( K3b::WritingModeAuto ) 0094 { 0095 d = new Private(); 0096 } 0097 0098 0099 K3b::DvdCopyJob::~DvdCopyJob() 0100 { 0101 delete d; 0102 } 0103 0104 0105 void K3b::DvdCopyJob::start() 0106 { 0107 jobStarted(); 0108 emit burning(false); 0109 0110 d->canceled = false; 0111 d->running = true; 0112 d->readerRunning = d->writerRunning = false; 0113 0114 emit newTask( i18n("Checking Source Medium") ); 0115 const K3b::ExternalBin* growisofsBin = k3bcore->externalBinManager()->binObject("growisofs"); 0116 if( m_onTheFly && 0117 growisofsBin && growisofsBin->version() < K3b::Version( 5, 12 ) ) { 0118 m_onTheFly = false; 0119 emit infoMessage( i18n("K3b does not support writing on-the-fly with growisofs %1.", 0120 growisofsBin->version()), MessageError ); 0121 emit infoMessage( i18n("Disabling on-the-fly writing."), MessageInfo ); 0122 } 0123 0124 emit newSubTask( i18n("Waiting for source medium") ); 0125 0126 // wait for a source disk 0127 if( waitForMedium( m_readerDevice, 0128 K3b::Device::STATE_COMPLETE|K3b::Device::STATE_INCOMPLETE, 0129 K3b::Device::MEDIA_WRITABLE_DVD|K3b::Device::MEDIA_DVD_ROM|K3b::Device::MEDIA_BD_ALL ) == Device::MEDIA_UNKNOWN ) { 0130 emit canceled(); 0131 d->running = false; 0132 jobFinished( false ); 0133 return; 0134 } 0135 0136 emit newSubTask( i18n("Checking source medium") ); 0137 0138 connect( K3b::Device::sendCommand( K3b::Device::DeviceHandler::CommandMediaInfo, m_readerDevice ), 0139 SIGNAL(finished(K3b::Device::DeviceHandler*)), 0140 this, 0141 SLOT(slotDiskInfoReady(K3b::Device::DeviceHandler*)) ); 0142 } 0143 0144 0145 void K3b::DvdCopyJob::slotDiskInfoReady( K3b::Device::DeviceHandler* dh ) 0146 { 0147 if( d->canceled ) { 0148 emit canceled(); 0149 jobFinished(false); 0150 d->running = false; 0151 } 0152 0153 d->sourceDiskInfo = dh->diskInfo(); 0154 0155 if( dh->diskInfo().empty() || dh->diskInfo().diskState() == K3b::Device::STATE_NO_MEDIA ) { 0156 emit infoMessage( i18n("No source medium found."), MessageError ); 0157 jobFinished(false); 0158 d->running = false; 0159 } 0160 else { 0161 // first let's determine which application to use 0162 d->usedWritingApp = writingApp(); 0163 const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject("cdrecord"); 0164 const K3b::ExternalBin* growisofsBin = k3bcore->externalBinManager()->binObject("growisofs"); 0165 if ( d->usedWritingApp == K3b::WritingAppAuto ) { 0166 // prefer growisofs to wodim, which doesn't work all that great for DVDs 0167 // (and doesn't support BluRay at all) 0168 if ( cdrecordBin && cdrecordBin->hasFeature( "wodim" ) ) 0169 d->usedWritingApp = K3b::WritingAppGrowisofs; 0170 // otherwise, let's default to cdrecord for the time being 0171 // FIXME: use growisofs for non-dao and non-auto mode 0172 else { 0173 if ( K3b::Device::isBdMedia( d->sourceDiskInfo.mediaType() ) ) { 0174 if ( cdrecordBin && cdrecordBin->hasFeature( "blu-ray" ) ) 0175 d->usedWritingApp = K3b::WritingAppCdrecord; 0176 else 0177 d->usedWritingApp = K3b::WritingAppGrowisofs; 0178 } else 0179 d->usedWritingApp = K3b::WritingAppCdrecord; 0180 } 0181 } 0182 0183 if( m_readerDevice->copyrightProtectionSystemType() == K3b::Device::COPYRIGHT_PROTECTION_CSS ) { // CSS is the only one we support ATM 0184 emit infoMessage( i18n("Found encrypted DVD."), MessageWarning ); 0185 // check for libdvdcss 0186 bool haveLibdvdcss = false; 0187 qDebug() << "(K3b::DvdCopyJob) trying to open libdvdcss."; 0188 if( K3b::LibDvdCss* libcss = K3b::LibDvdCss::create() ) { 0189 qDebug() << "(K3b::DvdCopyJob) succeeded."; 0190 qDebug() << "(K3b::DvdCopyJob) dvdcss_open(" << m_readerDevice->blockDeviceName() << ") = " 0191 << libcss->open(m_readerDevice) << Qt::endl; 0192 haveLibdvdcss = true; 0193 0194 delete libcss; 0195 } 0196 else 0197 qDebug() << "(K3b::DvdCopyJob) failed."; 0198 0199 if( !haveLibdvdcss ) { 0200 emit infoMessage( i18n("Cannot copy encrypted DVDs."), MessageError ); 0201 d->running = false; 0202 jobFinished( false ); 0203 return; 0204 } 0205 } 0206 0207 0208 // 0209 // We cannot rely on the kernel to determine the size of the DVD for some reason 0210 // On the other hand it is not always a good idea to rely on the size from the ISO9660 0211 // header since that may be wrong due to some buggy encoder or some boot code appended 0212 // after creating the image. 0213 // That is why we try our best to determine the size of the DVD. For DVD-ROM this is very 0214 // easy since it has only one track. The same goes for single session DVD-R(W) and DVD+R. 0215 // Multisession DVDs we will simply not copy. ;) 0216 // For DVD+RW and DVD-RW in restricted overwrite mode we are left with no other choice but 0217 // to use the ISO9660 header. 0218 // 0219 // On the other hand: in on-the-fly mode growisofs determines the size of the data to be written 0220 // by looking at the ISO9660 header when writing in DAO mode. So in this case 0221 // it would be best for us to do the same.... 0222 // 0223 // With growisofs 5.15 we have the option to specify the size of the image to be written in DAO mode. 0224 // 0225 0226 switch( dh->diskInfo().mediaType() ) { 0227 case K3b::Device::MEDIA_DVD_ROM: 0228 case K3b::Device::MEDIA_DVD_PLUS_R_DL: 0229 case K3b::Device::MEDIA_DVD_R_DL: 0230 case K3b::Device::MEDIA_DVD_R_DL_SEQ: 0231 case K3b::Device::MEDIA_DVD_R_DL_JUMP: 0232 if( !m_onlyCreateImage ) { 0233 if( dh->diskInfo().numLayers() > 1 && 0234 dh->diskInfo().size() > MediaSizeDvd4Gb ) { 0235 if( !(m_writerDevice->type() & (K3b::Device::DEVICE_DVD_R_DL|K3b::Device::DEVICE_DVD_PLUS_R_DL)) ) { 0236 emit infoMessage( i18n("The writer does not support writing Double Layer DVDs."), MessageError ); 0237 d->running = false; 0238 jobFinished(false); 0239 return; 0240 } 0241 else if( growisofsBin && !growisofsBin->hasFeature( "dual-layer" ) ) { 0242 emit infoMessage( i18n("This growisofs version does not support writing Double Layer DVDs."), MessageError ); 0243 d->running = false; 0244 jobFinished(false); 0245 return; 0246 } 0247 } 0248 } 0249 Q_FALLTHROUGH(); 0250 case K3b::Device::MEDIA_DVD_R: 0251 case K3b::Device::MEDIA_DVD_R_SEQ: 0252 case K3b::Device::MEDIA_DVD_RW: 0253 case K3b::Device::MEDIA_DVD_RW_SEQ: 0254 case K3b::Device::MEDIA_DVD_PLUS_R: 0255 case K3b::Device::MEDIA_BD_ROM: 0256 case K3b::Device::MEDIA_BD_R: 0257 case K3b::Device::MEDIA_BD_R_SRM: 0258 0259 if( dh->diskInfo().numSessions() > 1 ) { 0260 emit infoMessage( i18n("K3b does not support copying multi-session DVD or Blu-ray disks."), MessageError ); 0261 d->running = false; 0262 jobFinished(false); 0263 return; 0264 } 0265 0266 // growisofs only uses the size from the PVD for reserving 0267 // writable space in DAO mode 0268 // with version >= 5.15 growisofs supports specifying the size of the track 0269 if( m_writingMode != K3b::WritingModeSao || !m_onTheFly || m_onlyCreateImage || 0270 ( growisofsBin && growisofsBin->hasFeature( "daosize" ) ) || 0271 d->usedWritingApp == K3b::WritingAppCdrecord ) { 0272 d->lastSector = dh->toc().lastSector(); 0273 break; 0274 } 0275 0276 // fallthrough 0277 0278 case K3b::Device::MEDIA_DVD_PLUS_RW: 0279 case K3b::Device::MEDIA_DVD_RW_OVWR: 0280 case K3b::Device::MEDIA_BD_RE: 0281 { 0282 emit infoMessage( i18n("K3b relies on the size saved in the ISO 9660 header."), MessageWarning ); 0283 emit infoMessage( i18n("This might result in a corrupt copy if the source was mastered with buggy software."), MessageWarning ); 0284 0285 K3b::Iso9660 isoF( m_readerDevice, 0 ); 0286 if( isoF.open() ) { 0287 d->lastSector = ((long long)isoF.primaryDescriptor().logicalBlockSize*isoF.primaryDescriptor().volumeSpaceSize)/2048LL - 1; 0288 } 0289 else { 0290 emit infoMessage( i18n("Unable to determine the ISO 9660 filesystem size."), MessageError ); 0291 jobFinished(false); 0292 d->running = false; 0293 return; 0294 } 0295 } 0296 break; 0297 0298 case K3b::Device::MEDIA_DVD_RAM: 0299 emit infoMessage( i18n("K3b does not support copying DVD-RAM."), MessageError ); 0300 jobFinished(false); 0301 d->running = false; 0302 return; 0303 0304 default: 0305 emit infoMessage( i18n("Unsupported media type."), MessageError ); 0306 jobFinished(false); 0307 d->running = false; 0308 return; 0309 } 0310 0311 0312 if( !m_onTheFly ) { 0313 // 0314 // Check the image path 0315 // 0316 QFileInfo fi( m_imagePath ); 0317 if( !fi.isFile() || 0318 questionYesNo( i18n("Do you want to overwrite %1?",m_imagePath), 0319 i18n("File Exists") ) ) { 0320 if( fi.isDir() ) 0321 m_imagePath = K3b::findTempFile( "iso", m_imagePath ); 0322 else if( !QFileInfo( m_imagePath.section( '/', 0, -2 ) ).isDir() ) { 0323 emit infoMessage( i18n("Specified an unusable temporary path. Using default."), MessageWarning ); 0324 m_imagePath = K3b::findTempFile( "iso" ); 0325 } 0326 // else the user specified a file in an existing dir 0327 0328 emit infoMessage( i18n("Writing image file to %1.",m_imagePath), MessageInfo ); 0329 emit newSubTask( i18n("Reading source medium.") ); 0330 } 0331 else { 0332 jobFinished(false); 0333 d->running = false; 0334 return; 0335 } 0336 0337 // 0338 // check free temp space 0339 // 0340 KIO::filesize_t imageSpaceNeeded = (KIO::filesize_t)(d->lastSector.lba()+1)*2048; 0341 unsigned long avail, size; 0342 QString pathToTest = m_imagePath.left( m_imagePath.lastIndexOf( '/' ) ); 0343 if( !K3b::kbFreeOnFs( pathToTest, size, avail ) ) { 0344 emit infoMessage( i18n("Unable to determine free space in temporary folder '%1'.",pathToTest), MessageError ); 0345 jobFinished(false); 0346 d->running = false; 0347 return; 0348 } 0349 else { 0350 if( avail < imageSpaceNeeded/1024 ) { 0351 emit infoMessage( i18n("Not enough space left in temporary folder."), MessageError ); 0352 jobFinished(false); 0353 d->running = false; 0354 return; 0355 } 0356 } 0357 0358 d->imageFile.setName( m_imagePath ); 0359 if( !d->imageFile.open( QIODevice::WriteOnly ) ) { 0360 emit infoMessage( i18n("Unable to open '%1' for writing.",m_imagePath), MessageError ); 0361 jobFinished( false ); 0362 d->running = false; 0363 return; 0364 } 0365 } 0366 0367 if( K3b::isMounted( m_readerDevice ) ) { 0368 emit infoMessage( i18n("Unmounting source medium"), MessageInfo ); 0369 K3b::unmount( m_readerDevice ); 0370 } 0371 0372 if( m_onlyCreateImage || !m_onTheFly ) { 0373 emit newTask( i18n("Creating image") ); 0374 } 0375 else if( m_onTheFly && !m_onlyCreateImage ) { 0376 if( waitForDvd() ) { 0377 prepareWriter(); 0378 if( m_simulate ) 0379 emit newTask( i18n("Simulating copy") ); 0380 else if( m_copies > 1 ) 0381 emit newTask( i18n("Writing copy %1",d->doneCopies+1) ); 0382 else 0383 emit newTask( i18n("Writing copy") ); 0384 0385 emit burning(true); 0386 d->writerRunning = true; 0387 d->writerJob->start(); 0388 } 0389 else { 0390 if( d->canceled ) 0391 emit canceled(); 0392 jobFinished(false); 0393 d->running = false; 0394 return; 0395 } 0396 } 0397 0398 prepareReader(); 0399 d->readerRunning = true; 0400 d->dataTrackReader->start(); 0401 } 0402 } 0403 0404 0405 void K3b::DvdCopyJob::cancel() 0406 { 0407 if( d->running ) { 0408 d->canceled = true; 0409 if( d->readerRunning ) 0410 d->dataTrackReader->cancel(); 0411 if( d->writerRunning ) 0412 d->writerJob->cancel(); 0413 if ( d->verificationJob && d->verificationJob->active() ) 0414 d->verificationJob->cancel(); 0415 d->inPipe.close(); 0416 d->outPipe.close(); 0417 d->imageFile.close(); 0418 } 0419 else { 0420 qDebug() << "(K3b::DvdCopyJob) not running."; 0421 } 0422 } 0423 0424 0425 void K3b::DvdCopyJob::prepareReader() 0426 { 0427 if( !d->dataTrackReader ) { 0428 d->dataTrackReader = new K3b::DataTrackReader( this ); 0429 connect( d->dataTrackReader, SIGNAL(percent(int)), this, SLOT(slotReaderProgress(int)) ); 0430 connect( d->dataTrackReader, SIGNAL(processedSize(int,int)), this, SLOT(slotReaderProcessedSize(int,int)) ); 0431 connect( d->dataTrackReader, SIGNAL(finished(bool)), this, SLOT(slotReaderFinished(bool)) ); 0432 connect( d->dataTrackReader, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) ); 0433 connect( d->dataTrackReader, SIGNAL(newTask(QString)), this, SIGNAL(newSubTask(QString)) ); 0434 connect( d->dataTrackReader, SIGNAL(debuggingOutput(QString,QString)), 0435 this, SIGNAL(debuggingOutput(QString,QString)) ); 0436 } 0437 0438 d->dataTrackReader->setDevice( m_readerDevice ); 0439 d->dataTrackReader->setIgnoreErrors( m_ignoreReadErrors ); 0440 d->dataTrackReader->setRetries( m_readRetries ); 0441 d->dataTrackReader->setSectorRange( 0, d->lastSector ); 0442 0443 if( m_onTheFly && !m_onlyCreateImage ) 0444 // there are several uses of pipe->writeTo( d->writerJob->ioDevice(), ... ) in this file! 0445 #ifdef __GNUC__ 0446 #warning Growisofs needs stdin to be closed in order to exit gracefully. Cdrecord does not. However, if closed with cdrecord we loose parts of stderr. Why? 0447 #endif 0448 d->inPipe.writeTo( d->writerJob->ioDevice(), d->usedWritingApp == K3b::WritingAppGrowisofs ); 0449 else 0450 d->inPipe.writeTo( &d->imageFile, true ); 0451 0452 d->inPipe.open( true ); 0453 d->dataTrackReader->writeTo( &d->inPipe ); 0454 } 0455 0456 0457 // ALWAYS CALL WAITFORDVD BEFORE PREPAREWRITER! 0458 void K3b::DvdCopyJob::prepareWriter() 0459 { 0460 delete d->writerJob; 0461 0462 if ( d->usedWritingApp == K3b::WritingAppGrowisofs ) { 0463 K3b::GrowisofsWriter* job = new K3b::GrowisofsWriter( m_writerDevice, this, this ); 0464 0465 // these do only make sense with DVD-R(W) 0466 job->setSimulate( m_simulate ); 0467 job->setBurnSpeed( m_speed ); 0468 job->setWritingMode( d->usedWritingMode ); 0469 job->setCloseDvd( true ); 0470 0471 // 0472 // In case the first layer size is not known let the 0473 // split be determined by growisofs 0474 // 0475 if( d->sourceDiskInfo.numLayers() > 1 && 0476 d->sourceDiskInfo.firstLayerSize() > 0 ) { 0477 job->setLayerBreak( d->sourceDiskInfo.firstLayerSize().lba() ); 0478 } 0479 else { 0480 // this is only used in DAO mode with growisofs >= 5.15 0481 job->setTrackSize( d->lastSector.lba()+1 ); 0482 } 0483 0484 job->setImageToWrite( QString() ); // write to stdin 0485 0486 d->writerJob = job; 0487 } 0488 0489 else { 0490 K3b::CdrecordWriter* writer = new K3b::CdrecordWriter( m_writerDevice, this, this ); 0491 0492 writer->setWritingMode( d->usedWritingMode ); 0493 writer->setSimulate( m_simulate ); 0494 writer->setBurnSpeed( m_speed ); 0495 0496 writer->addArgument( "-data" ); 0497 writer->addArgument( QString("-tsize=%1s").arg( d->lastSector.lba()+1 ) )->addArgument("-"); 0498 0499 d->writerJob = writer; 0500 } 0501 0502 0503 connect( d->writerJob, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) ); 0504 connect( d->writerJob, SIGNAL(percent(int)), this, SLOT(slotWriterProgress(int)) ); 0505 connect( d->writerJob, SIGNAL(processedSize(int,int)), this, SIGNAL(processedSize(int,int)) ); 0506 connect( d->writerJob, SIGNAL(processedSubSize(int,int)), this, SIGNAL(processedSubSize(int,int)) ); 0507 connect( d->writerJob, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) ); 0508 connect( d->writerJob, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) ); 0509 connect( d->writerJob, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)), this, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)) ); 0510 connect( d->writerJob, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) ); 0511 // connect( d->writerJob, SIGNAL(newTask(QString)), this, SIGNAL(newTask(QString)) ); 0512 connect( d->writerJob, SIGNAL(newSubTask(QString)), this, SIGNAL(newSubTask(QString)) ); 0513 connect( d->writerJob, SIGNAL(debuggingOutput(QString,QString)), 0514 this, SIGNAL(debuggingOutput(QString,QString)) ); 0515 } 0516 0517 0518 void K3b::DvdCopyJob::slotReaderProgress( int p ) 0519 { 0520 if( !m_onTheFly || m_onlyCreateImage ) { 0521 emit subPercent( p ); 0522 0523 int bigParts = ( m_onlyCreateImage ? 1 : (m_simulate ? 2 : ( d->verifyData ? m_copies*2 : m_copies ) + 1 ) ); 0524 emit percent( p/bigParts ); 0525 } 0526 } 0527 0528 0529 void K3b::DvdCopyJob::slotReaderProcessedSize( int p, int c ) 0530 { 0531 if( !m_onTheFly || m_onlyCreateImage ) 0532 emit processedSubSize( p, c ); 0533 0534 if( m_onlyCreateImage ) 0535 emit processedSize( p, c ); 0536 } 0537 0538 0539 void K3b::DvdCopyJob::slotWriterProgress( int p ) 0540 { 0541 int bigParts = ( m_simulate ? 1 : ( d->verifyData ? m_copies*2 : m_copies ) ) + ( m_onTheFly ? 0 : 1 ); 0542 int doneParts = ( m_simulate ? 0 : ( d->verifyData ? d->doneCopies*2 : d->doneCopies ) ) + ( m_onTheFly ? 0 : 1 ); 0543 emit percent( 100*doneParts/bigParts + p/bigParts ); 0544 0545 emit subPercent( p ); 0546 } 0547 0548 0549 void K3b::DvdCopyJob::slotVerificationProgress( int p ) 0550 { 0551 int bigParts = ( m_simulate ? 1 : ( d->verifyData ? m_copies*2 : m_copies ) ) + ( m_onTheFly ? 0 : 1 ); 0552 int doneParts = ( m_simulate ? 0 : ( d->verifyData ? d->doneCopies*2 : d->doneCopies ) ) + ( m_onTheFly ? 0 : 1 ) + 1; 0553 emit percent( 100*doneParts/bigParts + p/bigParts ); 0554 } 0555 0556 0557 void K3b::DvdCopyJob::slotReaderFinished( bool success ) 0558 { 0559 d->readerRunning = false; 0560 0561 // already finished? 0562 if( !d->running ) 0563 return; 0564 0565 if( d->canceled ) { 0566 removeImageFiles(); 0567 emit canceled(); 0568 jobFinished(false); 0569 d->running = false; 0570 } 0571 0572 if( success ) { 0573 emit infoMessage( i18n("Successfully read source medium."), MessageSuccess ); 0574 if( m_onlyCreateImage ) { 0575 jobFinished(true); 0576 d->running = false; 0577 } 0578 else { 0579 if( m_writerDevice == m_readerDevice ) { 0580 // eject the media (we do this blocking to know if it worked 0581 // because if it did not it might happen that k3b overwrites a CD-RW 0582 // source) 0583 qDebug() << "Ejecting read medium" << m_readerDevice->blockDeviceName(); 0584 if( !K3b::eject( m_readerDevice ) ) { 0585 blockingInformation( i18n("K3b was unable to eject the source medium. Please do so manually.") ); 0586 } 0587 } 0588 0589 if( !m_onTheFly ) { 0590 0591 d->imageFile.close(); 0592 0593 if( waitForDvd() ) { 0594 prepareWriter(); 0595 if( m_copies > 1 ) 0596 emit newTask( i18n("Writing copy %1",d->doneCopies+1) ); 0597 else 0598 emit newTask( i18n("Writing copy") ); 0599 0600 emit burning(true); 0601 0602 d->writerRunning = true; 0603 d->writerJob->start(); 0604 #ifdef __GNUC__ 0605 #warning Growisofs needs stdin to be closed in order to exit gracefully. Cdrecord does not. However, if closed with cdrecord we loose parts of stderr. Why? 0606 #endif 0607 d->outPipe.writeTo( d->writerJob->ioDevice(), d->usedWritingApp == K3b::WritingAppGrowisofs ); 0608 d->outPipe.open( true ); 0609 } 0610 else { 0611 if( m_removeImageFiles ) 0612 removeImageFiles(); 0613 if( d->canceled ) 0614 emit canceled(); 0615 jobFinished(false); 0616 d->running = false; 0617 } 0618 } 0619 } 0620 } 0621 else { 0622 removeImageFiles(); 0623 jobFinished(false); 0624 d->running = false; 0625 } 0626 } 0627 0628 0629 void K3b::DvdCopyJob::slotWriterFinished( bool success ) 0630 { 0631 d->writerRunning = false; 0632 0633 // already finished? 0634 if( !d->running ) 0635 return; 0636 0637 if( d->canceled ) { 0638 if( m_removeImageFiles ) 0639 removeImageFiles(); 0640 emit canceled(); 0641 jobFinished(false); 0642 d->running = false; 0643 } 0644 0645 if( success ) { 0646 emit infoMessage( i18n("Successfully written copy %1.",d->doneCopies+1), MessageInfo ); 0647 0648 if( d->verifyData && !m_simulate ) { 0649 if( !d->verificationJob ) { 0650 d->verificationJob = new K3b::VerificationJob( this, this ); 0651 connect( d->verificationJob, SIGNAL(infoMessage(QString,int)), 0652 this, SIGNAL(infoMessage(QString,int)) ); 0653 connect( d->verificationJob, SIGNAL(newTask(QString)), 0654 this, SIGNAL(newSubTask(QString)) ); 0655 connect( d->verificationJob, SIGNAL(percent(int)), 0656 this, SLOT(slotVerificationProgress(int)) ); 0657 connect( d->verificationJob, SIGNAL(percent(int)), 0658 this, SIGNAL(subPercent(int)) ); 0659 connect( d->verificationJob, SIGNAL(finished(bool)), 0660 this, SLOT(slotVerificationFinished(bool)) ); 0661 connect( d->verificationJob, SIGNAL(debuggingOutput(QString,QString)), 0662 this, SIGNAL(debuggingOutput(QString,QString)) ); 0663 0664 } 0665 d->verificationJob->setDevice( m_writerDevice ); 0666 d->verificationJob->addTrack( 1, d->inPipe.checksum(), d->lastSector+1 ); 0667 0668 if( m_copies > 1 ) 0669 emit newTask( i18n("Verifying copy %1",d->doneCopies+1) ); 0670 else 0671 emit newTask( i18n("Verifying copy") ); 0672 0673 emit burning( false ); 0674 0675 d->verificationJob->start(); 0676 } 0677 0678 else if( ++d->doneCopies < m_copies ) { 0679 0680 if( !K3b::eject( m_writerDevice ) ) { 0681 blockingInformation( i18n("K3b was unable to eject the written medium. Please do so manually.") ); 0682 } 0683 0684 if( waitForDvd() ) { 0685 prepareWriter(); 0686 emit newTask( i18n("Writing copy %1",d->doneCopies+1) ); 0687 0688 emit burning(true); 0689 0690 d->writerRunning = true; 0691 d->writerJob->start(); 0692 } 0693 else { 0694 if( d->canceled ) 0695 emit canceled(); 0696 jobFinished(false); 0697 d->running = false; 0698 return; 0699 } 0700 0701 if( m_onTheFly ) { 0702 prepareReader(); 0703 d->readerRunning = true; 0704 d->dataTrackReader->start(); 0705 } 0706 else { 0707 #ifdef __GNUC__ 0708 #warning Growisofs needs stdin to be closed in order to exit gracefully. Cdrecord does not. However, if closed with cdrecord we loose parts of stderr. Why? 0709 #endif 0710 d->outPipe.writeTo( d->writerJob->ioDevice(), true/*d->usedWritingApp == K3b::WritingAppGrowisofs*/ ); 0711 d->outPipe.open( true ); 0712 } 0713 } 0714 else { 0715 if ( k3bcore->globalSettings()->ejectMedia() ) { 0716 K3b::Device::eject( m_writerDevice ); 0717 } 0718 if( m_removeImageFiles ) 0719 removeImageFiles(); 0720 d->running = false; 0721 jobFinished(true); 0722 } 0723 } 0724 else { 0725 if( m_removeImageFiles ) 0726 removeImageFiles(); 0727 d->running = false; 0728 jobFinished(false); 0729 } 0730 } 0731 0732 0733 void K3b::DvdCopyJob::slotVerificationFinished( bool success ) 0734 { 0735 if ( d->canceled ) { 0736 emit canceled(); 0737 jobFinished( false ); 0738 } 0739 0740 // we simply ignore the results from the verification, the verification 0741 // job already emits a message 0742 else if( ++d->doneCopies < m_copies ) { 0743 0744 if( waitForDvd() ) { 0745 prepareWriter(); 0746 emit newTask( i18n("Writing copy %1",d->doneCopies+1) ); 0747 0748 emit burning(true); 0749 0750 d->writerRunning = true; 0751 d->writerJob->start(); 0752 } 0753 else { 0754 if( d->canceled ) 0755 emit canceled(); 0756 jobFinished(false); 0757 d->running = false; 0758 return; 0759 } 0760 0761 if( m_onTheFly ) { 0762 prepareReader(); 0763 d->readerRunning = true; 0764 d->dataTrackReader->start(); 0765 } 0766 else { 0767 #ifdef __GNUC__ 0768 #warning Growisofs needs stdin to be closed in order to exit gracefully. Cdrecord does not. However, if closed with cdrecord we loose parts of stderr. Why? 0769 #endif 0770 d->outPipe.writeTo( d->writerJob->ioDevice(), d->usedWritingApp == K3b::WritingAppGrowisofs ); 0771 d->outPipe.open( true ); 0772 } 0773 } 0774 else { 0775 if( m_removeImageFiles ) 0776 removeImageFiles(); 0777 d->running = false; 0778 jobFinished( success ); 0779 } 0780 } 0781 0782 0783 // this is basically the same code as in K3b::DvdJob... :( 0784 // perhaps this should be moved to some K3b::GrowisofsHandler which also parses the growisofs output? 0785 bool K3b::DvdCopyJob::waitForDvd() 0786 { 0787 if ( !K3b::Device::isDvdMedia( d->sourceDiskInfo.mediaType() ) && 0788 !K3b::Device::isBdMedia( d->sourceDiskInfo.mediaType() ) ) { 0789 // this should NEVER happen 0790 emit infoMessage( i18n( "Unsupported media type: %1" , K3b::Device::mediaTypeString( d->sourceDiskInfo.mediaType() ) ), MessageError ); 0791 return false; 0792 } 0793 0794 Device::MediaType m = waitForMedium( m_writerDevice, 0795 K3b::Device::STATE_EMPTY, 0796 Device::MEDIA_WRITABLE_DVD|Device::MEDIA_WRITABLE_BD, 0797 d->sourceDiskInfo.size() ); 0798 0799 if( m == Device::MEDIA_UNKNOWN ) { 0800 cancel(); 0801 return false; 0802 } 0803 0804 else { 0805 // ------------------------------- 0806 // DVD Plus 0807 // ------------------------------- 0808 if( m & K3b::Device::MEDIA_DVD_PLUS_ALL ) { 0809 0810 if ( m & ( Device::MEDIA_DVD_PLUS_R|Device::MEDIA_DVD_PLUS_R_DL ) ) 0811 d->usedWritingMode = K3b::WritingModeSao; 0812 else 0813 d->usedWritingMode = K3b::WritingModeRestrictedOverwrite; 0814 0815 if( m_simulate ) { 0816 if( !questionYesNo( i18n("%1 media do not support write simulation. " 0817 "Do you really want to continue? The disc will actually be " 0818 "written to.", Device::mediaTypeString(m, true)), 0819 i18n("No Simulation with %1", Device::mediaTypeString(m, true)) ) ) { 0820 cancel(); 0821 return false; 0822 } 0823 0824 // m_simulate = false; 0825 emit newTask( i18n("Writing DVD copy") ); 0826 } 0827 0828 if( m_writingMode != K3b::WritingModeAuto && m_writingMode != K3b::WritingModeRestrictedOverwrite ) 0829 emit infoMessage( i18n("Writing mode ignored when writing DVD+R(W) media."), MessageInfo ); 0830 0831 emit infoMessage( i18n("Writing %1.", Device::mediaTypeString( m, true ) ), MessageInfo ); 0832 } 0833 0834 // ------------------------------- 0835 // DVD Minus 0836 // ------------------------------- 0837 else if ( m & K3b::Device::MEDIA_DVD_MINUS_ALL ) { 0838 if( m_simulate && !m_writerDevice->dvdMinusTestwrite() ) { 0839 if( !questionYesNo( i18n("Your writer (%1 %2) does not support simulation with DVD-R(W) media. " 0840 "Do you really want to continue? The media will actually be " 0841 "written to.", 0842 m_writerDevice->vendor(), 0843 m_writerDevice->description()), 0844 i18n("No Simulation with DVD-R(W)") ) ) { 0845 cancel(); 0846 return false; 0847 } 0848 0849 // m_simulate = false; 0850 } 0851 0852 // 0853 // We do not default to DAO in onthefly mode since otherwise growisofs would 0854 // use the size from the PVD to reserve space on the DVD and that can be bad 0855 // if this size is wrong 0856 // With growisofs 5.15 we have the option to specify the size of the image to be written in DAO mode. 0857 // 0858 // bool sizeWithDao = ( k3bcore->externalBinManager()->binObject( "growisofs" ) && 0859 // k3bcore->externalBinManager()->binObject( "growisofs" )->version >= K3b::Version( 5, 15, -1 ) ); 0860 0861 0862 // TODO: check for feature 0x21 0863 0864 if( m & K3b::Device::MEDIA_DVD_RW_OVWR ) { 0865 emit infoMessage( i18n("Writing DVD-RW in restricted overwrite mode."), MessageInfo ); 0866 d->usedWritingMode = K3b::WritingModeRestrictedOverwrite; 0867 } 0868 else if( m & (K3b::Device::MEDIA_DVD_RW_SEQ| 0869 K3b::Device::MEDIA_DVD_RW) ) { 0870 if( m_writingMode == K3b::WritingModeSao ) { 0871 // ( m_writingMode == K3b::WritingModeAuto && 0872 // ( sizeWithDao || !m_onTheFly ) ) ) { 0873 emit infoMessage( i18n("Writing DVD-RW in DAO mode."), MessageInfo ); 0874 d->usedWritingMode = K3b::WritingModeSao; 0875 } 0876 else { 0877 emit infoMessage( i18n("Writing DVD-RW in incremental mode."), MessageInfo ); 0878 d->usedWritingMode = K3b::WritingModeIncrementalSequential; 0879 } 0880 } 0881 else { 0882 0883 // FIXME: DVD-R DL jump and stuff 0884 0885 if( m_writingMode == K3b::WritingModeRestrictedOverwrite ) 0886 emit infoMessage( i18n("Restricted Overwrite is not possible with DVD-R media."), MessageInfo ); 0887 0888 if( m_writingMode == K3b::WritingModeSao ) { 0889 // ( m_writingMode == K3b::WritingModeAuto && 0890 // ( sizeWithDao || !m_onTheFly ) ) ) { 0891 emit infoMessage( i18n("Writing %1 in DAO mode.",K3b::Device::mediaTypeString(m, true) ), MessageInfo ); 0892 d->usedWritingMode = K3b::WritingModeSao; 0893 } 0894 else { 0895 emit infoMessage( i18n("Writing %1 in incremental mode.",K3b::Device::mediaTypeString(m, true) ), MessageInfo ); 0896 d->usedWritingMode = K3b::WritingModeIncrementalSequential; 0897 } 0898 } 0899 } 0900 0901 0902 // ------------------------------- 0903 // Blu-ray 0904 // ------------------------------- 0905 else { 0906 d->usedWritingMode = K3b::WritingModeSao; 0907 0908 if( m_simulate ) { 0909 if( !questionYesNo( i18n("%1 media do not support write simulation. " 0910 "Do you really want to continue? The disc will actually be " 0911 "written to.", Device::mediaTypeString(m, true)), 0912 i18n("No Simulation with %1", Device::mediaTypeString(m, true)) ) ) { 0913 cancel(); 0914 return false; 0915 } 0916 0917 m_simulate = false; 0918 emit newTask( i18n("Writing BD copy") ); 0919 } 0920 0921 emit infoMessage( i18n("Writing %1.", Device::mediaTypeString(m, true) ), MessageInfo ); 0922 } 0923 } 0924 0925 return true; 0926 } 0927 0928 0929 0930 void K3b::DvdCopyJob::removeImageFiles() 0931 { 0932 if( QFile::exists( m_imagePath ) ) { 0933 d->imageFile.remove(); 0934 emit infoMessage( i18n("Removed image file %1",m_imagePath), K3b::Job::MessageSuccess ); 0935 } 0936 } 0937 0938 0939 QString K3b::DvdCopyJob::jobDescription() const 0940 { 0941 if( m_onlyCreateImage ) { 0942 return i18n("Creating Image"); 0943 } 0944 else { 0945 if( m_onTheFly ) 0946 return i18n("Copying DVD or BD On-The-Fly"); 0947 else 0948 return i18n("Copying DVD or BD"); 0949 } 0950 } 0951 0952 0953 QString K3b::DvdCopyJob::jobDetails() const 0954 { 0955 return i18np("Creating 1 copy", 0956 "Creating %1 copies", 0957 (m_simulate||m_onlyCreateImage) ? 1 : m_copies ); 0958 } 0959 0960 0961 QString K3b::DvdCopyJob::jobSource() const 0962 { 0963 if( Device::Device* device = readingDevice() ) 0964 return device->vendor() + ' ' + device->description(); 0965 else 0966 return QString(); 0967 } 0968 0969 0970 QString K3b::DvdCopyJob::jobTarget() const 0971 { 0972 if( Device::Device* device = writer() ) 0973 return device->vendor() + ' ' + device->description(); 0974 else 0975 return m_imagePath; 0976 } 0977 0978 0979 void K3b::DvdCopyJob::setVerifyData( bool b ) 0980 { 0981 d->verifyData = b; 0982 } 0983 0984 #include "moc_k3bdvdcopyjob.cpp"