~aleteoryx/muditaos

f56609aa3b5fad0701ed91adc4c375a7737bdc7e — Lucjan Bryndza 5 years ago 8792133
[EGD-4502] Fix and remove old vfs boot

Remove old vfs bootconfig from the vfs which
will be removed in the later commit
M module-apps/application-settings/windows/Info.cpp => module-apps/application-settings/windows/Info.cpp +9 -7
@@ 15,7 15,7 @@
#include <gui/widgets/Style.hpp>

#include <i18n/i18n.hpp>
#include <vfs.hpp>
#include <boot/bootconfig.hpp>

namespace gui
{


@@ 48,12 48,14 @@ namespace gui
        addAlignedLabelWithValue(box, "GIT tag:", std::string(GIT_TAG));
        addAlignedLabelWithValue(box, "GIT branch:", std::string(GIT_BRANCH));
        addAlignedLabelWithValue(box, "Version:", std::string(VERSION));
        addAlignedLabelWithValue(box,
                                 "Bootloader:",
                                 (vfs.getBootConfig().bootloader_verion.empty()
                                      ? utils::localize.get("not available")
                                      : vfs.getBootConfig().bootloader_verion));

        {
            boot::BootConfig bootCfg;
            bootCfg.load();
            addAlignedLabelWithValue(box,
                                     "Bootloader:",
                                     (bootCfg.bootloader_version().empty() ? utils::localize.get("not available")
                                                                           : bootCfg.bootloader_version()));
        }
        std::string firmwareVersion;
        CellularServiceAPI::GetFirmwareVersion(getApplication(), firmwareVersion);
        addAlignedLabelWithValue(

M module-services/service-desktop/endpoints/update/UpdateMuditaOS.cpp => module-services/service-desktop/endpoints/update/UpdateMuditaOS.cpp +5 -4
@@ 49,6 49,7 @@ json11::Json FileInfo::to_json() const
UpdateMuditaOS::UpdateMuditaOS(ServiceDesktop *ownerService) : owner(ownerService)
{
    status = updateos::UpdateState::Initial;
    bootConfig.load();
}

updateos::UpdateError UpdateMuditaOS::setUpdateFile(fs::path updateFileToUse)


@@ 80,7 81,7 @@ updateos::UpdateError UpdateMuditaOS::runUpdate()
    informDebug("Prepraring temp dir");

    updateRunStatus.startTime   = utils::time::getCurrentTimestamp().getTime();
    updateRunStatus.fromVersion = vfs.getBootConfig().to_json();
    updateRunStatus.fromVersion = bootConfig.to_json();
    storeRunStatusInDB();

    updateos::UpdateError err = prepareTempDirForUpdate();


@@ 256,9 257,9 @@ updateos::UpdateError UpdateMuditaOS::verifyVersion()
    else {
        /* version comparison goes here */
        updateRunStatus.toVersion = targetVersionInfo;
        const bool ret            = vfs.getBootConfig().version_compare(
            targetVersionInfo[purefs::json::version_string].string_value(), vfs.getBootConfig().os_version);
        LOG_DEBUG("verifyVersion comaprison result == %s", ret ? "true" : "false");
        const bool ret = bootConfig.version_compare(targetVersionInfo[purefs::json::version_string].string_value(),
                                                    bootConfig.os_version());
        LOG_DEBUG("verifyVersion comparison result == %s", ret ? "true" : "false");
    }
    return updateos::UpdateError::NoError;
}

M module-services/service-desktop/endpoints/update/UpdateMuditaOS.hpp => module-services/service-desktop/endpoints/update/UpdateMuditaOS.hpp +2 -0
@@ 5,6 5,7 @@

#include <json/json11.hpp>
#include <module-utils/microtar/src/microtar.hpp>
#include <boot/bootconfig.hpp>

#include <cstdint>
#include <filesystem>


@@ 181,5 182,6 @@ class UpdateMuditaOS : public updateos::UpdateStats
    updateos::UpdateRunStatus updateRunStatus;
    json11::Json updateHistory;
    json11::Json targetVersionInfo;
    boot::BootConfig bootConfig;
    [[nodiscard]] static std::string readContent(const char *filename) noexcept;
};

M module-utils/CMakeLists.txt => module-utils/CMakeLists.txt +2 -0
@@ 93,3 93,5 @@ endif()
if (${ENABLE_TESTS})
    add_subdirectory( test )
endif()

add_subdirectory(bootconfig)

A module-utils/bootconfig/CMakeLists.txt => module-utils/bootconfig/CMakeLists.txt +18 -0
@@ 0,0 1,18 @@
cmake_minimum_required(VERSION 3.12)

project( utils-bootconfig VERSION 1.0
        DESCRIPTION "Library for processing boot config"
)

