diff --git a/src/common/checksums.cpp b/src/common/checksums.cpp index c0503cb72..258abe086 100644 --- a/src/common/checksums.cpp +++ b/src/common/checksums.cpp @@ -18,6 +18,7 @@ #include "config.h" #include "filesystembase.h" #include "common/checksums.h" +#include "asserts.h" #include #include @@ -225,21 +226,42 @@ QByteArray ComputeChecksum::checksumType() const void ComputeChecksum::start(const QString &filePath) { qCInfo(lcChecksums) << "Computing" << checksumType() << "checksum of" << filePath << "in a thread"; + startImpl(std::make_unique(filePath)); +} +void ComputeChecksum::start(std::unique_ptr device) +{ + ENFORCE(device); + qCInfo(lcChecksums) << "Computing" << checksumType() << "checksum of device" << device.get() << "in a thread"; + ASSERT(!device->parent()); + + startImpl(std::move(device)); +} + +void ComputeChecksum::startImpl(std::unique_ptr device) +{ connect(&_watcher, &QFutureWatcherBase::finished, this, &ComputeChecksum::slotCalculationDone, Qt::UniqueConnection); - // Capturing "file" extends its lifetime to the lifetime of the new thread. + // We'd prefer to move the unique_ptr into the lambda, but that's + // awkward with the C++ standard we're on + auto sharedDevice = QSharedPointer(device.release()); + // Bug: The thread will keep running even if ComputeChecksum is deleted. auto type = checksumType(); - _watcher.setFuture(QtConcurrent::run([filePath, type]() { - QFile file(filePath); - if (!file.open(QIODevice::ReadOnly)) { - qCWarning(lcChecksums) << "Could not open file" << filePath << "for reading to compute a checksum" << file.errorString(); + _watcher.setFuture(QtConcurrent::run([sharedDevice, type]() { + if (!sharedDevice->open(QIODevice::ReadOnly)) { + if (auto file = qobject_cast(sharedDevice.data())) { + qCWarning(lcChecksums) << "Could not open file" << file->fileName() + << "for reading to compute a checksum" << file->errorString(); + } else { + qCWarning(lcChecksums) << "Could not open device" << sharedDevice.data() + << "for reading to compute a checksum" << sharedDevice->errorString(); + } return QByteArray(); } - return ComputeChecksum::computeNow(&file, type); + return ComputeChecksum::computeNow(sharedDevice.data(), type); })); } @@ -328,6 +350,12 @@ void ValidateChecksumHeader::start(const QString &filePath, const QByteArray &ch calculator->start(filePath); } +void ValidateChecksumHeader::start(std::unique_ptr device, const QByteArray &checksumHeader) +{ + if (auto calculator = prepareStart(checksumHeader)) + calculator->start(std::move(device)); +} + void ValidateChecksumHeader::slotChecksumCalculated(const QByteArray &checksumType, const QByteArray &checksum) { diff --git a/src/common/checksums.h b/src/common/checksums.h index fbd92b322..1e5151c7a 100644 --- a/src/common/checksums.h +++ b/src/common/checksums.h @@ -100,6 +100,16 @@ public: */ void start(const QString &filePath); + /** + * Computes the checksum for the given device. + * + * done() is emitted when the calculation finishes. + * + * The device ownership transfers into the thread that + * will compute the checksum. It must not have a parent. + */ + void start(std::unique_ptr device); + /** * Computes the checksum synchronously. */ @@ -117,6 +127,8 @@ private slots: void slotCalculationDone(); private: + void startImpl(std::unique_ptr device); + QByteArray _checksumType; // watcher for the checksum calculation thread @@ -134,7 +146,7 @@ public: explicit ValidateChecksumHeader(QObject *parent = nullptr); /** - * Check a device's actual checksum against the provided checksumHeader + * Check a file's actual checksum against the provided checksumHeader * * If no checksum is there, or if a correct checksum is there, the signal validated() * will be emitted. In case of any kind of error, the signal validationFailed() will @@ -142,6 +154,16 @@ public: */ void start(const QString &filePath, const QByteArray &checksumHeader); + /** + * Check a device's actual checksum against the provided checksumHeader + * + * Like the other start() but works on an device. + * + * The device ownership transfers into the thread that + * will compute the checksum. It must not have a parent. + */ + void start(std::unique_ptr device, const QByteArray &checksumHeader); + signals: void validated(const QByteArray &checksumType, const QByteArray &checksum); void validationFailed(const QString &errMsg);