~aleteoryx/muditaos

6f21138e8587a063267c460f45b11efdb9121a25 — Marek Niepieklo 4 years ago 72ef95d
[CP-253] Add checksum to package transfer

Added CRC32 to FS download command
M module-services/service-desktop/WorkerDesktop.cpp => module-services/service-desktop/WorkerDesktop.cpp +29 -8
@@ 12,6 12,7 @@

#include <bsp/usb/usb.hpp>
#include <log.hpp>
#include <crc32.h>

#include <map>
#include <vector>


@@ 163,7 164,9 @@ bool WorkerDesktop::handleMessage(uint32_t queueID)
    return true;
}

sys::ReturnCodes WorkerDesktop::startDownload(const std::filesystem::path &destinationPath, uint32_t fileSize)
sys::ReturnCodes WorkerDesktop::startDownload(const std::filesystem::path &destinationPath,
                                              uint32_t fileSize,
                                              std::string fileCrc32)
{
    filePath = destinationPath;
    fileDes  = fopen(filePath.c_str(), "w");


@@ 177,8 180,11 @@ sys::ReturnCodes WorkerDesktop::startDownload(const std::filesystem::path &desti
    startTransferTimer();

    writeFileSizeExpected = fileSize;
    expectedFileCrc32     = fileCrc32;
    rawModeEnabled        = true;

    digestCrc32.reset();

    LOG_DEBUG("startDownload all checks passed starting download");
    return sys::ReturnCodes::Success;
}


@@ 205,18 211,31 @@ void WorkerDesktop::reloadTransferTimer()

void WorkerDesktop::stopTransfer(const TransferFailAction action)
{
    LOG_DEBUG("stopTransfer %s",
              action == TransferFailAction::removeDesitnationFile ? "remove desination file" : "do nothing");
    parser.setState(parserFSM::State::NoMsg);
    rawModeEnabled = false;

    auto removeFile     = (action == TransferFailAction::removeDesitnationFile);
    auto responseStatus = removeFile ? parserFSM::http::Code::NotAcceptable : parserFSM::http::Code::Accepted;

    const auto fileCrc32  = digestCrc32.getHash();
    const auto crc32Match = expectedFileCrc32.empty() ? true : (expectedFileCrc32.compare(fileCrc32) == 0);

    if (!crc32Match && !removeFile) {
        responseStatus = parserFSM::http::Code::NotAcceptable;
        removeFile     = true;

        LOG_ERROR("File %s transfer CRC32 mismatch, expected: %s, actual: %s",
                  filePath.c_str(),
                  expectedFileCrc32.c_str(),
                  fileCrc32.c_str());
    }

    parserFSM::Context responseContext;
    responseContext.setResponseStatus((action == TransferFailAction::removeDesitnationFile)
                                          ? parserFSM::http::Code::NotAcceptable
                                          : parserFSM::http::Code::Accepted);
    responseContext.setResponseStatus(responseStatus);
    responseContext.setEndpoint(parserFSM::EndpointType::filesystemUpload);
    json11::Json responseJson = json11::Json::object{{parserFSM::json::fileSize, std::to_string(writeFileDataWritten)},
                                                     {parserFSM::json::fileName, filePath.filename().c_str()}};
                                                     {parserFSM::json::fileName, filePath.filename().c_str()},
                                                     {parserFSM::json::fileCrc32, fileCrc32.c_str()}};
    responseContext.setResponseBody(responseJson);

    // close the file descriptor


@@ 229,7 248,7 @@ void WorkerDesktop::stopTransfer(const TransferFailAction action)
    writeFileSizeExpected = 0;
    writeFileDataWritten  = 0;

    if (action == TransferFailAction::removeDesitnationFile) {
    if (removeFile) {
        try {
            if (!std::filesystem::remove(filePath)) {
                LOG_ERROR("stopTransfer can't delete  %s", filePath.c_str());


@@ 254,6 273,8 @@ void WorkerDesktop::rawDataReceived(void *dataPtr, uint32_t dataLen)
            return;
        }

        digestCrc32.add(dataPtr, dataLen);

        const uint32_t bytesWritten = std::fwrite(dataPtr, 1, dataLen, fileDes);

        if (bytesWritten != dataLen) {

M module-services/service-desktop/endpoints/filesystem/FilesystemEndpoint.cpp => module-services/service-desktop/endpoints/filesystem/FilesystemEndpoint.cpp +3 -2
@@ 129,14 129,15 @@ auto FilesystemEndpoint::runPost(Context &context) -> sys::ReturnCodes
        fs::path filePath    = context.getBody()[parserFSM::json::fileName].string_value();
        fs::path tmpFilePath = purefs::dir::getUpdatesOSPath() / filePath;

        uint32_t fileSize = context.getBody()[parserFSM::json::fileSize].int_value();
        const uint32_t fileSize = context.getBody()[parserFSM::json::fileSize].int_value();
        const auto fileCrc32    = context.getBody()[parserFSM::json::fileCrc32].string_value();

        LOG_DEBUG("got owner, file %s", tmpFilePath.c_str());

        if (isWritable(tmpFilePath)) {
            LOG_INFO("download %" PRIu32 " bytes to: %s", fileSize, tmpFilePath.c_str());

            if (owner->desktopWorker->startDownload(tmpFilePath, fileSize) == sys::ReturnCodes::Success) {
            if (owner->desktopWorker->startDownload(tmpFilePath, fileSize, fileCrc32) == sys::ReturnCodes::Success) {
                context.setResponseStatus(parserFSM::http::Code::Accepted);
                returnCode = sys::ReturnCodes::Success;
            }

M module-services/service-desktop/parser/ParserUtils.hpp => module-services/service-desktop/parser/ParserUtils.hpp +1 -0
@@ 99,6 99,7 @@ namespace parserFSM
        inline constexpr auto accessTechnology = "accessTechnology";
        inline constexpr auto fileName         = "fileName";
        inline constexpr auto fileSize         = "fileSize";
        inline constexpr auto fileCrc32        = "fileCrc32";
        inline constexpr auto update           = "update";
        inline constexpr auto updateInfo       = "updateInfo";
        inline constexpr auto updateError      = "updateError";

M module-services/service-desktop/service-desktop/WorkerDesktop.hpp => module-services/service-desktop/service-desktop/WorkerDesktop.hpp +10 -3
@@ 3,8 3,6 @@

#pragma once

#include <filesystem>
#include <atomic>
#include "Service/Message.hpp"
#include "Service/Service.hpp"
#include "Service/Worker.hpp"


@@ 14,6 12,11 @@
#include "bsp/usb/usb.hpp"
#include "USBSecurityModel.hpp"

#include <crc32.h>

#include <filesystem>
#include <atomic>

namespace constants
{
    constexpr auto usbSuspendTimeout = std::chrono::seconds{1};


@@ 47,7 50,9 @@ class WorkerDesktop : public sys::Worker, public bsp::USBDeviceListener
        return receiveQueue;
    }

    sys::ReturnCodes startDownload(const std::filesystem::path &destinationPath, const uint32_t fileSize);
    sys::ReturnCodes startDownload(const std::filesystem::path &destinationPath,
                                   const uint32_t fileSize,
                                   std::string fileCrc32);
    void stopTransfer(const TransferFailAction action);

    void cancelTransferOnTimeout();


@@ 72,6 77,7 @@ class WorkerDesktop : public sys::Worker, public bsp::USBDeviceListener
    FILE *fileDes                  = nullptr;
    uint32_t writeFileSizeExpected = 0;
    uint32_t writeFileDataWritten  = 0;
    std::string expectedFileCrc32;
    std::filesystem::path filePath;
    std::atomic<bool> rawModeEnabled = false;
    const sdesktop::USBSecurityModel &securityModel;


@@ 82,4 88,5 @@ class WorkerDesktop : public sys::Worker, public bsp::USBDeviceListener
    bsp::USBDeviceStatus usbStatus = bsp::USBDeviceStatus::Disconnected;

    std::shared_ptr<sys::CpuSentinel> cpuSentinel;
    CRC32 digestCrc32;
};

M test/send_update_package.py => test/send_update_package.py +20 -5
@@ 8,13 8,13 @@ import sys
import os.path
import json
import atexit
import binascii

sys.path.append(
    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))

from harness.harness import Harness
from harness.interface.defs import status
from harness.utils import Timeout
from harness.interface.error import TestError, Error
from functools import partial



@@ 25,8 25,13 @@ CHUNK_SIZE = 1024 * 16
def send_update_package(harness, package_filepath: str):
    file_size = os.path.getsize(package_filepath)
    file_name = package_filepath.split('/')[-1]
    with open(package_filepath, 'rb') as file:
        file_data = open(package_filepath,'rb').read()
        file_crc32 = format((binascii.crc32(file_data) & 0xFFFFFFFF), '08x')

    print(f"Sending {file_name}, size {file_size}, CRC32 {file_crc32}")

    body = {"command": "download", "fileName": file_name, "fileSize": file_size}
    body = {"command": "download", "fileName": file_name, "fileSize": file_size, "fileCrc32" : file_crc32}
    ret = harness.endpoint_request("filesystem", "post", body)

    if ret["status"] != status["Accepted"]:


@@ 56,6 61,12 @@ def send_update_package(harness, package_filepath: str):
            stat = body["status"]
            print(f"Transfer status: {stat}")

        if "fileCrc32" in body:
            fileCrc32 = body["fileCrc32"]
            if fileCrc32 != file_crc32:
                print(f"Returned CRC32 mismatch: {fileCrc32}")
                return False

    print("Sending complete")

    return True


@@ 102,12 113,16 @@ def setPasscode(harness, flag):
def main():
    if len(sys.argv) == 1:
        print(f'Please pass update file path as the parameter: python {sys.argv[0]} file_path ')
        raise TestError(Error.PORT_NOT_FOUND)

    harness = Harness.from_detect()
        raise TestError(Error.OTHER_ERROR)

    package_filepath = str(sys.argv[1])

    if (not os.path.exists(package_filepath)):
        print(f'Update file {package_filepath} not found')
        raise TestError(Error.OTHER_ERROR)

    harness = Harness.from_detect()

    atexit.register(setPasscode, harness, True)

    setPasscode(harness, False)