diff --git a/autotests/jobtest.h b/autotests/jobtest.h --- a/autotests/jobtest.h +++ b/autotests/jobtest.h @@ -109,6 +109,9 @@ void createSymlinkWithOverwriteShouldWork(); void createBrokenSymlink(); + void cancelCopyAndCleanDest(); + void cancelCopyAndCleanDest_data(); + // Remote tests //void copyFileToSystem(); diff --git a/autotests/jobtest.cpp b/autotests/jobtest.cpp --- a/autotests/jobtest.cpp +++ b/autotests/jobtest.cpp @@ -2044,3 +2044,59 @@ } } +void JobTest::cancelCopyAndCleanDest_data() +{ + QTest::addColumn("suspend"); + QTest::addColumn("overwrite"); + + QTest::newRow("suspend job (without overwrite flag)") << true << false; + QTest::newRow("don't suspend job (without overwrite flag)") << false << false; + +#ifndef Q_OS_WIN + QTest::newRow("suspend job (with overwrite flag)") << true << true; + QTest::newRow("don't suspend job (with overwrite flag)") << false << true; +#endif +} + +void JobTest::cancelCopyAndCleanDest() +{ + QFETCH(bool, suspend); + QFETCH(bool, overwrite); + + const QString baseDir = homeTmpDir(); + const QString srcTemplate = baseDir + QStringLiteral("testfile_XXXXXX"); + const QString destFile = baseDir + QStringLiteral("testfile_copy"); + + + QTemporaryFile f(srcTemplate); + if (!f.open()) { + qFatal("Couldn't open %s", qPrintable(f.fileName())); + } + f.seek(9999999); + f.write("0"); + f.close(); + QCOMPARE(f.size(), 10000000); //~10MB + + if (overwrite) { + createTestFile(destFile); + } + + KIO::JobFlag m_overwriteFlag = overwrite ? KIO::Overwrite : KIO::DefaultFlags; + KIO::FileCopyJob *copyJob = KIO::file_copy(QUrl::fromLocalFile(f.fileName()), QUrl::fromLocalFile(destFile), -1, KIO::HideProgressInfo | m_overwriteFlag); + copyJob->setUiDelegate(nullptr); + QSignalSpy spyProcessedSize(copyJob, &KIO::Job::processedSize); + connect(copyJob, &KIO::Job::processedSize, this, [destFile, suspend, overwrite](KJob *job, qulonglong processedSize) { + if (processedSize > 0) { + const QString destToCheck = (!overwrite) ? destFile : destFile + QStringLiteral(".part"); + QVERIFY(QFile::exists(destToCheck)); + if (suspend) { + job->suspend(); + } + job->kill(); + QVERIFY(!QFile::exists(destToCheck)); + } + }); + QVERIFY(!copyJob->exec()); + QCOMPARE(spyProcessedSize.count(), 1); +} + diff --git a/src/core/filecopyjob.h b/src/core/filecopyjob.h --- a/src/core/filecopyjob.h +++ b/src/core/filecopyjob.h @@ -71,6 +71,7 @@ bool doSuspend() override; bool doResume() override; + bool doKill() override; Q_SIGNALS: /** diff --git a/src/core/filecopyjob.cpp b/src/core/filecopyjob.cpp --- a/src/core/filecopyjob.cpp +++ b/src/core/filecopyjob.cpp @@ -20,6 +20,7 @@ #include "filecopyjob.h" #include "job_p.h" +#include #include #include "kprotocolmanager.h" #include "scheduler.h" @@ -41,7 +42,7 @@ bool move, JobFlags flags) : m_sourceSize(filesize_t(-1)), m_src(src), m_dest(dest), m_moveJob(nullptr), m_copyJob(nullptr), m_delJob(nullptr), m_chmodJob(nullptr), m_getJob(nullptr), m_putJob(nullptr), m_permissions(permissions), - m_move(move), m_mustChmod(0), m_flags(flags) + m_move(move), m_mustChmod(0), m_bFileCopyInProgress(false), m_flags(flags) { } KIO::filesize_t m_sourceSize; @@ -60,6 +61,7 @@ bool m_canResume: 1; bool m_resumeAnswerSent: 1; bool m_mustChmod: 1; + bool m_bFileCopyInProgress: 1; JobFlags m_flags; void startBestCopyMethod(); @@ -241,8 +243,10 @@ } }); - q->connect(job, &KJob::processedSize, q, [q](KJob *job, qulonglong processedSize) { - Q_UNUSED(job); + q->connect(job, &KJob::processedSize, q, [q, this](KJob *job, qulonglong processedSize) { + if (job == m_copyJob) { + m_bFileCopyInProgress = processedSize > 0; + } q->setProcessedAmount(KJob::Bytes, processedSize); }); @@ -469,6 +473,12 @@ Q_D(FileCopyJob); //qDebug() << "this=" << this << "job=" << job; removeSubjob(job); + + // If result comes from copyjob then we are not writing anymore. + if (job == d->m_copyJob) { + d->m_bFileCopyInProgress = false; + } + // Did job have an error ? if (job->error()) { if ((job == d->m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) { @@ -566,6 +576,26 @@ } } +bool FileCopyJob::doKill() +{ + Q_D(FileCopyJob); + + // If we are interrupted in the middle of file copying, + // we may end up with corrupted file at the destination. + // It is better to clean up this file. If a copy is being + // made as part of move operation then delete the dest only if + // source file is intact (m_delJob == NULL). + if (d->m_bFileCopyInProgress && d->m_copyJob && d->m_dest.isLocalFile()) { + if (d->m_flags & Overwrite) { + QFile::remove(d->m_dest.toLocalFile() + QStringLiteral(".part")); + } else { + QFile::remove(d->m_dest.toLocalFile()); + } + } + + return Job::doKill(); +} + FileCopyJob *KIO::file_copy(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags) {