set ( SOURCES
    src/bootconfig.cpp
)

set( INCLUDES
    include
)

add_library( ${PROJECT_NAME} STATIC ${SOURCES} )

target_include_directories( ${PROJECT_NAME} PUBLIC ${INCLUDES} )
target_link_libraries( ${PROJECT_NAME} PRIVATE module-utils module-vfs)

A module-utils/bootconfig/include/boot/bootconfig.hpp => module-utils/bootconfig/include/boot/bootconfig.hpp +61 -0
@@ 0,0 1,61 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <module-utils/json/json11.hpp>
#include <string>
#include <filesystem>

namespace boot
{
    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;
        }
        void updateTimestamp();

      private:
        bool loadBootConfig(const std::filesystem::path &bootJsonPath);
        const std::filesystem::path getCurrentBootJSON();
        bool verifyCRC(const std::filesystem::path filePath);
        bool verifyCRC(const std::string filePath, const unsigned long crc32);

      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

A module-utils/bootconfig/src/bootconfig.cpp => module-utils/bootconfig/src/bootconfig.cpp +271 -0
@@ 0,0 1,271 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <boot/bootconfig.hpp>

#include <purefs/filesystem_paths.hpp>
#include <source/version.hpp>
#include <time/time_conversion.hpp>
#include <ticks.hpp>
#include <stdio.h>
#include <log/log.hpp>
#include <crc32/crc32.h>

namespace boot
{
    namespace
    {
        bool replaceWithString(const fs::path &fileToModify, const std::string &stringToWrite)
        {
            auto lamb = [](::FILE *stream) { fclose(stream); };
            std::unique_ptr<::FILE, decltype(lamb)> fp(fopen(fileToModify.c_str(), "w"), lamb);

            if (fp.get() != nullptr) {
                size_t dataWritten = fwrite(stringToWrite.c_str(), stringToWrite.length(), 1, fp.get());
                return dataWritten == 1;
            }
            else {
                return false;
            }
        }
        void computeCRC32(::FILE *file, unsigned long *outCrc32)
        {
            if (outCrc32 == nullptr)
                return;

            std::unique_ptr<unsigned char[]> buf(new unsigned char[purefs::buffer::crc_buf]);
            size_t bufLen;

            *outCrc32 = 0;

            while (!::feof(file)) {
                bufLen = ::fread(buf.get(), 1, purefs::buffer::crc_buf, file);
                if (bufLen <= 0)
                    break;

                *outCrc32 = Crc32_ComputeBuf(*outCrc32, buf.get(), bufLen);
            }
        }

        bool updateFileCRC32(const fs::path &file)
        {
            unsigned long fileCRC32 = 0;
            auto lamb               = [](::FILE *stream) { ::fclose(stream); };

            std::unique_ptr<::FILE, decltype(lamb)> fp(fopen(file.c_str(), "r"), lamb);

            if (fp.get() != nullptr) {
                std::unique_ptr<char[]> crc32Buf(new char[purefs::buffer::crc_char_size]);
                int written = 0;
                computeCRC32(fp.get(), &fileCRC32);
                LOG_INFO("updateFileCRC32 writing new crc32 %08" PRIX32 " for %s",
                         static_cast<std::uint32_t>(fileCRC32),
                         file.c_str());
                if (fileCRC32 != 0) {
                    if ((written = sprintf(crc32Buf.get(), "%08" PRIX32, fileCRC32)) !=
                        (purefs::buffer::crc_char_size - 1)) {
                        LOG_INFO("updateFileCRC32 can't prepare string for crc32, sprintf returned %d instead of %d",
                                 written,
                                 purefs::buffer::crc_char_size - 1);
                        return false;
                    }
                    fs::path fileCRC32Path = file;
                    fileCRC32Path += purefs::extension::crc32;

                    std::unique_ptr<::FILE, decltype(lamb)> fpCRC32(fopen(fileCRC32Path.c_str(), "w"), lamb);

                    if (fpCRC32.get() != nullptr) {
                        if (fwrite(crc32Buf.get(), 1, purefs::buffer::crc_char_size, fpCRC32.get()) ==
                            purefs::buffer::crc_char_size) {
                            LOG_INFO("updateFileCRC32 wrote \"%s\" in %s", crc32Buf.get(), fileCRC32Path.c_str());
                            return true;
                        }
                        else {
                            LOG_WARN("updateFileCRC32 can't write new crc32");
                            return false;
                        }
                    }
                    else {
                        LOG_WARN("updateFileCRC32 can't open crc32 file for write");
                        return false;
                    }
                }
            }
            else {
                LOG_WARN("updateFileCRC32 can't open file %s for write", file.c_str());
                return false;
            }

            return false;
        }
        std::string loadFileAsString(const fs::path &fileToLoad)
        {
            auto lamb = [](::FILE *stream) { ::fclose(stream); };
            std::unique_ptr<char[]> readBuf(new char[purefs::buffer::tar_buf]);
            std::unique_ptr<::FILE, decltype(lamb)> fp(fopen(fileToLoad.c_str(), "r"), lamb);
            std::string contents;
            size_t readSize;

            if (fp.get() != nullptr) {
                while (!feof(fp.get())) {
                    readSize = fread(readBuf.get(), 1, purefs::buffer::tar_buf, fp.get());
                    contents.append(static_cast<const char *>(readBuf.get()), readSize);
                }
            }

            return contents;
        }

    } // namespace

