File: checksumverifyoperation.cpp

package info (click to toggle)
qflipper 1.3.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 12,320 kB
  • sloc: cpp: 18,500; sh: 247; ansic: 191; xml: 38; python: 14; makefile: 5
file content (149 lines) | stat: -rw-r--r-- 5,031 bytes parent folder | download
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();
}