~aleteoryx/muditaos

b50eea9ed99be2e8e7a3a44bb14319b41c439be2 — Pawel Olejniczak 3 years ago ef556d4
[CP-1427] Cleanup leftovers after old backup restore implementation

The synchronization mechanism has been freed from the constraints
of the old backup and restore implementation.
M module-db/Database/Database.cpp => module-db/Database/Database.cpp +6 -6
@@ 242,9 242,9 @@ void Database::pragmaQuery(const std::string &pragmaStatement)
    }
}

bool Database::storeIntoFile(const std::filesystem::path &backupPath)
bool Database::storeIntoFile(const std::filesystem::path &syncPath)
{
    // kind of workaround-fix for creating the backup - if somehow DB can't be properly
    // kind of workaround-fix for creating the sync package - if somehow DB can't be properly
    // vacuumed into file, the transaction is still outgoing (probably nested transaction)
    // that's why we need to commit it manually
    if (sqlite3_get_autocommit(dbConnection) == 0) {


@@ 256,11 256,11 @@ bool Database::storeIntoFile(const std::filesystem::path &backupPath)
        LOG_INFO("sqlite3 autocommit after commit: %d", sqlite3_get_autocommit(dbConnection));
    }

    LOG_INFO("Backup database: %s, into file: %s - STARTED", dbName.c_str(), backupPath.c_str());
    if (const auto rc = execute("VACUUM main INTO '%q';", backupPath.c_str()); !rc) {
        LOG_ERROR("Backup database: %s, into file: %s - FAILED", dbName.c_str(), backupPath.c_str());
    LOG_INFO("Store database: %s, into file: %s - STARTED", dbName.c_str(), syncPath.c_str());
    if (const auto rc = execute("VACUUM main INTO '%q';", syncPath.c_str()); !rc) {
        LOG_ERROR("Store database: %s, into file: %s - FAILED", dbName.c_str(), syncPath.c_str());
        return false;
    }
    LOG_INFO("Backup database: %s, into file: %s - SUCCEDED", dbName.c_str(), backupPath.c_str());
    LOG_INFO("Store database: %s, into file: %s - SUCCEEDED", dbName.c_str(), syncPath.c_str());
    return true;
}

M module-db/Database/Database.hpp => module-db/Database/Database.hpp +1 -1
@@ 32,7 32,7 @@ class Database
    // Must be invoked before closing system in order to properly close OS layer
    static bool deinitialize();

    bool storeIntoFile(const std::filesystem::path &backupPath);
    bool storeIntoFile(const std::filesystem::path &syncPath);

    uint32_t getLastInsertRowId();
    void pragmaQuery(const std::string &pragmaStatement);

M module-services/service-db/DBServiceAPI.cpp => module-services/service-db/DBServiceAPI.cpp +1 -16
@@ 279,28 279,13 @@ auto DBServiceAPI::CalllogUpdate(sys::Service *serv, const CalllogRecord &rec) -
    return ((ret.first == sys::ReturnCodes::Success) && (calllogResponse->retCode != 0));
}

auto DBServiceAPI::DBBackup(sys::Service *serv, std::string backupPath) -> bool
{
    LOG_INFO("DBBackup %s", backupPath.c_str());

    std::shared_ptr<DBServiceMessageBackup> msg =
        std::make_shared<DBServiceMessageBackup>(MessageType::DBServiceBackup, backupPath);

    auto ret = serv->bus.sendUnicastSync(msg, service::name::db, constants::BackupTimeoutInMs);
    if (auto retMsg = dynamic_cast<DBServiceResponseMessage *>(ret.second.get()); retMsg) {
        return retMsg->retCode;
    }
    LOG_ERROR("DBBackup error, return code: %s", c_str(ret.first));
    return false;
}

