[impl] Allow to postpone file deletion if file is dll exe file that is still in use

This commit is contained in:
Peter Sykora 2018-03-31 14:51:05 +02:00
parent d142df6366
commit 4e1edd2a33
6 changed files with 95 additions and 14 deletions

View File

@ -15,10 +15,11 @@
class ModuleManager class ModuleManager
{ {
public: 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_appBaseDir(appBaseDir)
, m_db(db) , m_db(db)
, m_downloader(downloader) , m_downloader(downloader)
, m_tempDir(tempDir)
{} {}
public: public:
@ -35,4 +36,5 @@ private:
std::string m_appBaseDir; std::string m_appBaseDir;
IModuleDatabase & m_db; IModuleDatabase & m_db;
IDownloader& m_downloader; IDownloader& m_downloader;
std::string m_tempDir;
}; };

View File

@ -177,6 +177,7 @@
<ClInclude Include="api\SystemParamsProvider.h" /> <ClInclude Include="api\SystemParamsProvider.h" />
<ClInclude Include="api\SystemParamsProvider_win.h" /> <ClInclude Include="api\SystemParamsProvider_win.h" />
<ClInclude Include="src\HashUtils.h" /> <ClInclude Include="src\HashUtils.h" />
<ClInclude Include="src\SafeFileUtils.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="src\CachedDownloader.cpp" /> <ClCompile Include="src\CachedDownloader.cpp" />
@ -188,6 +189,7 @@
<ClCompile Include="src\LicenseClient.cpp" /> <ClCompile Include="src\LicenseClient.cpp" />
<ClCompile Include="src\ModuleManager.cpp" /> <ClCompile Include="src\ModuleManager.cpp" />
<ClCompile Include="src\ModuleUpdate.cpp" /> <ClCompile Include="src\ModuleUpdate.cpp" />
<ClCompile Include="src\SafeFileUtils.cpp" />
<ClCompile Include="src\SystemParams.cpp" /> <ClCompile Include="src\SystemParams.cpp" />
<ClCompile Include="src\SystemParamsProvider_win.cpp" /> <ClCompile Include="src\SystemParamsProvider_win.cpp" />
</ItemGroup> </ItemGroup>

View File

@ -60,6 +60,9 @@
<ClInclude Include="api\IDownloader.h"> <ClInclude Include="api\IDownloader.h">
<Filter>api</Filter> <Filter>api</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\SafeFileUtils.h">
<Filter>src</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="src\HashUtils.cpp"> <ClCompile Include="src\HashUtils.cpp">
@ -95,5 +98,8 @@
<ClCompile Include="src\CachedDownloader.cpp"> <ClCompile Include="src\CachedDownloader.cpp">
<Filter>src</Filter> <Filter>src</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\SafeFileUtils.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,6 +1,7 @@
#include "ModuleManager.h" #include "ModuleManager.h"
#include "HashUtils.h" #include "HashUtils.h"
#include "SafeFileUtils.h"
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
@ -8,6 +9,8 @@
#include <algorithm> #include <algorithm>
#include <cstdio> #include <cstdio>
namespace fs = boost::filesystem;
void ModuleManager::applyUpdate(const std::string & moduleId, const ModuleUpdate & update) void ModuleManager::applyUpdate(const std::string & moduleId, const ModuleUpdate & update)
{ {
auto module = m_db.findModule(moduleId).value_or(Module{ moduleId, -1 , {} }); 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<uint32_t>(ModuleUpdateFlags::incremental))) if (!(update.flag & static_cast<uint32_t>(ModuleUpdateFlags::incremental)))
{ {
// We should remove old files first before extracting new ones // We should remove old files first before extracting new ones
boost::filesystem::path basePath(m_appBaseDir); fs::path basePath(m_appBaseDir);
std::for_each(module.filePaths.begin(), module.filePaths.end(), [&basePath](const auto& filePath) 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(); module.filePaths.clear();
} }
@ -45,7 +48,7 @@ void ModuleManager::applyUpdate(Module & module, const ModuleUpdate & update)
module.filePaths.insert(filePaths.begin(), filePaths.end()); 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); return m_downloader.download(updateUrl);
} }
@ -61,21 +64,21 @@ std::string zipErrorToStr(int err)
return buf; return buf;
} }
inline std::set<std::string> ModuleManager::extractUpdate(const boost::filesystem::path& archivePath, const std::string & targetPath) std::set<std::string> ModuleManager::extractUpdate(const fs::path& archivePath, const std::string & targetPath)
{ {
struct zip_file *zf = nullptr; struct zip_file *zf = nullptr;
struct zip_stat sb; struct zip_stat sb;
int err; int err;
boost::filesystem::path basePath(m_appBaseDir); fs::path basePath(m_appBaseDir);
basePath /= targetPath; 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"); throw std::runtime_error("Target path is not a directory");
} }
@ -103,7 +106,7 @@ inline std::set<std::string> ModuleManager::extractUpdate(const boost::filesyste
auto outPathRel = basePathRel / sb.name; auto outPathRel = basePathRel / sb.name;
if (sb.name[len - 1] == '/') 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"); throw std::runtime_error("Directory can't be extracted");
} }
@ -116,8 +119,9 @@ inline std::set<std::string> ModuleManager::extractUpdate(const boost::filesyste
throw std::runtime_error("Can't open file from the archive"); 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; uint64_t sum = 0;
while (sum != sb.size) { while (sum != sb.size) {
@ -130,7 +134,7 @@ inline std::set<std::string> ModuleManager::extractUpdate(const boost::filesyste
sum += len; sum += len;
} }
} }
boost::filesystem::last_write_time(outPath, sb.mtime); fs::last_write_time(file.path(), sb.mtime);
extractedFiles.insert(outPathRel.string()); extractedFiles.insert(outPathRel.string());
zip_fclose(zf); zip_fclose(zf);

46
src/SafeFileUtils.cpp Normal file
View File

@ -0,0 +1,46 @@
#include "SafeFileUtils.h"
#include <winerror.h>
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);
}

21
src/SafeFileUtils.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <boost/filesystem.hpp>
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);