~aleteoryx/muditaos

6ab09d5ffd058c85484bd9dcd6f3cc9d865549bc — Piotr Tański 4 years ago 18a3886
[EGD-7459] Fixed factory reset process

Fixed the factory reset process.
M module-apps/application-settings/windows/system/SystemMainWindow.cpp => module-apps/application-settings/windows/system/SystemMainWindow.cpp +1 -3
@@ 41,9 41,7 @@ namespace gui
                                            utils::translate("app_settings_display_factory_reset_confirmation"),
                                            "",
                                            [this]() {
                                                auto msg = std::make_shared<sdesktop::FactoryMessage>();
                                                application->bus.sendUnicast(msg, service::name::service_desktop);
                                                application->returnToPreviousWindow(2);
                                                sys::SystemManagerCommon::FactoryReset(application);
                                                return true;
                                            }});
                    LOG_INFO("switching to %s page", window.c_str());

M module-services/service-appmgr/model/ApplicationManagerCommon.cpp => module-services/service-appmgr/model/ApplicationManagerCommon.cpp +2 -0
@@ 163,6 163,8 @@ namespace app::manager
        case sys::CloseReason::RegularPowerDown:
            [[fallthrough]];
        case sys::CloseReason::Reboot:
            [[fallthrough]];
        case sys::CloseReason::FactoryReset:
            break;
        }
        handleActionRequest(&act);

M module-services/service-db/ServiceDBCommon.cpp => module-services/service-db/ServiceDBCommon.cpp +24 -0
@@ 6,6 6,8 @@
#include <service-db/ServiceDBCommon.hpp>
#include <service-db/agents/quotes/QuotesAgent.cpp>

#include <purefs/filesystem_paths.hpp>

static const auto service_db_stack = 1024 * 24;

ServiceDBCommon::ServiceDBCommon() : sys::Service(service::name::db, "", service_db_stack, sys::ServicePriority::Idle)


@@ 63,9 65,31 @@ sys::ReturnCodes ServiceDBCommon::DeinitHandler()

void ServiceDBCommon::ProcessCloseReason(sys::CloseReason closeReason)
{
    if (closeReason == sys::CloseReason::FactoryReset) {
        factoryReset();
    }
    sendCloseReadyMessage(this);
}

void ServiceDBCommon::factoryReset() const
{
    constexpr std::array fileExtensions = {".db", ".db-journal", ".db-wal"};

    LOG_INFO("Performing DB factory reset...");
    const auto userOSPath = purefs::dir::getUserDiskPath();
    for (const auto &f : std::filesystem::directory_iterator(userOSPath)) {
        if (const auto it = std::find(fileExtensions.begin(), fileExtensions.end(), f.path().extension());
            it != fileExtensions.end()) {
            if (const auto removeStatus = std::filesystem::remove(f.path()); !removeStatus) {
                LOG_ERROR("Unable to delete file: %s", f.path().c_str());
            }
            else {
                LOG_INFO("File deleted: %s", f.path().c_str());
            }
        }
    }
}

