From 44ef5ddd5f8f871f84fd97a9b30ced80e417b803 Mon Sep 17 00:00:00 2001 From: Mateusz Piesta Date: Wed, 7 Dec 2022 10:54:50 +0100 Subject: [PATCH] [MOS-807] Unpacking update package * Added unpacking and validating of update package * Removed 'utils-bootconfig' * Added 'tar' module * Minor compilation issues fixed --- in_docker.sh | 2 +- module-apps/CMakeLists.txt | 1 - .../application-desktop/CMakeLists.txt | 1 - .../locks/data/PhoneLockMessages.hpp | 2 + .../service-cellular/SMSParser.hpp | 1 + .../service-desktop/CMakeLists.txt | 1 - .../service-desktop/endpoints/CMakeLists.txt | 5 +- .../security/SecurityEndpointHelper.hpp | 2 + .../include/endpoints/update/UpdateHelper.hpp | 9 +- .../endpoints/update/UpdateHelper.cpp | 162 +++++++++++++++++- .../include/service-desktop/Constants.hpp | 1 - .../board/linux/renderer/CMakeLists.txt | 2 +- module-utils/CMakeLists.txt | 3 +- module-utils/bootconfig/CMakeLists.txt | 24 --- .../bootconfig/include/boot/bootconfig.hpp | 72 -------- .../bootconfig/include/boot/bootconstants.hpp | 12 -- module-utils/bootconfig/src/bootconfig.cpp | 123 ------------- module-utils/tar/CMakeLists.txt | 7 + module-utils/tar/include/tar/tar.hpp | 79 +++++++++ module-utils/tar/tar.cpp | 122 +++++++++++++ module-utils/tar/test/CMakeLists.txt | 8 + module-utils/tar/test/test.tar | Bin 0 -> 339456 bytes module-utils/tar/test/test_tar.cpp | 30 ++++ module-utils/time/time/time_conversion.cpp | 6 +- 24 files changed, 430 insertions(+), 245 deletions(-) delete mode 100644 module-utils/bootconfig/CMakeLists.txt delete mode 100644 module-utils/bootconfig/include/boot/bootconfig.hpp delete mode 100644 module-utils/bootconfig/include/boot/bootconstants.hpp delete mode 100644 module-utils/bootconfig/src/bootconfig.cpp create mode 100644 module-utils/tar/CMakeLists.txt create mode 100644 module-utils/tar/include/tar/tar.hpp create mode 100644 module-utils/tar/tar.cpp create mode 100644 module-utils/tar/test/CMakeLists.txt create mode 100644 module-utils/tar/test/test.tar create mode 100644 module-utils/tar/test/test_tar.cpp diff --git a/in_docker.sh b/in_docker.sh index 958682333cca0daa524c17f45cde69ed92e688f3..5af6fd964cbe53686881526ef604bf0098ea745d 100755 --- a/in_docker.sh +++ b/in_docker.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. # For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md diff --git a/module-apps/CMakeLists.txt b/module-apps/CMakeLists.txt index 7c35e07aed6d371cca7688bf6abea1598c55bcc0..351c47f00468f0be783e8d42a4a15cbef951a12a 100644 --- a/module-apps/CMakeLists.txt +++ b/module-apps/CMakeLists.txt @@ -88,7 +88,6 @@ target_link_libraries(${PROJECT_NAME} service-evtmgr service-time-api utils-time - utils-bootconfig PUBLIC module-audio module-bsp diff --git a/module-apps/application-desktop/CMakeLists.txt b/module-apps/application-desktop/CMakeLists.txt index 9a569f5e212caee15a441f5679e3c76cbbeebf72..78a45a6658394d2cc63921ec4b165bf1f6b33a15 100644 --- a/module-apps/application-desktop/CMakeLists.txt +++ b/module-apps/application-desktop/CMakeLists.txt @@ -67,7 +67,6 @@ target_link_libraries(application-desktop service-time utils-time utf8 - utils-bootconfig PUBLIC apps-common module-sys diff --git a/module-apps/apps-common/locks/data/PhoneLockMessages.hpp b/module-apps/apps-common/locks/data/PhoneLockMessages.hpp index 7c2a13b7d5261bf7acc92bf6fc50ca1550220d0b..f514c1086bc362090aab699d47c6eda1f78d9b76 100644 --- a/module-apps/apps-common/locks/data/PhoneLockMessages.hpp +++ b/module-apps/apps-common/locks/data/PhoneLockMessages.hpp @@ -5,6 +5,8 @@ #include +#include + namespace locks { class UnlockPhone : public sys::DataMessage diff --git a/module-services/service-cellular/SMSParser.hpp b/module-services/service-cellular/SMSParser.hpp index de4d4dd0eb5b463054cdea7684f226ddba022f6d..225bfb265ae422b2a940742826ab7e1642f75193 100644 --- a/module-services/service-cellular/SMSParser.hpp +++ b/module-services/service-cellular/SMSParser.hpp @@ -5,6 +5,7 @@ #include #include +#include namespace SMSParser { diff --git a/module-services/service-desktop/CMakeLists.txt b/module-services/service-desktop/CMakeLists.txt index c1d1b4c6392b1a5b639ca985f2b240591b231205..471742475adf4bfa9158a671f66a608bdd2490d3 100644 --- a/module-services/service-desktop/CMakeLists.txt +++ b/module-services/service-desktop/CMakeLists.txt @@ -53,7 +53,6 @@ target_link_libraries(service-desktop service-cellular service-evtmgr utf8 - utils-bootconfig Microsoft.GSL::GSL json::json microtar::microtar diff --git a/module-services/service-desktop/endpoints/CMakeLists.txt b/module-services/service-desktop/endpoints/CMakeLists.txt index ea73682e4e38721bce0682077891a6cc230499f6..72493a4e1c674783fcfb9533d2d228d2f6cb5dd4 100644 --- a/module-services/service-desktop/endpoints/CMakeLists.txt +++ b/module-services/service-desktop/endpoints/CMakeLists.txt @@ -102,7 +102,10 @@ target_link_libraries( desktop-endpoint-base PRIVATE base64::base64 - microtar::microtar + tar + json + hash-library + pure-core ) add_library(desktop-endpoints INTERFACE) diff --git a/module-services/service-desktop/endpoints/include/endpoints/security/SecurityEndpointHelper.hpp b/module-services/service-desktop/endpoints/include/endpoints/security/SecurityEndpointHelper.hpp index 5a7386043ed0bf60d763ccb9c5b5ccb406742493..7d865190c5f9bebf21eb4c28557ed125da2a2921 100644 --- a/module-services/service-desktop/endpoints/include/endpoints/security/SecurityEndpointHelper.hpp +++ b/module-services/service-desktop/endpoints/include/endpoints/security/SecurityEndpointHelper.hpp @@ -5,6 +5,8 @@ #include +#include + namespace sdesktop::endpoints { diff --git a/module-services/service-desktop/endpoints/include/endpoints/update/UpdateHelper.hpp b/module-services/service-desktop/endpoints/include/endpoints/update/UpdateHelper.hpp index bab667a7ab7d428a2c88e7a4a0b699e76b5db170..8ccd1b44cec36decacb0332f85e6db4136faf994 100644 --- a/module-services/service-desktop/endpoints/include/endpoints/update/UpdateHelper.hpp +++ b/module-services/service-desktop/endpoints/include/endpoints/update/UpdateHelper.hpp @@ -5,16 +5,21 @@ #include +#include + namespace sdesktop::endpoints { class UpdateHelper : public BaseHelper { public: - explicit UpdateHelper(sys::Service *p) : BaseHelper(p) - {} + explicit UpdateHelper(sys::Service *p); auto processPost(Context &context) -> ProcessResult final; auto processPut(Context &context) -> ProcessResult final; void preProcess(http::Method method, Context &context) final; + + private: + std::filesystem::path updatePackagePath; + std::filesystem::path binariesPath; }; } // namespace sdesktop::endpoints diff --git a/module-services/service-desktop/endpoints/update/UpdateHelper.cpp b/module-services/service-desktop/endpoints/update/UpdateHelper.cpp index eb52b05f1cba21fef0667aee659d8e2380ea7d70..c493bcfe0ff91c4525b293c9089c273b0b332ed3 100644 --- a/module-services/service-desktop/endpoints/update/UpdateHelper.cpp +++ b/module-services/service-desktop/endpoints/update/UpdateHelper.cpp @@ -1,19 +1,160 @@ // Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md +#include #include #include #include #include #include #include +#include +#include +#include +#include +#include #include +#include +#include namespace sdesktop::endpoints { + constexpr auto chunkSize = 1024 * 128; - auto constexpr updatePackageFile = "update.tar"; + struct UpdatePackageEntries + { + explicit UpdatePackageEntries(const json11::Json &json) + { + constexpr auto recovery_token = "recovery"; + constexpr auto bootloader_token = "bootloader"; + constexpr auto os_token = "os"; + constexpr auto filename_token = "filename"; + constexpr auto hash_token = "md5sum"; + + recovery = {json[recovery_token][filename_token].string_value(), + json[recovery_token][hash_token].string_value()}; + bootloader = {json[bootloader_token][filename_token].string_value(), + json[bootloader_token][hash_token].string_value()}; + os = {json[os_token][filename_token].string_value(), json[os_token][hash_token].string_value()}; + } + + using Entry = std::pair; + Entry os; + Entry bootloader; + Entry recovery; + }; + + std::optional fetchVersionJsonFromFile(const std::filesystem::path &path) + { + std::ifstream versionJsonFile{path}; + if (not versionJsonFile.is_open()) { + return std::nullopt; + } + std::stringstream buffer; + buffer << versionJsonFile.rdbuf(); + std::string err; + const auto versionJson = json11::Json::parse(buffer.str(), err); + if (!err.empty()) { + LOG_ERROR("Parsing '%s' failed", path.c_str()); + return std::nullopt; + } + return versionJson; + } + + std::optional getUpdatePackageEntries(const std::filesystem::path &path) + { + if (const auto version = fetchVersionJsonFromFile(path)) { + return UpdatePackageEntries{version.value()}; + } + return std::nullopt; + } + + bool validateImageEntry(const std::filesystem::path &path, const std::string &hash) + { + auto fd = std::fopen(path.c_str(), "rb"); + if (fd == nullptr) { + return false; + } + + MD5 md5; + std::vector raw_data(chunkSize); + std::size_t bytesRead; + while ((bytesRead = std::fread(raw_data.data(), 1, chunkSize, fd)) > 0) { + md5.add(raw_data.data(), bytesRead); + } + + std::fclose(fd); + return md5.getHash() == hash; + } + + bool removeDirectory(const std::filesystem::path &path) + { + if (std::filesystem::is_directory(path)) { + LOG_INFO("'%s' exists, removing", path.c_str()); + std::error_code errorCode; + if (std::filesystem::remove_all(path, errorCode) == 0) { + return false; + } + } + return true; + } + + bool unpackUpdatePackage(const std::filesystem::path &path, const std::filesystem::path &where) + { + if (not removeDirectory(where)) { + LOG_ERROR("Removing '%s' directory failed", path.c_str()); + return false; + } + + try { + LOG_INFO("Unpacking '%s' to '%s'", path.c_str(), where.c_str()); + tar::unpack(path, where); + } + catch (const std::filesystem::filesystem_error &err) { + LOG_ERROR("Unpacking tar '%s' failed with %s", path.c_str(), err.what()); + return false; + } + return true; + } + + bool validateUpdatePackage(const std::filesystem::path &packagePath, const std::filesystem::path &binariesPath) + { + LOG_INFO("Validating '%s' package", packagePath.c_str()); + const auto entries = getUpdatePackageEntries(packagePath / purefs::file::version_json); + if (not entries) { + LOG_ERROR("Fetching package entries failed"); + return false; + } + const auto prefix = packagePath / binariesPath; + return validateImageEntry(prefix / entries->os.first, entries->os.second) and + validateImageEntry(prefix / entries->recovery.first, entries->recovery.second) and + validateImageEntry(prefix / entries->bootloader.first, entries->bootloader.second); + } + + bool checkAvailableSpace(const std::filesystem::path &path, const std::filesystem::path &updatePackage) + { + struct statvfs stat + {}; + if (statvfs(path.c_str(), &stat) < 0) { + LOG_ERROR("Failed to stat '%s'", path.c_str()); + return false; + } + + const auto requiredSpace = std::filesystem::file_size(updatePackage); + const auto freeSpace = (static_cast(stat.f_bfree) * static_cast(stat.f_bsize)); + LOG_INFO("Checking available space: %" PRIu64 " bytes, required: %" PRIu64 " bytes", + freeSpace, + static_cast(requiredSpace)); + + return freeSpace >= requiredSpace; + } + + bool checkUpdatePackageFile(const std::filesystem::path &path) + { + LOG_INFO("Checking if update package exist, '%s'", path.c_str()); + return std::filesystem::exists(path); + } void UpdateHelper::preProcess(http::Method method, Context &context) { @@ -28,10 +169,24 @@ namespace sdesktop::endpoints return {sent::no, ResponseContext{.status = http::Code::BadRequest}}; } - if (!std::filesystem::exists(purefs::dir::getUserDiskPath() / updatePackageFile)) { + if (not checkUpdatePackageFile(purefs::dir::getTemporaryPath() / sdesktop::paths::updateFilename)) { return {sent::no, ResponseContext{.status = http::Code::NotFound}}; } + if (not checkAvailableSpace(purefs::dir::getUserDiskPath(), + purefs::dir::getTemporaryPath() / sdesktop::paths::updateFilename)) { + return {sent::no, ResponseContext{.status = http::Code::NotAcceptable}}; + } + + if (not unpackUpdatePackage(purefs::dir::getTemporaryPath() / sdesktop::paths::updateFilename, + updatePackagePath)) { + return {sent::no, ResponseContext{.status = http::Code::UnprocessableEntity}}; + } + + if (not validateUpdatePackage(updatePackagePath, binariesPath)) { + return {sent::no, ResponseContext{.status = http::Code::UnprocessableEntity}}; + } + if (sys::SystemManagerCommon::RebootToRecovery(owner, sys::RecoveryReason::Update)) { return {sent::no, ResponseContext{.status = http::Code::NoContent}}; } @@ -50,5 +205,8 @@ namespace sdesktop::endpoints return {sent::no, ResponseContext{.status = code}}; } + UpdateHelper::UpdateHelper(sys::Service *p) + : BaseHelper(p), updatePackagePath{purefs::dir::getTemporaryPath() / "update"}, binariesPath{get_binary_dir()} + {} } // namespace sdesktop::endpoints diff --git a/module-services/service-desktop/include/service-desktop/Constants.hpp b/module-services/service-desktop/include/service-desktop/Constants.hpp index 249f5a003fdac15749f24617ce3d8794d5485cf1..48c59414db83f27bc8228c007b63d7c726cec7d2 100644 --- a/module-services/service-desktop/include/service-desktop/Constants.hpp +++ b/module-services/service-desktop/include/service-desktop/Constants.hpp @@ -11,7 +11,6 @@ namespace service::name } namespace sdesktop::paths { - inline constexpr auto updateFilename = "update.tar"; inline constexpr auto syncFilename = "sync.tar"; inline constexpr auto backupFilename = "backup.tar"; diff --git a/module-services/service-eink/board/linux/renderer/CMakeLists.txt b/module-services/service-eink/board/linux/renderer/CMakeLists.txt index 8bad3d1dcd109b4d4d1d4fd1c24cc16993d0ff7b..f8ce7d732b128335430f540df53bd2e4de25fa14 100644 --- a/module-services/service-eink/board/linux/renderer/CMakeLists.txt +++ b/module-services/service-eink/board/linux/renderer/CMakeLists.txt @@ -22,7 +22,7 @@ target_link_libraries( ${PROJECT_NAME} ${GTKMM_LIBRARIES} ) target_include_directories(${PROJECT_NAME} PUBLIC ${GTKMM_LIBRARY_DIRS} ) target_include_directories( ${PROJECT_NAME} PUBLIC ${GTKMM_INCLUDE_DIRS} ) target_link_libraries( ${PROJECT_NAME} module-bsp ) -target_link_libraries( ${PROJECT_NAME} ${LIBRT} rt pthread ) +target_link_libraries( ${PROJECT_NAME} ${LIBRT} pthread ) #key_code target_include_directories( ${PROJECT_NAME} PUBLIC "${CMAKE_SOURCE_DIR}/" ) diff --git a/module-utils/CMakeLists.txt b/module-utils/CMakeLists.txt index 4935bea44f4e272ef7d7e628aa91db26694f9995..190de84fdc8ae4b996dae75af6fa8f7c4379543c 100644 --- a/module-utils/CMakeLists.txt +++ b/module-utils/CMakeLists.txt @@ -2,7 +2,7 @@ add_library(module-utils INTERFACE) -add_subdirectory(bootconfig) +add_subdirectory(tar) add_subdirectory(Clipboard) add_subdirectory(EventStore) add_subdirectory(i18n) @@ -23,7 +23,6 @@ target_link_libraries(module-utils log-api rrule utility - utils-bootconfig utils-locale utils-math utils-phonenumber diff --git a/module-utils/bootconfig/CMakeLists.txt b/module-utils/bootconfig/CMakeLists.txt deleted file mode 100644 index 59ccf603c4bf70e6511afd49a4498dd47a8edb52..0000000000000000000000000000000000000000 --- a/module-utils/bootconfig/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -add_library(utils-bootconfig STATIC) - -target_sources(utils-bootconfig - PRIVATE - src/bootconfig.cpp -) - -target_include_directories(utils-bootconfig - PUBLIC - $ -) - -target_link_libraries(utils-bootconfig - PRIVATE - module-os - purefs-paths - utils-time - utility - version-header - - Microsoft.GSL::GSL - json::json - hash-library::hash-library -) diff --git a/module-utils/bootconfig/include/boot/bootconfig.hpp b/module-utils/bootconfig/include/boot/bootconfig.hpp deleted file mode 100644 index ece09392e8cf581fedbb2f416bdb6434f642973b..0000000000000000000000000000000000000000 --- a/module-utils/bootconfig/include/boot/bootconfig.hpp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. -// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md - -#pragma once -#include -#include -#include - -namespace boot -{ - namespace json - { - inline constexpr auto main = "main"; - inline constexpr auto os_type = "ostype"; - inline constexpr auto os_image = "imagename"; - inline constexpr auto os_version = "version"; - inline constexpr auto timestamp = "timestamp"; - inline constexpr auto misc = "misc"; - inline constexpr auto git_info = "git"; - inline constexpr auto os_git_revision = "git_commit"; - inline constexpr auto os_git_branch = "git_branch"; - inline constexpr auto bootloader = "bootloader"; - } // namespace json - - class BootConfig - { - public: - BootConfig(); - static int version_compare(const std::string &v1, const std::string &v2); - [[nodiscard]] json11::Json to_json() const; - int load(); - int save(); - auto os_image() -> const std::string & - { - return m_os_image; - } - auto os_type() -> const std::string & - { - return m_os_type; - } - auto os_version() -> const std::string & - { - return m_os_version; - } - auto bootloader_version() -> const std::string & - { - return m_bootloader_version; - } - auto timestamp() -> const std::string & - { - return m_timestamp; - } - auto os_root_path() -> const std::filesystem::path & - { - return m_os_root_path; - } - - private: - bool loadBootConfig(const std::filesystem::path &bootJsonPath); - std::filesystem::path getCurrentBootJSON(); - - private: - std::string m_os_image{"boot.bin"}; - std::string m_os_type{"current"}; - std::string m_os_version; - std::string m_bootloader_version; - std::string m_timestamp; - json11::Json m_boot_json_parsed; - std::filesystem::path m_os_root_path; - std::filesystem::path m_boot_json; - }; -} // namespace boot diff --git a/module-utils/bootconfig/include/boot/bootconstants.hpp b/module-utils/bootconfig/include/boot/bootconstants.hpp deleted file mode 100644 index c31c04a56132e6f126fe667a8eaad796fb71ac00..0000000000000000000000000000000000000000 --- a/module-utils/bootconfig/include/boot/bootconstants.hpp +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. -// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md - -#pragma once -namespace boot::consts -{ - inline constexpr auto tar_buf = 8192 * 4; - inline constexpr auto crc_char_size = 8; - inline constexpr auto crc_radix = 16; - inline constexpr auto ext_crc32 = ".crc32"; - inline constexpr auto crc_buf = 1024; -} // namespace boot::consts diff --git a/module-utils/bootconfig/src/bootconfig.cpp b/module-utils/bootconfig/src/bootconfig.cpp deleted file mode 100644 index 31b9979e321332385581354cc5c526910650397e..0000000000000000000000000000000000000000 --- a/module-utils/bootconfig/src/bootconfig.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. -// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md -#include -#include - -#include -#include -#include -#include -#include