From 4e1edd2a33d196dbfab4350d457cbe41231ec693 Mon Sep 17 00:00:00 2001 From: Peter Sykora Date: Sat, 31 Mar 2018 14:51:05 +0200 Subject: [PATCH] [impl] Allow to postpone file deletion if file is dll exe file that is still in use --- api/ModuleManager.h | 4 ++- libLicenseClient.vcxproj | 2 ++ libLicenseClient.vcxproj.filters | 6 +++++ src/ModuleManager.cpp | 30 ++++++++++++--------- src/SafeFileUtils.cpp | 46 ++++++++++++++++++++++++++++++++ src/SafeFileUtils.h | 21 +++++++++++++++ 6 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 src/SafeFileUtils.cpp create mode 100644 src/SafeFileUtils.h diff --git a/api/ModuleManager.h b/api/ModuleManager.h index b45e3e6..cb66d0a 100644 --- a/api/ModuleManager.h +++ b/api/ModuleManager.h @@ -15,10 +15,11 @@ class ModuleManager { public: - ModuleManager(const std::string& appBaseDir, IModuleDatabase& db, IDownloader& downloader) + ModuleManager(const std::string& appBaseDir, IModuleDatabase& db, IDownloader& downloader, const std::string& tempDir) : m_appBaseDir(appBaseDir) , m_db(db) , m_downloader(downloader) + , m_tempDir(tempDir) {} public: @@ -35,4 +36,5 @@ private: std::string m_appBaseDir; IModuleDatabase & m_db; IDownloader& m_downloader; + std::string m_tempDir; }; diff --git a/libLicenseClient.vcxproj b/libLicenseClient.vcxproj index b7921bd..dce4c93 100644 --- a/libLicenseClient.vcxproj +++ b/libLicenseClient.vcxproj @@ -177,6 +177,7 @@ + @@ -188,6 +189,7 @@ + diff --git a/libLicenseClient.vcxproj.filters b/libLicenseClient.vcxproj.filters index 18bc2d3..6fe4622 100644 --- a/libLicenseClient.vcxproj.filters +++ b/libLicenseClient.vcxproj.filters @@ -60,6 +60,9 @@ api + + src + @@ -95,5 +98,8 @@ src + + src + \ No newline at end of file diff --git a/src/ModuleManager.cpp b/src/ModuleManager.cpp index a6a7f00..ab78dae 100644 --- a/src/ModuleManager.cpp +++ b/src/ModuleManager.cpp @@ -1,6 +1,7 @@ #include "ModuleManager.h" #include "HashUtils.h" +#include "SafeFileUtils.h" #include #include @@ -8,6 +9,8 @@ #include #include +namespace fs = boost::filesystem; + void ModuleManager::applyUpdate(const std::string & moduleId, const ModuleUpdate & update) { auto module = m_db.findModule(moduleId).value_or(Module{ moduleId, -1 , {} }); @@ -30,10 +33,10 @@ void ModuleManager::applyUpdate(Module & module, const ModuleUpdate & update) if (!(update.flag & static_cast(ModuleUpdateFlags::incremental))) { // We should remove old files first before extracting new ones - boost::filesystem::path basePath(m_appBaseDir); - std::for_each(module.filePaths.begin(), module.filePaths.end(), [&basePath](const auto& filePath) + fs::path basePath(m_appBaseDir); + std::for_each(module.filePaths.begin(), module.filePaths.end(), [&basePath, this](const auto& filePath) { - boost::filesystem::remove(basePath / filePath); + safeRemoveFile(basePath / filePath, m_tempDir); }); module.filePaths.clear(); } @@ -45,7 +48,7 @@ void ModuleManager::applyUpdate(Module & module, const ModuleUpdate & update) module.filePaths.insert(filePaths.begin(), filePaths.end()); } -boost::filesystem::path ModuleManager::retrieveUpdate(const std::string & updateUrl) +fs::path ModuleManager::retrieveUpdate(const std::string & updateUrl) { return m_downloader.download(updateUrl); } @@ -61,21 +64,21 @@ std::string zipErrorToStr(int err) return buf; } -inline std::set ModuleManager::extractUpdate(const boost::filesystem::path& archivePath, const std::string & targetPath) +std::set ModuleManager::extractUpdate(const fs::path& archivePath, const std::string & targetPath) { struct zip_file *zf = nullptr; struct zip_stat sb; int err; - boost::filesystem::path basePath(m_appBaseDir); + fs::path basePath(m_appBaseDir); basePath /= targetPath; - boost::filesystem::path basePathRel(targetPath); + fs::path basePathRel(targetPath); - if (!boost::filesystem::exists(basePath)) + if (!fs::exists(basePath)) { - boost::filesystem::create_directories(basePath); + fs::create_directories(basePath); } - if (!boost::filesystem::is_directory(basePath)) + if (!fs::is_directory(basePath)) { throw std::runtime_error("Target path is not a directory"); } @@ -103,7 +106,7 @@ inline std::set ModuleManager::extractUpdate(const boost::filesyste auto outPathRel = basePathRel / sb.name; if (sb.name[len - 1] == '/') { - if (!boost::filesystem::is_directory(outPath) && !boost::filesystem::create_directory(outPath)) + if (!fs::is_directory(outPath) && !fs::create_directory(outPath)) { throw std::runtime_error("Directory can't be extracted"); } @@ -116,8 +119,9 @@ inline std::set ModuleManager::extractUpdate(const boost::filesyste throw std::runtime_error("Can't open file from the archive"); } + auto file = safeCreateFile(outPath, m_tempDir); { - std::ofstream os(outPath.string(), std::ofstream::binary); + std::ofstream os = std::ofstream(file.path().native(), std::ofstream::binary); uint64_t sum = 0; while (sum != sb.size) { @@ -130,7 +134,7 @@ inline std::set ModuleManager::extractUpdate(const boost::filesyste sum += len; } } - boost::filesystem::last_write_time(outPath, sb.mtime); + fs::last_write_time(file.path(), sb.mtime); extractedFiles.insert(outPathRel.string()); zip_fclose(zf); diff --git a/src/SafeFileUtils.cpp b/src/SafeFileUtils.cpp new file mode 100644 index 0000000..28c5bf1 --- /dev/null +++ b/src/SafeFileUtils.cpp @@ -0,0 +1,46 @@ +#include "SafeFileUtils.h" +#include + +namespace fs = boost::filesystem; + +void safeRemoveFile(const fs::path& p, const fs::path& tempFolder) +{ + try + { + bool result = fs::remove(p); + } + catch (fs::filesystem_error& e) + { + if (e.code().category() == boost::system::system_category() + && e.code().value() == ERROR_ACCESS_DENIED) + { + auto cleanupFolder = tempFolder; + + if (!fs::exists(cleanupFolder)) + { + fs::create_directories(cleanupFolder); + } + + if (!fs::is_directory(cleanupFolder)) + { + throw std::runtime_error(".upd/.remove folder is not directory"); + } + + auto fileName = fs::unique_path(); + fs::rename(p, cleanupFolder / fileName); + } + else + { + throw; + } + } +} + +SafeFile safeCreateFile(const fs::path & p, const fs::path& tempFolder) +{ + if (fs::exists(p)) + { + safeRemoveFile(p, tempFolder); + } + return SafeFile(p); +} diff --git a/src/SafeFileUtils.h b/src/SafeFileUtils.h new file mode 100644 index 0000000..a350f85 --- /dev/null +++ b/src/SafeFileUtils.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +void safeRemoveFile(const boost::filesystem::path& p, const boost::filesystem::path& tempFolder); + +class SafeFile +{ +public: + SafeFile(const boost::filesystem::path& p) + : m_path(p) + {} + +public: + const boost::filesystem::path& path() { return m_path; } + +private: + boost::filesystem::path m_path; +}; + +SafeFile safeCreateFile(const boost::filesystem::path& p, const boost::filesystem::path& tempFolder);