sys::ReturnCodes ServiceDBCommon::SwitchPowerModeHandler(const sys::ServicePowerMode mode)
{
    LOG_FATAL("[%s] PowerModeHandler: %s", this->GetName().c_str(), c_str(mode));

M module-services/service-db/include/service-db/ServiceDBCommon.hpp => module-services/service-db/include/service-db/ServiceDBCommon.hpp +3 -0
@@ 11,6 11,9 @@

class ServiceDBCommon : public sys::Service
{
  private:
    void factoryReset() const;

  protected:
    virtual db::Interface *getInterface(db::Interface::Name interface);
    std::set<std::unique_ptr<DatabaseAgent>> databaseAgents;

M module-services/service-desktop/CMakeLists.txt => module-services/service-desktop/CMakeLists.txt +0 -1
@@ 22,7 22,6 @@ set(SOURCES
    endpoints/developerMode/event/DomRequest.cpp
    endpoints/developerMode/event/ATRequest.cpp
    endpoints/deviceInfo/DeviceInfoEndpoint.cpp
    endpoints/factoryReset/FactoryReset.cpp
    endpoints/factoryReset/FactoryResetEndpoint.cpp
    endpoints/messages/MessageHelper.cpp
    endpoints/messages/MessagesEndpoint.cpp

M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +1 -2
@@ 6,7 6,6 @@
#include "service-desktop/ServiceDesktop.hpp"
#include "service-desktop/WorkerDesktop.hpp"
#include "service-cellular/CellularMessage.hpp"
#include "endpoints/factoryReset/FactoryReset.hpp"
#include "endpoints/backup/BackupRestore.hpp"

#include <Common/Query.hpp>


@@ 143,7 142,7 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
        auto *factoryMessage = dynamic_cast<sdesktop::FactoryMessage *>(msg);
        if (factoryMessage != nullptr) {
            LOG_DEBUG("ServiceDesktop: FactoryMessage received");
            FactoryReset::Run(this);
            sys::SystemManagerCommon::FactoryReset(this);
        }
        return sys::MessageNone{};
    });

D module-services/service-desktop/endpoints/factoryReset/FactoryReset.cpp => module-services/service-desktop/endpoints/factoryReset/FactoryReset.cpp +0 -238
@@ 1,238 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "FactoryReset.hpp"
#include <SystemManager/SystemManagerCommon.hpp>
#include <log.hpp>
#include <service-db/DBServiceName.hpp>
#include <Utils.hpp>

#include <cstdint>
#include <filesystem>
#include <memory>
#include <vector>
#include <purefs/filesystem_paths.hpp>
#include <purefs/fs/filesystem.hpp>
#include <limits.h>

namespace sys
{
    class Service;
} // namespace sys

namespace FactoryReset
{
    namespace
    {
        inline constexpr auto copy_buf = 8192 * 4;
    } // namespace

    static bool CopyFile(const std::string &sourcefile, const std::string &targetfile);

    static int recurseDepth            = 0;
    static const int max_recurse_depth = 120; /* 120 is just an arbitrary value of max number of recursive calls.
                                               * If more then error is assumed, not the real depth of directories."
                                               */
    static const int max_filepath_length = PATH_MAX;

    bool Run(sys::Service *ownerService)
    {
        LOG_INFO("Restoring factory state started...");

        recurseDepth          = 0;
        const auto userOSPath = purefs::dir::getUserDiskPath();

        if (std::filesystem::is_directory(userOSPath.c_str()) && std::filesystem::is_empty(userOSPath.c_str())) {
            LOG_ERROR("Restoring factory state aborted");
            LOG_ERROR("Directory %s seems empty.", userOSPath.c_str());
            return false;
        }

        if (ownerService != nullptr) {
            LOG_INFO("Closing ServiceDB...");
            std::string dbServiceName = service::name::db;
            sys::SystemManagerCommon::DestroySystemService(dbServiceName, ownerService);
        }

        DeleteSelectedUserFiles(userOSPath);

        LOG_INFO("Rebooting...");
        sys::SystemManagerCommon::Reboot(ownerService);
        return true;
    }

    bool DeleteSelectedUserFiles(const std::filesystem::path &userOSPath)
    {
        bool returnStatus                        = true;
        std::vector<std::string> selectedFileExt = {".db", ".db-journal", ".db-wal"};

        LOG_INFO("Delete DB files which will be recreated with factory content after reboot:");
        for (const auto &f : std::filesystem::directory_iterator(userOSPath.c_str())) {
            for (const auto &ext : selectedFileExt) {
                if (f.path().extension() == ext) {
                    auto removeStatus = std::filesystem::remove(f.path());
                    if (!removeStatus) {
                        LOG_ERROR("Error deleting file %s, aborting...", f.path().c_str());
                        returnStatus = false;
                    }
                    else {
                        LOG_INFO("%s deleted.", f.path().c_str());
                    }
                    break;
                }
            }
        }
        return returnStatus;
    }

    bool DeleteDirContent(const std::string &dir)
    {
        for (auto &direntry : std::filesystem::directory_iterator(dir.c_str())) {
            if (!((direntry.path().string() != ".") && (direntry.path().string() != "..") &&
                  (direntry.path().string() != "..."))) {
                continue;
            }
            std::string delpath = dir;
            delpath += "/";
            delpath += direntry.path().string();

            if (std::filesystem::is_directory(direntry)) {
                if (direntry.path().string() != purefs::dir::getFactoryOSPath()) {
                    LOG_INFO("FactoryReset: recursively deleting dir...");
                    try {
                        std::filesystem::remove_all(delpath.c_str());
                    }
                    catch (const std::filesystem::filesystem_error &e) {
                        LOG_ERROR("FactoryReset: error deleting dir, aborting...");
                        return false;
                    }
                }
            }
            else {
                LOG_INFO("FactoryReset: deleting file...");
                if (std::filesystem::remove(delpath.c_str())) {
                    LOG_ERROR("FactoryReset: error deleting file, aborting...");
                    return false;
                }
            }
        }

        return true;
    }

    bool CopyDirContent(const std::string &sourcedir, const std::string &targetdir)
    {
        if (recurseDepth >= max_recurse_depth) {
            LOG_ERROR("FactoryReset: recurse level %d (too high), error assumed, skipping restore of dir",
                      recurseDepth);
            return false;
        }

        const auto factoryOSPath = purefs::dir::getFactoryOSPath();

        for (auto &direntry : std::filesystem::directory_iterator(sourcedir.c_str())) {
            if ((direntry.path().string() == ".") || (direntry.path().string() == "..") ||
                (direntry.path().string() == "...")) {
                continue;
            }

            std::string sourcepath = sourcedir;
            sourcepath += "/";
            sourcepath += direntry.path().string();

            std::string targetpath = targetdir;
            targetpath += "/";
            targetpath += direntry.path().string();

            if ((sourcepath.size() >= max_filepath_length) || (targetpath.size() >= max_filepath_length)) {
                LOG_ERROR("FactoryReset: path length (source or target) exceeds system limit of %d",
                          max_filepath_length);
                LOG_ERROR("FactoryReset: skipping restore of directory");
                return false;
            }

            if (std::filesystem::is_directory(direntry)) {
                if (targetpath == factoryOSPath) {
                    continue;
                }

                LOG_INFO("FactoryReset: restoring directory");

                try {
                    if (std::filesystem::create_directory(targetpath.c_str())) {
                        LOG_ERROR("FactoryReset: create dir failed");
                        return false;
                    }
                }
                catch (const std::filesystem::filesystem_error &err) {
                    LOG_FATAL("Exception while creating dir");
                    return false;
                }

                recurseDepth++;

                if (!CopyDirContent(sourcepath, targetpath)) {
                    recurseDepth--;
                    return false;
                }

                recurseDepth--;
            }
            else {
                LOG_INFO("FactoryReset: restoring file");

                if (!CopyFile(sourcepath, targetpath)) {
                    return false;
                }
            }
        }

        return true;
    }

    static bool CopyFile(const std::string &sourcefile, const std::string &targetfile)
    {
        bool ret  = true;
        auto lamb = [](std::FILE *stream) { std::fclose(stream); };

        std::unique_ptr<std::FILE, decltype(lamb)> sf(std::fopen(sourcefile.c_str(), "r"), lamb);
        std::unique_ptr<std::FILE, decltype(lamb)> tf(std::fopen(targetfile.c_str(), "w"), lamb);

        if (sf && tf) {
            std::unique_ptr<unsigned char[]> buffer(new unsigned char[copy_buf]);

            if (buffer) {
                uint32_t loopcount = (std::filesystem::file_size(sourcefile) / copy_buf) + 1u;
                uint32_t readsize  = copy_buf;

                for (uint32_t i = 0u; i < loopcount; i++) {
                    if (i + 1u == loopcount) {
                        readsize = std::filesystem::file_size(sourcefile) % copy_buf;
                    }

                    if (std::fread(buffer.get(), 1, readsize, sf.get()) != readsize) {
                        LOG_ERROR("FactoryReset: read from sourcefile failed");
                        ret = false;
                        break;
                    }

                    if (std::fwrite(buffer.get(), 1, readsize, tf.get()) != readsize) {
                        LOG_ERROR("FactoryReset: write to targetfile failed");
                        ret = false;
                        break;
                    }
                }
            }
            else {
                LOG_ERROR("FactoryReset: unable to open copy buffer");
                ret = false;
            }
        }
        else {
            LOG_ERROR("FactoryReset: unable to open source or target file");
            ret = false;
        }

        return ret;
    }
} // namespace FactoryReset

D module-services/service-desktop/endpoints/factoryReset/FactoryReset.hpp => module-services/service-desktop/endpoints/factoryReset/FactoryReset.hpp +0 -20
@@ 1,20 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 <purefs/filesystem_paths.hpp>
#include <Service/Service.hpp>
#include <string>

namespace sys
{
    class Service;
} // namespace sys

namespace FactoryReset
{
    bool Run(sys::Service *ownerService);
    bool DeleteSelectedUserFiles(const std::filesystem::path &userOSPath);
    bool DeleteDirContent(const std::string &dir);
    bool CopyDirContent(const std::string &sourcedir, const std::string &targetdir);
} // namespace FactoryReset

M module-services/service-desktop/tests/unittest.cpp => module-services/service-desktop/tests/unittest.cpp +0 -1
@@ 5,7 5,6 @@
#include <endpoints/EndpointFactory.hpp>
#include <endpoints/contacts/ContactHelper.hpp>
#include <endpoints/contacts/ContactsEndpoint.hpp>
#include <endpoints/factoryReset/FactoryReset.hpp>
#include <endpoints/messages/MessageHelper.hpp>
#include <endpoints/filesystem/FileContext.hpp>
#include <endpoints/filesystem/FileOperations.hpp>

M module-sys/Service/Common.hpp => module-sys/Service/Common.hpp +1 -0
@@ 49,6 49,7 @@ namespace sys
    {
        RegularPowerDown,
        Reboot,
        FactoryReset,
        SystemBrownout,
        LowBattery
    };

M module-sys/SystemManager/SystemManagerCommon.cpp => module-sys/SystemManager/SystemManagerCommon.cpp +11 -1
@@ 232,9 232,16 @@ namespace sys
        return true;
    }

    bool SystemManagerCommon::FactoryReset(Service *s)
    {
        return s->bus.sendUnicast(std::make_shared<SystemManagerCmd>(Code::FactoryReset, CloseReason::FactoryReset),
                                  service::name::system_manager);
    }

    bool SystemManagerCommon::Reboot(Service *s)
    {
        s->bus.sendUnicast(std::make_shared<SystemManagerCmd>(Code::Reboot), service::name::system_manager);
        s->bus.sendUnicast(std::make_shared<SystemManagerCmd>(Code::Reboot, CloseReason::Reboot),
                           service::name::system_manager);
        return true;
    }



@@ 474,6 481,9 @@ namespace sys
                case Code::RebootToUpdate:
                    RebootHandler(State::RebootToUpdate, data->updateReason);
                    break;
                case Code::FactoryReset:
                    CloseSystemHandler(CloseReason::FactoryReset);
                    break;
                case Code::None:
                    break;
                }

M module-sys/SystemManager/SystemManagerCommon.hpp => module-sys/SystemManager/SystemManagerCommon.hpp +3 -0
@@ 39,6 39,7 @@ namespace sys
        Restore,
        Reboot,
        RebootToUpdate,
        FactoryReset,
        None,
    };



@@ 92,6 93,8 @@ namespace sys

        static bool Restore(Service *s);

        static bool FactoryReset(Service *s);

        static bool Reboot(Service *s);

        static bool RebootToUpdate(Service *s, UpdateReason updateReason);