auto DBServiceAPI::DBPrepareSyncPackage(sys::Service *serv, const std::string &syncPackagePath) -> bool
{
    LOG_INFO("DBPrepareSyncPackage %s", syncPackagePath.c_str());

    auto msg = std::make_shared<DBServiceMessageSyncPackage>(MessageType::DBSyncPackage, syncPackagePath);

    auto ret = serv->bus.sendUnicastSync(std::move(msg), service::name::db, DefaultTimeoutInMs);
    auto ret = serv->bus.sendUnicastSync(std::move(msg), service::name::db, constants::DefaultTimeoutInMs);
    if (auto retMsg = dynamic_cast<DBServiceResponseMessage *>(ret.second.get()); retMsg) {
        return retMsg->retCode;
    }

M module-services/service-db/include/service-db/DBServiceAPI.hpp => module-services/service-db/include/service-db/DBServiceAPI.hpp +0 -1
@@ 110,7 110,6 @@ class DBServiceAPI
    [[deprecated]] static auto CalllogRemove(sys::Service *serv, uint32_t id) -> bool;
    [[deprecated]] static auto CalllogUpdate(sys::Service *serv, const CalllogRecord &rec) -> bool;

    static auto DBBackup(sys::Service *serv, std::string backupPath) -> bool;
    static auto DBPrepareSyncPackage(sys::Service *serv, const std::string &syncPackagePath) -> bool;

    static auto IsContactInFavourites(sys::Service *serv, const utils::PhoneNumber::View &numberView) -> bool;

D module-services/service-desktop/BackupRestore.cpp => module-services/service-desktop/BackupRestore.cpp +0 -747
@@ 1,747 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 <service-desktop/BackupRestore.hpp>
#include <endpoints/JsonKeyNames.hpp>

#include <SystemManager/SystemManagerCommon.hpp>
#include <log/log.hpp>
#include <microtar.hpp>
#include <purefs/filesystem_paths.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <service-db/DBServiceName.hpp>
#include <Utils.hpp>

#include <cassert>
#include <filesystem>
#include <purefs/filesystem_paths.hpp>
#include <memory>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <sys/statvfs.h>
#include <gsl/gsl>
#include <json11.hpp>
#include <Split.hpp>

namespace sys
{
    class Service;
} // namespace sys

namespace bkp
{
    inline constexpr auto backupInfo = "backup.json";
};

static bool isValidDirentry(const std::filesystem::directory_entry &direntry)
{
    return direntry.path() != "." && direntry.path() != ".." && direntry.path() != "...";
}

BackupSyncRestore::CompletionCode BackupSyncRestore::BackupUserFiles(sys::Service *ownerService,
                                                                     std::filesystem::path &path)
{
    assert(ownerService != nullptr);
    LOG_INFO("Backup started...");

    if (BackupSyncRestore::RemoveBackupDir(path) == false) {
        return CompletionCode::FSError;
    }

    if (BackupSyncRestore::CreateBackupDir(path) == false) {
        return CompletionCode::FSError;
    }

    LOG_DEBUG("Database backup started...");
    if (DBServiceAPI::DBBackup(ownerService, path) == false) {
        LOG_ERROR("Database backup failed, quitting...");
        BackupSyncRestore::RemoveBackupDir(path);
        return CompletionCode::DBError;
    }

    if (WriteBackupInfo(ownerService, path) == false) {
        LOG_ERROR("Failed to save backup info");
        BackupSyncRestore::RemoveBackupDir(path);
        return CompletionCode::CopyError;
    }

    LOG_DEBUG("Packing files");
    if (BackupSyncRestore::PackUserFiles(path) == false) {
        LOG_ERROR("Failed pack backup");
        BackupSyncRestore::RemoveBackupDir(path);
        return CompletionCode::PackError;
    }

    return CompletionCode::Success;
}

BackupSyncRestore::CompletionCode BackupSyncRestore::PrepareSyncPackage(sys::Service *ownerService,
                                                                        std::filesystem::path &path)
{
    assert(ownerService != nullptr);
    LOG_INFO("Sync package preparation started...");

    if (!BackupSyncRestore::RemoveSyncDir(path)) {
        return CompletionCode::FSError;
    }

    if (!BackupSyncRestore::CreateSyncDir(path)) {
        return CompletionCode::FSError;
    }

    LOG_DEBUG("Sync package preparation started...");
    if (!DBServiceAPI::DBPrepareSyncPackage(ownerService, path)) {
        LOG_ERROR("Sync package preparation, quitting...");
        BackupSyncRestore::RemoveSyncDir(path);
        return CompletionCode::DBError;
    }

    LOG_DEBUG("Packing files");
    if (!BackupSyncRestore::PackSyncFiles(path)) {
        LOG_ERROR("Failed pack sync files");
        BackupSyncRestore::RemoveSyncDir(path);
        return CompletionCode::PackError;
    }

    return CompletionCode::Success;
}

bool BackupSyncRestore::WriteBackupInfo(sys::Service *ownerService, const std::filesystem::path &path)
{
    LOG_INFO("Writing backup info");

    if (!std::filesystem::is_directory(path)) {
        LOG_ERROR("%s is not a directory", path.c_str());
        return false;
    }

    const auto version_json = purefs::dir::getCurrentOSPath() / purefs::file::version_json;

    std::error_code errorCode;
    if (!std::filesystem::copy_file(version_json, path / bkp::backupInfo, errorCode)) {
        LOG_ERROR("Failed to copy %s -> %s, error: %d",
                  (version_json).c_str(),
                  (path / bkp::backupInfo).c_str(),
                  errorCode.value());
        return false;
    }

    LOG_DEBUG("%s copied to %s", (version_json).c_str(), (path / bkp::backupInfo).c_str());
    return true;
}

BackupSyncRestore::CompletionCode BackupSyncRestore::RestoreUserFiles(sys::Service *ownerService,
                                                                      const std::filesystem::path &path)
{
    assert(ownerService != nullptr);

    LOG_INFO("Restore started");

    if (BackupSyncRestore::UnpackBackupFile(path) == false) {
        LOG_ERROR("Can't unpack user files");
        return CompletionCode::UnpackError;
    }

    if (BackupSyncRestore::CanRestoreFromBackup(TempPathForBackupFile(path)) == false) {
        LOG_ERROR("Can't restore user files");
        return CompletionCode::FSError;
    }

    if (sys::SystemManagerCommon::Restore(ownerService) == false) {
        LOG_ERROR("Can't enter system restore state");
        return CompletionCode::OtherError;
    }

    LOG_INFO("Entered restore state");

    if (BackupSyncRestore::ReplaceUserFiles(path) == false) {
        LOG_ERROR("Can't restore user files");
        return CompletionCode::CopyError;
    }

    return CompletionCode::Success;
}

bool BackupSyncRestore::RemoveBackupDir(const std::filesystem::path &path)
{
    /* prepare directories */
    if (std::filesystem::is_directory(path)) {
        LOG_INFO("Removing backup directory %s...", path.c_str());
        std::error_code errorCode;

        if (!std::filesystem::remove_all(path, errorCode)) {
            LOG_ERROR("Removing backup directory %s failed, error: %d.", path.c_str(), errorCode.value());
            return false;
        }
    }

    return true;
}

bool BackupSyncRestore::RemoveSyncDir(const std::filesystem::path &path)
{
    /* prepare directories */
    if (std::filesystem::is_directory(path)) {
        LOG_INFO("Removing sync directory %s...", path.c_str());
        std::error_code errorCode;

        if (std::filesystem::remove_all(path, errorCode) == 0) {
            LOG_ERROR("Removing sync directory %s failed, error: %d.", path.c_str(), errorCode.value());
            return false;
        }
    }

    return true;
}

bool BackupSyncRestore::CreateBackupDir(const std::filesystem::path &path)
{
    LOG_INFO("Creating backup directory %s...", path.c_str());
    std::error_code errorCode;

    if (!std::filesystem::exists(path)) {
        if (!std::filesystem::create_directories(path, errorCode)) {
            LOG_ERROR("Failed to create directory: %s, error: %d", path.c_str(), errorCode.value());
            return false;
        }
    }

    return true;
}

bool BackupSyncRestore::CreateSyncDir(const std::filesystem::path &path)
{
    LOG_INFO("Creating sync directory %s...", path.c_str());
    std::error_code errorCode;

    if (!std::filesystem::exists(path)) {
        if (!std::filesystem::create_directories(path, errorCode)) {
            LOG_ERROR("Failed to create directory: %s, error: %d", path.c_str(), errorCode.value());
            return false;
        }
    }

    return true;
}

bool BackupSyncRestore::PackUserFiles(const std::filesystem::path &path)
{
    if (std::filesystem::is_empty(path)) {
        LOG_ERROR("Backup dir is empty, nothing to backup, quitting...");
        BackupSyncRestore::RemoveBackupDir(path);
        return false;
    }

    std::filesystem::path tarFilePath = (purefs::dir::getBackupOSPath() / path.filename());
    mtar_t tarFile;

    LOG_INFO("Opening tar %s file...", tarFilePath.c_str());

    int ret = mtar_open(&tarFile, tarFilePath.c_str(), "w");
    if (ret != MTAR_ESUCCESS) {
        LOG_ERROR("Opening tar file failed, quitting...");
        BackupSyncRestore::RemoveBackupDir(path);
        return false;
    }

    auto buffer                       = std::make_unique<unsigned char[]>(purefs::buffer::tar_buf);
    constexpr size_t streamBufferSize = 64 * 1024;
    auto streamBuffer                 = std::make_unique<char[]>(streamBufferSize);
    setvbuf(tarFile.stream, streamBuffer.get(), _IOFBF, streamBufferSize);
    std::error_code errorCode;

    for (auto &direntry : std::filesystem::directory_iterator(path)) {
        if (!isValidDirentry(direntry)) {
            continue;
        }

        LOG_INFO("Archiving file ...");
        auto *file = std::fopen(direntry.path().string().c_str(), "r");

        if (file == nullptr) {
            LOG_ERROR("Archiving file failed, cannot open file, quitting...");
            mtar_close(&tarFile);
            BackupSyncRestore::RemoveBackupDir(path);
            return false;
        }

        LOG_DEBUG("Writting tar header ...");

        if (mtar_write_file_header(&tarFile,
                                   direntry.path().filename().c_str(),
                                   static_cast<unsigned>(std::filesystem::file_size(direntry))) != MTAR_ESUCCESS) {
            LOG_ERROR("Writing tar header failed");
            std::fclose(file);
            mtar_close(&tarFile);
            BackupSyncRestore::RemoveBackupDir(path);
            return false;
        }

        uintmax_t filesize = std::filesystem::file_size(direntry.path(), errorCode);
        if (errorCode) {
            LOG_ERROR("Failed to get size for file %s, error: %d", direntry.path().c_str(), errorCode.value());
            BackupSyncRestore::RemoveBackupDir(path);
            return false;
        }
        uint32_t loopcount = (filesize / purefs::buffer::tar_buf) + 1u;
        uint32_t readsize;

        for (uint32_t i = 0u; i < loopcount; i++) {
            if (i + 1u == loopcount) {
                readsize = filesize % purefs::buffer::tar_buf;
            }
            else {
                readsize = purefs::buffer::tar_buf;
            }

            LOG_DEBUG("Reading file ...");

            if (std::fread(buffer.get(), 1, readsize, file) != readsize) {
                LOG_ERROR("Reading file failed, quitting...");
                std::fclose(file);
                mtar_close(&tarFile);
                BackupSyncRestore::RemoveBackupDir(path);
                return false;
            }

            LOG_DEBUG("Writting into backup...");
            if (mtar_write_data(&tarFile, buffer.get(), readsize) != MTAR_ESUCCESS) {
                LOG_ERROR("Writting into backup failed, quitting...");
                std::fclose(file);
                mtar_close(&tarFile);
                BackupSyncRestore::RemoveBackupDir(path);
                return false;
            }
        }

        LOG_INFO("Closing file...");
        if (std::fclose(file) != 0) {
            LOG_ERROR("Closing file failed, quitting...");
            mtar_close(&tarFile);
            BackupSyncRestore::RemoveBackupDir(path);
            return false;
        }

        LOG_INFO("Deleting file ...");

        if (!std::filesystem::remove(direntry.path(), errorCode)) {
            LOG_ERROR("Deleting file failed, error: %d, quitting...", errorCode.value());
            mtar_close(&tarFile);
            BackupSyncRestore::RemoveBackupDir(path);
            return false;
        }
    }

    LOG_INFO("Finalizing tar file...");
    if (mtar_finalize(&tarFile) != MTAR_ESUCCESS) {
        LOG_ERROR("Finalizing tar file failed, quitting....");
        mtar_close(&tarFile);
        BackupSyncRestore::RemoveBackupDir(path);
        return false;
    }

    LOG_INFO("Closing tar file...");
    if (mtar_close(&tarFile) != MTAR_ESUCCESS) {
        LOG_ERROR("Closing tar file failed, quitting...");
        BackupSyncRestore::RemoveBackupDir(path);
        return false;
    }

    return true;
}

bool BackupSyncRestore::PackSyncFiles(const std::filesystem::path &path)
{
    if (std::filesystem::is_empty(path)) {
        LOG_ERROR("Sync dir is empty, quitting...");
        BackupSyncRestore::RemoveSyncDir(path);
        return false;
    }

    std::filesystem::path tarFilePath = (purefs::dir::getSyncPackagePath() / path.filename());
    mtar_t tarFile;

    LOG_INFO("Opening tar %s file...", tarFilePath.c_str());

    int ret = mtar_open(&tarFile, tarFilePath.c_str(), "w");
    if (ret != MTAR_ESUCCESS) {
        LOG_ERROR("Opening tar file failed, quitting...");
        BackupSyncRestore::RemoveSyncDir(path);
        return false;
    }

    auto buffer                       = std::make_unique<unsigned char[]>(purefs::buffer::tar_buf);
    constexpr size_t streamBufferSize = 64 * 1024;
    auto streamBuffer                 = std::make_unique<char[]>(streamBufferSize);
    setvbuf(tarFile.stream, streamBuffer.get(), _IOFBF, streamBufferSize);
    std::error_code errorCode;

    for (auto &direntry : std::filesystem::directory_iterator(path)) {
        if (!isValidDirentry(direntry)) {
            continue;
        }

        LOG_INFO("Archiving file ...");
        auto *file = std::fopen(direntry.path().string().c_str(), "r");

        if (file == nullptr) {
            LOG_ERROR("Archiving file failed, cannot open file, quitting...");
            mtar_close(&tarFile);
            BackupSyncRestore::RemoveSyncDir(path);
            return false;
        }

        LOG_DEBUG("Writting tar header ...");

        if (mtar_write_file_header(&tarFile,
                                   direntry.path().filename().c_str(),
                                   static_cast<unsigned>(std::filesystem::file_size(direntry))) != MTAR_ESUCCESS) {
            LOG_ERROR("Writing tar header failed");
            std::fclose(file);
            mtar_close(&tarFile);
            BackupSyncRestore::RemoveSyncDir(path);
            return false;
        }

        uintmax_t filesize = std::filesystem::file_size(direntry.path(), errorCode);
        if (errorCode) {
            LOG_ERROR("Failed to get size for file %s, error: %d", direntry.path().c_str(), errorCode.value());
            BackupSyncRestore::RemoveSyncDir(path);
            return false;
        }
        uint32_t loopcount = (filesize / purefs::buffer::tar_buf) + 1u;
        uint32_t readsize;

        for (uint32_t i = 0u; i < loopcount; i++) {
            if (i + 1u == loopcount) {
                readsize = filesize % purefs::buffer::tar_buf;
            }
            else {
                readsize = purefs::buffer::tar_buf;
            }

            LOG_DEBUG("Reading file ...");

            if (std::fread(buffer.get(), 1, readsize, file) != readsize) {
                LOG_ERROR("Reading file failed, quitting...");
                std::fclose(file);
                mtar_close(&tarFile);
                BackupSyncRestore::RemoveSyncDir(path);
                return false;
            }

            LOG_DEBUG("Writting into sync package...");
            if (mtar_write_data(&tarFile, buffer.get(), readsize) != MTAR_ESUCCESS) {
                LOG_ERROR("Writting into sync package failed, quitting...");
                std::fclose(file);
                mtar_close(&tarFile);
                BackupSyncRestore::RemoveSyncDir(path);
                return false;
            }
        }

        LOG_INFO("Closing file...");
        if (std::fclose(file) != 0) {
            LOG_ERROR("Closing file failed, quitting...");
            mtar_close(&tarFile);
            BackupSyncRestore::RemoveSyncDir(path);
            return false;
        }

        LOG_INFO("Deleting file ...");

        if (!std::filesystem::remove(direntry.path(), errorCode)) {
            LOG_ERROR("Deleting file failed, error: %d, quitting...", errorCode.value());
            mtar_close(&tarFile);
            BackupSyncRestore::RemoveSyncDir(path);
            return false;
        }
    }

    LOG_INFO("Finalizing tar file...");
    if (mtar_finalize(&tarFile) != MTAR_ESUCCESS) {
        LOG_ERROR("Finalizing tar file failed, quitting....");
        mtar_close(&tarFile);
        BackupSyncRestore::RemoveSyncDir(path);
        return false;
    }

    LOG_INFO("Closing tar file...");
    if (mtar_close(&tarFile) != MTAR_ESUCCESS) {
        LOG_ERROR("Closing tar file failed, quitting...");
        BackupSyncRestore::RemoveSyncDir(path);
        return false;
    }

    return true;
}

auto BackupSyncRestore::TempPathForBackupFile(const std::filesystem::path &tarFilePath) -> std::filesystem::path const
{
    std::filesystem::path extractFolder;

    if (tarFilePath.has_extension()) {
        extractFolder = tarFilePath.stem();
    }
    else {
        extractFolder = tarFilePath.filename();
    }

    return purefs::dir::getTemporaryPath() / extractFolder;
}

bool BackupSyncRestore::UnpackBackupFile(const std::filesystem::path &tarFilePath)
{
    mtar_t tarFile;
    mtar_header_t tarHeader;
    std::error_code errorCode;

    auto extractDestination = TempPathForBackupFile(tarFilePath);

    LOG_INFO("Creating temporary directory");
    if (!std::filesystem::exists(extractDestination, errorCode)) {
        if (!std::filesystem::create_directories(extractDestination, errorCode)) {
            LOG_ERROR("Can't create temporary directory, error: %d", errorCode.value());
            return false;
        }
    }

    LOG_INFO("Opening tar file ...");

    int ret = mtar_open(&tarFile, tarFilePath.c_str(), "r");

    if (ret != MTAR_ESUCCESS) {
        LOG_ERROR("Opening tar file failed, quitting...");
        return false;
    }

    auto buffer                       = std::make_unique<unsigned char[]>(purefs::buffer::tar_buf);
    constexpr size_t streamBufferSize = 64 * 1024;
    auto streamBuffer                 = std::make_unique<char[]>(streamBufferSize);
    setvbuf(tarFile.stream, streamBuffer.get(), _IOFBF, streamBufferSize);

    do {
        ret = mtar_read_header(&tarFile, &tarHeader);
        LOG_DEBUG("Reading tar header name... Name: %s", tarHeader.name);

        if ((tarHeader.type == MTAR_TREG) && (ret == MTAR_ESUCCESS)) {
            LOG_DEBUG("Extracting tar file ...");

            std::filesystem::path extractedFile = extractDestination / tarHeader.name;
            auto *file                          = std::fopen(extractedFile.c_str(), "w");

            if (file == nullptr) {
                LOG_ERROR("Can't open file for writing");
                mtar_close(&tarFile);
                return false;
            }

            constexpr size_t streamBufferSize = 16 * 1024;
            auto streamBuffer                 = std::make_unique<char[]>(streamBufferSize);
            setvbuf(file, streamBuffer.get(), _IOFBF, streamBufferSize);

            uint32_t readsize      = 0u;
            uint32_t remainingData = tarHeader.size;

            do {
                readsize = remainingData > purefs::buffer::tar_buf ? purefs::buffer::tar_buf : remainingData;
                memset(buffer.get(), 0, purefs::buffer::tar_buf);

                if (mtar_read_data(&tarFile, buffer.get(), readsize) != MTAR_ESUCCESS) {
                    LOG_ERROR("Extracting file failed, quitting...");
                    mtar_close(&tarFile);
                    std::fclose(file);
                    std::filesystem::remove(extractedFile.c_str());
                    return false;
                }

                if (std::fwrite(buffer.get(), 1, readsize, file) != readsize) {
                    LOG_ERROR("writting file failed, quitting...");
                    mtar_close(&tarFile);
                    std::fclose(file);
                    std::filesystem::remove(extractedFile.c_str());
                    return false;
                }

                remainingData = tarFile.remaining_data;
            } while (remainingData);

            LOG_INFO("Extracting file succeeded");
            std::fclose(file);
        }
        else {
            LOG_DEBUG("Found header %d, skipping", tarHeader.type);
        }

        ret = mtar_next(&tarFile);
        LOG_DEBUG("Reading tar next status %s", mtar_strerror(ret));
    } while (ret == MTAR_ESUCCESS);

    LOG_DEBUG("Cleanup");
    mtar_close(&tarFile);
    errorCode.clear();
    std::filesystem::remove(tarFilePath.c_str(), errorCode);

    if (errorCode) {
        LOG_WARN("Can't cleanup temporary dir, error: %d", errorCode.value());
    }

    return ret == MTAR_ENULLRECORD ? true : false;
}

std::string BackupSyncRestore::ReadFileAsString(const std::filesystem::path &fileToRead)
{
    const auto file = std::ifstream(fileToRead);

    if (!file.is_open()) {
        LOG_ERROR("Can't open %s file", fileToRead.string().c_str());
        return {};
    }

    std::ostringstream fileContents;
    fileContents << file.rdbuf();

    return fileContents.str();
}

using namespace sdesktop::endpoints;

std::string BackupSyncRestore::ReadVersionFromJsonFile(const std::filesystem::path &jsonFilePath)
{
    const auto jsonFile = ReadFileAsString(jsonFilePath);

    if (jsonFile.empty()) {
        LOG_ERROR("Can't read data from %s file", jsonFilePath.c_str());
        return {};
    }

    std::string err;
    const auto jsonFile_Root = json11::Json::parse(jsonFile, err);
    if (jsonFile_Root.is_null()) {
        LOG_ERROR("Can't parse %s file: %s", jsonFilePath.c_str(), err.c_str());
        return {};
    }

    auto bootVersionObject = jsonFile_Root[json::boot].object_items();

    if (!bootVersionObject.count(json::version)) {
        LOG_ERROR("No OS version in %s file", jsonFilePath.c_str());
        return {};
    }

    return (bootVersionObject[json::version]).string_value();
}

bool BackupSyncRestore::CheckBackupVersion(const std::filesystem::path &extractedBackup)
{
    auto versionOK           = true;
    const auto backupVersion = ReadVersionFromJsonFile(extractedBackup / bkp::backupInfo);

    if (backupVersion.empty()) {
        LOG_ERROR("Backup OS version empty");
        return false;
    }

    const auto currentVersion = ReadVersionFromJsonFile(purefs::dir::getCurrentOSPath() / purefs::file::version_json);

    if (currentVersion.empty()) {
        LOG_ERROR("Current OS version empty");
        return false;
    }

    LOG_DEBUG("Current OS version %s, backup version %s", (currentVersion).c_str(), (backupVersion).c_str());

    auto const currentVersionTokens = utils::split(currentVersion, ".");
    auto const backupVersionTokens  = utils::split(backupVersion, ".");

    if (currentVersionTokens[0] < backupVersionTokens[0]) { //  major version
        versionOK = false;
    }
    else if (currentVersionTokens[0] == backupVersionTokens[0] && //  major version
             currentVersionTokens[1] < backupVersionTokens[1]) {  //  minor version
        versionOK = false;
    }

    if (!versionOK) {
        LOG_ERROR(
            "Current OS version %s older than backup version %s", (currentVersion).c_str(), (backupVersion).c_str());
    }
    else {
        LOG_DEBUG("OK to restore backup version %s", (backupVersion).c_str());
    }

    return versionOK;
}

bool BackupSyncRestore::CanRestoreFromBackup(const std::filesystem::path &extractedBackup)
{
    return CheckBackupVersion(extractedBackup);
}

bool BackupSyncRestore::ReplaceUserFiles(const std::filesystem::path &path)
{
    /* replace existing files that have respective backup files existing */
    const auto tempDir = purefs::dir::getTemporaryPath() / path.stem();

    if (std::filesystem::is_directory(tempDir) && std::filesystem::is_empty(tempDir)) {
        LOG_INFO("Dir empty, nothing to restore, quitting...");
        return false;
    }

    const std::filesystem::path userDir   = purefs::dir::getUserDiskPath();
    const std::filesystem::path backupDir = purefs::dir::getBackupOSPath();
    std::error_code errorCode;

    for (auto &direntry : std::filesystem::directory_iterator(tempDir, errorCode)) {
        if (errorCode) {
            LOG_INFO("Can't list contents of temp dir");
            return false;
        }

        if (!isValidDirentry(direntry)) {
            continue;
        }

        // dont restore the information file
        if (direntry.path().filename() == bkp::backupInfo) {
            continue;
        }

        LOG_INFO("Restoring backup file ...");

        if (std::filesystem::remove(userDir / direntry.path().filename(), errorCode)) {
            std::filesystem::rename(
                tempDir / direntry.path().filename(), userDir / direntry.path().filename(), errorCode);
            if (errorCode) {
                LOG_ERROR("Can't move file. Restore failed, error: %d", errorCode.value());
                return false;
            }
        }
        else {
            LOG_WARN("Can't remove file, error: %d", errorCode.value());
            // we should continue, there can be new files in the backup
        }
    }

    return true;
}

json11::Json BackupSyncRestore::GetBackupFiles()
{
    auto dirEntryVector = std::vector<std::string>();
    std::error_code errorCode;
    for (const auto &p : std::filesystem::directory_iterator(purefs::dir::getBackupOSPath(), errorCode)) {
        if (errorCode) {
            LOG_ERROR("Can't get directory %s contents: %d", purefs::dir::getBackupOSPath().c_str(), errorCode.value());
            return json11::Json();
        }
        if (!p.is_directory()) {
            LOG_DEBUG("Possible restore file");
            dirEntryVector.push_back(p.path().filename());
        }
    }

    return dirEntryVector;
}

M module-services/service-desktop/CMakeLists.txt => module-services/service-desktop/CMakeLists.txt +2 -2
@@ 7,7 7,7 @@ add_library(service-desktop STATIC)
target_sources(
        service-desktop
    PRIVATE
        BackupRestore.cpp
        Sync.cpp
        DesktopEvent.cpp
        DeveloperModeMessage.cpp
        DesktopMessages.cpp


@@ 23,7 23,7 @@ target_sources(
        parser/ParserFSM.hpp
        parser/MessageHandler.hpp
    PUBLIC
        include/service-desktop/BackupRestore.hpp
        include/service-desktop/Sync.hpp
        include/service-desktop/Constants.hpp
        include/service-desktop/DesktopEvent.hpp
        include/service-desktop/DesktopMessages.hpp

M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +8 -21
@@ 90,16 90,6 @@ sys::MessagePointer ServiceDesktop::DataReceivedHandler(sys::DataMessage * /*msg
    return response;
}

std::string ServiceDesktop::prepareBackupFilename()
{
    std::array<char, 64> backupFileName{};
    std::time_t now;
    std::time(&now);
    std::strftime(backupFileName.data(), backupFileName.size(), "%FT%OH%OM%OSZ", std::localtime(&now));

    return std::string(backupFileName.data());
}

std::string ServiceDesktop::prepareSyncFilename()
{
    const std::size_t maxFileNameSize = 64;


@@ 113,10 103,9 @@ std::string ServiceDesktop::prepareSyncFilename()

void ServiceDesktop::prepareSyncData()
{
    backupSyncRestoreStatus.operation     = BackupSyncRestore::Operation::Sync;
    backupSyncRestoreStatus.taskId        = prepareSyncFilename();
    backupSyncRestoreStatus.state         = BackupSyncRestore::OperationState::Stopped;
    backupSyncRestoreStatus.backupTempDir = purefs::dir::getTemporaryPath() / backupSyncRestoreStatus.taskId;
    syncStatus.taskId  = prepareSyncFilename();
    syncStatus.state   = Sync::OperationState::Stopped;
    syncStatus.tempDir = purefs::dir::getTemporaryPath() / syncStatus.taskId;
}

auto ServiceDesktop::requestLogsFlush() -> void


@@ 317,18 306,16 @@ auto ServiceDesktop::handle(sdesktop::developerMode::DeveloperModeRequest *msg) 

auto ServiceDesktop::handle(sdesktop::SyncMessage * /*msg*/) -> std::shared_ptr<sys::Message>
{
    backupSyncRestoreStatus.state = BackupSyncRestore::OperationState::Running;
    backupSyncRestoreStatus.completionCode =
        BackupSyncRestore::PrepareSyncPackage(this, backupSyncRestoreStatus.backupTempDir);
    backupSyncRestoreStatus.location = backupSyncRestoreStatus.taskId;
    syncStatus.state          = Sync::OperationState::Running;
    syncStatus.completionCode = Sync::PrepareSyncPackage(this, syncStatus.tempDir);

    if (backupSyncRestoreStatus.completionCode == BackupSyncRestore::CompletionCode::Success) {
    if (syncStatus.completionCode == Sync::CompletionCode::Success) {
        LOG_INFO("Sync package preparation finished");
        backupSyncRestoreStatus.state = BackupSyncRestore::OperationState::Finished;
        syncStatus.state = Sync::OperationState::Finished;
    }
    else {
        LOG_ERROR("Sync package preparation failed");
        backupSyncRestoreStatus.state = BackupSyncRestore::OperationState::Error;
        syncStatus.state = Sync::OperationState::Error;
    }

    return sys::MessageNone{};

A module-services/service-desktop/Sync.cpp => module-services/service-desktop/Sync.cpp +179 -0
@@ 0,0 1,179 @@
// 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/Sync.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <purefs/filesystem_paths.hpp>

#include <fstream>
#include <gsl/util>
#include <microtar.hpp>

namespace sys
{
    class Service;
} // namespace sys

static bool isValidDirentry(const std::filesystem::directory_entry &direntry)
{
    return direntry.path() != "." && direntry.path() != ".." && direntry.path() != "...";
}

Sync::CompletionCode Sync::PrepareSyncPackage(sys::Service *ownerService, std::filesystem::path &path)
{
    assert(ownerService != nullptr);
    LOG_INFO("Sync package preparation started...");

    if (!Sync::RemoveSyncDir(path)) {
        return CompletionCode::FSError;
    }

    if (!Sync::CreateSyncDir(path)) {
        return CompletionCode::FSError;
    }

    LOG_DEBUG("Sync package preparation started...");
    if (!DBServiceAPI::DBPrepareSyncPackage(ownerService, path)) {
        LOG_ERROR("Sync package preparation, quitting...");
        Sync::RemoveSyncDir(path);
        return CompletionCode::DBError;
    }

    LOG_DEBUG("Packing files");
    if (!Sync::PackSyncFiles(path)) {
        LOG_ERROR("Failed pack sync files");
        Sync::RemoveSyncDir(path);
        return CompletionCode::PackError;
    }

    if (!Sync::RemoveSyncDir(path)) {
        return CompletionCode::FSError;
    }

    return CompletionCode::Success;
}

bool Sync::RemoveSyncDir(const std::filesystem::path &path)
{
    /* prepare directories */
    if (std::filesystem::is_directory(path)) {
        LOG_INFO("Removing sync directory %s...", path.c_str());
        std::error_code errorCode;

        if (std::filesystem::remove_all(path, errorCode) == 0) {
            LOG_ERROR("Removing sync directory %s failed, error: %d.", path.c_str(), errorCode.value());
            return false;
        }
    }

    return true;
}

bool Sync::CreateSyncDir(const std::filesystem::path &path)
{
    LOG_INFO("Creating sync directory %s...", path.c_str());
    if (std::filesystem::exists(path)) {
        LOG_ERROR("Sync directory already exists!");
        return false;
    }
    std::error_code errorCode;
    if (!std::filesystem::create_directories(path, errorCode)) {
        LOG_ERROR("Failed to create directory: %s, error: %d", path.c_str(), errorCode.value());
        return false;
    }
    return true;
}

bool Sync::PackSyncFiles(const std::filesystem::path &path)
{
    if (std::filesystem::is_empty(path)) {
        LOG_ERROR("Sync dir is empty, quitting...");
        return false;
    }

    auto isTarFileOpen                = false;
    std::filesystem::path tarFilePath = (purefs::dir::getSyncPackagePath() / path.filename());
    mtar_t tarFile;

    auto cleanup = gsl::finally([&isTarFileOpen, &tarFile]() {
        if (isTarFileOpen) {
            mtar_close(&tarFile);
        }
    });

    LOG_INFO("Opening tar %s file...", tarFilePath.c_str());

    int ret = mtar_open(&tarFile, tarFilePath.c_str(), "w");
    if (ret != MTAR_ESUCCESS) {
        LOG_ERROR("Opening tar file failed, quitting...");
        return false;
    }
    isTarFileOpen = true;

    auto fileStreamBuffer = std::make_unique<char[]>(purefs::buffer::tar_buf);
    auto tarStreamBuffer  = std::make_unique<char[]>(purefs::buffer::tar_buf);
    setvbuf(tarFile.stream, tarStreamBuffer.get(), _IOFBF, purefs::buffer::tar_buf);
    std::error_code errorCode;

    for (const auto &direntry : std::filesystem::directory_iterator(path)) {
        if (!isValidDirentry(direntry)) {
            continue;
        }

        LOG_INFO("Archiving file ...");
        LOG_DEBUG("Writing tar header ...");

        if (mtar_write_file_header(&tarFile,
                                   direntry.path().filename().c_str(),
                                   static_cast<unsigned>(std::filesystem::file_size(direntry))) != MTAR_ESUCCESS) {
            LOG_ERROR("Writing tar header failed");
            return false;
        }

        const auto filesize = std::filesystem::file_size(direntry.path(), errorCode);
        if (errorCode) {
            LOG_ERROR("Failed to get size for file %s, error: %d", direntry.path().c_str(), errorCode.value());
            return false;
        }
        const auto loopCount = (filesize / purefs::buffer::tar_buf) + 1u;
        std::ifstream fileStream(direntry.path().string().c_str());

        for (auto i = 0u; i < loopCount; i++) {
            if (fileStream.bad()) {
                LOG_ERROR("File stream error.");
                return false;
            }
            uint32_t readSize;
            if (i + 1u == loopCount) {
                readSize = filesize % purefs::buffer::tar_buf;
            }
            else {
                readSize = purefs::buffer::tar_buf;
            }

            LOG_DEBUG("Reading file ...");
            fileStream.read(fileStreamBuffer.get(), readSize);

            LOG_DEBUG("Writing into sync package...");
            if (mtar_write_data(&tarFile, fileStreamBuffer.get(), readSize) != MTAR_ESUCCESS) {
                LOG_ERROR("Writing into sync package failed, quitting...");
                return false;
            }
        }
    }

    LOG_INFO("Finalizing tar file...");
    if (mtar_finalize(&tarFile) != MTAR_ESUCCESS) {
        LOG_ERROR("Finalizing tar file failed, quitting....");
        return false;
    }

    LOG_INFO("Closing tar file...");
    if (mtar_close(&tarFile) != MTAR_ESUCCESS) {
        LOG_ERROR("Closing tar file failed, quitting...");
        return false;
    }
    isTarFileOpen = false;

    return true;
}

M module-services/service-desktop/endpoints/backup/BackupHelper.cpp => module-services/service-desktop/endpoints/backup/BackupHelper.cpp +26 -36
@@ 19,10 19,8 @@ namespace sdesktop::endpoints
        if (context.getBody()[json::messages::category].string_value() == json::messages::categorySync) {
            return checkSyncState(context);
        }
        context.setResponseStatus(http::Code::BadRequest);
        putToSendQueue(context.createSimpleResponse());

        return {sent::yes, std::nullopt};
        return {sent::no, ResponseContext{.status = http::Code::BadRequest}};
    }

    auto BackupHelper::processPost(Context &context) -> ProcessResult


@@ 34,10 32,8 @@ namespace sdesktop::endpoints
        else if (category == json::messages::categoryBackup) {
            return executeBackupRequest(context);
        }
        context.setResponseStatus(http::Code::BadRequest);
        putToSendQueue(context.createSimpleResponse());

        return {sent::yes, std::nullopt};
        return {sent::no, ResponseContext{.status = http::Code::BadRequest}};
    }

    auto BackupHelper::executeBackupRequest([[maybe_unused]] Context &context) -> ProcessResult


@@ 49,14 45,14 @@ namespace sdesktop::endpoints
        return {sent::no, ResponseContext{.status = http::Code::InternalServerError}};
    }

    auto BackupHelper::executeSyncRequest(Context &context) -> ProcessResult
    auto BackupHelper::executeSyncRequest([[maybe_unused]] Context &context) -> ProcessResult
    {
        auto ownerServicePtr = static_cast<ServiceDesktop *>(owner);

        if (ownerServicePtr->getBackupSyncRestoreStatus().state == BackupSyncRestore::OperationState::Running) {
        if (ownerServicePtr->getSyncStatus().state == Sync::OperationState::Running) {
            LOG_DEBUG("Sync already running");
            // a sync package preparation is already running, don't start a second task
            context.setResponseStatus(http::Code::NotAcceptable);
            return {sent::no, ResponseContext{.status = http::Code::NotAcceptable}};
        }
        else {
            LOG_DEBUG("Starting a sync package preparation");


@@ 67,44 63,38 @@ namespace sdesktop::endpoints
            ownerServicePtr->bus.sendUnicast(std::make_shared<sdesktop::SyncMessage>(), service::name::service_desktop);

            // return new generated sync package info
            context.setResponseBody(ownerServicePtr->getBackupSyncRestoreStatus());
        }

        putToSendQueue(context.createSimpleResponse());

        return {sent::yes, std::nullopt};
            return {sent::no,
                    ResponseContext{.status = http::Code::Accepted,
                                    .body   = json11::Json::object{
                                        {sdesktop::endpoints::json::taskId, ownerServicePtr->getSyncStatus().taskId}}}};
        }
    }

    auto BackupHelper::checkSyncState(Context &context) -> ProcessResult
    {
        auto ownerServicePtr = static_cast<ServiceDesktop *>(owner);
        auto status          = http::Code::BadRequest;

        if (context.getBody()[json::taskId].is_string()) {
            if (ownerServicePtr->getBackupSyncRestoreStatus().taskId ==
                context.getBody()[json::taskId].string_value()) {
                if (ownerServicePtr->getBackupSyncRestoreStatus().state ==
                    BackupSyncRestore::OperationState::Finished) {
                    context.setResponseStatus(http::Code::SeeOther);
                }
                else {
                    context.setResponseStatus(http::Code::OK);
                }

                context.setResponseBody(ownerServicePtr->getBackupSyncRestoreStatus());
            }
            else {
                context.setResponseStatus(http::Code::NotFound);
            }
        }
        else {
        if (!context.getBody()[json::taskId].is_string()) {
            LOG_DEBUG("Backup task not found");
            context.setResponseStatus(http::Code::BadRequest);
            return {sent::no, ResponseContext{.status = status}};
        }

        LOG_DEBUG("Responding");
        putToSendQueue(context.createSimpleResponse());
        if (ownerServicePtr->getSyncStatus().taskId != context.getBody()[json::taskId].string_value()) {
            status = http::Code::NotFound;
            return {sent::no, ResponseContext{.status = status}};
        }

        auto syncStatus = ownerServicePtr->getSyncStatus();

        return {sent::yes, std::nullopt};
        if (syncStatus.state == Sync::OperationState::Finished) {
            status = http::Code::SeeOther;
        }
        else {
            status = http::Code::OK;
        }
        return {sent::no, ResponseContext{.status = status, .body = syncStatus}};
    }

} // namespace sdesktop::endpoints

M module-services/service-desktop/endpoints/include/endpoints/BaseHelper.hpp => module-services/service-desktop/endpoints/include/endpoints/BaseHelper.hpp +2 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 56,6 56,7 @@ namespace sdesktop::endpoints
      public:
        explicit BaseHelper(sys::Service *p) : owner(p)
        {}
        virtual ~BaseHelper() = default;

        /// generall processing function
        ///

M module-services/service-desktop/endpoints/include/endpoints/backup/BackupEndpoint.hpp => module-services/service-desktop/endpoints/include/endpoints/backup/BackupEndpoint.hpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 11,7 11,7 @@ namespace sdesktop::endpoints
    class BackupEndpoint : public Endpoint
    {
      public:
        BackupEndpoint(sys::Service *ownerServicePtr)
        explicit BackupEndpoint(sys::Service *ownerServicePtr)
            : Endpoint(ownerServicePtr), helper(std::make_unique<BackupHelper>(ownerServicePtr))
        {
            debugName = "BackupEndpoint";

M module-services/service-desktop/endpoints/include/endpoints/factoryReset/FactoryResetEndpoint.hpp => module-services/service-desktop/endpoints/include/endpoints/factoryReset/FactoryResetEndpoint.hpp +2 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 14,12 14,11 @@ namespace sdesktop::endpoints
    {

      public:
        FactoryResetEndpoint(sys::Service *ownerServicePtr) : Endpoint(ownerServicePtr)
        explicit FactoryResetEndpoint(sys::Service *ownerServicePtr) : Endpoint(ownerServicePtr)
        {
            debugName = "FactoryResetEndpoint";
        }
        auto handle(Context &context) -> void override;
        auto makeFactoryReset(Context &context) -> bool;
    };

} // namespace sdesktop::endpoints

M module-services/service-desktop/include/service-desktop/ServiceDesktop.hpp => module-services/service-desktop/include/service-desktop/ServiceDesktop.hpp +4 -5
@@ 13,7 13,7 @@
#include <service-bluetooth/messages/BondedDevices.hpp>
#include <service-bluetooth/messages/ResponseVisibleDevices.hpp>
#include <service-db/DBServiceName.hpp>
#include <service-desktop/BackupRestore.hpp>
#include <service-desktop/Sync.hpp>
#include <service-desktop/OutboxNotifications.hpp>
#include <service-evtmgr/BatteryMessages.hpp>



@@ 55,19 55,18 @@ class ServiceDesktop : public sys::Service
    ~ServiceDesktop() override;

    std::unique_ptr<WorkerDesktop> desktopWorker;
    BackupSyncRestore::OperationStatus backupSyncRestoreStatus;
    Sync::OperationStatus syncStatus;

    sys::ReturnCodes InitHandler() override;
    sys::ReturnCodes DeinitHandler() override;
    sys::ReturnCodes SwitchPowerModeHandler(sys::ServicePowerMode mode) override;
    sys::MessagePointer DataReceivedHandler(sys::DataMessage *msg, sys::ResponseMessage *resp) override;

    std::string prepareBackupFilename();
    std::string prepareSyncFilename();
    void prepareSyncData();
    const BackupSyncRestore::OperationStatus getBackupSyncRestoreStatus()
    const Sync::OperationStatus getSyncStatus()
    {
        return backupSyncRestoreStatus;
        return syncStatus;
    }
    const sdesktop::USBSecurityModel *getSecurity()
    {

R module-services/service-desktop/include/service-desktop/BackupRestore.hpp => module-services/service-desktop/include/service-desktop/Sync.hpp +3 -37
@@ 3,7 3,6 @@

#pragma once

#include <Service/Service.hpp>
#include <endpoints/JsonKeyNames.hpp>
#include <json11.hpp>
#include <filesystem>


@@ 13,16 12,9 @@ namespace sys
    class Service;
} // namespace sys

class BackupSyncRestore
class Sync
{
  public:
    enum class Operation
    {
        Backup,
        Sync,
        Restore
    };

    enum class OperationState
    {
        Stopped,


@@ 49,12 41,9 @@ class BackupSyncRestore
    enum class CompletionCode
    {
        Success,
        VersionConflict,
        DBError,
        FSError,
        UnpackError,
        PackError,
        CopyError,
        OtherError
    };



@@ 63,18 52,12 @@ class BackupSyncRestore
        switch (code) {
        case CompletionCode::Success:
            return "Success";
        case CompletionCode::VersionConflict:
            return "Version conflict";
        case CompletionCode::DBError:
            return "DB operation error";
        case CompletionCode::FSError:
            return "FSError error";
        case CompletionCode::UnpackError:
            return "Backup unpack failed";
        case CompletionCode::PackError:
            return "Backup pack failed";
        case CompletionCode::CopyError:
            return "File copying failed";
        case CompletionCode::OtherError:
            return "Undetermined error";
        }


@@ 83,16 66,13 @@ class BackupSyncRestore

    struct OperationStatus
    {
        std::filesystem::path backupTempDir;
        std::filesystem::path location;
        std::filesystem::path tempDir;
        CompletionCode completionCode = CompletionCode::Success;
        std::string taskId;
        OperationState state = OperationState::Stopped;
        Operation operation  = Operation::Backup;
        json11::Json to_json() const
        {
            auto response = json11::Json::object{{sdesktop::endpoints::json::taskId, taskId},
                                                 {sdesktop::endpoints::json::state, opToString(state)}};
            auto response = json11::Json::object{{sdesktop::endpoints::json::state, opToString(state)}};

            if (state == OperationState::Error) {
                response[sdesktop::endpoints::json::reason] = completionCodeToString(completionCode);


@@ 102,24 82,10 @@ class BackupSyncRestore
        }
    };

    static CompletionCode BackupUserFiles(sys::Service *ownerService, std::filesystem::path &path);
    static CompletionCode PrepareSyncPackage(sys::Service *ownerService, std::filesystem::path &path);
    static CompletionCode RestoreUserFiles(sys::Service *ownerService, const std::filesystem::path &path);
    static json11::Json GetBackupFiles();

  private:
    static bool RemoveBackupDir(const std::filesystem::path &path);
    static bool CreateBackupDir(const std::filesystem::path &path);
    static bool RemoveSyncDir(const std::filesystem::path &path);
    static bool CreateSyncDir(const std::filesystem::path &path);
    static bool PackUserFiles(const std::filesystem::path &path);
    static bool PackSyncFiles(const std::filesystem::path &path);
    static bool UnpackBackupFile(const std::filesystem::path &path);
    static std::string ReadVersionFromJsonFile(const std::filesystem::path &jsonFilePath);
    static bool CheckBackupVersion(const std::filesystem::path &extractedBackup);
    static bool CanRestoreFromBackup(const std::filesystem::path &path);
    static auto TempPathForBackupFile(const std::filesystem::path &tarFilePath) -> std::filesystem::path const;
    static bool ReplaceUserFiles(const std::filesystem::path &path);
    static bool WriteBackupInfo(sys::Service *ownerService, const std::filesystem::path &path);
    static std::string ReadFileAsString(const std::filesystem::path &fileToRead);
};

M module-vfs/paths/include/purefs/filesystem_paths.hpp => module-vfs/paths/include/purefs/filesystem_paths.hpp +1 -1
@@ 42,6 42,6 @@ namespace purefs

    namespace buffer
    {
        constexpr inline auto tar_buf = 8192 * 4;
        constexpr inline auto tar_buf = 1024;
    }
} // namespace purefs