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)