1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
#include "checksumverifyoperation.h"
#include <QDir>
#include <QFile>
#include <QDirIterator>
#include <QCryptographicHash>
#include <QDebug>
#include <QLoggingCategory>
#include "flipperzero/devicestate.h"
#include "flipperzero/protobufsession.h"
#include "flipperzero/rpc/storagemd5sumoperation.h"
Q_DECLARE_LOGGING_CATEGORY(CATEGORY_DEBUG)
using namespace Flipper;
using namespace Zero;
ChecksumVerifyOperation::ChecksumVerifyOperation(ProtobufSession *rpc, DeviceState *deviceState,
const QList<QUrl> &urlsToCheck, const QByteArray &remoteRootPath, QObject *parent):
AbstractUtilityOperation(rpc, deviceState, parent),
m_remoteRootPath(remoteRootPath),
m_urlsToCheck(urlsToCheck)
{}
const QString ChecksumVerifyOperation::description() const
{
const auto numFiles = m_urlsToCheck.size();
return QStringLiteral("Verify checksum of %1 %2 @%3").arg(QString::number(numFiles), (numFiles == 1) ? "item" : "items", deviceState()->deviceInfo().name);
}
const QList<QUrl> &ChecksumVerifyOperation::changedUrls() const
{
return m_changedUrls;
}
void ChecksumVerifyOperation::nextStateLogic()
{
if(operationState() == Ready) {
setOperationState(ReadingFileList);
readFileList();
} else if(operationState() == ReadingFileList) {
setOperationState(VerifyingMd5Sum);
verifyMd5Sums();
} else if(operationState() == VerifyingMd5Sum) {
finish();
} else {
finishWithError(BackendError::UnknownError, QStringLiteral("Unexpected state"));
}
}
void ChecksumVerifyOperation::readFileList()
{
for(const auto &url : qAsConst(m_urlsToCheck)) {
const QFileInfo fileInfo(url.adjusted(QUrl::StripTrailingSlash).toLocalFile());
const QDir topmostDir = fileInfo.dir();
if(fileInfo.isFile()) {
m_flatFileList.append({fileInfo, topmostDir});
} else if(fileInfo.isDir()) {
QDir dir(fileInfo.absoluteFilePath());
dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden);
dir.setSorting(QDir::Name | QDir::DirsFirst);
QDirIterator it(dir, QDirIterator::Subdirectories);
while(it.hasNext()) {
const QFileInfo fileInfo(it.next());
if(fileInfo.isFile()) {
m_flatFileList.append({fileInfo, topmostDir});
}
}
}
}
advanceOperationState();
}
void ChecksumVerifyOperation::verifyMd5Sums()
{
const auto totalSize = std::accumulate(m_flatFileList.cbegin(), m_flatFileList.cend(), (qint64)0,
[](qint64 sum, const FileListElement &arg) {
return sum + arg.fileInfo.size();
});
auto filesRemaining = m_flatFileList.size();
setProgress(0.0);
for(const auto &entry : qAsConst(m_flatFileList)) {
const auto &fileInfo = entry.fileInfo;
const auto &topmostDir = entry.topmostDir;
const auto absoluteLocalFilePath = fileInfo.absoluteFilePath();
const auto relativeLocalFilePath = topmostDir.relativeFilePath(absoluteLocalFilePath);
const auto absoluteRemoteFilePath = m_remoteRootPath + QByteArrayLiteral("/") + relativeLocalFilePath.toLocal8Bit();
const auto isLastFile = (--filesRemaining == 0);
auto *operation = rpc()->storageMd5Sum(absoluteRemoteFilePath);
connect(operation, &AbstractOperation::finished, this, [=]() {
if(operation->isError()) {
finishWithError(operation->error(), operation->errorString());
return;
}
const auto checksumRemote = operation->md5Sum();
if(checksumRemote.isEmpty()) {
m_changedUrls.append(QUrl::fromLocalFile(fileInfo.absoluteFilePath()));
qCDebug(CATEGORY_DEBUG) << "File does not exist:" << absoluteRemoteFilePath;
} else {
const auto checksumLocal = calculateMd5Sum(fileInfo);
if(checksumRemote != checksumLocal) {
m_changedUrls.append(QUrl::fromLocalFile(fileInfo.absoluteFilePath()));
qCDebug(CATEGORY_DEBUG) << "File changed:" << absoluteRemoteFilePath
<< "old:" << checksumRemote << "new:" << checksumLocal;
} else {
qCDebug(CATEGORY_DEBUG) << "File is identical:" << absoluteRemoteFilePath;
}
}
setProgress(progress() + 100.0 * fileInfo.size() / totalSize);
if(isLastFile) {
advanceOperationState();
}
});
}
}
const QByteArray ChecksumVerifyOperation::calculateMd5Sum(const QFileInfo &fileInfo)
{
QFile file(fileInfo.absoluteFilePath());
if(!file.open(QIODevice::ReadOnly)) {
return QByteArray();
}
QCryptographicHash hash(QCryptographicHash::Md5);
hash.addData(&file);
return hash.result().toHex();
}
|