diff --git a/autotests/jobtest.h b/autotests/jobtest.h --- a/autotests/jobtest.h +++ b/autotests/jobtest.h @@ -42,6 +42,7 @@ void storedPutIODevice(); void storedPutIODeviceFile(); void storedPutIODeviceTempFile(); + void storedPutIODeviceFastDevice(); void storedPutIODeviceSlowDevice(); void storedPutIODeviceSlowDeviceBigChunk(); void copyFileToSamePartition(); diff --git a/autotests/jobtest.cpp b/autotests/jobtest.cpp --- a/autotests/jobtest.cpp +++ b/autotests/jobtest.cpp @@ -297,6 +297,42 @@ QVERIFY(QFile::remove(dest)); } +void JobTest::storedPutIODeviceFastDevice() +{ + const QString filePath = homeTmpDir() + "fileFromHome"; + const QUrl u = QUrl::fromLocalFile(filePath); + const QByteArray putDataContents = "This is the put data"; + QBuffer putDataBuffer; + QVERIFY(putDataBuffer.open(QIODevice::ReadWrite)); + + KIO::StoredTransferJob *job = KIO::storedPut(&putDataBuffer, u, 0600, KIO::Overwrite | KIO::HideProgressInfo); + QSignalSpy spyPercent(job, SIGNAL(percent(KJob*,ulong))); + QVERIFY(spyPercent.isValid()); + QDateTime mtime = QDateTime::currentDateTime().addSecs(-30); // 30 seconds ago + mtime.setTime_t(mtime.toTime_t()); // hack for losing the milliseconds + job->setModificationTime(mtime); + job->setTotalSize(putDataContents.size()); + job->setUiDelegate(nullptr); + job->setAsyncDataEnabled(true); + + // Emit the readChannelFinished even before the job has had time to start + const auto pos = putDataBuffer.pos(); + int size = putDataBuffer.write(putDataContents); + putDataBuffer.seek(pos); + putDataBuffer.readChannelFinished(); + + QVERIFY(job->exec()); + QCOMPARE(size, putDataContents.size()); + QCOMPARE(putDataBuffer.bytesAvailable(), 0); + + QFileInfo fileInfo(filePath); + QVERIFY(fileInfo.exists()); + QCOMPARE(fileInfo.size(), (long long)putDataContents.size()); + QCOMPARE((int)fileInfo.permissions(), (int)(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser)); + QCOMPARE(fileInfo.lastModified(), mtime); + QVERIFY(!spyPercent.isEmpty()); +} + void JobTest::storedPutIODeviceSlowDevice() { const QString filePath = homeTmpDir() + "fileFromHome"; diff --git a/src/core/job_p.h b/src/core/job_p.h --- a/src/core/job_p.h +++ b/src/core/job_p.h @@ -235,14 +235,15 @@ const QByteArray &_staticData) : SimpleJobPrivate(url, command, packedArgs), m_internalSuspended(false), m_errorPage(false), - staticData(_staticData), m_isMimetypeEmitted(false), m_subJob(nullptr) + staticData(_staticData), m_isMimetypeEmitted(false), + m_closedBeforeStart(false), m_subJob(nullptr) { } inline TransferJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs, QIODevice *ioDevice) : SimpleJobPrivate(url, command, packedArgs), m_internalSuspended(false), m_errorPage(false), - m_isMimetypeEmitted(false), m_subJob(nullptr), + m_isMimetypeEmitted(false), m_closedBeforeStart(false), m_subJob(nullptr), m_outgoingDataSource(QPointer(ioDevice)) { } @@ -253,6 +254,7 @@ QList m_redirectionList; QString m_mimetype; bool m_isMimetypeEmitted; + bool m_closedBeforeStart; TransferJob *m_subJob; QPointer m_outgoingDataSource; @@ -279,6 +281,7 @@ */ virtual void slotDataReqFromDevice(); void slotIODeviceClosed(); + void slotIODeviceClosedBeforeStart(); void slotErrorPage(); void slotCanResume(KIO::filesize_t offset); diff --git a/src/core/transferjob.h b/src/core/transferjob.h --- a/src/core/transferjob.h +++ b/src/core/transferjob.h @@ -206,6 +206,7 @@ Q_PRIVATE_SLOT(d_func(), void slotSubUrlData(KIO::Job *, const QByteArray &)) Q_PRIVATE_SLOT(d_func(), void slotDataReqFromDevice()) Q_PRIVATE_SLOT(d_func(), void slotIODeviceClosed()) + Q_PRIVATE_SLOT(d_func(), void slotIODeviceClosedBeforeStart()) Q_DECLARE_PRIVATE(TransferJob) // A FileCopyJob may control one or more TransferJobs diff --git a/src/core/transferjob.cpp b/src/core/transferjob.cpp --- a/src/core/transferjob.cpp +++ b/src/core/transferjob.cpp @@ -35,6 +35,12 @@ if (d->m_command == CMD_PUT) { d->m_extraFlags |= JobPrivate::EF_TransferJobDataSent; } + + if (d->m_outgoingDataSource) { + connect(d->m_outgoingDataSource, SIGNAL(readChannelFinished()), + SLOT(slotIODeviceClosedBeforeStart())); + } + } TransferJob::~TransferJob() @@ -312,7 +318,13 @@ SLOT(slotDataReqFromDevice())); q->connect(m_outgoingDataSource, SIGNAL(readChannelFinished()), SLOT(slotIODeviceClosed())); - if (m_outgoingDataSource->bytesAvailable() > 0) { + // We don't really need to disconnect since we're never checking + // m_closedBeforeStart again but it's the proper thing to do logically + QObject::disconnect(m_outgoingDataSource, SIGNAL(readChannelFinished()), + q, SLOT(slotIODeviceClosedBeforeStart())); + if (m_closedBeforeStart) { + QMetaObject::invokeMethod(q, "slotIODeviceClosed", Qt::QueuedConnection); + } else if (m_outgoingDataSource->bytesAvailable() > 0) { QMetaObject::invokeMethod(q, "slotDataReqFromDevice", Qt::QueuedConnection); } } else { @@ -425,6 +437,11 @@ } } +void TransferJobPrivate::slotIODeviceClosedBeforeStart() +{ + m_closedBeforeStart = true; +} + void TransferJobPrivate::slotIODeviceClosed() { Q_Q(TransferJob);