1 /*
2 SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org>
3 SPDX-FileCopyrightText: 2011 Michal Malek <michalm@jabster.pl>
4 SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
5
6 SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "k3bclonejob.h"
10
11 #include "k3breadcdreader.h"
12 #include "k3bcdrecordwriter.h"
13 #include "k3bexternalbinmanager.h"
14 #include "k3bdevice.h"
15 #include "k3bdevicehandler.h"
16 #include "k3bglobals.h"
17 #include "k3bcore.h"
18 #include "k3bclonetocreader.h"
19 #include "k3bglobalsettings.h"
20 #include "k3b_i18n.h"
21
22 #include <QDebug>
23 #include <QFile>
24 #include <QFileInfo>
25
26
27
28 class K3b::CloneJob::Private
29 {
30 public:
Private()31 Private()
32 : doneCopies(0) {
33 }
34
35 int doneCopies;
36 };
37
38
CloneJob(K3b::JobHandler * hdl,QObject * parent)39 K3b::CloneJob::CloneJob( K3b::JobHandler* hdl, QObject* parent )
40 : K3b::BurnJob( hdl, parent ),
41 m_writerDevice(0),
42 m_readerDevice(0),
43 m_writerJob(0),
44 m_readcdReader(0),
45 m_removeImageFiles(false),
46 m_canceled(false),
47 m_running(false),
48 m_simulate(false),
49 m_speed(1),
50 m_copies(1),
51 m_onlyCreateImage(false),
52 m_onlyBurnExistingImage(false),
53 m_readRetries(128)
54 {
55 d = new Private;
56 }
57
58
~CloneJob()59 K3b::CloneJob::~CloneJob()
60 {
61 delete d;
62 }
63
64
start()65 void K3b::CloneJob::start()
66 {
67 jobStarted();
68
69 m_canceled = false;
70 m_running = true;
71
72
73 // TODO: check the cd size and warn the user if not enough space
74
75 //
76 // We first check if cdrecord has clone support
77 // The readcdReader will check the same for readcd
78 //
79 const K3b::ExternalBin* cdrecordBin = k3bcore->externalBinManager()->binObject( "cdrecord" );
80 if( !cdrecordBin ) {
81 emit infoMessage( i18n("Could not find %1 executable.",QString("cdrecord")), MessageError );
82 jobFinished(false);
83 m_running = false;
84 return;
85 }
86 else if( !cdrecordBin->hasFeature( "clone" ) ) {
87 emit infoMessage( i18n("Cdrecord version %1 does not have cloning support.",cdrecordBin->version()), MessageError );
88 jobFinished(false);
89 m_running = false;
90 return;
91 }
92
93 if( (!m_onlyCreateImage && !writer()) ||
94 (!m_onlyBurnExistingImage && !readingDevice()) ) {
95 emit infoMessage( i18n("No device set."), MessageError );
96 jobFinished(false);
97 m_running = false;
98 return;
99 }
100
101 if( !m_onlyCreateImage ) {
102 if( !writer()->supportsWritingMode( K3b::Device::WRITINGMODE_RAW_R96R ) &&
103 !writer()->supportsWritingMode( K3b::Device::WRITINGMODE_RAW_R16 ) ) {
104 emit infoMessage( i18n("CD writer %1 (%2) does not support cloning.",
105 writer()->vendor(),
106 writer()->description()), MessageError );
107 m_running = false;
108 jobFinished(false);
109 return;
110 }
111 }
112
113 if( m_imagePath.isEmpty() ) {
114 m_imagePath = K3b::findTempFile( "img" );
115 }
116 else if( QFileInfo(m_imagePath).isDir() ) {
117 m_imagePath = K3b::findTempFile( "img", m_imagePath );
118 }
119
120 if( m_onlyBurnExistingImage ) {
121 startWriting();
122 }
123 else {
124 emit burning( false );
125
126 prepareReader();
127
128 if( waitForMedium( readingDevice(),
129 K3b::Device::STATE_COMPLETE,
130 K3b::Device::MEDIA_WRITABLE_CD|K3b::Device::MEDIA_CD_ROM ) == Device::MEDIA_UNKNOWN ) {
131 m_running = false;
132 emit canceled();
133 jobFinished(false);
134 return;
135 }
136
137 emit newTask( i18n("Reading clone image") );
138
139 m_readcdReader->start();
140 }
141 }
142
143
prepareReader()144 void K3b::CloneJob::prepareReader()
145 {
146 if( !m_readcdReader ) {
147 m_readcdReader = new K3b::ReadcdReader( this, this );
148 connect( m_readcdReader, SIGNAL(percent(int)), this, SLOT(slotReadingPercent(int)) );
149 connect( m_readcdReader, SIGNAL(percent(int)), this, SIGNAL(subPercent(int)) );
150 connect( m_readcdReader, SIGNAL(processedSize(int,int)), this, SIGNAL(processedSubSize(int,int)) );
151 connect( m_readcdReader, SIGNAL(finished(bool)), this, SLOT(slotReadingFinished(bool)) );
152 connect( m_readcdReader, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) );
153 connect( m_readcdReader, SIGNAL(newTask(QString)), this, SIGNAL(newSubTask(QString)) );
154 connect( m_readcdReader, SIGNAL(debuggingOutput(QString,QString)),
155 this, SIGNAL(debuggingOutput(QString,QString)) );
156 }
157
158 m_readcdReader->setReadDevice( readingDevice() );
159 m_readcdReader->setReadSpeed( 0 ); // MAX
160 m_readcdReader->setDisableCorrection( m_noCorrection );
161 m_readcdReader->setImagePath( m_imagePath );
162 m_readcdReader->setClone( true );
163 m_readcdReader->setRetries( m_readRetries );
164 }
165
166
prepareWriter()167 void K3b::CloneJob::prepareWriter()
168 {
169 if( !m_writerJob ) {
170 m_writerJob = new K3b::CdrecordWriter( writer(), this, this );
171 connect( m_writerJob, SIGNAL(infoMessage(QString,int)), this, SIGNAL(infoMessage(QString,int)) );
172 connect( m_writerJob, SIGNAL(percent(int)), this, SLOT(slotWriterPercent(int)) );
173 connect( m_writerJob, SIGNAL(percent(int)), this, SIGNAL(subPercent(int)) );
174 connect( m_writerJob, SIGNAL(nextTrack(int,int)), this, SLOT(slotWriterNextTrack(int,int)) );
175 connect( m_writerJob, SIGNAL(processedSize(int,int)), this, SIGNAL(processedSubSize(int,int)) );
176 connect( m_writerJob, SIGNAL(buffer(int)), this, SIGNAL(bufferStatus(int)) );
177 connect( m_writerJob, SIGNAL(deviceBuffer(int)), this, SIGNAL(deviceBuffer(int)) );
178 connect( m_writerJob, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)), this, SIGNAL(writeSpeed(int,K3b::Device::SpeedMultiplicator)) );
179 connect( m_writerJob, SIGNAL(finished(bool)), this, SLOT(slotWriterFinished(bool)) );
180 // connect( m_writerJob, SIGNAL(newTask(QString)), this, SIGNAL(newTask(QString)) );
181 connect( m_writerJob, SIGNAL(newSubTask(QString)), this, SIGNAL(newSubTask(QString)) );
182 connect( m_writerJob, SIGNAL(debuggingOutput(QString,QString)),
183 this, SIGNAL(debuggingOutput(QString,QString)) );
184 }
185
186 m_writerJob->clearArguments();
187 m_writerJob->setWritingMode( K3b::WritingModeRaw );
188 m_writerJob->setClone( true );
189 m_writerJob->setSimulate( m_simulate );
190 m_writerJob->setBurnSpeed( m_speed );
191 m_writerJob->addArgument( m_imagePath );
192 }
193
194
cancel()195 void K3b::CloneJob::cancel()
196 {
197 if( m_running ) {
198 m_canceled = true;
199 if( m_readcdReader )
200 m_readcdReader->cancel();
201 if( m_writerJob )
202 m_writerJob->cancel();
203 }
204 }
205
206
slotWriterPercent(int p)207 void K3b::CloneJob::slotWriterPercent( int p )
208 {
209 if( m_onlyBurnExistingImage )
210 emit percent( (int)((double)(d->doneCopies)*100.0/(double)(m_copies) + (double)p/(double)(m_copies)) );
211 else
212 emit percent( (int)((double)(1+d->doneCopies)*100.0/(double)(1+m_copies) + (double)p/(double)(1+m_copies)) );
213 }
214
215
slotWriterNextTrack(int t,int tt)216 void K3b::CloneJob::slotWriterNextTrack( int t, int tt )
217 {
218 emit newSubTask( i18n("Writing Track %1 of %2",t,tt) );
219 }
220
221
slotWriterFinished(bool success)222 void K3b::CloneJob::slotWriterFinished( bool success )
223 {
224 if( m_canceled ) {
225 removeImageFiles();
226 m_running = false;
227 emit canceled();
228 jobFinished(false);
229 return;
230 }
231
232 if( success ) {
233 d->doneCopies++;
234
235 emit infoMessage( i18n("Successfully written clone copy %1.",d->doneCopies), MessageInfo );
236
237 if( d->doneCopies < m_copies ) {
238 K3b::Device::eject( writer() );
239 startWriting();
240 }
241 else {
242 if ( k3bcore->globalSettings()->ejectMedia() ) {
243 K3b::Device::eject( writer() );
244 }
245
246 if( m_removeImageFiles )
247 removeImageFiles();
248 m_running = false;
249 jobFinished(true);
250 }
251 }
252 else {
253 removeImageFiles();
254 m_running = false;
255 jobFinished(false);
256 }
257 }
258
259
slotReadingPercent(int p)260 void K3b::CloneJob::slotReadingPercent( int p )
261 {
262 emit percent( m_onlyCreateImage ? p : (int)((double)p/(double)(1+m_copies)) );
263 }
264
265
slotReadingFinished(bool success)266 void K3b::CloneJob::slotReadingFinished( bool success )
267 {
268 if( m_canceled ) {
269 removeImageFiles();
270 m_running = false;
271 emit canceled();
272 jobFinished(false);
273 return;
274 }
275
276 if( success ) {
277 //
278 // Make a quick test if the image is really valid.
279 // Readcd does not seem to have proper exit codes
280 //
281 K3b::CloneTocReader ctr( m_imagePath );
282 if( ctr.isValid() ) {
283 emit infoMessage( i18n("Successfully read disk."), MessageInfo );
284 if( m_onlyCreateImage ) {
285 m_running = false;
286 jobFinished(true);
287 }
288 else {
289 if( writer() == readingDevice() )
290 K3b::Device::eject( writer() );
291 startWriting();
292 }
293 }
294 else {
295 emit infoMessage( i18n("Failed to read disk completely in clone mode."), MessageError );
296 removeImageFiles();
297 m_running = false;
298 jobFinished(false);
299 }
300 }
301 else {
302 emit infoMessage( i18n("Error while reading disk."), MessageError );
303 removeImageFiles();
304 m_running = false;
305 jobFinished(false);
306 }
307 }
308
309
startWriting()310 void K3b::CloneJob::startWriting()
311 {
312 emit burning( true );
313
314 // start writing
315 prepareWriter();
316
317 if( waitForMedium( writer(),
318 K3b::Device::STATE_EMPTY,
319 K3b::Device::MEDIA_WRITABLE_CD ) == Device::MEDIA_UNKNOWN ) {
320 removeImageFiles();
321 m_running = false;
322 emit canceled();
323 jobFinished(false);
324 return;
325 }
326
327 if( m_simulate )
328 emit newTask( i18n("Simulating clone copy") );
329 else
330 emit newTask( i18n("Writing clone copy %1",d->doneCopies+1) );
331
332 m_writerJob->start();
333 }
334
335
removeImageFiles()336 void K3b::CloneJob::removeImageFiles()
337 {
338 if( !m_onlyBurnExistingImage ) {
339 emit infoMessage( i18n("Removing image files."), MessageInfo );
340 if( QFile::exists( m_imagePath ) )
341 QFile::remove( m_imagePath );
342 if( QFile::exists( m_imagePath + ".toc" ) )
343 QFile::remove( m_imagePath + ".toc" );
344 }
345 }
346
347
jobDescription() const348 QString K3b::CloneJob::jobDescription() const
349 {
350 if( m_onlyCreateImage )
351 return i18n("Creating Clone Image");
352 else if( m_onlyBurnExistingImage ) {
353 if( m_simulate )
354 return i18n("Simulating Clone Image");
355 else
356 return i18n("Burning Clone Image");
357 }
358 else if( m_simulate )
359 return i18n("Simulating CD Cloning");
360 else
361 return i18n("Cloning CD");
362 }
363
364
jobDetails() const365 QString K3b::CloneJob::jobDetails() const
366 {
367 return i18np("Creating 1 clone copy",
368 "Creating %1 clone copies",
369 (m_simulate||m_onlyCreateImage) ? 1 : m_copies );
370 }
371
372
jobSource() const373 QString K3b::CloneJob::jobSource() const
374 {
375 if( m_readerDevice )
376 return m_readerDevice->vendor() + ' ' + m_readerDevice->description();
377 else
378 return QString();
379 }
380
381
jobTarget() const382 QString K3b::CloneJob::jobTarget() const
383 {
384 if( m_writerDevice )
385 return m_writerDevice->vendor() + ' ' + m_writerDevice->description();
386 else
387 return m_imagePath;
388 }
389
390
391