M cmake/modules/DiskImage.cmake => cmake/modules/DiskImage.cmake +7 -2
@@ 10,6 10,7 @@ function(add_image)
if(NOT ${PROJECT_TARGET_NAME} STREQUAL "linux")
set(HAS_BOOTFILE YES)
set(HAS_UPDATER YES)
+ set(HAS_VERSION YES)
endif()
set(SCRIPT_PATH ${CMAKE_SOURCE_DIR}/tools/generate_image.sh)
@@ 29,7 30,11 @@ function(add_image)
set(BIN_FILE_PATH "")
endif()
- set(VERSION_FILE_PATH ${CMAKE_BINARY_DIR}/${_ARG_PRODUCT}-version.json)
+ if(HAS_VERSION)
+ set(VERSION_FILE_PATH ${CMAKE_BINARY_DIR}/${_ARG_PRODUCT}-version.json)
+ else()
+ set(VERSION_FILE_PATH "")
+ endif()
if(HAS_UPDATER)
set(UPDATER_FILE_PATH ${CMAKE_BINARY_DIR}/updater.bin)
@@ 68,7 73,7 @@ function(add_image)
message("Adding disk image target: ${DISK_IMAGE_NAME}")
add_custom_target(${_ARG_PRODUCT}-disk-img
- DEPENDS ${DISK_IMAGE_NAME})
+ DEPENDS ${DISK_IMAGE_NAME} ${VERSION_FILE_PATH})
endfunction()
M module-platform/CommonPlatform.cpp => module-platform/CommonPlatform.cpp +8 -0
@@ 19,5 19,13 @@ namespace platform
if (!std::filesystem::exists(purefs::dir::getCrashDumpsPath())) {
std::filesystem::create_directories(purefs::dir::getCrashDumpsPath());
}
+
+ if (!std::filesystem::exists(purefs::dir::getBackupOSPath())) {
+ std::filesystem::create_directories(purefs::dir::getBackupOSPath());
+ }
+
+ if (!std::filesystem::exists(purefs::dir::getTemporaryPath())) {
+ std::filesystem::create_directories(purefs::dir::getTemporaryPath());
+ }
}
} // namespace platform
M module-services/service-db/ServiceDBCommon.cpp => module-services/service-db/ServiceDBCommon.cpp +1 -0
@@ 60,6 60,7 @@ sys::ReturnCodes ServiceDBCommon::InitHandler()
sys::ReturnCodes ServiceDBCommon::DeinitHandler()
{
+ Database::deinitialize();
return sys::ReturnCodes::Success;
}
R module-services/service-desktop/endpoints/backup/BackupRestore.cpp => module-services/service-desktop/BackupRestore.cpp +226 -182
@@ 1,7 1,8 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
-#include <endpoints/backup/BackupRestore.hpp>
+#include <service-desktop/BackupRestore.hpp>
+#include <endpoints/JsonKeyNames.hpp>
#include <SystemManager/SystemManagerCommon.hpp>
#include <log/log.hpp>
@@ 17,160 18,130 @@
#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 const long unsigned int empty_dirlist_size = 2;
static bool isValidDirentry(const std::filesystem::directory_entry &direntry)
{
return direntry.path() != "." && direntry.path() != ".." && direntry.path() != "...";
}
-// this replaces std::filesystem::copy_file that is broken at
-// the time this is implemented (chown issues after copy)
-// once fixed this can be replaced
-static bool copyFile(const std::filesystem::path &from, const std::filesystem::path &to)
-{
- std::FILE *fromFp = std::fopen(from.c_str(), "r");
- std::FILE *toFp = std::fopen(to.c_str(), "w");
-
- auto end = gsl::finally([fromFp, toFp] {
- std::fclose(fromFp);
- std::fclose(toFp);
- });
-
- if (fromFp == nullptr) {
- LOG_ERROR("can't open file for reading");
- return false;
- }
-
- if (toFp == nullptr) {
- LOG_ERROR("can't open file for writing");
- return false;
- }
-
- std::unique_ptr<unsigned char[]> buffer = std::make_unique<unsigned char[]>(purefs::buffer::tar_buf);
- size_t bytes;
-
- constexpr size_t streamBufferSize = 64 * 1024;
- auto streamBufferFrom = std::make_unique<char[]>(streamBufferSize);
- auto streamBufferTo = std::make_unique<char[]>(streamBufferSize);
- setvbuf(fromFp, streamBufferFrom.get(), _IOFBF, streamBufferSize);
- setvbuf(toFp, streamBufferTo.get(), _IOFBF, streamBufferSize);
-
- while ((bytes = std::fread(buffer.get(), purefs::buffer::tar_buf, 1, fromFp)) != 0) {
- if (std::fwrite(buffer.get(), 1, bytes, toFp) != bytes) {
- return false;
- }
- }
-
- return true;
-}
-bool BackupRestore::BackupUserFiles(sys::Service *ownerService, std::filesystem::path &path)
+BackupRestore::CompletionCode BackupRestore::BackupUserFiles(sys::Service *ownerService, std::filesystem::path &path)
{
assert(ownerService != nullptr);
- LOG_INFO("BackupUserFiles: backup started...");
+ LOG_INFO("Backup started...");
if (BackupRestore::RemoveBackupDir(path) == false) {
- return false;
+ return CompletionCode::FSError;
}
if (BackupRestore::CreateBackupDir(path) == false) {
- return false;
+ return CompletionCode::FSError;
}
- LOG_INFO("BackupUserFiles: database backup started...");
-
+ LOG_DEBUG("Database backup started...");
if (DBServiceAPI::DBBackup(ownerService, path) == false) {
- LOG_ERROR("BackupUserFiles: database backup failed, quitting...");
+ LOG_ERROR("Database backup failed, quitting...");
BackupRestore::RemoveBackupDir(path);
- return false;
+ return CompletionCode::DBError;
}
if (WriteBackupInfo(ownerService, path) == false) {
- LOG_ERROR("Failed to write backup info");
+ LOG_ERROR("Failed to save backup info");
BackupRestore::RemoveBackupDir(path);
- return false;
+ return CompletionCode::CopyError;
+ }
+
+ LOG_DEBUG("Packing files");
+ if (BackupRestore::PackUserFiles(path) == false) {
+ LOG_ERROR("Failed pack backup");
+ BackupRestore::RemoveBackupDir(path);
+ return CompletionCode::PackError;
}
- LOG_INFO("BackupUserFiles: packing files");
- return BackupRestore::PackUserFiles(path);
+ return CompletionCode::Success;
}
bool BackupRestore::WriteBackupInfo(sys::Service *ownerService, const std::filesystem::path &path)
{
LOG_INFO("Writing backup info");
- if (std::filesystem::is_directory(path)) {
- try {
- copyFile(purefs::dir::getRootDiskPath() / purefs::file::boot_json, path / bkp::backupInfo);
-
- LOG_DEBUG("%s copied to %s",
- (purefs::dir::getRootDiskPath() / purefs::file::boot_json).c_str(),
- (path / bkp::backupInfo).c_str());
+ if (!std::filesystem::is_directory(path)) {
+ LOG_ERROR("%s is not a directory", path.c_str());
+ return false;
+ }
- return true;
- }
- catch (std::filesystem::filesystem_error &e) {
- LOG_ERROR("failed to copy %s->%s error:\"%s\"",
- (purefs::dir::getRootDiskPath() / purefs::file::boot_json).c_str(),
- (path / bkp::backupInfo).c_str(),
- e.what());
+ const auto version_json = purefs::dir::getCurrentOSPath() / purefs::file::version_json;
- return false;
- }
- }
- else {
- LOG_ERROR("%s is not a directory", path.c_str());
+ 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;
}
-bool BackupRestore::RestoreUserFiles(sys::Service *ownerService, const std::filesystem::path &path)
+BackupRestore::CompletionCode BackupRestore::RestoreUserFiles(sys::Service *ownerService,
+ const std::filesystem::path &path)
{
assert(ownerService != nullptr);
- LOG_INFO("RestoreUserFiles: restore started");
+
+ LOG_INFO("Restore started");
if (BackupRestore::UnpackBackupFile(path) == false) {
- return false;
+ LOG_ERROR("Can't unpack user files");
+ return CompletionCode::UnpackError;
}
- if (sys::SystemManagerCommon::Restore(ownerService) == false) {
- LOG_ERROR("Can't enter update system state");
- return false;
+ if (BackupRestore::CanRestoreFromBackup(TempPathForBackupFile(path)) == false) {
+ LOG_ERROR("Can't restore user files");
+ return CompletionCode::FSError;
}
- else {
- LOG_INFO("entered update state");
- if (BackupRestore::ReplaceUserFiles(path) == false) {
- LOG_ERROR("can't restore user files");
- return false;
- }
+ if (sys::SystemManagerCommon::Restore(ownerService) == false) {
+ LOG_ERROR("Can't enter system restore state");
+ return CompletionCode::OtherError;
}
- // BackupRestore::RemoveBackupDir(path);
+ LOG_INFO("Entered restore state");
- return true;
+ if (BackupRestore::ReplaceUserFiles(path) == false) {
+ LOG_ERROR("Can't restore user files");
+ return CompletionCode::CopyError;
+ }
+
+ return CompletionCode::Success;
}
-bool BackupRestore::RemoveBackupDir(std::filesystem::path &path)
+bool BackupRestore::RemoveBackupDir(const std::filesystem::path &path)
{
/* prepare directories */
if (std::filesystem::is_directory(path)) {
- LOG_INFO("RemoveBackupDir: removing backup directory %s...", path.c_str());
+ LOG_INFO("Removing backup directory %s...", path.c_str());
+ std::error_code errorCode;
- try {
- std::filesystem::remove_all(path);
- }
- catch (const std::filesystem::filesystem_error &e) {
- LOG_ERROR("RemoveBackupDir: removing backup directory %s failed, error: %s.", path.c_str(), e.what());
+ if (!std::filesystem::remove_all(path, errorCode)) {
+ LOG_ERROR("Removing backup directory %s failed, error: %d.", path.c_str(), errorCode.value());
return false;
}
}
@@ 178,32 149,14 @@ bool BackupRestore::RemoveBackupDir(std::filesystem::path &path)
return true;
}
-bool BackupRestore::CreateBackupDir(std::filesystem::path &path)
+bool BackupRestore::CreateBackupDir(const std::filesystem::path &path)
{
- LOG_INFO("CreateBackupDir: creating backup directory %s...", path.c_str());
+ LOG_INFO("Creating backup directory %s...", path.c_str());
std::error_code errorCode;
- if (!std::filesystem::is_directory(purefs::dir::getBackupOSPath())) {
- if (!std::filesystem::create_directory(purefs::dir::getBackupOSPath(), errorCode)) {
- LOG_ERROR("CreateBackupDir: creating backup directory %s failed. \"%s\"",
- purefs::dir::getBackupOSPath().c_str(),
- errorCode.message().c_str());
- return false;
- }
- }
-
- if (!std::filesystem::is_directory(purefs::dir::getTemporaryPath())) {
- if (!std::filesystem::create_directory(purefs::dir::getTemporaryPath(), errorCode)) {
- LOG_ERROR("CreateBackupDir: creating backup directory %s failed. \"%s\"",
- purefs::dir::getTemporaryPath().c_str(),
- errorCode.message().c_str());
- return false;
- }
- }
-
- if (!std::filesystem::is_directory(path)) {
- if (!std::filesystem::create_directory(path, errorCode)) {
- LOG_ERROR("CreateBackupDir: creating backup directory failed.");
+ 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;
}
}
@@ 211,23 164,22 @@ bool BackupRestore::CreateBackupDir(std::filesystem::path &path)
return true;
}
-bool BackupRestore::PackUserFiles(std::filesystem::path &path)
+bool BackupRestore::PackUserFiles(const std::filesystem::path &path)
{
if (std::filesystem::is_empty(path)) {
- LOG_ERROR("backup dir is empty, nothing to backup, quitting...");
+ LOG_ERROR("Backup dir is empty, nothing to backup, quitting...");
BackupRestore::RemoveBackupDir(path);
return false;
}
- std::filesystem::path tarFilePath =
- (purefs::dir::getBackupOSPath() / path.filename()).replace_extension(purefs::extension::tar);
+ std::filesystem::path tarFilePath = (purefs::dir::getBackupOSPath() / path.filename());
mtar_t tarFile;
- LOG_INFO("opening tar file...");
+ 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...");
+ LOG_ERROR("Opening tar file failed, quitting...");
BackupRestore::RemoveBackupDir(path);
return false;
}
@@ 236,38 188,38 @@ bool BackupRestore::PackUserFiles(std::filesystem::path &path)
constexpr size_t streamBufferSize = 64 * 1024;
auto streamBuffer = std::make_unique<char[]>(streamBufferSize);
setvbuf(tarFile.stream, streamBuffer.get(), _IOFBF, streamBufferSize);
- std::error_code e;
+ std::error_code errorCode;
for (auto &direntry : std::filesystem::directory_iterator(path)) {
if (!isValidDirentry(direntry)) {
continue;
}
- LOG_INFO("archiving file ...");
+ 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...");
+ LOG_ERROR("Archiving file failed, cannot open file, quitting...");
mtar_close(&tarFile);
BackupRestore::RemoveBackupDir(path);
return false;
}
- LOG_DEBUG("writting tar header ...");
+ 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");
+ LOG_ERROR("Writing tar header failed");
std::fclose(file);
mtar_close(&tarFile);
BackupRestore::RemoveBackupDir(path);
return false;
}
- uintmax_t filesize = std::filesystem::file_size(direntry.path(), e);
- if (e) {
- LOG_ERROR("failed to get size for file");
+ 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());
BackupRestore::RemoveBackupDir(path);
return false;
}
@@ 282,19 234,19 @@ bool BackupRestore::PackUserFiles(std::filesystem::path &path)
readsize = purefs::buffer::tar_buf;
}
- LOG_DEBUG("reading file ...");
+ LOG_DEBUG("Reading file ...");
if (std::fread(buffer.get(), 1, readsize, file) != readsize) {
- LOG_ERROR("reading file failed, quitting...");
+ LOG_ERROR("Reading file failed, quitting...");
std::fclose(file);
mtar_close(&tarFile);
BackupRestore::RemoveBackupDir(path);
return false;
}
- LOG_DEBUG("writting into backup...");
+ LOG_DEBUG("Writting into backup...");
if (mtar_write_data(&tarFile, buffer.get(), readsize) != MTAR_ESUCCESS) {
- LOG_ERROR("PackUserFiles: writting into backup failed, quitting...");
+ LOG_ERROR("Writting into backup failed, quitting...");
std::fclose(file);
mtar_close(&tarFile);
BackupRestore::RemoveBackupDir(path);
@@ 302,35 254,35 @@ bool BackupRestore::PackUserFiles(std::filesystem::path &path)
}
}
- LOG_INFO("closing file...");
+ LOG_INFO("Closing file...");
if (std::fclose(file) != 0) {
- LOG_ERROR("PackUserFiles: closing file failed, quitting...");
+ LOG_ERROR("Closing file failed, quitting...");
mtar_close(&tarFile);
BackupRestore::RemoveBackupDir(path);
return false;
}
- LOG_INFO("deleting file ...");
+ LOG_INFO("Deleting file ...");
- if (std::remove(direntry.path().c_str()) != 0) {
- LOG_ERROR("PackUserFiles: deleting file failed, quitting...");
+ if (!std::filesystem::remove(direntry.path(), errorCode)) {
+ LOG_ERROR("Deleting file failed, error: %d, quitting...", errorCode.value());
mtar_close(&tarFile);
BackupRestore::RemoveBackupDir(path);
return false;
}
}
- LOG_INFO("finalizing tar file...");
+ LOG_INFO("Finalizing tar file...");
if (mtar_finalize(&tarFile) != MTAR_ESUCCESS) {
- LOG_ERROR("PackUserFiles: finalizing tar file failed, quitting....");
+ LOG_ERROR("Finalizing tar file failed, quitting....");
mtar_close(&tarFile);
BackupRestore::RemoveBackupDir(path);
return false;
}
- LOG_INFO("closing tar file...");
+ LOG_INFO("Closing tar file...");
if (mtar_close(&tarFile) != MTAR_ESUCCESS) {
- LOG_ERROR("PackUserFiles: closing tar file failed, quitting...");
+ LOG_ERROR("Closing tar file failed, quitting...");
BackupRestore::RemoveBackupDir(path);
return false;
}
@@ 338,29 290,42 @@ bool BackupRestore::PackUserFiles(std::filesystem::path &path)
return true;
}
+auto BackupRestore::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 BackupRestore::UnpackBackupFile(const std::filesystem::path &tarFilePath)
{
mtar_t tarFile;
mtar_header_t tarHeader;
- std::error_code e;
+ std::error_code errorCode;
- auto extractDestination = purefs::dir::getTemporaryPath() / tarFilePath.stem();
+ auto extractDestination = TempPathForBackupFile(tarFilePath);
- LOG_INFO("creating temporary directory");
- if (!std::filesystem::is_directory(extractDestination, e)) {
- std::filesystem::create_directory(extractDestination, e);
- if (e) {
- LOG_ERROR("Can't create temporary directory");
+ 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 ...");
+ 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...");
+ LOG_ERROR("Opening tar file failed, quitting...");
return false;
}
@@ 371,16 336,16 @@ bool BackupRestore::UnpackBackupFile(const std::filesystem::path &tarFilePath)
do {
ret = mtar_read_header(&tarFile, &tarHeader);
- LOG_DEBUG("reading tar header name...");
+ LOG_DEBUG("Reading tar header name...");
if ((tarHeader.type == MTAR_TREG) && (ret == MTAR_ESUCCESS)) {
- LOG_DEBUG("extracting tar file ...");
+ 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");
+ LOG_ERROR("Can't open file for writing");
mtar_close(&tarFile);
return false;
}
@@ 402,60 367,138 @@ bool BackupRestore::UnpackBackupFile(const std::filesystem::path &tarFilePath)
}
if (mtar_read_data(&tarFile, buffer.get(), readsize) != MTAR_ESUCCESS) {
- LOG_ERROR("extracting file failed, quitting...");
+ LOG_ERROR("Extracting file failed, quitting...");
mtar_close(&tarFile);
std::fclose(file);
- std::remove(extractedFile.c_str());
+ std::filesystem::remove(extractedFile.c_str());
return false;
}
- if (std::fwrite(buffer.get(), readsize, 1, file) != readsize) {
+ if (std::fwrite(buffer.get(), 1, readsize, file) != readsize) {
LOG_ERROR("writting file failed, quitting...");
mtar_close(&tarFile);
std::fclose(file);
- std::remove(extractedFile.c_str());
+ std::filesystem::remove(extractedFile.c_str());
return false;
}
}
- LOG_INFO("extracting file succeeded");
+ LOG_INFO("Extracting file succeeded");
std::fclose(file);
}
else {
- LOG_DEBUG("found header %d, skipping", tarHeader.type);
+ LOG_DEBUG("Found header %d, skipping", tarHeader.type);
}
ret = mtar_next(&tarFile);
- LOG_DEBUG("reading tar next status %s", mtar_strerror(ret));
+ LOG_DEBUG("Reading tar next status %s", mtar_strerror(ret));
} while (ret == MTAR_ESUCCESS);
- LOG_DEBUG("cleanup");
+ LOG_DEBUG("Cleanup");
mtar_close(&tarFile);
- std::remove(tarFilePath.c_str());
+ errorCode.clear();
+ std::filesystem::remove(tarFilePath.c_str(), errorCode);
+
+ if (errorCode) {
+ LOG_WARN("Can't cleanup temporary dir, error: %d", errorCode.value());
+ }
+
+ return true;
+}
+
+std::string BackupRestore::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();
+}
- if (e) {
- LOG_WARN("can't cleanup temporary dir");
+using namespace sdesktop::endpoints;
+
+std::string BackupRestore::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 BackupRestore::CheckBackupVersion(const std::filesystem::path &extractedBackup)
+{
+ 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;
+ }
+
+ auto const currentVersionTokens = utils::split(currentVersion, ".");
+ auto const backupVersionTokens = utils::split(backupVersion, ".");
+
+ if (currentVersionTokens[0] < backupVersionTokens[0] || // major version
+ currentVersionTokens[1] < backupVersionTokens[1]) { // minor version
+ LOG_ERROR(
+ "Current OS version %s older than backup version %s", (currentVersion).c_str(), (backupVersion).c_str());
+ return false;
}
return true;
}
+bool BackupRestore::CanRestoreFromBackup(const std::filesystem::path &extractedBackup)
+{
+ return CheckBackupVersion(extractedBackup);
+}
+
bool BackupRestore::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...");
+ 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 e;
+ std::error_code errorCode;
- for (auto &direntry : std::filesystem::directory_iterator(tempDir, e)) {
- if (e) {
+ for (auto &direntry : std::filesystem::directory_iterator(tempDir, errorCode)) {
+ if (errorCode) {
LOG_INFO("Can't list contents of temp dir");
return false;
}
@@ 469,17 512,18 @@ bool BackupRestore::ReplaceUserFiles(const std::filesystem::path &path)
continue;
}
- LOG_INFO("restoring backup file ...");
+ LOG_INFO("Restoring backup file ...");
- if (std::filesystem::remove(userDir / direntry.path().filename(), e)) {
- std::filesystem::rename(tempDir / direntry.path().filename(), userDir / direntry.path().filename(), e);
- if (e) {
- LOG_ERROR("can't rename file. Restore failed");
+ 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");
+ LOG_WARN("Can't remove file, error: %d", errorCode.value());
// we should continue, there can be new files in the backup
}
}
@@ 490,14 534,14 @@ bool BackupRestore::ReplaceUserFiles(const std::filesystem::path &path)
json11::Json BackupRestore::GetBackupFiles()
{
auto dirEntryVector = std::vector<std::string>();
- std::error_code e;
- for (const auto &p : std::filesystem::directory_iterator(purefs::dir::getBackupOSPath(), e)) {
- if (e) {
- LOG_ERROR("Can't get directory %s contents", purefs::dir::getBackupOSPath().c_str());
+ 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() && p.path().extension() == purefs::extension::tar) {
- LOG_DEBUG("possible restore file");
+ if (!p.is_directory()) {
+ LOG_DEBUG("Possible restore file");
dirEntryVector.push_back(p.path().filename());
}
}
M module-services/service-desktop/CMakeLists.txt => module-services/service-desktop/CMakeLists.txt +3 -0
@@ 7,6 7,7 @@ add_library(service-desktop STATIC)
target_sources(
service-desktop
PRIVATE
+ BackupRestore.cpp
DesktopEvent.cpp
DeveloperModeMessage.cpp
DesktopMessages.cpp
@@ 20,6 21,7 @@ target_sources(
parser/ParserFSM.hpp
parser/MessageHandler.hpp
PUBLIC
+ include/service-desktop/BackupRestore.hpp
include/service-desktop/Constants.hpp
include/service-desktop/DesktopEvent.hpp
include/service-desktop/DesktopMessages.hpp
@@ 49,6 51,7 @@ target_link_libraries(service-desktop
utils-bootconfig
Microsoft.GSL::GSL
json::json
+ microtar::microtar
$<$<STREQUAL:${PROJECT_TARGET},TARGET_RT1051>:usb_stack::usb_stack>
PUBLIC
module-cellular
M module-services/service-desktop/ServiceDesktop.cpp => module-services/service-desktop/ServiceDesktop.cpp +37 -20
@@ 6,9 6,9 @@
#include <service-desktop/ServiceDesktop.hpp>
#include <service-desktop/WorkerDesktop.hpp>
#include <service-cellular/CellularMessage.hpp>
+#include <service-desktop/BackupRestore.hpp>
#include <endpoints/EndpointFactory.hpp>
#include <endpoints/bluetooth/BluetoothMessagesHandler.hpp>
-#include <endpoints/backup/BackupRestore.hpp>
#include <Common/Query.hpp>
#include <MessageType.hpp>
@@ 115,13 115,19 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
connect(sdesktop::BackupMessage(), [&](sys::Message *msg) {
sdesktop::BackupMessage *backupMessage = dynamic_cast<sdesktop::BackupMessage *>(msg);
if (backupMessage != nullptr) {
-
- backupRestoreStatus.state = OperationState::Running;
- backupRestoreStatus.lastOperationResult =
+ backupRestoreStatus.state = BackupRestore::OperationState::Running;
+ backupRestoreStatus.completionCode =
BackupRestore::BackupUserFiles(this, backupRestoreStatus.backupTempDir);
- backupRestoreStatus.location =
- (purefs::dir::getBackupOSPath() / backupRestoreStatus.task).replace_extension(purefs::extension::tar);
- backupRestoreStatus.state = OperationState::Stopped;
+ backupRestoreStatus.location = backupRestoreStatus.taskId;
+
+ if (backupRestoreStatus.completionCode == BackupRestore::CompletionCode::Success) {
+ LOG_INFO("Backup finished");
+ backupRestoreStatus.state = BackupRestore::OperationState::Finished;
+ }
+ else {
+ LOG_ERROR("Backup failed");
+ backupRestoreStatus.state = BackupRestore::OperationState::Error;
+ }
}
return sys::MessageNone{};
});
@@ 129,16 135,17 @@ sys::ReturnCodes ServiceDesktop::InitHandler()
connect(sdesktop::RestoreMessage(), [&](sys::Message *msg) {
sdesktop::RestoreMessage *restoreMessage = dynamic_cast<sdesktop::RestoreMessage *>(msg);
if (restoreMessage != nullptr) {
- backupRestoreStatus.state = OperationState::Running;
- backupRestoreStatus.lastOperationResult =
- BackupRestore::RestoreUserFiles(this, backupRestoreStatus.location);
+ backupRestoreStatus.state = BackupRestore::OperationState::Running;
+ backupRestoreStatus.completionCode = BackupRestore::RestoreUserFiles(this, backupRestoreStatus.location);
- backupRestoreStatus.state = OperationState::Stopped;
- if (backupRestoreStatus.lastOperationResult == true) {
+ if (backupRestoreStatus.completionCode == BackupRestore::CompletionCode::Success) {
+ LOG_DEBUG("Restore finished");
+ backupRestoreStatus.state = BackupRestore::OperationState::Finished;
sys::SystemManagerCommon::Reboot(this);
}
else {
LOG_ERROR("Restore failed");
+ backupRestoreStatus.state = BackupRestore::OperationState::Error;
}
}
return sys::MessageNone{};
@@ 259,18 266,28 @@ sys::MessagePointer ServiceDesktop::DataReceivedHandler(sys::DataMessage *msg, s
return std::make_shared<sys::ResponseMessage>();
}
+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());
+}
+
void ServiceDesktop::prepareBackupData()
{
- backupRestoreStatus.operation = ServiceDesktop::Operation::Backup;
- backupRestoreStatus.task = std::to_string(static_cast<uint32_t>(std::time(nullptr)));
- backupRestoreStatus.state = OperationState::Stopped;
- backupRestoreStatus.backupTempDir = purefs::dir::getTemporaryPath() / backupRestoreStatus.task;
+ backupRestoreStatus.operation = BackupRestore::Operation::Backup;
+ backupRestoreStatus.taskId = prepareBackupFilename();
+ backupRestoreStatus.state = BackupRestore::OperationState::Stopped;
+ backupRestoreStatus.backupTempDir = purefs::dir::getTemporaryPath() / backupRestoreStatus.taskId;
}
void ServiceDesktop::prepareRestoreData(const std::filesystem::path &restoreLocation)
{
- backupRestoreStatus.operation = ServiceDesktop::Operation::Restore;
- backupRestoreStatus.location = purefs::dir::getBackupOSPath() / restoreLocation;
- backupRestoreStatus.state = OperationState::Stopped;
- backupRestoreStatus.task = restoreLocation.filename();
+ backupRestoreStatus.operation = BackupRestore::Operation::Restore;
+ backupRestoreStatus.taskId = restoreLocation.filename();
+ backupRestoreStatus.state = BackupRestore::OperationState::Stopped;
+ backupRestoreStatus.location = purefs::dir::getBackupOSPath() / backupRestoreStatus.taskId;
}
M module-services/service-desktop/endpoints/CMakeLists.txt => module-services/service-desktop/endpoints/CMakeLists.txt +5 -3
@@ 40,7 40,7 @@ target_sources(
desktop-endpoints-common
PRIVATE
backup/BackupEndpoint.cpp
- backup/BackupRestore.cpp
+ backup/BackupHelper.cpp
bluetooth/BluetoothEndpoint.cpp
bluetooth/BluetoothEventMessages.cpp
bluetooth/BluetoothHelper.cpp
@@ 57,13 57,14 @@ target_sources(
filesystem/FilesystemEndpoint.cpp
nullEndpoint/NullEndpoint.cpp
restore/RestoreEndpoint.cpp
+ restore/RestoreHelper.cpp
security/SecurityEndpoint.cpp
security/SecurityEndpointHelper.cpp
update/UpdateEndpoint.cpp
update/UpdateHelper.cpp
PUBLIC
include/endpoints/backup/BackupEndpoint.hpp
- include/endpoints/backup/BackupRestore.hpp
+ include/endpoints/backup/BackupHelper.hpp
include/endpoints/bluetooth/BluetoothEndpoint.hpp
include/endpoints/bluetooth/BluetoothEventMessages.hpp
include/endpoints/bluetooth/BluetoothHelper.hpp
@@ 80,6 81,7 @@ target_sources(
include/endpoints/filesystem/FilesystemEndpoint.hpp
include/endpoints/nullEndpoint/NullEndpoint.hpp
include/endpoints/restore/RestoreEndpoint.hpp
+ include/endpoints/restore/RestoreHelper.hpp
include/endpoints/security/SecurityEndpoint.hpp
include/endpoints/security/SecurityEndpointHelper.hpp
include/endpoints/update/UpdateEndpoint.hpp
@@ 97,8 99,8 @@ target_link_libraries(
PUBLIC
desktop-endpoint-base
PRIVATE
- microtar
base64::base64
+ microtar::microtar
)
add_library(desktop-endpoints INTERFACE)
M module-services/service-desktop/endpoints/backup/BackupEndpoint.cpp => module-services/service-desktop/endpoints/backup/BackupEndpoint.cpp +11 -59
@@ 2,79 2,31 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <endpoints/backup/BackupEndpoint.hpp>
-
-#include <endpoints/JsonKeyNames.hpp>
#include <endpoints/message/Sender.hpp>
-#include <service-desktop/DesktopMessages.hpp>
-#include <service-desktop/ServiceDesktop.hpp>
-
-#include <json11.hpp>
-#include <purefs/filesystem_paths.hpp>
-
-#include <filesystem>
-#include <memory>
namespace sdesktop::endpoints
{
- using sender::putToSendQueue;
-
auto BackupEndpoint::handle(Context &context) -> void
{
- switch (context.getMethod()) {
- case http::Method::get:
- request(context);
- break;
- case http::Method::post:
- case http::Method::put:
- case http::Method::del:
- context.setResponseStatus(http::Code::BadRequest);
- putToSendQueue(context.createSimpleResponse());
- break;
- }
- }
- auto BackupEndpoint::request(Context &context) -> sys::ReturnCodes
- {
- json11::Json responseBodyJson;
- auto owner = static_cast<ServiceDesktop *>(ownerServicePtr);
-
- if (context.getBody()[json::task].is_string()) {
- if (owner->getBackupRestoreStatus().task == context.getBody()[json::task].string_value()) {
- if (owner->getBackupRestoreStatus().state != ServiceDesktop::OperationState::Running) {
- context.setResponseStatus(http::Code::SeeOther);
- }
+ auto [sent, response] = helper->process(context.getMethod(), context);
- context.setResponseBody(owner->getBackupRestoreStatus());
- }
- else {
- context.setResponseStatus(http::Code::NotFound);
- }
+ if (sent == sent::delayed) {
+ LOG_DEBUG("There is no proper delayed serving mechanism - depend on invisible context caching");
}
- else if (context.getBody()[json::request] == true) {
- if (owner->getBackupRestoreStatus().state == ServiceDesktop::OperationState::Running) {
- // a backup is already running, don't start a second task
+ if (sent == sent::no) {
+ if (not response) {
+ LOG_ERROR("Response not sent & response not created : respond with error");
context.setResponseStatus(http::Code::NotAcceptable);
}
else {
- // initialize new backup information
- owner->prepareBackupData();
-
- // start the backup process in the background
- ownerServicePtr->bus.sendUnicast(std::make_shared<sdesktop::BackupMessage>(),
- service::name::service_desktop);
-
- // return new generated backup info
- context.setResponseBody(owner->getBackupRestoreStatus());
+ context.setResponse(response.value());
}
+
+ sender::putToSendQueue(context.createSimpleResponse());
}
- else {
- // unknown request for backup endpoint
- context.setResponseStatus(http::Code::BadRequest);
+ if (sent == sent::yes and response) {
+ LOG_ERROR("Response set when we already handled response in handler");
}
-
- LOG_DEBUG("responding");
- putToSendQueue(context.createSimpleResponse());
-
- return sys::ReturnCodes::Success;
}
} // namespace sdesktop::endpoints
A module-services/service-desktop/endpoints/backup/BackupHelper.cpp => module-services/service-desktop/endpoints/backup/BackupHelper.cpp +87 -0
@@ 0,0 1,87 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include <endpoints/Context.hpp>
+#include <endpoints/backup/BackupHelper.hpp>
+#include <endpoints/JsonKeyNames.hpp>
+#include <endpoints/message/Sender.hpp>
+#include <service-desktop/DesktopMessages.hpp>
+#include <service-desktop/ServiceDesktop.hpp>
+
+#include <json11.hpp>
+#include <purefs/filesystem_paths.hpp>
+
+#include <filesystem>
+
+namespace sdesktop::endpoints
+{
+ using sender::putToSendQueue;
+
+ auto BackupHelper::processGet(Context &context) -> ProcessResult
+ {
+ return checkState(context);
+ }
+
+ auto BackupHelper::processPost(Context &context) -> ProcessResult
+ {
+ return executeRequest(context);
+ }
+
+ auto BackupHelper::executeRequest(Context &context) -> ProcessResult
+ {
+ auto ownerServicePtr = static_cast<ServiceDesktop *>(owner);
+
+ if (ownerServicePtr->getBackupRestoreStatus().state == BackupRestore::OperationState::Running) {
+ LOG_DEBUG("Backup already running");
+ // a backup is already running, don't start a second task
+ context.setResponseStatus(http::Code::NotAcceptable);
+ }
+ else {
+ LOG_DEBUG("Starting backup");
+ // initialize new backup information
+ ownerServicePtr->prepareBackupData();
+
+ // start the backup process in the background
+ ownerServicePtr->bus.sendUnicast(std::make_shared<sdesktop::BackupMessage>(),
+ service::name::service_desktop);
+
+ // return new generated backup info
+ context.setResponseBody(ownerServicePtr->getBackupRestoreStatus());
+ }
+
+ putToSendQueue(context.createSimpleResponse());
+
+ return {sent::yes, std::nullopt};
+ }
+
+ auto BackupHelper::checkState(Context &context) -> ProcessResult
+ {
+ auto ownerServicePtr = static_cast<ServiceDesktop *>(owner);
+
+ if (context.getBody()[json::taskId].is_string()) {
+ if (ownerServicePtr->getBackupRestoreStatus().taskId == context.getBody()[json::taskId].string_value()) {
+ if (ownerServicePtr->getBackupRestoreStatus().state == BackupRestore::OperationState::Finished) {
+ context.setResponseStatus(http::Code::SeeOther);
+ }
+ else {
+ context.setResponseStatus(http::Code::OK);
+ }
+
+ context.setResponseBody(ownerServicePtr->getBackupRestoreStatus());
+ }
+ else {
+ context.setResponseStatus(http::Code::NotFound);
+ }
+ }
+ else {
+ LOG_DEBUG("Backup task not found");
+ context.setResponseStatus(http::Code::BadRequest);
+ }
+
+ LOG_DEBUG("Responding");
+ putToSendQueue(context.createSimpleResponse());
+
+ return {sent::yes, std::nullopt};
+ }
+
+} // namespace sdesktop::endpoints
M module-services/service-desktop/endpoints/include/endpoints/JsonKeyNames.hpp => module-services/service-desktop/endpoints/include/endpoints/JsonKeyNames.hpp +4 -1
@@ 35,11 35,13 @@ namespace sdesktop::endpoints::json
inline constexpr auto updateHistory = "updateHistory";
inline constexpr auto versionString = "string";
inline constexpr auto fileExists = "fileExists";
+ inline constexpr auto boot = "boot";
inline constexpr auto version = "version";
- inline constexpr auto task = "task";
+ inline constexpr auto taskId = "id";
inline constexpr auto state = "state";
inline constexpr auto success = "success";
inline constexpr auto request = "request";
+ inline constexpr auto restore = "restore";
inline constexpr auto finished = "finished";
inline constexpr auto pending = "pending";
inline constexpr auto location = "location";
@@ 48,6 50,7 @@ namespace sdesktop::endpoints::json
inline constexpr auto caseColour = "caseColour";
inline constexpr auto fileList = "fileList";
inline constexpr auto files = "files";
+ inline constexpr auto backupLocation = "backupLocation";
namespace filesystem
{
M module-services/service-desktop/endpoints/include/endpoints/backup/BackupEndpoint.hpp => module-services/service-desktop/endpoints/include/endpoints/backup/BackupEndpoint.hpp +7 -11
@@ 4,27 4,23 @@
#pragma once
#include <endpoints/Endpoint.hpp>
-
-#include <system/Common.hpp>
-
-#include <string>
-
-namespace sys
-{
- class Service;
-} // namespace sys
+#include "BackupHelper.hpp"
namespace sdesktop::endpoints
{
class BackupEndpoint : public Endpoint
{
public:
- BackupEndpoint(sys::Service *ownerServicePtr) : Endpoint(ownerServicePtr)
+ BackupEndpoint(sys::Service *ownerServicePtr)
+ : Endpoint(ownerServicePtr), helper(std::make_unique<BackupHelper>(ownerServicePtr))
{
debugName = "BackupEndpoint";
}
+
auto handle(Context &context) -> void override;
- auto request(Context &context) -> sys::ReturnCodes;
+
+ private:
+ const std::unique_ptr<BackupHelper> helper;
};
} // namespace sdesktop::endpoints
A module-services/service-desktop/endpoints/include/endpoints/backup/BackupHelper.hpp => module-services/service-desktop/endpoints/include/endpoints/backup/BackupHelper.hpp +23 -0
@@ 0,0 1,23 @@
+// 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 <endpoints/BaseHelper.hpp>
+
+namespace sdesktop::endpoints
+{
+ class BackupHelper : public BaseHelper
+ {
+ public:
+ explicit BackupHelper(sys::Service *p) : BaseHelper(p)
+ {}
+
+ auto processPost(Context &context) -> ProcessResult final;
+ auto processGet(Context &context) -> ProcessResult final;
+
+ private:
+ auto executeRequest(Context &context) -> ProcessResult;
+ auto checkState(Context &context) -> ProcessResult;
+ };
+} // namespace sdesktop::endpoints
D module-services/service-desktop/endpoints/include/endpoints/backup/BackupRestore.hpp => module-services/service-desktop/endpoints/include/endpoints/backup/BackupRestore.hpp +0 -36
@@ 1,36 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 <Service/Service.hpp>
-#include <filesystem>
-#include <json11.hpp>
-
-namespace sys
-{
- class Service;
-} // namespace sys
-
-namespace bkp
-{
- inline constexpr auto backupInfo = "backup.json";
-};
-
-class BackupRestore
-{
- public:
- BackupRestore(){};
- ~BackupRestore(){};
- static bool BackupUserFiles(sys::Service *ownerService, std::filesystem::path &path);
- static bool RestoreUserFiles(sys::Service *ownerService, const std::filesystem::path &path);
- static json11::Json GetBackupFiles();
-
- private:
- static bool RemoveBackupDir(std::filesystem::path &path);
- static bool CreateBackupDir(std::filesystem::path &path);
- static bool PackUserFiles(std::filesystem::path &path);
- static bool UnpackBackupFile(const std::filesystem::path &path);
- static bool ReplaceUserFiles(const std::filesystem::path &path);
- static bool WriteBackupInfo(sys::Service *ownerService, const std::filesystem::path &path);
-};
M module-services/service-desktop/endpoints/include/endpoints/restore/RestoreEndpoint.hpp => module-services/service-desktop/endpoints/include/endpoints/restore/RestoreEndpoint.hpp +6 -2
@@ 3,6 3,7 @@
#pragma once
+#include "RestoreHelper.hpp"
#include <endpoints/Endpoint.hpp>
namespace sdesktop::endpoints
@@ 11,12 12,15 @@ namespace sdesktop::endpoints
class RestoreEndpoint : public Endpoint
{
public:
- explicit RestoreEndpoint(sys::Service *ownerServicePtr) : Endpoint(ownerServicePtr)
+ explicit RestoreEndpoint(sys::Service *ownerServicePtr)
+ : Endpoint(ownerServicePtr), helper(std::make_unique<RestoreHelper>(ownerServicePtr))
{
debugName = "RestoreEndpoint";
}
auto handle(Context &context) -> void override;
- auto request(Context &context) -> sys::ReturnCodes;
+
+ private:
+ const std::unique_ptr<RestoreHelper> helper;
};
} // namespace sdesktop::endpoints
A module-services/service-desktop/endpoints/include/endpoints/restore/RestoreHelper.hpp => module-services/service-desktop/endpoints/include/endpoints/restore/RestoreHelper.hpp +23 -0
@@ 0,0 1,23 @@
+// 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 <endpoints/BaseHelper.hpp>
+
+namespace sdesktop::endpoints
+{
+ class RestoreHelper : public BaseHelper
+ {
+ public:
+ explicit RestoreHelper(sys::Service *p) : BaseHelper(p)
+ {}
+
+ auto processPost(Context &context) -> ProcessResult final;
+ auto processGet(Context &context) -> ProcessResult final;
+
+ private:
+ auto executeRequest(Context &context) -> ProcessResult;
+ auto checkState(Context &context) -> ProcessResult;
+ };
+} // namespace sdesktop::endpoints
M module-services/service-desktop/endpoints/restore/RestoreEndpoint.cpp => module-services/service-desktop/endpoints/restore/RestoreEndpoint.cpp +11 -77
@@ 2,97 2,31 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <endpoints/restore/RestoreEndpoint.hpp>
-
-#include <endpoints/JsonKeyNames.hpp>
#include <endpoints/message/Sender.hpp>
-#include <service-desktop/DesktopMessages.hpp>
-#include <service-desktop/ServiceDesktop.hpp>
-#include <endpoints/backup/BackupRestore.hpp>
-#include <purefs/filesystem_paths.hpp>
-#include <memory>
namespace sdesktop::endpoints
{
- using sender::putToSendQueue;
-
auto RestoreEndpoint::handle(Context &context) -> void
{
- switch (context.getMethod()) {
- case http::Method::get:
- context.setResponseBody(BackupRestore::GetBackupFiles());
- break;
- case http::Method::post:
- request(context);
- break;
- case http::Method::put:
- case http::Method::del:
- context.setResponseStatus(http::Code::BadRequest);
- break;
- }
-
- putToSendQueue(context.createSimpleResponse());
- }
-
- auto RestoreEndpoint::request(Context &context) -> sys::ReturnCodes
- {
- json11::Json responseBodyJson;
- auto owner = static_cast<ServiceDesktop *>(ownerServicePtr);
-
- if (context.getBody()[json::task].is_string()) {
- if (owner->getBackupRestoreStatus().task == context.getBody()[json::task].string_value()) {
- if (owner->getBackupRestoreStatus().state == ServiceDesktop::OperationState::Running) {
- LOG_WARN("looks like a previous job is running can't start a new one");
- context.setResponseStatus(http::Code::SeeOther);
- }
+ auto [sent, response] = helper->process(context.getMethod(), context);
- context.setResponseBody(owner->getBackupRestoreStatus());
- }
- else {
- context.setResponseStatus(http::Code::NotFound);
- }
+ if (sent == sent::delayed) {
+ LOG_DEBUG("There is no proper delayed serving mechanism - depend on invisible context caching");
}
- else if (context.getBody()[json::request] == true) {
- if (owner->getBackupRestoreStatus().state == ServiceDesktop::OperationState::Running) {
- LOG_WARN("looks like a job is running, try again later");
+ if (sent == sent::no) {
+ if (not response) {
+ LOG_ERROR("Response not sent & response not created : respond with error");
context.setResponseStatus(http::Code::NotAcceptable);
}
else {
- const std::filesystem::path location(context.getBody()[json::location].string_value());
- if (location.empty()) {
- LOG_ERROR("no location in request");
- context.setResponseBody(json11::Json::object({{"msg", "no location passed"}}));
- context.setResponseStatus(http::Code::NotAcceptable);
-
- return sys::ReturnCodes::Failure;
- }
- if (!std::filesystem::exists(purefs::dir::getBackupOSPath() / location)) {
- LOG_ERROR("file %s does not exist", (purefs::dir::getBackupOSPath() / location).c_str());
- context.setResponseBody(json11::Json::object({{"msg", "passed location is not readable"}}));
- context.setResponseStatus(http::Code::NotFound);
-
- return sys::ReturnCodes::Failure;
- }
- // initialize new restore information
- owner->prepareRestoreData(location);
-
- // start the request process
- ownerServicePtr->bus.sendUnicast(std::make_shared<sdesktop::RestoreMessage>(),
- service::name::service_desktop);
-
- // return new generated restore info
- context.setResponseBody(owner->getBackupRestoreStatus());
+ context.setResponse(response.value());
}
+
+ sender::putToSendQueue(context.createSimpleResponse());
}
- else {
- // unknown request for backup endpoint
- context.setResponseStatus(http::Code::BadRequest);
+ if (sent == sent::yes and response) {
+ LOG_ERROR("Response set when we already handled response in handler");
}
-
- LOG_DEBUG("responding");
- putToSendQueue(context.createSimpleResponse());
-
- return sys::ReturnCodes::Success;
}
-
} // namespace sdesktop::endpoints
A module-services/service-desktop/endpoints/restore/RestoreHelper.cpp => module-services/service-desktop/endpoints/restore/RestoreHelper.cpp +106 -0
@@ 0,0 1,106 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include <endpoints/Context.hpp>
+#include <endpoints/restore/RestoreHelper.hpp>
+#include <endpoints/JsonKeyNames.hpp>
+#include <endpoints/message/Sender.hpp>
+#include <service-desktop/DesktopMessages.hpp>
+#include <service-desktop/ServiceDesktop.hpp>
+#include <json11.hpp>
+#include <purefs/filesystem_paths.hpp>
+
+#include <filesystem>
+
+namespace sdesktop::endpoints
+{
+ using sender::putToSendQueue;
+
+ auto RestoreHelper::processGet(Context &context) -> ProcessResult
+ {
+ return checkState(context);
+ }
+
+ auto RestoreHelper::processPost(Context &context) -> ProcessResult
+ {
+ return executeRequest(context);
+ }
+
+ auto RestoreHelper::checkState(Context &context) -> ProcessResult
+ {
+ auto ownerService = static_cast<ServiceDesktop *>(owner);
+
+ if (context.getBody()[json::taskId].is_string()) {
+ if (ownerService->getBackupRestoreStatus().taskId == context.getBody()[json::taskId].string_value()) {
+ context.setResponseStatus(http::Code::OK);
+ context.setResponseBody(ownerService->getBackupRestoreStatus());
+ }
+ else {
+ return {sent::no, ResponseContext{.status = http::Code::NotFound}};
+ }
+ }
+ else if (context.getBody()[json::request].is_string()) {
+ const std::string request(context.getBody()[json::request].string_value());
+
+ if (request.compare(json::fileList)) {
+
+ return {sent::no, ResponseContext{.status = http::Code::BadRequest}};
+ }
+ auto filesList = json11::Json::object{{json::files, BackupRestore::GetBackupFiles()}};
+ context.setResponseBody(filesList);
+ }
+
+ LOG_DEBUG("Responding");
+ putToSendQueue(context.createSimpleResponse());
+
+ return {sent::yes, std::nullopt};
+ }
+
+ auto RestoreHelper::executeRequest(Context &context) -> ProcessResult
+ {
+ auto ownerService = static_cast<ServiceDesktop *>(owner);
+
+ if (context.getBody()[json::restore].is_string()) {
+ if (ownerService->getBackupRestoreStatus().state == BackupRestore::OperationState::Running) {
+ LOG_WARN("Restore is running, try again later");
+
+ return {sent::no, ResponseContext{.status = http::Code::NotAcceptable}};
+ }
+ else {
+ const std::filesystem::path restorePoint(context.getBody()[json::restore].string_value());
+ if (restorePoint.empty()) {
+ LOG_ERROR("no restorePoint in request");
+ context.setResponseBody(json11::Json::object({{json::reason, "No restore point passed"}}));
+
+ return {sent::no, ResponseContext{.status = http::Code::NotAcceptable}};
+ }
+ if (!std::filesystem::exists(purefs::dir::getBackupOSPath() / restorePoint)) {
+ LOG_ERROR("Restore point %s does not exist", (restorePoint).c_str());
+ context.setResponseBody(json11::Json::object({{json::reason, "Invalid restore point"}}));
+
+ return {sent::no, ResponseContext{.status = http::Code::NotFound}};
+ }
+ // initialize new restore information
+ ownerService->prepareRestoreData(restorePoint);
+
+ // start the request process
+ ownerService->bus.sendUnicast(std::make_shared<sdesktop::RestoreMessage>(),
+ service::name::service_desktop);
+
+ // return new generated restore info
+ context.setResponseBody(ownerService->getBackupRestoreStatus());
+ }
+ }
+ else {
+ // unknown request for backup endpoint
+
+ return {sent::no, ResponseContext{.status = http::Code::BadRequest}};
+ }
+
+ LOG_DEBUG("Responding");
+ putToSendQueue(context.createSimpleResponse());
+
+ return {sent::yes, std::nullopt};
+ }
+
+} // namespace sdesktop::endpoints
A module-services/service-desktop/include/service-desktop/BackupRestore.hpp => module-services/service-desktop/include/service-desktop/BackupRestore.hpp +120 -0
@@ 0,0 1,120 @@
+// 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 <Service/Service.hpp>
+#include <endpoints/JsonKeyNames.hpp>
+#include <json11.hpp>
+#include <filesystem>
+
+namespace sys
+{
+ class Service;
+} // namespace sys
+
+class BackupRestore
+{
+ public:
+ enum class Operation
+ {
+ Backup,
+ Restore
+ };
+
+ enum class OperationState
+ {
+ Stopped,
+ Running,
+ Finished,
+ Error
+ };
+
+ static const std::string opToString(const OperationState &op)
+ {
+ switch (op) {
+ case OperationState::Stopped:
+ return "stopped";
+ case OperationState::Running:
+ return "running";
+ case OperationState::Finished:
+ return "finished";
+ case OperationState::Error:
+ return "error";
+ }
+ return "unknown";
+ }
+
+ enum class CompletionCode
+ {
+ Success,
+ VersionConflict,
+ DBError,
+ FSError,
+ UnpackError,
+ PackError,
+ CopyError,
+ OtherError
+ };
+
+ static const std::string completionCodeToString(const CompletionCode &code)
+ {
+ 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";
+ }
+ return "unknown";
+ }
+
+ struct OperationStatus
+ {
+ std::filesystem::path backupTempDir;
+ std::filesystem::path location;
+ 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)}};
+
+ if (state == OperationState::Error) {
+ response[sdesktop::endpoints::json::reason] = completionCodeToString(completionCode);
+ }
+
+ return response;
+ }
+ };
+
+ static CompletionCode BackupUserFiles(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 PackUserFiles(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-services/service-desktop/include/service-desktop/ServiceDesktop.hpp => module-services/service-desktop/include/service-desktop/ServiceDesktop.hpp +5 -43
@@ 11,9 11,9 @@
#include "Timers/TimerHandle.hpp"
#include "Constants.hpp"
#include "USBSecurityModel.hpp"
-
+#include <service-desktop/BackupRestore.hpp>
#include <service-db/DBServiceName.hpp>
-#include <endpoints/JsonKeyNames.hpp>
+
#include <bsp/usb/usb.hpp>
#include <filesystem>
@@ 51,46 51,7 @@ class ServiceDesktop : public sys::Service
ServiceDesktop();
~ServiceDesktop() override;
- enum class Operation
- {
- Backup,
- Restore
- };
- enum class OperationState
- {
- Stopped,
- Running,
- Error
- };
-
- static const std::string opToString(const OperationState &op)
- {
- switch (op) {
- case OperationState::Stopped:
- return "stopped";
- case OperationState::Running:
- return "running";
- case OperationState::Error:
- return "error";
- default:
- return "unkown";
- }
- }
- struct BackupRestoreStatus
- {
- std::filesystem::path backupTempDir;
- std::filesystem::path location;
- bool lastOperationResult = false;
- std::string task;
- OperationState state = OperationState::Stopped;
- Operation operation = Operation::Backup;
- json11::Json to_json() const
- {
- return json11::Json::object{{sdesktop::endpoints::json::task, task},
- {sdesktop::endpoints::json::state, opToString(state)},
- {sdesktop::endpoints::json::location, location.string()}};
- }
- } backupRestoreStatus;
+ BackupRestore::OperationStatus backupRestoreStatus;
sys::ReturnCodes InitHandler() override;
sys::ReturnCodes DeinitHandler() override;
@@ 100,9 61,10 @@ class ServiceDesktop : public sys::Service
std::unique_ptr<WorkerDesktop> desktopWorker;
+ std::string prepareBackupFilename();
void prepareBackupData();
void prepareRestoreData(const std::filesystem::path &restoreLocation);
- const BackupRestoreStatus getBackupRestoreStatus()
+ const BackupRestore::OperationStatus getBackupRestoreStatus()
{
return backupRestoreStatus;
}
M module-vfs/paths/include/purefs/filesystem_paths.hpp => module-vfs/paths/include/purefs/filesystem_paths.hpp +1 -0
@@ 28,6 28,7 @@ namespace purefs
{
constexpr inline auto boot_json = ".boot.json";
constexpr inline auto boot_bin = "boot.bin";
+ constexpr inline auto version_json = "version.json";
} // namespace file
namespace extension
M products/PurePhone/services/db/ServiceDB.cpp => products/PurePhone/services/db/ServiceDB.cpp +6 -0
@@ 293,6 293,12 @@ bool ServiceDB::StoreIntoBackup(const std::filesystem::path &backupPath)
return false;
}
+ if (notificationsDB->storeIntoFile(backupPath / std::filesystem::path(notificationsDB->getName()).filename()) ==
+ false) {
+ LOG_ERROR("notificationsDB backup failed");
+ return false;
+ }
+
for (auto &db : databaseAgents) {
if (db.get() && db.get()->getAgentName() == "settingsAgent") {
M products/PurePhone/services/desktop/endpoints/deviceInfo/DeviceInfoEndpoint.cpp => products/PurePhone/services/desktop/endpoints/deviceInfo/DeviceInfoEndpoint.cpp +2 -1
@@ 160,7 160,8 @@ namespace sdesktop::endpoints
{json::currentRTCTime, std::to_string(static_cast<uint32_t>(std::time(nullptr)))},
{json::version, std::string(VERSION)},
{json::serialNumber, getSerialNumber()},
- {json::caseColour, getCaseColour()}}));
+ {json::caseColour, getCaseColour()},
+ {json::backupLocation, purefs::dir::getBackupOSPath().string()}}));
context.setResponseStatus(http::Code::OK);
return true;
M test/get_os_log.py => test/get_os_log.py +4 -3
@@ 3,7 3,7 @@
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
import sys
-import os.path
+import os
import atexit
from harness.harness import Harness
@@ 38,11 38,12 @@ def main():
f'Please pass log storage directory as the parameter: \'python {sys.argv[0]} <log dir>\' ')
raise TestError(Error.OTHER_ERROR)
- log_save_dir = str(sys.argv[1])
+ log_save_dir = os.path.join(os.path.abspath(str(sys.argv[1])), '')
if (not os.path.exists(log_save_dir)):
print(f'Log storage directory {log_save_dir} not found')
- raise TestError(Error.OTHER_ERROR)
+ os.makedirs(log_save_dir, exist_ok = True)
+ # raise TestError(Error.OTHER_ERROR)
harness = Harness.from_detect()
M test/harness => test/harness +1 -1
@@ 1,1 1,1 @@
-Subproject commit 8a33fa2634c6e24b9099e23404ab5104678ac426
+Subproject commit 8aa7fe8ad3276583688f6ae6a7370f7e686e1810
M test/pytest/service-desktop/test_backup.py => test/pytest/service-desktop/test_backup.py +53 -53
@@ 2,67 2,67 @@
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
import pytest
import time
-import os
from harness import log
-from harness.interface.defs import status
-
+from harness.utils import Timeout
+from harness.interface.error import Error, TestError
+from harness.api.filesystem import get_log_file_with_path
+from harness.api.device_info import GetDeviceInfo
+from harness.api.backup import BackupInit, BackupGetState
@pytest.mark.service_desktop_test
@pytest.mark.rt1051
@pytest.mark.usefixtures("phone_unlocked")
@pytest.mark.backup
def test_backup(harness):
- body = { "request": True }
- log.debug("backup testing");
-
- # this requests a backup start
- response = harness.endpoint_request("backup", "get", body)
- assert response["body"] != ""
- assert response["body"]["task"] != ""
- task = response["body"]["task"]
- assert response["status"] == status["OK"]
-
+ log.debug("Requesting backup");
+
+ resp = BackupInit().run(harness)
+
+ assert resp.taskId != ""
+ taskId = resp.taskId
# in response we get a task ID and status 200
- log.debug("backup started, waiting for results")
-
+ log.debug("Backup started, waiting for results")
+
+ time.sleep(1) # wait for the endpoint to be ready
+
# start polling for backup status and wait for it to end
i = 0
- while True:
- i = i+1
- time.sleep(1) # wait for the endpoint to be ready
+ try:
+ with Timeout.limit(seconds=30):
+ while True:
+ i = i+1
+
+ # now that we know the task ID we can poll for it's status
+ # body = { "id": response.taskId }
+ resp = BackupGetState(taskId).run(harness)
+
+ # Backup is still running
+ if resp.state == "running":
+ log.debug("Backup is running...")
+
+ # Backup has stopped, should be OK and finished, status is 303
+ # and redirects to a location as per the documentation
+ elif resp.state == "finished":
+ log.debug("Backup ended, checking results")
+
+ resp = GetDeviceInfo().run(harness)
+
+ bkpPath = resp.diag_info["backupLocation"]
+
+ # Retrieving backup file from phone
+ p = bkpPath + "/" + taskId
+ log.debug(f'Backup file path: {p}')
+ get_log_file_with_path(harness, p, "./")
+ break
+
+ # Backup ended with error
+ elif resp.state == "error":
+ log.debug(f'Backup failed: {resp.reason}')
+ raise TestError(Error.TEST_FAILED)
- # now that we know the task ID we can poll for it's status
- body = { "request": True, "task": task }
- response = harness.endpoint_request("backup", "get", body)
-
- # backup is still running
- if response["body"]["state"] == "running":
- assert response["status"] == 200
- log.debug("backup is running...")
-
- # backup has stopped, should be OK and finished, status is 303
- # and redirects to a location as per the documentation
- if response["body"]["state"] == "stopped":
- log.debug("backup ended, check results")
- assert response["status"] == 303
- assert response["body"]["location"] != ""
-
- # see if the location is a valid backup path, extensions is .tar
- # and starts with a /
- p = response["body"]["location"]
- p_split = os.path.splitext(p)
- assert p_split[1] == ".tar"
- assert p_split[0][0] == "/"
-
- break
-
- # wait for a moment
- log.debug("sleeping for 1 second")
- time.sleep(1)
-
- # max 30 second timeout
- if i > 30:
- # this is bas, and the test fails here
- log.error("backup timeout reached")
- assert False
- break
+ # wait for a moment
+ log.debug("Sleeping 1s")
+ time.sleep(1)
+ except Timeout as e:
+ log.error("Backup timeout reached")
+ raise TestError(Error.OtherError)
M test/pytest/service-desktop/test_device_info.py => test/pytest/service-desktop/test_device_info.py +1 -0
@@ 28,4 28,5 @@ def test_device_info(harness):
assert ret.diag_info["version"] is not None
assert ret.diag_info["serialNumber"] is not None
assert ret.diag_info["caseColour"] is not None
+ assert ret.diag_info["backupLocation"] is not None
M test/pytest/service-desktop/test_restore.py => test/pytest/service-desktop/test_restore.py +117 -29
@@ 2,39 2,127 @@
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
import pytest
import time
-from harness.interface.defs import status
+from harness.interface.error import Error, TestError
+from harness.utils import Timeout
from harness import log
+from harness.harness import Harness
+from harness.api.filesystem import put_file
+from harness.api.device_info import GetDeviceInfo
+from harness.api.restore import RestoreInit, RestoreGetBackupList, RestoreGetState
+def put_backup_file(harness, backupFile: str = ""):
+ if (backupFile == ""):
+ log.info("Enter path to the backup file to upload to phone:")
+ backupFile = input("> ")
-@pytest.mark.service_desktop_test
-@pytest.mark.rt1051
-@pytest.mark.usefixtures("phone_unlocked")
-@pytest.mark.restore
-def test_restore(harness):
+ if (backupFile != ""):
+ backupPath = GetDeviceInfo().run(harness).diag_info["backupLocation"]
+ log.debug(f'Sending backup file {backupFile} to {backupPath}')
+ put_file(harness, backupFile, backupPath)
+ else:
+ log.debug("No backup file name provided")
+ assert False
+
+def list_restore_points(harness):
# this requests the list of available files
- body = { "request": True }
- response = harness.endpoint_request("restore", "get", body)
-
- # should be ok 200
- assert response["status"] == status["OK"]
- log.debug("check if body is an array")
-
+ resp = RestoreGetBackupList().run(harness)
+
+ backupList = resp.files
+
# body should be an array of files
- assert isinstance(response["body"], list) == True
-
+ assert isinstance(backupList, list) == True
+
# chose the first entry for test if array is > 0
- if len(response["body"]) > 0:
- restore_task = response["body"][0]
- log.debug("there are possible backup files on target, test restore %s" % (restore_task))
- # this starts a restore process with a file as parameter
- body = { "request":True, "location": restore_task }
- response = harness.endpoint_request("restore", "post", body)
-
- # we can't really test for results here, as the phone should reset
- # in case the restore process lasts longer we should be able to poll
- # but only on rt1051
- assert response["body"]["location"] != ""
- assert response["body"]["state"] != ""
+ if len(backupList) == 0:
+ return False, []
else:
- log.error("no possisble backup files on phone, run backup first")
- assert False
+ return True, backupList
+
+def select_restore_point(backupList):
+ availableBackups = len(backupList)
+
+ if availableBackups > 1:
+ log.info("List of restore points:")
+ idx = 1
+ for restore_point in backupList:
+ log.info(f'{idx}: {restore_point} ')
+ idx += 1
+
+ log.info('0: Cancel')
+ log.info("Select a restore point:")
+ fileIdx = int(input())
+
+ if (fileIdx == 0):
+ log.debug("Test canceled by user")
+ assert False
+ else:
+ log.info("Selecting first available backup")
+ fileIdx = availableBackups
+
+ return backupList[fileIdx - 1]
+
+def run_restore(harness, backupList):
+ restore_point = select_restore_point(backupList)
+ log.debug(f"Running restore for {restore_point}")
+
+ # this starts a restore process with a file as parameter
+ resp = RestoreInit(restore_point).run(harness)
+
+ log.debug("Restore started, waiting for results")
+ time.sleep(1) # wait for the endpoint to be ready
+
+ # start polling for Restore status and wait for it to end
+ i = 0
+ try:
+ with Timeout.limit(seconds=30):
+ while True:
+ i = i+1
+
+ # now that we know the task ID we can poll for it's status
+ resp = RestoreGetState(restore_point).run(harness)
+
+ # Restore is still running
+ if resp.state == "running":
+ log.debug("Restore is running...")
+
+ # Restore has stopped, should be OK and finished, status is 303
+ # and redirects to a location as per the documentation
+ elif resp.state == "finished":
+ log.debug("Restore ended, check results")
+ break
+
+ # Restore has stopped, should be OK and finished, status is 303
+ # and redirects to a location as per the documentation
+ elif resp.state == "error":
+ log.debug(f'Restore failed: {resp.reason}')
+ raise TestError(Error.TEST_FAILED)
+
+ # wait for a moment
+ log.debug("Sleeping 1s")
+ time.sleep(1)
+ except Timeout as e:
+ log.error("Restore timeout reached")
+
+@pytest.mark.service_desktop_test
+@pytest.mark.rt1051
+@pytest.mark.usefixtures("phone_unlocked")
+@pytest.mark.restore
+def test_restore(harness: Harness):
+
+ try:
+ ret, list = list_restore_points(harness)
+ if ret == False:
+ log.error("No backup files on phone, run backup first")
+ # put_backup_file(harness)
+ assert False
+
+ ret, list = list_restore_points(harness)
+ if ret == True:
+ run_restore(harness, list)
+ else:
+ log.error("No backup files on phone")
+ raise TestError(Error.OTHER_ERROR)
+
+ except TestError as err:
+ log.debug(err)
+ log.info("Restore ended, phone is restarting")
M third-party/microtar/CMakeLists.txt => third-party/microtar/CMakeLists.txt +3 -0
@@ 1,7 1,10 @@
add_library(microtar)
+add_library(microtar::microtar ALIAS microtar)
target_sources(microtar
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/src/microtar.cpp
+
+ PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/src/microtar.hpp
)
M tools/generate_image.sh => tools/generate_image.sh +2 -2
@@ 4,7 4,7 @@
usage() {
cat << ==usage
-Usage: $(basename $0) image_path image_partitions build_dir version.json_file [boot.bin_file] [updater.bin_file]
+Usage: $(basename $0) image_path image_partitions build_dir [version.json_file] [boot.bin_file] [updater.bin_file]
image_path - Destination image path name e.g., PurePhone.img
image_partitions - Path to image_partitions.map product-specific file
sysroot - product's system root e.g., build-rt1051-RelWithDebInfo/sysroot
@@ 14,7 14,7 @@ Usage: $(basename $0) image_path image_partitions build_dir version.json_file [b
==usage
}
-if [[ ( $# -ne 4 ) && ( $# -ne 6 ) ]]; then
+if [[ ( $# -ne 3 ) && ( $# -ne 6 ) ]]; then
echo "Error! Invalid argument count"
usage
exit -1