1
0
mirror of synced 2024-12-01 02:37:18 +01:00

impr: Don't memory map files, never keep a write handle open for long

Closes #592
This commit is contained in:
WerWolv 2023-02-17 10:26:09 +01:00
parent e48761b5c0
commit bf8089dc7e
5 changed files with 72 additions and 119 deletions

View File

@ -8,12 +8,20 @@
#include <hex/helpers/fs.hpp> #include <hex/helpers/fs.hpp>
#include <sys/stat.h>
#if defined(OS_MACOS) #if defined(OS_MACOS)
#include <unistd.h>
#include <sys/fcntl.h>
#define off64_t off_t #define off64_t off_t
#define fopen64 fopen #define fopen64 fopen
#define fseeko64 fseek #define fseeko64 fseek
#define ftello64 ftell #define ftello64 ftell
#define ftruncate64 ftruncate #define ftruncate64 ftruncate
#elif defined(OS_LINUX)
#include <unistd.h>
#include <fcntl.h>
#endif #endif
namespace hex::fs { namespace hex::fs {
@ -65,6 +73,8 @@ namespace hex::fs {
void disableBuffering(); void disableBuffering();
std::optional<struct stat> getFileInfo();
private: private:
FILE *m_file; FILE *m_file;
std::fs::path m_path; std::fs::path m_path;

View File

@ -168,4 +168,18 @@ namespace hex::fs {
std::setvbuf(this->m_file, nullptr, _IONBF, 0); std::setvbuf(this->m_file, nullptr, _IONBF, 0);
} }
std::optional<struct stat> File::getFileInfo() {
struct stat fileInfo = { };
#if defined(OS_WINDOWS)
if (wstat(this->m_path.c_str(), &fileInfo) != 0)
return std::nullopt;
#else
if (stat(hex::toUTF8String(this->m_path).c_str(), &fileInfo) != 0)
return std::nullopt;
#endif
return fileInfo;
}
} }

View File

@ -114,6 +114,10 @@ namespace hex::prv {
for (auto &[patchAddress, patch] : getPatches()) { for (auto &[patchAddress, patch] : getPatches()) {
this->writeRaw(patchAddress - this->getBaseAddress(), &patch, 1); this->writeRaw(patchAddress - this->getBaseAddress(), &patch, 1);
} }
if (!this->isWritable())
return;
this->markDirty(); this->markDirty();
this->m_patches.emplace_back(); this->m_patches.emplace_back();

View File

@ -1,23 +1,10 @@
#pragma once #pragma once
#include <hex/providers/provider.hpp> #include <hex/providers/provider.hpp>
#include <hex/helpers/file.hpp>
#include <string_view> #include <string_view>
#include <sys/stat.h>
#if defined(OS_WINDOWS)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#elif defined(OS_MACOS)
#include <sys/mman.h>
#include <unistd.h>
#include <sys/fcntl.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#endif
namespace hex::plugin::builtin { namespace hex::plugin::builtin {
@ -69,14 +56,17 @@ namespace hex::plugin::builtin {
protected: protected:
std::fs::path m_path; std::fs::path m_path;
void *m_mappedFile = nullptr; fs::File m_file;
size_t m_fileSize = 0; size_t m_fileSize = 0;
struct stat m_fileStats = { }; std::optional<struct stat> m_fileStats;
bool m_fileStatsValid = false;
bool m_emptyFile = false;
std::mutex m_mutex;
bool m_readable = false, m_writable = false; bool m_readable = false, m_writable = false;
bool m_openReadOnly = true;
u64 m_bufferStartAddress;
std::vector<u8> m_buffer;
}; };
} }

View File