    BootConfig::BootConfig() : m_os_root_path(purefs::dir::getRootDiskPath())
    {}
    json11::Json BootConfig::to_json() const
    {
        return json11::Json::object{
            {purefs::json::main,
             json11::Json::object{{purefs::json::os_image, m_os_image},
                                  {purefs::json::os_type, m_os_type},
                                  {purefs::json::os_version, m_os_version},
                                  {purefs::json::timestamp, m_timestamp}}},

            {purefs::json::git_info,
             json11::Json::object{{purefs::json::os_git_tag, std::string(GIT_TAG)},
                                  {purefs::json::os_git_revision, std::string(GIT_REV)},
                                  {purefs::json::os_git_branch, std::string(GIT_BRANCH)}}},
            {purefs::json::bootloader, json11::Json::object{{purefs::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;
    }

    void BootConfig::updateTimestamp()
    {
        m_timestamp = utils::time::Timestamp().str("%c");
        LOG_INFO("vfs::updateTimestamp \"%s\"", to_json().dump().c_str());

        if (replaceWithString(m_boot_json, to_json().dump())) {
            updateFileCRC32(m_boot_json);
        }
    }
    bool BootConfig::loadBootConfig(const std::filesystem::path &bootJsonPath)
    {
        std::string parseErrors  = "";
        std::string jsonContents = loadFileAsString(bootJsonPath);

        LOG_INFO("vfs::getOSRootFromJSON parsing %s", bootJsonPath.c_str());
        LOG_INFO("vfs::getOSRootFromJSON \"%s\"", jsonContents.c_str());

        m_boot_json_parsed = json11::Json::parse(jsonContents, parseErrors);

        if (parseErrors == "") {
            m_os_type      = m_boot_json_parsed[purefs::json::main][purefs::json::os_type].string_value();
            m_os_image     = m_boot_json_parsed[purefs::json::main][purefs::json::os_image].string_value();
            m_os_root_path = purefs::createPath(purefs::dir::getRootDiskPath(), m_os_type);
            m_boot_json    = bootJsonPath;
            m_bootloader_version =
                m_boot_json_parsed[purefs::json::bootloader][purefs::json::os_version].string_value();
            m_timestamp  = utils::time::Timestamp().str("%c");
            m_os_version = std::string(VERSION);

            LOG_INFO("boot_config: %s", to_json().dump().c_str());
            return true;
        }
        else {
            m_os_type      = PATH_CURRENT;
            m_os_image     = purefs::file::boot_bin;
            m_os_root_path = purefs::createPath(purefs::dir::getRootDiskPath(), m_os_type);
            m_boot_json    = bootJsonPath;
            m_timestamp    = utils::time::Timestamp().str("%c");
            m_os_version   = std::string(VERSION);
            LOG_WARN("vfs::getOSRootFromJSON failed to parse %s: \"%s\"", bootJsonPath.c_str(), parseErrors.c_str());
            return false;
        }
    }
    const std::filesystem::path BootConfig::getCurrentBootJSON()
    {
        if (verifyCRC(purefs::file::boot_json)) {
            return purefs::createPath(purefs::dir::getRootDiskPath(), purefs::file::boot_json);
        }
        LOG_INFO("vfs::getCurrentBootJSON crc check failed on %s", purefs::file::boot_json);
        // replace broken .boot.json with a default one
        replaceWithString(purefs::dir::getRootDiskPath() / purefs::file::boot_json, to_json().dump());
        return purefs::createPath(purefs::dir::getRootDiskPath(), purefs::file::boot_json);
    }

    bool BootConfig::verifyCRC(const std::string filePath, const unsigned long crc32)
    {
        unsigned long crc32Read;
        auto lamb = [](::FILE *stream) { ::fclose(stream); };

        std::unique_ptr<::FILE, decltype(lamb)> fp(::fopen(filePath.c_str(), "r"), lamb);

        if (fp.get() != nullptr) {
            computeCRC32(fp.get(), &crc32Read);
            LOG_INFO("verifyCRC computed crc32 for %s is %08" PRIX32,
                     filePath.c_str(),
                     static_cast<std::uint32_t>(crc32Read));
            return (crc32Read == crc32);
        }
        LOG_ERROR("verifyCRC can't open %s", filePath.c_str());
        return (false);
    }

    bool BootConfig::verifyCRC(const std::filesystem::path filePath)
    {
        auto lamb = [](::FILE *stream) { ::fclose(stream); };
        std::unique_ptr<char[]> crcBuf(new char[purefs::buffer::crc_char_size]);
        size_t readSize;
        fs::path crcFilePath(filePath);
        crcFilePath += purefs::extension::crc32;

        std::unique_ptr<::FILE, decltype(lamb)> fp(::fopen(crcFilePath.c_str(), "r"), lamb);

        if (fp.get() != nullptr) {
            if ((readSize = ::fread(crcBuf.get(), 1, purefs::buffer::crc_char_size, fp.get())) !=
                (purefs::buffer::crc_char_size)) {
                LOG_ERROR("verifyCRC fread on %s returned different size then %d [%zu]",
                          crcFilePath.c_str(),
                          purefs::buffer::crc_char_size,
                          readSize);
                return false;
            }

            const unsigned long crc32Read = strtoull(crcBuf.get(), nullptr, purefs::buffer::crc_radix);

            LOG_INFO("verifyCRC read %s string:\"%s\" hex:%08lX", crcFilePath.c_str(), crcBuf.get(), crc32Read);
            return verifyCRC(filePath, crc32Read);
        }
        LOG_ERROR("verifyCRC can't open %s", crcFilePath.c_str());
        return false;
    }
} // namespace boot

M module-vfs/CMakeLists.txt => module-vfs/CMakeLists.txt +4 -2
@@ 75,8 75,8 @@ set(SOURCES ""
        src/purefs/vfs_subsystem.cpp
        drivers/src/purefs/fs/filesystem_vfat.cpp
        drivers/src/purefs/fs/filesystem_littlefs.cpp
        src/deprecated/vfs-utils.cpp
        src/deprecated/vfs.cpp
        src/deprecated/vfs-utils.cpp
        src/deprecated/vfsNotifier.cpp
)



@@ 126,7 126,9 @@ target_include_directories(${PROJECT_NAME}
)

target_link_libraries(${PROJECT_NAME} PUBLIC ${TARGET_LIBRARIES} module-bsp module-utils)
target_link_libraries(${PROJECT_NAME} PRIVATE littlefs)
target_link_libraries(${PROJECT_NAME} PRIVATE littlefs )
# TODO: Temporary only remove when removing old vfs
target_link_libraries(${PROJECT_NAME} PUBLIC utils-bootconfig )

if (${ENABLE_TESTS})
    add_subdirectory(tests)

M module-vfs/board/cross/free_rtos_custom/portable/vfs.cpp => module-vfs/board/cross/free_rtos_custom/portable/vfs.cpp +8 -10
@@ 23,22 23,20 @@ void vfs::Init()
    /* Print out information on the disk. */
    FF_eMMC_user_DiskShowPartition(emmcFFDisk);

    bootConfig.os_root_path = purefs::dir::getRootDiskPath();

    chnNotifier.onFileSystemInitialized();
    initDone = true;

    if (loadBootConfig(getCurrentBootJSON())) {
        LOG_INFO("vfs::Init osType %s root:%s", bootConfig.os_type.c_str(), bootConfig.os_root_path.c_str());
        if (ff_chdir(bootConfig.os_root_path.c_str()) != 0) {
            LOG_ERROR("vfs::Init can't chdir to %s", bootConfig.os_root_path.c_str());
    int err  = bootConfig.load();
    if (!err) {
        LOG_INFO("vfs::Init osType %s root:%s", bootConfig.os_type().c_str(), bootConfig.os_root_path().c_str());
        if (ff_chdir(bootConfig.os_root_path().c_str()) != 0) {
            LOG_ERROR("vfs::Init can't chdir to %s", bootConfig.os_root_path().c_str());
        }
    }
    else {
        LOG_ERROR("vfs::Init unable to determine OS type, fallback to %s", bootConfig.os_root_path.c_str());
        LOG_ERROR("vfs::Init unable to determine OS type, fallback to %s", bootConfig.os_root_path().c_str());
    }

    LOG_INFO("vfs::Init running on ARM osRootPath: %s", bootConfig.os_root_path.c_str());
    chnNotifier.onFileSystemInitialized();
    LOG_INFO("vfs::Init running on ARM osRootPath: %s", bootConfig.os_root_path().c_str());

    // this should already exist and have user data in it
    // if it does not create an exmpty directory so that sqlite3 does not fault

M module-vfs/board/linux/free_rtos_custom/portable/vfs.cpp => module-vfs/board/linux/free_rtos_custom/portable/vfs.cpp +11 -9
@@ 47,19 47,22 @@ void vfs::Init()
    /* Print out information on the disk. */
    freertos_fat::internals::diskShowPartition(emmcFFDisk);

    bootConfig.os_root_path = purefs::dir::getRootDiskPath();

    if (loadBootConfig(getCurrentBootJSON())) {
        LOG_INFO("vfs::Init osType %s root:%s", bootConfig.os_type.c_str(), bootConfig.os_root_path.c_str());
        if (ff_chdir(bootConfig.os_root_path.c_str()) != 0) {
            LOG_ERROR("vfs::Init can't chdir to %s", bootConfig.os_root_path.c_str());
    initDone = true;
    int err  = bootConfig.load();
    if (!err) {
        if (ff_chdir(bootConfig.os_root_path().c_str()) != 0) {
            LOG_ERROR("vfs::Init can't chdir to %s", bootConfig.os_root_path().c_str());
        }
        LOG_INFO("vfs::Init osType %s root:%s", bootConfig.os_type().c_str(), bootConfig.os_root_path().c_str());
        if (ff_chdir(bootConfig.os_root_path().c_str()) != 0) {
            LOG_ERROR("vfs::Init can't chdir to %s", bootConfig.os_root_path().c_str());
        }
    }
    else {
        LOG_ERROR("vfs::Init unable to determine OS type, fallback to %s", bootConfig.os_root_path.c_str());
        LOG_ERROR("vfs::Init unable to determine OS type, fallback to %s", bootConfig.os_root_path().c_str());
    }

    LOG_INFO("vfs::Init running on ARM osRootPath: %s", bootConfig.os_root_path.c_str());
    LOG_INFO("vfs::Init running on ARM osRootPath: %s", bootConfig.os_root_path().c_str());

    // this should already exist and have user data in it
    // if it does not create an exmpty directory so that sqlite3 does not fault


@@ 73,6 76,5 @@ void vfs::Init()
        LOG_INFO("vfs::Init looks like %s exists", userDiskPath.c_str());
    }
    chnNotifier.onFileSystemInitialized();
    initDone = true;
}
#pragma GCC diagnostic pop

M module-vfs/include/user/deprecated/vfs.hpp => module-vfs/include/user/deprecated/vfs.hpp +2 -30
@@ 8,10 8,9 @@
#include <vector>
#include <sstream>
#include <filesystem>
#include <crc32/crc32.h>
#include <log/log.hpp>
#include <json/json11.hpp>
#include <atomic>
#include <boot/bootconfig.hpp>
#include "vfsNotifier.hpp"
#include "vfs_globals.hpp"



@@ 59,21 58,6 @@ namespace purefs
        inline constexpr auto bootloader      = "bootloader";
    } // namespace json

    struct BootConfig
    {
        std::string os_image = "boot.bin";
        std::string os_type  = "current";
        std::string os_version;
        std::string bootloader_verion;
        std::string timestamp;
        json11::Json boot_json_parsed;

        fs::path os_root_path;
        fs::path boot_json;

        static int version_compare(const std::string &v1, const std::string &v2);
        [[nodiscard]] json11::Json to_json() const;
    };
}; // namespace purefs

/* NOTE: VFS global object class is now deprecated more information


@@ 141,10 125,6 @@ class vfs
    [[deprecated]] std::string loadFileAsString(const fs::path &fileToLoad);
    [[deprecated]] bool replaceWithString(const fs::path &fileToModify, const std::string &stringToWrite);
    [[deprecated]] void updateTimestamp();
    [[deprecated]] struct purefs::BootConfig &getBootConfig()
    {
        return bootConfig;
    }
    [[deprecated]] void registerNotificationHandler(vfsn::utility::vfsNotifier::NotifyHandler handler)
    {
        chnNotifier.registerNotificationHandler(handler);


@@ 157,23 137,15 @@ class vfs

    FF_Disk_t *emmcFFDisk{};

    [[deprecated]] static void computeCRC32(FILE *file, unsigned long *outCrc32);
    [[deprecated]] static bool verifyCRC(const std::string filePath, const unsigned long crc32);
    [[deprecated]] static bool verifyCRC(const fs::path filePath);
    [[deprecated]] static std::string generateRandomId(size_t length);
    auto isInitialized() const noexcept
    {
        return initDone.load();
    }

  private:
    bool updateFileCRC32(const fs::path &file);
    const fs::path getCurrentBootJSON();
    bool loadBootConfig(const fs::path &bootJsonPath);
    bool updateBootConfig(const fs::path &bootJsonPath);
    struct purefs::BootConfig bootConfig;
    vfsn::utility::vfsNotifier chnNotifier;
    static std::atomic<bool> initDone;
    boot::BootConfig bootConfig;
};

extern vfs vfs;

M module-vfs/src/deprecated/vfs-utils.cpp => module-vfs/src/deprecated/vfs-utils.cpp +0 -236
@@ 9,88 9,6 @@

#include <purefs/filesystem_paths.hpp>

std::string vfs::generateRandomId(size_t length = 0)
{
    const std::string CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    std::random_device random_device;
    std::mt19937 generator(random_device());
    generator.seed(utils::time::Timestamp().getTime());
    std::uniform_int_distribution<> distribution(0, CHARACTERS.size() - 1);

    std::string random_string;

    for (std::size_t i = 0; i < length; ++i) {
        random_string += CHARACTERS[distribution(generator)];
    }

    return random_string;
}

void vfs::computeCRC32(vfs::FILE *file, unsigned long *outCrc32)
{
    if (outCrc32 == nullptr)
        return;

    std::unique_ptr<unsigned char[]> buf(new unsigned char[purefs::buffer::crc_buf]);
    size_t bufLen;

    *outCrc32 = 0;

    while (!::vfs.eof(file)) {
        bufLen = ::vfs.fread(buf.get(), 1, purefs::buffer::crc_buf, file);
        if (bufLen <= 0)
            break;

        *outCrc32 = Crc32_ComputeBuf(*outCrc32, buf.get(), bufLen);
    }
}

bool vfs::verifyCRC(const std::string filePath, const unsigned long crc32)
{
    unsigned long crc32Read;
    auto lamb = [](vfs::FILE *stream) { ::vfs.fclose(stream); };

    std::unique_ptr<vfs::FILE, decltype(lamb)> fp(::vfs.fopen(filePath.c_str(), "r"), lamb);

    if (fp.get() != nullptr) {
        computeCRC32(fp.get(), &crc32Read);
        LOG_INFO(
            "verifyCRC computed crc32 for %s is %08" PRIX32, filePath.c_str(), static_cast<std::uint32_t>(crc32Read));
        return (crc32Read == crc32);
    }
    LOG_ERROR("verifyCRC can't open %s", filePath.c_str());
    return (false);
}

bool vfs::verifyCRC(const fs::path filePath)
{
    auto lamb = [](vfs::FILE *stream) { ::vfs.fclose(stream); };
    std::unique_ptr<char[]> crcBuf(new char[purefs::buffer::crc_char_size]);
    size_t readSize;
    fs::path crcFilePath(filePath);
    crcFilePath += purefs::extension::crc32;

    std::unique_ptr<vfs::FILE, decltype(lamb)> fp(::vfs.fopen(crcFilePath.c_str(), "r"), lamb);

    if (fp.get() != nullptr) {
        if ((readSize = ::vfs.fread(crcBuf.get(), 1, purefs::buffer::crc_char_size, fp.get())) !=
            (purefs::buffer::crc_char_size)) {
            LOG_ERROR("verifyCRC fread on %s returned different size then %d [%zu]",
                      crcFilePath.c_str(),
                      purefs::buffer::crc_char_size,
                      readSize);
            return false;
        }

        const unsigned long crc32Read = strtoull(crcBuf.get(), nullptr, purefs::buffer::crc_radix);

        LOG_INFO("verifyCRC read %s string:\"%s\" hex:%08lX", crcFilePath.c_str(), crcBuf.get(), crc32Read);
        return verifyCRC(filePath, crc32Read);
    }
    LOG_ERROR("verifyCRC can't open %s", crcFilePath.c_str());
    return false;
}

std::string vfs::loadFileAsString(const fs::path &fileToLoad)
{


@@ 124,157 42,3 @@ bool vfs::replaceWithString(const fs::path &fileToModify, const std::string &str
    }
}

const fs::path vfs::getCurrentBootJSON()
{
    if (verifyCRC(purefs::file::boot_json)) {
        return relativeToRoot(purefs::file::boot_json);
    }

    LOG_INFO("vfs::getCurrentBootJSON crc check failed on %s", purefs::file::boot_json);
    // replace broken .boot.json with a default one
    replaceWithString(purefs::dir::getRootDiskPath() / purefs::file::boot_json, bootConfig.to_json().dump());

    return relativeToRoot(purefs::file::boot_json);
}

bool vfs::loadBootConfig(const fs::path &bootJsonPath)
{
    std::string parseErrors  = "";
    std::string jsonContents = loadFileAsString(bootJsonPath);

    LOG_INFO("vfs::getOSRootFromJSON parsing %s", bootJsonPath.c_str());
    LOG_INFO("vfs::getOSRootFromJSON \"%s\"", jsonContents.c_str());

    bootConfig.boot_json_parsed = json11::Json::parse(jsonContents, parseErrors);

    if (parseErrors == "") {
        bootConfig.os_type  = bootConfig.boot_json_parsed[purefs::json::main][purefs::json::os_type].string_value();
        bootConfig.os_image = bootConfig.boot_json_parsed[purefs::json::main][purefs::json::os_image].string_value();
        bootConfig.os_root_path = purefs::createPath(purefs::dir::getRootDiskPath(), bootConfig.os_type);
        bootConfig.boot_json    = bootJsonPath;
        bootConfig.bootloader_verion =
            bootConfig.boot_json_parsed[purefs::json::bootloader][purefs::json::os_version].string_value();
        bootConfig.timestamp  = utils::time::Timestamp().str("%c");
        bootConfig.os_version = std::string(VERSION);

        LOG_INFO("boot_config: %s", bootConfig.to_json().dump().c_str());
        return true;
    }
    else {
        bootConfig.os_type      = PATH_CURRENT;
        bootConfig.os_image     = purefs::file::boot_bin;
        bootConfig.os_root_path = purefs::createPath(purefs::dir::getRootDiskPath(), bootConfig.os_type);
        bootConfig.boot_json    = bootJsonPath;
        bootConfig.timestamp    = utils::time::Timestamp().str("%c");
        bootConfig.os_version   = std::string(VERSION);

        LOG_WARN("vfs::getOSRootFromJSON failed to parse %s: \"%s\"", bootJsonPath.c_str(), parseErrors.c_str());
        return false;
    }
}

bool vfs::updateFileCRC32(const fs::path &file)
{
    unsigned long fileCRC32 = 0;
    auto lamb               = [](vfs::FILE *stream) { ::vfs.fclose(stream); };

    std::unique_ptr<vfs::FILE, decltype(lamb)> fp(fopen(file.c_str(), "r"), lamb);

    if (fp.get() != nullptr) {
        std::unique_ptr<char[]> crc32Buf(new char[purefs::buffer::crc_char_size]);
        int written = 0;
        computeCRC32(fp.get(), &fileCRC32);
        LOG_INFO("updateFileCRC32 writing new crc32 %08" PRIX32 " for %s",
                 static_cast<std::uint32_t>(fileCRC32),
                 file.c_str());
        if (fileCRC32 != 0) {
            if ((written = sprintf(crc32Buf.get(), "%08" PRIX32, fileCRC32)) != (purefs::buffer::crc_char_size - 1)) {
                LOG_INFO("updateFileCRC32 can't prepare string for crc32, sprintf returned %d instead of %d",
                         written,
                         purefs::buffer::crc_char_size - 1);
                return false;
            }
            fs::path fileCRC32Path = file;
            fileCRC32Path += purefs::extension::crc32;

            std::unique_ptr<vfs::FILE, decltype(lamb)> fpCRC32(fopen(fileCRC32Path.c_str(), "w"), lamb);

            if (fpCRC32.get() != nullptr) {
                if (fwrite(crc32Buf.get(), 1, purefs::buffer::crc_char_size, fpCRC32.get()) ==
                    purefs::buffer::crc_char_size) {
                    LOG_INFO("updateFileCRC32 wrote \"%s\" in %s", crc32Buf.get(), fileCRC32Path.c_str());
                    return true;
                }
                else {
                    LOG_WARN("updateFileCRC32 can't write new crc32");
                    return false;
                }
            }
            else {
                LOG_WARN("updateFileCRC32 can't open crc32 file for write");
                return false;
            }
        }
    }
    else {
        LOG_WARN("updateFileCRC32 can't open file %s for write", file.c_str());
        return false;
    }

    return false;
}

void vfs::updateTimestamp()
{
    bootConfig.timestamp = utils::time::Timestamp().str("%c");
    LOG_INFO("vfs::updateTimestamp \"%s\"", bootConfig.to_json().dump().c_str());

    if (replaceWithString(bootConfig.boot_json, bootConfig.to_json().dump())) {
        updateFileCRC32(bootConfig.boot_json);
    }
}

json11::Json purefs::BootConfig::to_json() const
{
    return json11::Json::object{
        {purefs::json::main,
         json11::Json::object{{purefs::json::os_image, os_image},
                              {purefs::json::os_type, os_type},
                              {purefs::json::os_version, os_version},
                              {purefs::json::timestamp, timestamp}}},

        {purefs::json::git_info,
         json11::Json::object{{purefs::json::os_git_tag, std::string(GIT_TAG)},
                              {purefs::json::os_git_revision, std::string(GIT_REV)},
                              {purefs::json::os_git_branch, std::string(GIT_BRANCH)}}},
        {purefs::json::bootloader, json11::Json::object{{purefs::json::os_version, bootloader_verion}}}};
}

// Method to compare two version strings
//   v1 <  v2  -> -1
//   v1 == v2  ->  0
int purefs::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;
}

M module-vfs/src/deprecated/vfs.cpp => module-vfs/src/deprecated/vfs.cpp +3 -3
@@ 289,16 289,16 @@ std::string vfs::relativeToRoot(const std::string path)
    fs::path fsPath(path);

    if (fsPath.is_absolute()) {
        if (bootConfig.os_root_path.root_directory() == fsPath.root_directory())
        if (bootConfig.os_root_path().root_directory() == fsPath.root_directory())
            return fsPath;
        else
            return purefs::createPath(purefs::dir::getRootDiskPath(), fsPath.relative_path()).c_str();
    }

    if (path.empty())
        return bootConfig.os_root_path;
        return bootConfig.os_root_path();
    else
        return bootConfig.os_root_path / fsPath;
        return bootConfig.os_root_path() / fsPath;
}

bool vfs::isDir(const char *path)

M module-vfs/tests/CMakeLists.txt => module-vfs/tests/CMakeLists.txt +1 -1
@@ 7,7 7,7 @@ add_catch2_executable(
    SRCS
        ${CMAKE_CURRENT_LIST_DIR}/unittest_vfs.cpp
    LIBS
        module-vfs
        module-vfs module-utils
)

add_catch2_executable(

M module-vfs/tests/unittest_vfs.cpp => module-vfs/tests/unittest_vfs.cpp +0 -61
@@ 80,70 80,9 @@ TEST_CASE("Test vfs case 1")
    }
}

#define RANDDOM_TESTS 4
TEST_CASE("Random strings")
{
    const std::string allowedChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    std::string randomIds8, randomIds16, randomIds32;
    randomIds8  = vfs.generateRandomId(8);
    randomIds16 = vfs.generateRandomId(16);
    randomIds32 = vfs.generateRandomId(32);

    REQUIRE(randomIds8.size() == 8);
    for (unsigned int i = 0; i < randomIds8.size(); i++)
        REQUIRE(allowedChars.find_first_of(randomIds8[i]) != std::string::npos);

    REQUIRE(randomIds16.size() == 16);
    for (unsigned int i = 0; i < randomIds16.size(); i++)
        REQUIRE(allowedChars.find_first_of(randomIds16[i]) != std::string::npos);

    REQUIRE(randomIds32.size() == 32);
    for (unsigned int i = 0; i < randomIds32.size(); i++)
        REQUIRE(allowedChars.find_first_of(randomIds32[i]) != std::string::npos);
}

TEST_CASE("CRC32 tests")
{
    unsigned long crc32 = 0;
    char crcBuf[purefs::buffer::crc_char_size];
    std::string randomData = vfs.generateRandomId(128);
    auto fd                = vfs.fopen("testFile.txt", "w");
    REQUIRE(fd != nullptr);

    auto bytesWritten = vfs.fwrite(randomData.c_str(), 1, randomData.size(), fd);
    REQUIRE(bytesWritten == randomData.size());
    REQUIRE(vfs.fclose(fd) == 0);

    fd = vfs.fopen("testFile.txt", "r");
    vfs.computeCRC32(fd, &crc32);
    sprintf(crcBuf, "%lX", crc32);
    auto fdCRC   = vfs.fopen("testFile.txt.crc32", "w");
    bytesWritten = vfs.fwrite(&crcBuf, 1, purefs::buffer::crc_char_size, fdCRC);
    REQUIRE(bytesWritten == purefs::buffer::crc_char_size);
    REQUIRE(vfs.fclose(fdCRC) == 0);
    REQUIRE(vfs.fclose(fd) == 0);

    REQUIRE(vfs.verifyCRC("testFile.txt") == true);
    REQUIRE(vfs.remove("testFile.txt") == 0);
    REQUIRE(vfs.remove("testFile.txt.crc32") == 0);
}

TEST_CASE("File loading and saving")
{
    static constexpr auto test_str = "abcd";
    auto fd                        = vfs.fopen("test1.txt", "w");
    REQUIRE(fd != nullptr);
    const auto slen = std::strlen(test_str);
    REQUIRE(vfs.fwrite(test_str, 1, slen, fd) == slen);
    vfs.fclose(fd);
    std::string fileContents = vfs.loadFileAsString("test1.txt");
    REQUIRE(strcmp(fileContents.c_str(), test_str) == 0);
    vfs.mkdir("module-vfs/test_dirx");
    vfs.replaceWithString("module-vfs/test_dirx/testWrite.txt", "this is a test");
    fileContents = vfs.loadFileAsString("module-vfs/test_dirx/testWrite.txt");
    REQUIRE(strcmp(fileContents.c_str(), "this is a test") == 0);
    REQUIRE(vfs.remove("module-vfs/test_dirx/testWrite.txt") == 0);
}

TEST_CASE("VFS lseek check")
{