M in_docker.sh => in_docker.sh +1 -1
@@ 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
M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +0 -1
@@ 88,7 88,6 @@ target_link_libraries(${PROJECT_NAME}
service-evtmgr
service-time-api
utils-time
- utils-bootconfig
PUBLIC
module-audio
module-bsp
M module-apps/application-desktop/CMakeLists.txt => module-apps/application-desktop/CMakeLists.txt +0 -1
@@ 67,7 67,6 @@ target_link_libraries(application-desktop
service-time
utils-time
utf8
- utils-bootconfig
PUBLIC
apps-common
module-sys
M module-apps/apps-common/locks/data/PhoneLockMessages.hpp => module-apps/apps-common/locks/data/PhoneLockMessages.hpp +2 -0
@@ 5,6 5,8 @@
#include <Service/Message.hpp>
+#include <ctime>
+
namespace locks
{
class UnlockPhone : public sys::DataMessage
M module-services/service-cellular/SMSParser.hpp => module-services/service-cellular/SMSParser.hpp +1 -0
@@ 5,6 5,7 @@
#include <cstdint>
#include <string>
+#include <ctime>
namespace SMSParser
{
M module-services/service-desktop/CMakeLists.txt => module-services/service-desktop/CMakeLists.txt +0 -1
@@ 53,7 53,6 @@ target_link_libraries(service-desktop
service-cellular
service-evtmgr
utf8
- utils-bootconfig
Microsoft.GSL::GSL
json::json
microtar::microtar
M module-services/service-desktop/endpoints/CMakeLists.txt => module-services/service-desktop/endpoints/CMakeLists.txt +4 -1
@@ 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)
M module-services/service-desktop/endpoints/include/endpoints/security/SecurityEndpointHelper.hpp => module-services/service-desktop/endpoints/include/endpoints/security/SecurityEndpointHelper.hpp +2 -0
@@ 5,6 5,8 @@
#include <endpoints/BaseHelper.hpp>
+#include <ctime>
+
namespace sdesktop::endpoints
{
M module-services/service-desktop/endpoints/include/endpoints/update/UpdateHelper.hpp => module-services/service-desktop/endpoints/include/endpoints/update/UpdateHelper.hpp +7 -2
@@ 5,16 5,21 @@
#include <endpoints/BaseHelper.hpp>
+#include <filesystem>
+
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
M module-services/service-desktop/endpoints/update/UpdateHelper.cpp => module-services/service-desktop/endpoints/update/UpdateHelper.cpp +160 -2
@@ 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 <service-desktop/Constants.hpp>
#include <endpoints/Context.hpp>
#include <endpoints/JsonKeyNames.hpp>
#include <endpoints/update/UpdateHelper.hpp>
#include <log/log.hpp>
#include <SystemManager/SystemManagerCommon.hpp>
#include <purefs/filesystem_paths.hpp>
+#include <tar/tar.hpp>
+#include <json11.hpp>
+#include <md5.h>
+#include <hal/boot_control.h>
+#include <sys/statvfs.h>
#include <filesystem>
+#include <optional>
+#include <fstream>
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<std::string, std::string>;
+ Entry os;
+ Entry bootloader;
+ Entry recovery;
+ };
+
+ std::optional<json11::Json> 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<UpdatePackageEntries> 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<std::byte> 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<std::uint64_t>(stat.f_bfree) * static_cast<std::uint64_t>(stat.f_bsize));
+ LOG_INFO("Checking available space: %" PRIu64 " bytes, required: %" PRIu64 " bytes",
+ freeSpace,
+ static_cast<std::uint64_t>(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
M module-services/service-desktop/include/service-desktop/Constants.hpp => module-services/service-desktop/include/service-desktop/Constants.hpp +0 -1
@@ 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";
M module-services/service-eink/board/linux/renderer/CMakeLists.txt => module-services/service-eink/board/linux/renderer/CMakeLists.txt +1 -1
@@ 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}/" )
M module-utils/CMakeLists.txt => module-utils/CMakeLists.txt +1 -2
@@ 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
D module-utils/bootconfig/CMakeLists.txt => module-utils/bootconfig/CMakeLists.txt +0 -24
@@ 1,24 0,0 @@
-add_library(utils-bootconfig STATIC)
-
-target_sources(utils-bootconfig
- PRIVATE
- src/bootconfig.cpp
-)
-
-target_include_directories(utils-bootconfig
- PUBLIC
- $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
-)
-
-target_link_libraries(utils-bootconfig
- PRIVATE
- module-os
- purefs-paths
- utils-time
- utility
- version-header
-
- Microsoft.GSL::GSL
- json::json
- hash-library::hash-library
-)
D module-utils/bootconfig/include/boot/bootconfig.hpp => module-utils/bootconfig/include/boot/bootconfig.hpp +0 -72
@@ 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 <json11.hpp>
-#include <string>
-#include <filesystem>
-
-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
D module-utils/bootconfig/include/boot/bootconstants.hpp => module-utils/bootconfig/include/boot/bootconstants.hpp +0 -12
@@ 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
D module-utils/bootconfig/src/bootconfig.cpp => module-utils/bootconfig/src/bootconfig.cpp +0 -123
@@ 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 <boot/bootconfig.hpp>
-#include <boot/bootconstants.hpp>
-
-#include <gsl/util>
-#include <limits.h>
-#include <purefs/filesystem_paths.hpp>
-#include <product/version.hpp>
-#include <time/time_conversion.hpp>
-#include <ticks.hpp>
-#include <cstdio>
-#include <log/log.hpp>
-#include <array>
-#include <Utils.hpp>
-
-#include <fstream>
-
-#include <purefs/filesystem_paths.hpp>
-
-namespace boot
-{
- namespace
- {
- std::string loadFileAsString(const std::filesystem::path &fileToLoad)
- {
- std::string content;
- std::ifstream in(fileToLoad);
- std::getline(in, content, std::string::traits_type::to_char_type(std::string::traits_type::eof()));
- return content;
- }
- } // namespace
-
- BootConfig::BootConfig() : m_os_root_path(purefs::dir::getSystemDiskPath())
- {}
-
- json11::Json BootConfig::to_json() const
- {
- return json11::Json::object{
- {boot::json::main,
- json11::Json::object{{boot::json::os_image, m_os_image},
- {boot::json::os_type, m_os_type},
- {boot::json::os_version, m_os_version},
- {boot::json::timestamp, m_timestamp}}},
-
- {boot::json::git_info,
- json11::Json::object{{boot::json::os_git_revision, std::string(GIT_REV)},
- {boot::json::os_git_branch, std::string(GIT_BRANCH)}}},
- {boot::json::bootloader, json11::Json::object{{boot::json::os_version, m_bootloader_version}}}};
- }
-
- int BootConfig::load()
- {
- return !loadBootConfig(getCurrentBootJSON());
- }
-
- // Method to compare two version strings
- // v1 < v2 -> -1
- // v1 == v2 -> 0
- int BootConfig::version_compare(const std::string &v1, const std::string &v2)
- {
- size_t i = 0, j = 0;
- while (i < v1.length() || j < v2.length()) {
- int acc1 = 0, acc2 = 0;
-
- while (i < v1.length() && v1[i] != '.') {
- acc1 = acc1 * 10 + (v1[i] - '0');
- i++;
- }
- while (j < v2.length() && v2[j] != '.') {
- acc2 = acc2 * 10 + (v2[j] - '0');
- j++;
- }
-
- if (acc1 < acc2)
- return -1;
- if (acc1 > acc2)
- return +1;
-
- ++i;
- ++j;
- }
- return 0;
- }
-
- bool BootConfig::loadBootConfig(const std::filesystem::path &bootJsonPath)
- {
- std::string parseErrors = "";
- std::string jsonContents = loadFileAsString(bootJsonPath);
-
- LOG_INFO("parsed %s: \"%s\"", bootJsonPath.c_str(), jsonContents.c_str());
-
- m_boot_json_parsed = json11::Json::parse(jsonContents, parseErrors);
-
- if (parseErrors == "") {
- m_os_type = m_boot_json_parsed[boot::json::main][boot::json::os_type].string_value();
- m_os_image = m_boot_json_parsed[boot::json::main][boot::json::os_image].string_value();
- m_os_root_path = purefs::createPath(purefs::dir::getSystemDiskPath(), m_os_type);
- m_boot_json = bootJsonPath;
- m_bootloader_version = m_boot_json_parsed[boot::json::bootloader][boot::json::os_version].string_value();
- m_timestamp = utils::time::getCurrentTimestamp().str("%c");
- m_os_version = std::string(VERSION);
-
- LOG_INFO("boot_config: %s", to_json().dump().c_str());
- return true;
- }
- else {
- m_os_image = purefs::file::os_bin;
- m_os_root_path = purefs::dir::getSystemDiskPath();
- m_boot_json = bootJsonPath;
- m_timestamp = utils::time::getCurrentTimestamp().str("%c");
- m_os_version = std::string(VERSION);
- LOG_WARN("%s failed to parse %s: \"%s\"", __FUNCTION__, bootJsonPath.c_str(), parseErrors.c_str());
- return false;
- }
- }
-
- std::filesystem::path BootConfig::getCurrentBootJSON()
- {
- auto boot_json_path = purefs::dir::getUserDiskPath() / purefs::file::boot_json;
- return boot_json_path;
- }
-} // namespace boot
A module-utils/tar/CMakeLists.txt => module-utils/tar/CMakeLists.txt +7 -0
@@ 0,0 1,7 @@
+add_library(tar tar.cpp)
+target_link_libraries(tar PUBLIC microtar)
+target_include_directories(tar PUBLIC include)
+
+if (${ENABLE_TESTS})
+ add_subdirectory(test)
+endif()<
\ No newline at end of file
A module-utils/tar/include/tar/tar.hpp => module-utils/tar/include/tar/tar.hpp +79 -0
@@ 0,0 1,79 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <filesystem>
+#include <optional>
+#include <vector>
+
+#include <microtar.hpp>
+
+namespace tar
+{
+ class entry
+ {
+ public:
+ ~entry();
+
+ std::size_t size() const;
+ std::filesystem::path name() const;
+ bool is_file() const;
+ bool is_directory() const;
+
+ void read(const std::byte *data, std::size_t size) const;
+
+ private:
+ explicit entry(const std::filesystem::path &path);
+ friend class iterator;
+ mutable mtar_t handle{};
+ mtar_header_t tar_header;
+ };
+
+ class iterator
+ {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = entry;
+ using pointer = entry *;
+ using reference = entry &;
+
+ iterator() = default;
+
+ explicit iterator(const std::filesystem::path &path);
+
+ reference operator*() const;
+ pointer operator->() const;
+
+ iterator &operator++();
+ iterator operator++(int);
+
+ friend bool operator==(const iterator &a, const iterator &b);
+ friend bool operator!=(const iterator &a, const iterator &b);
+
+ private:
+ std::shared_ptr<entry> entry_;
+ };
+
+ /** @brief Enable range-based `for` using iterator.
+ *
+ * e.g. `for (const auto& entry : tar::iterator("<tar_name>")) ...`
+ */
+ inline iterator begin(const iterator &it) noexcept
+ {
+ return it;
+ }
+
+ inline iterator end(iterator) noexcept
+ {
+ return iterator{};
+ }
+
+ /**
+ * Unpack contents of tar file
+ * @param path path to a tar file
+ * @param where where to store contents of a tar file
+ */
+ void unpack(const std::filesystem::path &path, const std::filesystem::path &where = {});
+} // namespace tar
A module-utils/tar/tar.cpp => module-utils/tar/tar.cpp +122 -0
@@ 0,0 1,122 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "tar/tar.hpp"
+
+#include <fstream>
+#include <vector>
+#include <iterator>
+
+namespace
+{
+ bool isValidDirectoryEntry(const tar::entry &entry)
+ {
+ return entry.name() != "." and entry.name() != ".." and entry.name() != "...";
+ }
+
+ void write_to_file(const tar::entry &entry, const std::filesystem::path &path)
+ {
+ constexpr std::size_t chunk_size = 1024 * 128;
+ std::ofstream out_file{path, std::ios::binary};
+ if (not out_file.is_open()) {
+ throw std::filesystem::filesystem_error("During opening " + path.string(), std::error_code{});
+ }
+
+ std::vector<std::byte> raw_data(chunk_size);
+ auto bytes_left = entry.size();
+ while (bytes_left > 0) {
+ const std::size_t to_read = bytes_left > chunk_size ? chunk_size : bytes_left;
+ entry.read(raw_data.data(), to_read);
+
+ out_file.write(reinterpret_cast<const char *>(raw_data.data()), to_read);
+ bytes_left -= to_read;
+ }
+ }
+} // namespace
+
+namespace tar
+{
+ entry::entry(const std::filesystem::path &path)
+ {
+ if (mtar_open(&handle, path.c_str(), "rb") != MTAR_ESUCCESS) {
+ throw std::filesystem::filesystem_error("During opening tar file " + path.string(), std::error_code{});
+ }
+ if (mtar_read_header(&handle, &tar_header) != MTAR_ESUCCESS) {
+ throw std::filesystem::filesystem_error("During reading from tar file " + path.string(), std::error_code{});
+ }
+ }
+ entry::~entry()
+ {
+ mtar_close(&handle);
+ }
+ std::size_t entry::size() const
+ {
+ return tar_header.size;
+ }
+ bool entry::is_file() const
+ {
+ return tar_header.type == MTAR_TREG;
+ }
+ bool entry::is_directory() const
+ {
+ return tar_header.type == MTAR_TDIR;
+ }
+ std::filesystem::path entry::name() const
+ {
+ return tar_header.name;
+ }
+ void entry::read(const std::byte *data, const std::size_t size) const
+ {
+ const std::size_t to_read = size > tar_header.size ? tar_header.size : size;
+ if (mtar_read_data(&handle, (void *)(data), to_read) != MTAR_ESUCCESS) {
+ throw std::filesystem::filesystem_error("During reading from tar file", std::error_code{});
+ }
+ }
+
+ iterator::iterator(const std::filesystem::path &path) : entry_{new entry(path)}
+ {}
+ entry &iterator::operator*() const
+ {
+ return *entry_;
+ }
+ iterator::pointer iterator::operator->() const
+ {
+ return &**this;
+ }
+ iterator &iterator::operator++()
+ {
+ if (mtar_next(&entry_->handle) != MTAR_ESUCCESS or
+ mtar_read_header(&entry_->handle, &entry_->tar_header) != MTAR_ESUCCESS) {
+ entry_ = {};
+ }
+
+ return *this;
+ }
+ iterator iterator::operator++(int)
+ {
+ iterator tmp = *this;
+ ++(*this);
+ return tmp;
+ }
+ bool operator==(const iterator &a, const iterator &b)
+ {
+ return a.entry_ == b.entry_;
+ }
+ bool operator!=(const iterator &a, const iterator &b)
+ {
+ return a.entry_ != b.entry_;
+ }
+
+ void unpack(const std::filesystem::path &path, const std::filesystem::path &where)
+ {
+ for (const auto &entry : iterator(path)) {
+ const auto full_path = where / entry.name();
+ if (entry.is_directory() and isValidDirectoryEntry(entry)) {
+ std::filesystem::create_directories(full_path);
+ }
+ else if (entry.is_file()) {
+ write_to_file(entry, full_path);
+ }
+ }
+ }
+} // namespace tar<
\ No newline at end of file
A module-utils/tar/test/CMakeLists.txt => module-utils/tar/test/CMakeLists.txt +8 -0
@@ 0,0 1,8 @@
+add_catch2_executable(
+ NAME
+ tar
+ SRCS
+ test_tar.cpp
+ LIBS
+ tar
+)
A module-utils/tar/test/test.tar => module-utils/tar/test/test.tar +0 -0
A module-utils/tar/test/test_tar.cpp => module-utils/tar/test/test_tar.cpp +30 -0
@@ 0,0 1,30 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include <catch2/catch.hpp>
+
+#include <tar/tar.hpp>
+
+constexpr auto test_tar_entries_count = 68;
+
+std::filesystem::path get_test_data(const std::string &name)
+{
+ const std::string path = __FILE__;
+ return std::filesystem::path{path.substr(0, path.rfind('/'))} / name;
+}
+
+TEST_CASE("iterator")
+{
+ auto count{0};
+ for ([[maybe_unused]] const auto &entry : tar::iterator(get_test_data("test.tar"))) {
+ count++;
+ }
+ REQUIRE(count == test_tar_entries_count);
+}
+
+TEST_CASE("Unpack")
+{
+ REQUIRE_NOTHROW(tar::unpack(get_test_data("test.tar")));
+ REQUIRE_NOTHROW(tar::unpack(get_test_data("test.tar"), "prefix"));
+ REQUIRE_THROWS(tar::unpack(""));
+}
M module-utils/time/time/time_conversion.cpp => module-utils/time/time/time_conversion.cpp +5 -1
@@ 270,7 270,11 @@ namespace utils::time
seconds = (this->duration % secondsInHour) % secondsInMinute;
if (verboseConversion) {
- LOG_DEBUG("duration %" PRIu64 " - %lu hours %lu minutes %lu seconds", duration, hours, minutes, seconds);
+ LOG_DEBUG("duration %" PRIu64 " - %lu hours %lu minutes %lu seconds",
+ static_cast<std::uint64_t>(duration),
+ hours,
+ minutes,
+ seconds);
}
}