// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include #include #include #include #include #include #include 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_DEBUG("Sync package preparation started"); if (!Sync::RemoveSyncDir(path)) { return CompletionCode::FSError; } if (!Sync::CreateSyncDir(path)) { return CompletionCode::FSError; } if (!DBServiceAPI::DBPrepareSyncPackage(ownerService, path)) { LOG_ERROR("Sync package preparation failed, quiting"); 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; } LOG_DEBUG("Sync package preparation finished"); return CompletionCode::Success; } bool Sync::RemoveSyncDir(const std::filesystem::path &path) { /* prepare directories */ if (std::filesystem::is_directory(path)) { LOG_DEBUG("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::getTemporaryPath() / sdesktop::paths::syncFilename); 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(purefs::buffer::tar_buf); auto tarStreamBuffer = std::make_unique(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_DEBUG("Archiving file "); LOG_DEBUG("Writing tar header"); if (mtar_write_file_header(&tarFile, direntry.path().filename().c_str(), static_cast(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; } fileStream.read(fileStreamBuffer.get(), readSize); if (mtar_write_data(&tarFile, fileStreamBuffer.get(), readSize) != MTAR_ESUCCESS) { LOG_ERROR("Writing into sync package failed, quitting"); return false; } } } LOG_DEBUG("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; }