@ -15,7 +15,7 @@
namespace hex::plugin::builtin { namespace hex::plugin::builtin {
bool FileProvider::isAvailable() const { bool FileProvider::isAvailable() const {
return this->m_mappedFile != nullptr; return this->m_file.isValid();
} }
bool FileProvider::isReadable() const { bool FileProvider::isReadable() const {
@ -60,14 +60,29 @@ namespace hex::plugin::builtin {
if (offset > (this->getActualSize() - size) || buffer == nullptr || size == 0) if (offset > (this->getActualSize() - size) || buffer == nullptr || size == 0)
return; return;
std::memcpy(buffer, reinterpret_cast<u8 *>(this->m_mappedFile) + offset, size); std::scoped_lock lock(this->m_mutex);
if (offset < this->m_bufferStartAddress || offset + size > this->m_bufferStartAddress + this->m_buffer.size() || this->m_buffer.size() < size) {
this->m_buffer.resize(std::min<size_t>(std::max<size_t>(0x100, size), this->getActualSize() - offset));
this->m_file.seek(offset);
this->m_file.readBuffer(this->m_buffer.data(), this->m_buffer.size());
this->m_bufferStartAddress = offset;
}
std::memcpy(buffer, this->m_buffer.data() + (offset - this->m_bufferStartAddress), std::min<size_t>(size, this->m_buffer.size()));
} }
void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) { void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0) if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0)
return; return;
std::memcpy(reinterpret_cast<u8 *>(this->m_mappedFile) + offset, buffer, size); std::scoped_lock lock(this->m_mutex);
fs::File writeFile(this->m_path, fs::File::Mode::Write);
if (!writeFile.isValid())
return;
writeFile.seek(offset);
writeFile.write(reinterpret_cast<const u8*>(buffer), size);
this->m_buffer.clear();
} }
void FileProvider::save() { void FileProvider::save() {
@ -100,7 +115,6 @@ namespace hex::plugin::builtin {
fs::File file(this->m_path, fs::File::Mode::Write); fs::File file(this->m_path, fs::File::Mode::Write);
file.setSize(newSize); file.setSize(newSize);
this->m_fileSize = file.getSize();
} }
(void)this->open(); (void)this->open();
@ -164,10 +178,10 @@ namespace hex::plugin::builtin {
result.emplace_back("hex.builtin.provider.file.path"_lang, hex::toUTF8String(this->m_path)); result.emplace_back("hex.builtin.provider.file.path"_lang, hex::toUTF8String(this->m_path));
result.emplace_back("hex.builtin.provider.file.size"_lang, hex::toByteString(this->getActualSize())); result.emplace_back("hex.builtin.provider.file.size"_lang, hex::toByteString(this->getActualSize()));
if (this->m_fileStatsValid) { if (this->m_fileStats.has_value()) {
result.emplace_back("hex.builtin.provider.file.creation"_lang, hex::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(this->m_fileStats.st_ctime))); result.emplace_back("hex.builtin.provider.file.creation"_lang, hex::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(this->m_fileStats->st_ctime)));
result.emplace_back("hex.builtin.provider.file.access"_lang, hex::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(this->m_fileStats.st_atime))); result.emplace_back("hex.builtin.provider.file.access"_lang, hex::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(this->m_fileStats->st_atime)));
result.emplace_back("hex.builtin.provider.file.modification"_lang, hex::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(this->m_fileStats.st_mtime))); result.emplace_back("hex.builtin.provider.file.modification"_lang, hex::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(this->m_fileStats->st_mtime)));
} }
return result; return result;
@ -181,13 +195,13 @@ namespace hex::plugin::builtin {
else if (category == "file_extension") else if (category == "file_extension")
return hex::toUTF8String(this->m_path.extension()); return hex::toUTF8String(this->m_path.extension());
else if (category == "creation_time") else if (category == "creation_time")
return this->m_fileStats.st_ctime; return this->m_fileStats->st_ctime;
else if (category == "access_time") else if (category == "access_time")
return this->m_fileStats.st_atime; return this->m_fileStats->st_atime;
else if (category == "modification_time") else if (category == "modification_time")
return this->m_fileStats.st_mtime; return this->m_fileStats->st_mtime;
else if (category == "permissions") else if (category == "permissions")
return this->m_fileStats.st_mode & 0777; return this->m_fileStats->st_mode & 0777;
else else
return Provider::queryInformation(category, argument); return Provider::queryInformation(category, argument);
} }
@ -206,101 +220,22 @@ namespace hex::plugin::builtin {
this->m_readable = true; this->m_readable = true;
this->m_writable = true; this->m_writable = true;
#if defined(OS_WINDOWS) fs::File file(this->m_path, fs::File::Mode::Read);
const auto &path = this->m_path.native(); if (!file.isValid()) {
this->m_writable = false;
this->m_readable = false;
return false;
}
this->m_fileStatsValid = wstat(path.c_str(), &this->m_fileStats) == 0; this->m_fileStats = file.getFileInfo();
this->m_fileSize = file.getSize();
LARGE_INTEGER fileSize = {}; this->m_file = std::move(file);
auto file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
GetFileSizeEx(file, &fileSize);
this->m_fileSize = fileSize.QuadPart;
CloseHandle(file);
file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (file == nullptr || file == INVALID_HANDLE_VALUE) {
file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
this->m_writable = false;
}
if (file == nullptr || file == INVALID_HANDLE_VALUE) {
return false;
}
ON_SCOPE_EXIT { CloseHandle(file); };
if (this->m_fileSize > 0) {
HANDLE mapping = CreateFileMapping(file, nullptr, PAGE_READWRITE, 0, 0, nullptr);
ON_SCOPE_EXIT { CloseHandle(mapping); };
if (mapping == nullptr || mapping == INVALID_HANDLE_VALUE) {
mapping = CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (mapping == nullptr || mapping == INVALID_HANDLE_VALUE)
return false;
}
this->m_mappedFile = MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
this->m_mappedFile = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
this->m_readable = false;
return false;
}
}
} else if (!this->m_emptyFile) {
this->m_emptyFile = true;
this->resize(1);
} else {
return false;
}
#else
const auto &path = this->m_path.native();
this->m_fileStatsValid = stat(path.c_str(), &this->m_fileStats) == 0;
int mmapprot = PROT_READ | PROT_WRITE;
auto file = ::open(path.c_str(), O_RDWR);
if (file == -1) {
file = ::open(path.c_str(), O_RDONLY);
this->m_writable = false;
mmapprot &= ~(PROT_WRITE);
}
if (file == -1) {
this->m_readable = false;
return false;
}
ON_SCOPE_EXIT { ::close(file); };
this->m_fileSize = this->m_fileStats.st_size;
this->m_mappedFile = ::mmap(nullptr, this->m_fileSize, mmapprot, MAP_SHARED, file, 0);
if (this->m_mappedFile == MAP_FAILED)
return false;
#endif
return true; return true;
} }
void FileProvider::close() { void FileProvider::close() {
#if defined(OS_WINDOWS)
if (this->m_mappedFile != nullptr)
::UnmapViewOfFile(this->m_mappedFile);
#else
if (this->m_mappedFile != nullptr)
::munmap(this->m_mappedFile, this->m_fileSize);
#endif
} }
void FileProvider::loadSettings(const nlohmann::json &settings) { void FileProvider::loadSettings(const nlohmann::json &settings) {